diff --git a/Mage.Tests/src/test/java/org/mage/test/game/ends/GameIsADrawTest.java b/Mage.Tests/src/test/java/org/mage/test/game/ends/GameIsADrawTest.java index 19a4f1eb6e8..4660ff92c84 100644 --- a/Mage.Tests/src/test/java/org/mage/test/game/ends/GameIsADrawTest.java +++ b/Mage.Tests/src/test/java/org/mage/test/game/ends/GameIsADrawTest.java @@ -7,6 +7,7 @@ package org.mage.test.game.ends; import mage.constants.PhaseStep; import mage.constants.Zone; +import mage.game.permanent.Permanent; import org.junit.Assert; import org.junit.Test; import org.mage.test.serverside.base.CardTestPlayerBase; @@ -77,4 +78,45 @@ public class GameIsADrawTest extends CardTestPlayerBase { } + /** + * So here I made a simple infinite loop with Stuffy Doll and Pariah's + * Shield, which should make the game a draw. But instead, it just keeps + * going... + */ + @Test + public void GameDrawByInfiniteLoop() { + addCard(Zone.BATTLEFIELD, playerA, "Plains", 5); + + // All damage that would be dealt to you is dealt to equipped creature instead. + // Equip {3} + addCard(Zone.BATTLEFIELD, playerA, "Pariah's Shield", 1); // Artifact Equipment {5} + + // As Stuffy Doll enters the battlefield, choose a player. + // Stuffy Doll is indestructible. + // Whenever Stuffy Doll is dealt damage, it deals that much damage to the chosen player. + // {T}: Stuffy Doll deals 1 damage to itself. + addCard(Zone.HAND, playerA, "Stuffy Doll", 1); // Artifact Creature {5} 0/1 + + castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Stuffy Doll"); + setChoice(playerA, "PlayerA"); + setChoice(playerA, "PlayerA"); + + activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Equip", "Stuffy Doll"); + activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}"); + setStopAt(3, PhaseStep.END_TURN); + execute(); + + assertPermanentCount(playerA, "Stuffy Doll", 1); + Permanent shield = getPermanent("Pariah's Shield"); + Assert.assertTrue("Pariah's Shield is attached", shield.getAttachedTo() != null); + + Assert.assertFalse("Player A has not won.", playerA.hasWon()); + Assert.assertFalse("Player B has not won.", playerB.hasWon()); + + Assert.assertTrue("Game has ended.", currentGame.hasEnded()); + + Assert.assertTrue("Inifinite loop detected, game has be de a draw.", currentGame.isADraw()); + + } + } diff --git a/Mage/src/main/java/mage/game/GameImpl.java b/Mage/src/main/java/mage/game/GameImpl.java index a297bcc1ca2..050a77182d6 100644 --- a/Mage/src/main/java/mage/game/GameImpl.java +++ b/Mage/src/main/java/mage/game/GameImpl.java @@ -1233,6 +1233,7 @@ public abstract class GameImpl implements Game, Serializable { @Override public void playPriority(UUID activePlayerId, boolean resuming) { int errorContinueCounter = 0; + int infiniteLoopCounter = 0; int bookmark = 0; clearAllBookmarks(); try { @@ -1286,6 +1287,13 @@ public abstract class GameImpl implements Game, Serializable { } if (allPassed()) { if (!state.getStack().isEmpty()) { + if (getStack().size() < 4) { + infiniteLoopCounter++; + if (infiniteLoopCounter > 15) { + isInfiniteLoop(); + infiniteLoopCounter = 0; + } + } //20091005 - 115.4 resolve(); applyEffects(); @@ -1294,6 +1302,7 @@ public abstract class GameImpl implements Game, Serializable { resetShortLivingLKI(); break; } else { + infiniteLoopCounter = 0; resetLKI(); return; } @@ -1350,6 +1359,30 @@ public abstract class GameImpl implements Game, Serializable { } } + protected void isInfiniteLoop() { + StackObject stackObject = getStack().getFirst(); + if (stackObject != null) { + Player controller = getPlayer(stackObject.getControllerId()); + if (controller != null) { + for (UUID playerId : getState().getPlayersInRange(controller.getId(), this)) { + Player player = getPlayer(playerId); + if (!player.chooseUse(Outcome.Detriment, "Draw game because of infinite looping?", null, this)) { + informPlayers(controller.getLogName() + " has NOT confirmed that the game is a draw because of infinite looping."); + return; + } + informPlayers(controller.getLogName() + " has confirmed that the game is a draw because of infinite looping."); + } + for (UUID playerId : getState().getPlayersInRange(controller.getId(), this)) { + Player player = getPlayer(playerId); + if (player != null) { + player.drew(this); + } + } + } + } + + } + protected boolean allPassed() { for (Player player : state.getPlayers().values()) { if (!player.isPassed() && player.canRespond()) {