From d2b8928e606964dec2f8d4cd37bd322ddeec67cb Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Mon, 25 May 2020 18:09:28 +0200 Subject: [PATCH] * Fixed a bug that the win method for a player did not handle the range correctly and did erroneously end the game while still multiple players alive (related to #6553). --- .../mage/cards/a/ApproachOfTheSecondSun.java | 6 +- .../mage/test/multiplayer/PlayerWinsTest.java | 64 +++++++++++++++++++ .../main/java/mage/players/PlayerImpl.java | 14 ++-- 3 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java diff --git a/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java b/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java index ea4a3c9548..f47ae0b081 100644 --- a/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java +++ b/Mage.Sets/src/mage/cards/a/ApproachOfTheSecondSun.java @@ -27,6 +27,8 @@ public final class ApproachOfTheSecondSun extends CardImpl { public ApproachOfTheSecondSun(UUID ownerId, CardSetInfo setInfo) { super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{W}"); + // If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, + // you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top and you gain 7 life. getSpellAbility().addEffect(new ApproachOfTheSecondSunEffect()); getSpellAbility().addWatcher(new ApproachOfTheSecondSunWatcher()); } @@ -46,7 +48,7 @@ class ApproachOfTheSecondSunEffect extends OneShotEffect { public ApproachOfTheSecondSunEffect() { super(Outcome.Win); this.staticText - = "If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, you win the game. " + = "If this spell was cast from your hand and you've cast another spell named {this} this game, you win the game. " + "Otherwise, put {this} into its owner's library seventh from the top and you gain 7 life."; } @@ -93,7 +95,7 @@ class ApproachOfTheSecondSunEffect extends OneShotEffect { class ApproachOfTheSecondSunWatcher extends Watcher { - private Map approachesCast = new HashMap<>(); + private final Map approachesCast = new HashMap<>(); public ApproachOfTheSecondSunWatcher() { super(WatcherScope.GAME); diff --git a/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java new file mode 100644 index 0000000000..e2bea870ea --- /dev/null +++ b/Mage.Tests/src/test/java/org/mage/test/multiplayer/PlayerWinsTest.java @@ -0,0 +1,64 @@ +/* + * 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 org.mage.test.multiplayer; + +import java.io.FileNotFoundException; +import mage.constants.MultiplayerAttackOption; +import mage.constants.PhaseStep; +import mage.constants.RangeOfInfluence; +import mage.constants.Zone; +import mage.game.FreeForAll; +import mage.game.Game; +import mage.game.GameException; +import mage.game.mulligan.MulliganType; +import org.junit.Assert; +import org.junit.Test; +import org.mage.test.serverside.base.CardTestMultiPlayerBase; + + +/** + * @author LevelX2 + */ +public class PlayerWinsTest extends CardTestMultiPlayerBase { + + @Override + protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException { + Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ONE, MulliganType.GAME_DEFAULT.getMulligan(0), 40); + // Player order: A -> D -> C -> B + playerA = createPlayer(game, playerA, "PlayerA"); + playerB = createPlayer(game, playerB, "PlayerB"); + playerC = createPlayer(game, playerC, "PlayerC"); + playerD = createPlayer(game, playerD, "PlayerD"); + return game; + } + + /** + * Tests multiplayer effects Player order: A -> D -> C -> B + */ + @Test + public void ApproachOfTheSecondSunTest() { + + // If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game, + // you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top and you gain 7 life. + addCard(Zone.HAND, playerA, "Approach of the Second Sun", 2); // Sorcery {6}{W} + addCard(Zone.BATTLEFIELD, playerA, "Plains", 7); + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Approach of the Second Sun"); + castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Approach of the Second Sun"); + + setStopAt(5, PhaseStep.POSTCOMBAT_MAIN); + execute(); + + assertLife(playerA, 47); + assertLife(playerC, 40); + Assert.assertTrue("Player D has lost the game", !playerD.isInGame()); + Assert.assertTrue("Player B has lost the game", !playerB.isInGame()); + Assert.assertTrue("Player C is in the game", playerC.isInGame()); + Assert.assertTrue("Player A is in the game", playerA.isInGame()); + + } + +} diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index a4a8667daf..1b745b1056 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -2526,12 +2526,14 @@ public abstract class PlayerImpl implements Player, Serializable { opponent.lostForced(game); } } - // if no more opponents alive, you win and the game ends + // if no more opponents alive (independant from range), you win and the game ends int opponentsAlive = 0; - for (UUID opponentId : game.getOpponents(playerId)) { - Player opponent = game.getPlayer(opponentId); - if (opponent != null && !opponent.hasLost()) { - opponentsAlive++; + for (UUID playerIdToCheck : game.getPlayerList()) { + if (game.isOpponent(this, playerIdToCheck)) { // Check without range + Player opponent = game.getPlayer(playerIdToCheck); + if (opponent != null && !opponent.hasLost()) { + opponentsAlive++; + } } } if (opponentsAlive == 0 && !hasWon()) { @@ -2541,7 +2543,7 @@ public abstract class PlayerImpl implements Player, Serializable { game.end(); } } else { - logger.debug("player won -> but already lost before: " + this.getName()); + logger.debug("player won -> but already lost before or other players still alive: " + this.getName()); } } }