diff --git a/Mage.Tests/src/test/java/org/mage/test/cards/control/PlayerUnderControlTest.java b/Mage.Tests/src/test/java/org/mage/test/cards/control/PlayerUnderControlTest.java new file mode 100644 index 00000000000..995f5895180 --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/cards/control/PlayerUnderControlTest.java @@ -0,0 +1,83 @@ +package org.mage.test.cards.control; + +import mage.constants.PhaseStep; +import mage.constants.Zone; +import mage.view.GameView; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestPlayerBase; + +/** + * Test Framework do not support under control commands, so check only game related info and data + * + * @author JayDi85 + */ +public class PlayerUnderControlTest extends CardTestPlayerBase { + + @Test + public void test_ClientSideDataMustBeHidden() { + // possible bug: after control ends - player still able to view opponent's hands + + // When you cast Emrakul, you gain control of target opponent during that player's next turn. + // After that turn, that player takes an extra turn. + addCard(Zone.HAND, playerA, "Emrakul, the Promised End"); // {13} + addCard(Zone.BATTLEFIELD, playerA, "Forest", 13); + // + addCard(Zone.HAND, playerB, "Lightning Bolt"); + addCard(Zone.BATTLEFIELD, playerB, "Mountain", 1); + + // prepare control effect + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Emrakul, the Promised End"); + addTarget(playerA, playerB); + waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN); + + checkUnderControl("turn 1 - A, B normal", 1, PhaseStep.PRECOMBAT_MAIN, false); + checkUnderControl("turn 2 - B under A", 2, PhaseStep.PRECOMBAT_MAIN, true); + checkUnderControl("turn 3 - A, B normal", 3, PhaseStep.PRECOMBAT_MAIN, false); + + setStrictChooseMode(true); + setStopAt(3, PhaseStep.END_TURN); + execute(); + } + + private void checkUnderControl(String info, int turnNum, PhaseStep step, boolean mustHaveControl) { + runCode(info, turnNum, step, playerA, (info1, player, game) -> { + GameView viewA = getGameView(playerA); + GameView viewB = getGameView(playerB); + if (mustHaveControl) { + Assert.assertTrue(info, playerA.isGameUnderControl()); + Assert.assertFalse(info, playerB.isGameUnderControl()); + + Assert.assertTrue(info, playerA.getPlayersUnderYourControl().contains(playerB.getId())); + Assert.assertTrue(info, playerB.getPlayersUnderYourControl().isEmpty()); + + Assert.assertTrue(info, playerA.getTurnControllers().isEmpty()); + Assert.assertTrue(info, playerB.getTurnControllers().contains(playerA.getId())); + + Assert.assertEquals(info, playerA.getTurnControlledBy(), playerA.getId()); + Assert.assertEquals(info, playerB.getTurnControlledBy(), playerA.getId()); + + // client side + Assert.assertFalse(info, viewA.getOpponentHands().isEmpty()); + Assert.assertTrue(info, viewB.getOpponentHands().isEmpty()); + } else { + // A,B normal + Assert.assertTrue(info, playerA.isGameUnderControl()); + Assert.assertTrue(info, playerB.isGameUnderControl()); + + Assert.assertTrue(info, playerA.getPlayersUnderYourControl().isEmpty()); + Assert.assertTrue(info, playerB.getPlayersUnderYourControl().isEmpty()); + + Assert.assertTrue(info, playerA.getTurnControllers().isEmpty()); + Assert.assertTrue(info, playerB.getTurnControllers().isEmpty()); + + Assert.assertEquals(info, playerA.getTurnControlledBy(), playerA.getId()); + Assert.assertEquals(info, playerB.getTurnControlledBy(), playerB.getId()); + + // client side + Assert.assertTrue(info, viewA.getOpponentHands().isEmpty()); + Assert.assertTrue(info, viewB.getOpponentHands().isEmpty()); + } + }); + } +} diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index bea27ece9f4..80ce3115065 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -3134,13 +3134,13 @@ public class TestPlayer implements Player { } @Override - public void setGameUnderYourControl(boolean value) { - computerPlayer.setGameUnderYourControl(value); + public void setGameUnderYourControl(Game game, boolean value) { + computerPlayer.setGameUnderYourControl(game, value); } @Override - public void setGameUnderYourControl(boolean value, boolean fullRestore) { - computerPlayer.setGameUnderYourControl(value, fullRestore); + public void setGameUnderYourControl(Game game, boolean value, boolean fullRestore) { + computerPlayer.setGameUnderYourControl(game, value, fullRestore); } @Override diff --git a/Mage/src/main/java/mage/abilities/AbilityImpl.java b/Mage/src/main/java/mage/abilities/AbilityImpl.java index e8848440b9d..65198348e02 100644 --- a/Mage/src/main/java/mage/abilities/AbilityImpl.java +++ b/Mage/src/main/java/mage/abilities/AbilityImpl.java @@ -378,7 +378,7 @@ public abstract class AbilityImpl implements Ability { // unit tests only: it allows to add targets/choices by two ways: // 1. From cast/activate command params (process it here) - // 2. From single addTarget/setChoice, it's a preffered method for tests (process it in normal choose dialogs like human player) + // 2. From single addTarget/setChoice, it's a preferred method for tests (process it in normal choose dialogs like human player) if (controller.isTestsMode()) { if (!controller.addTargets(this, game)) { return false; diff --git a/Mage/src/main/java/mage/abilities/effects/common/LoseControlOnOtherPlayersControllerEffect.java b/Mage/src/main/java/mage/abilities/effects/common/LoseControlOnOtherPlayersControllerEffect.java deleted file mode 100644 index 691553c7f6a..00000000000 --- a/Mage/src/main/java/mage/abilities/effects/common/LoseControlOnOtherPlayersControllerEffect.java +++ /dev/null @@ -1,41 +0,0 @@ - - -package mage.abilities.effects.common; - -import mage.constants.Outcome; -import mage.abilities.Ability; -import mage.abilities.effects.OneShotEffect; -import mage.game.Game; -import mage.players.Player; - -/** - * TODO: delete, there are already end turn code with control reset - * @author nantuko - */ -public class LoseControlOnOtherPlayersControllerEffect extends OneShotEffect { - - public LoseControlOnOtherPlayersControllerEffect(String controllingPlayerName, String controlledPlayerName) { - super(Outcome.Detriment); - staticText = controllingPlayerName + " lost control over " + controlledPlayerName; - } - - protected LoseControlOnOtherPlayersControllerEffect(final LoseControlOnOtherPlayersControllerEffect effect) { - super(effect); - } - - @Override - public LoseControlOnOtherPlayersControllerEffect copy() { - return new LoseControlOnOtherPlayersControllerEffect(this); - } - - @Override - public boolean apply(Game game, Ability source) { - Player player = game.getPlayer(source.getControllerId()); - if (player != null) { - player.resetOtherTurnsControlled(); - return true; - } - return false; - } - -} diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index fc4269a71e9..63147190c01 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -1852,7 +1852,7 @@ public abstract class GameImpl implements Game { if (turnController != null) { Player targetPlayer = getPlayer(spellControllerId); if (targetPlayer != null) { - targetPlayer.setGameUnderYourControl(true, false); + targetPlayer.setGameUnderYourControl(this, true, false); informPlayers(turnController.getLogName() + " lost control over " + targetPlayer.getLogName()); if (targetPlayer.getTurnControlledBy().equals(turnController.getId())) { turnController.getPlayersUnderYourControl().remove(targetPlayer.getId()); diff --git a/Mage/src/main/java/mage/game/turn/Turn.java b/Mage/src/main/java/mage/game/turn/Turn.java index 3aa40f53653..995c0b3c48f 100644 --- a/Mage/src/main/java/mage/game/turn/Turn.java +++ b/Mage/src/main/java/mage/game/turn/Turn.java @@ -233,7 +233,7 @@ public class Turn implements Serializable { if (player != controllingPlayer && controllingPlayer != null) { game.informPlayers(controllingPlayer.getLogName() + " lost control over " + player.getLogName()); } - player.setGameUnderYourControl(true); + player.setGameUnderYourControl(game, true); } }); diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 136e841bcb4..14b9df75af5 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -388,7 +388,7 @@ public interface Player extends MageItem, Copyable { * * @param value */ - void setGameUnderYourControl(boolean value); + void setGameUnderYourControl(Game game, boolean value); /** * Return player's turn control to prev player @@ -396,7 +396,7 @@ public interface Player extends MageItem, Copyable { * @param value * @param fullRestore return turn control to own */ - void setGameUnderYourControl(boolean value, boolean fullRestore); + void setGameUnderYourControl(Game game, boolean value, boolean fullRestore); void setTestMode(boolean value); diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 0b693a17178..73ec0e6b91e 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -160,6 +160,7 @@ public abstract class PlayerImpl implements Player, Serializable { protected List alternativeSourceCosts = new ArrayList<>(); // TODO: rework turn controller to use single list (see other todos) + // see PlayerUnderControlTest //protected Stack allTurnControllers = new Stack<>(); protected boolean isGameUnderControl = true; // TODO: replace with allTurnControllers.isEmpty protected UUID turnController; // null on own control TODO: replace with allTurnControllers.last @@ -619,7 +620,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (!playerUnderControlId.equals(this.getId())) { this.playersUnderYourControl.add(playerUnderControlId); if (!playerUnderControl.hasLeft() && !playerUnderControl.hasLost()) { - playerUnderControl.setGameUnderYourControl(false); + playerUnderControl.setGameUnderYourControl(game, false); } // control will reset on start of the turn } @@ -663,14 +664,15 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public void setGameUnderYourControl(boolean value) { - setGameUnderYourControl(value, true); + public void setGameUnderYourControl(Game game, boolean value) { + setGameUnderYourControl(game, value, true); } @Override - public void setGameUnderYourControl(boolean value, boolean fullRestore) { + public void setGameUnderYourControl(Game game, boolean value, boolean fullRestore) { this.isGameUnderControl = value; if (isGameUnderControl) { + removeMeFromPlayersUnderControl(game); if (fullRestore) { // to own this.turnControllers.clear(); @@ -687,11 +689,26 @@ public abstract class PlayerImpl implements Player, Serializable { } else { this.turnController = turnControllers.get(turnControllers.size() - 1); isGameUnderControl = false; + addMeToPlayersUnderControl(game, this.turnController); } } } } + private void removeMeFromPlayersUnderControl(Game game) { + game.getPlayers().values().forEach(p -> { + p.getPlayersUnderYourControl().remove(this.getId()); + }); + } + + private void addMeToPlayersUnderControl(Game game, UUID newTurnController) { + game.getPlayers().values().forEach(p -> { + if (p.getId().equals(newTurnController)) { + p.getPlayersUnderYourControl().add(this.getId()); + } + }); + } + @Override public void endOfTurn(Game game) { this.passedTurn = false; diff --git a/Mage/src/main/java/mage/util/CardUtil.java b/Mage/src/main/java/mage/util/CardUtil.java index d19068d52e3..f1073e32c4c 100644 --- a/Mage/src/main/java/mage/util/CardUtil.java +++ b/Mage/src/main/java/mage/util/CardUtil.java @@ -1434,7 +1434,7 @@ public final class CardUtil { * @param playerUnderControl */ public static void takeControlUnderPlayerEnd(Game game, Ability source, Player controller, Player playerUnderControl) { - playerUnderControl.setGameUnderYourControl(true, false); + playerUnderControl.setGameUnderYourControl(game, true, false); if (!playerUnderControl.getTurnControlledBy().equals(controller.getId())) { game.informPlayers(controller.getLogName() + " return control of the turn to " + playerUnderControl.getLogName() + CardUtil.getSourceLogName(game, source)); controller.getPlayersUnderYourControl().remove(playerUnderControl.getId());