fix 514.3a, give player priority on cleanup when something happened (#12115)

This commit is contained in:
Susucre 2024-04-12 15:31:53 +02:00 committed by GitHub
parent f1791a3c70
commit 72a2e32d1c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 106 additions and 7 deletions

View file

@ -0,0 +1,83 @@
package org.mage.test.cards.single.iko;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author Susucr
*/
public class RielleTheEverwiseTest extends CardTestPlayerBase {
/**
* {@link mage.cards.r.RielleTheEverwise} Rielle, the Everwise {1}{U}{R}
* Legendary Creature Human Wizard
* Rielle, the Everwise gets +1/+0 for each instant and sorcery card in your graveyard.
* Whenever you discard one or more cards for the first time each turn, draw that many cards.
* 0/3
*/
private static final String rielle = "Rielle, the Everwise";
private static void checkMidExecute(String info, Player player, Game game, int stack, int hand, int life) {
Assert.assertEquals(info + " - stack size", stack, game.getStack().size());
Assert.assertEquals(info + " - hand size", hand, player.getHand().size());
Assert.assertEquals(info + " - life", life, player.getLife());
}
// Bug: you had no play priority to respond to Rielle's trigger at end step.
@Test
public void test_CleanupDiscard_Opp_Response() {
setStrictChooseMode(true);
skipInitShuffling();
addCard(Zone.BATTLEFIELD, playerA, rielle);
addCard(Zone.HAND, playerA, "Island", 10);
addCard(Zone.LIBRARY, playerA, "Island", 3);
addCard(Zone.HAND, playerB, "Lightning Bolt");
addCard(Zone.HAND, playerB, "Shock");
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2);
// Choose to discard Islands on first cleanup
setChoice(playerA, "Island", 3);
// Rielle's Trigger on the stack.
runCode("Trigger on the stack", 1, PhaseStep.CLEANUP, playerB,
(i, p, g) -> checkMidExecute(i, playerA, g, 1, 7, 20));
// Cast Bolt in response of Rielle's trigger.
castSpell(1, PhaseStep.CLEANUP, playerB, "Lightning Bolt", playerA);
runCode("Trigger+Bolt on the stack", 1, PhaseStep.CLEANUP, playerB,
(i, p, g) -> checkMidExecute(i, playerA, g, 2, 7, 20));
waitStackResolved(1, PhaseStep.CLEANUP, 1); // resolves first bolt
runCode("Trigger+Bolt on the stack", 1, PhaseStep.CLEANUP, playerB,
(i, p, g) -> checkMidExecute(i, playerA, g, 1, 7, 20 - 3));
waitStackResolved(1, PhaseStep.CLEANUP, 1); // resolves rielle trigger
runCode("Trigger+Bolt on the stack", 1, PhaseStep.CLEANUP, playerB,
(i, p, g) -> checkMidExecute(i, playerA, g, 0, 10, 20 - 3));
// Cast Shock after Rielle's trigger resolved.
castSpell(1, PhaseStep.CLEANUP, playerB, "Shock", playerA);
runCode("Trigger+Bolt on the stack", 1, PhaseStep.CLEANUP, playerB,
(i, p, g) -> checkMidExecute(i, playerA, g, 1, 10, 20 - 3));
waitStackResolved(1, PhaseStep.CLEANUP, 1);
runCode("Trigger+Bolt on the stack", 1, PhaseStep.CLEANUP, playerB,
(i, p, g) -> checkMidExecute(i, playerA, g, 0, 10, 20 - 3 - 2));
// Discard for the second cleanup.
setChoice(playerA, "Island", 3);
setStopAt(2, PhaseStep.UPKEEP);
execute();
assertLife(playerA, 20 - 3 - 2);
assertHandCount(playerA, 7);
assertGraveyardCount(playerA, "Island", 3 + 3);
}
}

View file

@ -28,8 +28,9 @@ public class EndPhase extends Phase {
@Override @Override
protected void playStep(Game game) { protected void playStep(Game game) {
if (currentStep.getType() == PhaseStep.CLEANUP) { if (currentStep.getType() == PhaseStep.CLEANUP) {
game.getState().increaseStepNum();
game.getTurn().setEndTurnRequested(false); // so triggers trigger again game.getTurn().setEndTurnRequested(false); // so triggers trigger again
currentStep.beginStep(game, activePlayerId); prePriority(game, activePlayerId);
// 514.3a At this point, the game checks to see if any state-based actions would be performed // 514.3a At this point, the game checks to see if any state-based actions would be performed
// and/or any triggered abilities are waiting to be put onto the stack (including those that // and/or any triggered abilities are waiting to be put onto the stack (including those that
// trigger "at the beginning of the next cleanup step"). If so, those state-based actions are // trigger "at the beginning of the next cleanup step"). If so, those state-based actions are
@ -37,10 +38,19 @@ public class EndPhase extends Phase {
// priority. Players may cast spells and activate abilities. Once the stack is empty and all players // priority. Players may cast spells and activate abilities. Once the stack is empty and all players
// pass in succession, another cleanup step begins // pass in succession, another cleanup step begins
if (game.checkStateAndTriggered()) { if (game.checkStateAndTriggered()) {
game.playPriority(activePlayerId, true); // Queues a new cleanup step
playStep(game); game.getState().getTurnMods().add(new TurnMod(activePlayerId).withExtraStep(new CleanupStep()));
// resume priority
if (!game.isPaused() && !game.checkIfGameIsOver() && !game.executingRollback()) {
currentStep.priority(game, activePlayerId, false);
if (game.executingRollback()) {
return;
}
}
}
if (!game.isPaused() && !game.checkIfGameIsOver() && !game.executingRollback()) {
postPriority(game, activePlayerId);
} }
currentStep.endStep(game, activePlayerId);
} else { } else {
super.playStep(game); super.playStep(game);
} }

View file

@ -275,4 +275,7 @@ public abstract class Phase implements Serializable {
} }
} }
public String toString() {
return type.toString();
}
} }

View file

@ -1,15 +1,15 @@
package mage.game.turn; package mage.game.turn;
import java.io.Serializable;
import java.util.UUID;
import mage.constants.PhaseStep; import mage.constants.PhaseStep;
import mage.game.Game; import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType; import mage.game.events.GameEvent.EventType;
import mage.util.Copyable; import mage.util.Copyable;
import java.io.Serializable;
import java.util.UUID;
/** /**
* Game's step * Game's step
* <p> * <p>
@ -85,4 +85,7 @@ public abstract class Step implements Serializable, Copyable<Step> {
return stepPart; return stepPart;
} }
public String toString() {
return type.getStepText();
}
} }