mirror of
https://github.com/correl/mage.git
synced 2024-11-15 11:09:30 +00:00
* Fixed that end turn effects (e.g. Day's Undoing) did not remove triggered abilities waiting to go onto the stack.
This commit is contained in:
parent
88b5398f48
commit
de47259a49
5 changed files with 151 additions and 73 deletions
|
@ -44,9 +44,9 @@ import mage.game.permanent.token.ZombieToken;
|
|||
* @author fireshoes
|
||||
*/
|
||||
public class UndeadServant extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard(" card named Undead Servant");
|
||||
|
||||
|
||||
private static final FilterCard filter = new FilterCard("card named Undead Servant");
|
||||
|
||||
static {
|
||||
filter.add(new NamePredicate("Undead Servant"));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package org.mage.test.cards.abilities.other;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public class EndTurnEffectTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* Additional bug: Days Undoing and Sphinx's Tutelage are broken. You
|
||||
* shouldn't get triggers off of Tutelage, since the turn ends, but it has
|
||||
* you resolve them in your cleanup step.
|
||||
*
|
||||
* http://tabakrules.tumblr.com/post/122350751009/days-undoing-has-been-officially-spoiled-on
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testSpellsAffinity() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
|
||||
// Whenever you draw a card, target opponent puts the top two cards of his or her library into his or her graveyard. If they're both nonland cards that share a color, repeat this process.
|
||||
// {5}{U}: Draw a card, then discard a card.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Sphinx's Tutelage");
|
||||
|
||||
// Each player shuffles his or her hand and graveyard into his or her library, then draws seven cards. If it's your turn, end the turn.
|
||||
addCard(Zone.HAND, playerA, "Day's Undoing"); //Sorcery {2}{U}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Day's Undoing");
|
||||
|
||||
setStopAt(2, PhaseStep.UPKEEP);
|
||||
execute();
|
||||
|
||||
assertExileCount("Day's Undoing", 1);
|
||||
|
||||
assertHandCount(playerA, 7);
|
||||
assertHandCount(playerB, 7);
|
||||
|
||||
assertGraveyardCount(playerB, 0); // because the trigegrs of Sphinx's Tutelage cease to exist
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -25,12 +25,11 @@
|
|||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
|
||||
public class EndTurnEffect extends OneShotEffect {
|
||||
|
@ -46,8 +45,9 @@ public class EndTurnEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (!game.isSimulation())
|
||||
if (!game.isSimulation()) {
|
||||
game.informPlayers("The current turn ends");
|
||||
}
|
||||
return game.endTurn();
|
||||
}
|
||||
|
||||
|
@ -56,4 +56,3 @@ public class EndTurnEffect extends OneShotEffect {
|
|||
return new EndTurnEffect(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -757,6 +757,13 @@ public class GameState implements Serializable, Copyable<GameState> {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all waiting triggers (needed for turn end effects)
|
||||
*/
|
||||
public void clearTriggeredAbilities() {
|
||||
this.triggered.clear();
|
||||
}
|
||||
|
||||
public void addTriggeredAbility(TriggeredAbility ability) {
|
||||
this.triggered.add(ability);
|
||||
}
|
||||
|
|
|
@ -1,31 +1,30 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.game.turn;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
@ -56,7 +55,7 @@ public class Turn implements Serializable {
|
|||
private final List<Phase> phases = new ArrayList<>();
|
||||
private boolean declareAttackersStepStarted = false;
|
||||
private boolean endTurn; // indicates that an end turn effect has resolved.
|
||||
|
||||
|
||||
public Turn() {
|
||||
endTurn = false;
|
||||
phases.add(new BeginningPhase());
|
||||
|
@ -71,7 +70,7 @@ public class Turn implements Serializable {
|
|||
this.currentPhase = turn.currentPhase.copy();
|
||||
}
|
||||
this.activePlayerId = turn.activePlayerId;
|
||||
for (Phase phase: turn.phases) {
|
||||
for (Phase phase : turn.phases) {
|
||||
this.phases.add(phase.copy());
|
||||
}
|
||||
this.declareAttackersStepStarted = turn.declareAttackersStepStarted;
|
||||
|
@ -91,7 +90,7 @@ public class Turn implements Serializable {
|
|||
}
|
||||
|
||||
public Phase getPhase(TurnPhase turnPhase) {
|
||||
for (Phase phase: phases) {
|
||||
for (Phase phase : phases) {
|
||||
if (phase.getType() == turnPhase) {
|
||||
return phase;
|
||||
}
|
||||
|
@ -133,7 +132,7 @@ public class Turn implements Serializable {
|
|||
this.activePlayerId = activePlayer.getId();
|
||||
resetCounts();
|
||||
game.getPlayer(activePlayer.getId()).beginTurn(game);
|
||||
for (Phase phase: phases) {
|
||||
for (Phase phase : phases) {
|
||||
if (game.isPaused() || game.gameOver(null)) {
|
||||
return;
|
||||
}
|
||||
|
@ -142,11 +141,11 @@ public class Turn implements Serializable {
|
|||
game.fireEvent(new GameEvent(GameEvent.EventType.PHASE_CHANGED, activePlayer.getId(), null, activePlayer.getId()));
|
||||
if (!game.getState().getTurnMods().skipPhase(activePlayer.getId(), currentPhase.getType())) {
|
||||
if (phase.play(game, activePlayer.getId())) {
|
||||
if(game.executingRollback()) {
|
||||
if (game.executingRollback()) {
|
||||
return;
|
||||
}
|
||||
//20091005 - 500.4/703.4n
|
||||
game.emptyManaPools();
|
||||
game.emptyManaPools();
|
||||
game.saveState(false);
|
||||
|
||||
//20091005 - 500.8
|
||||
|
@ -208,7 +207,7 @@ public class Turn implements Serializable {
|
|||
}
|
||||
|
||||
private void resetCounts() {
|
||||
for (Phase phase: phases) {
|
||||
for (Phase phase : phases) {
|
||||
phase.resetCount();
|
||||
}
|
||||
}
|
||||
|
@ -223,7 +222,7 @@ public class Turn implements Serializable {
|
|||
return false;
|
||||
}
|
||||
Phase phase;
|
||||
switch(extraPhase) {
|
||||
switch (extraPhase) {
|
||||
case BEGINNING:
|
||||
phase = new BeginningPhase();
|
||||
break;
|
||||
|
@ -251,52 +250,51 @@ public class Turn implements Serializable {
|
|||
}
|
||||
|
||||
/*protected void playExtraTurns(Game game) {
|
||||
while (game.getState().getTurnMods().extraTurn(activePlayerId)) {
|
||||
this.play(game, activePlayerId);
|
||||
}
|
||||
}*/
|
||||
/**
|
||||
* Used for some spells with end turn effect (e.g. Time Stop).
|
||||
*
|
||||
* @param game
|
||||
* @param activePlayerId
|
||||
*/
|
||||
while (game.getState().getTurnMods().extraTurn(activePlayerId)) {
|
||||
this.play(game, activePlayerId);
|
||||
}
|
||||
}*/
|
||||
/**
|
||||
* Used for some spells with end turn effect (e.g. Time Stop).
|
||||
*
|
||||
* @param game
|
||||
* @param activePlayerId
|
||||
*/
|
||||
public void endTurn(Game game, UUID activePlayerId) {
|
||||
// Ending the turn this way (Time Stop) means the following things happen in order:
|
||||
|
||||
// Ending the turn this way (Time Stop) means the following things happen in order:
|
||||
|
||||
setEndTurnRequested(true);
|
||||
|
||||
// 1) All spells and abilities on the stack are exiled. This includes Time Stop, though it will continue to resolve.
|
||||
// It also includes spells and abilities that can't be countered.
|
||||
|
||||
// 1) All spells and abilities on the stack are exiled. This includes Time Stop, though it will continue to resolve.
|
||||
// It also includes spells and abilities that can't be countered.
|
||||
while (!game.getStack().isEmpty()) {
|
||||
StackObject stackObject = game.getStack().removeLast();
|
||||
if (stackObject instanceof Spell) {
|
||||
((Spell) stackObject).moveToExile(null, "", null, game);
|
||||
}
|
||||
}
|
||||
// 2) All attacking and blocking creatures are removed from combat.
|
||||
for (UUID attackerId: game.getCombat().getAttackers()) {
|
||||
// 2) All attacking and blocking creatures are removed from combat.
|
||||
for (UUID attackerId : game.getCombat().getAttackers()) {
|
||||
Permanent permanent = game.getPermanent(attackerId);
|
||||
if (permanent != null) {
|
||||
permanent.removeFromCombat(game);
|
||||
}
|
||||
game.getCombat().removeAttacker(attackerId, game);
|
||||
}
|
||||
for (UUID blockerId: game.getCombat().getBlockers()) {
|
||||
for (UUID blockerId : game.getCombat().getBlockers()) {
|
||||
Permanent permanent = game.getPermanent(blockerId);
|
||||
if (permanent != null) {
|
||||
permanent.removeFromCombat(game);
|
||||
}
|
||||
}
|
||||
|
||||
// 3) State-based actions are checked. No player gets priority, and no triggered abilities are put onto the stack.
|
||||
|
||||
// 3) State-based actions are checked. No player gets priority, and no triggered abilities are put onto the stack.
|
||||
// seems like trigger events have to be removed: http://tabakrules.tumblr.com/post/122350751009/days-undoing-has-been-officially-spoiled-on
|
||||
game.getState().clearTriggeredAbilities();
|
||||
game.checkStateAndTriggered(); // triggered effects don't go to stack because check of endTurnRequested
|
||||
|
||||
// 4) The current phase and/or step ends.
|
||||
|
||||
// 4) The current phase and/or step ends.
|
||||
// The game skips straight to the cleanup step. The cleanup step happens in its entirety.
|
||||
// this is caused by the endTurnRequest state
|
||||
|
||||
}
|
||||
|
||||
public boolean isDeclareAttackersStepStarted() {
|
||||
|
@ -310,23 +308,23 @@ public class Turn implements Serializable {
|
|||
public void setEndTurnRequested(boolean endTurn) {
|
||||
this.endTurn = endTurn;
|
||||
}
|
||||
|
||||
|
||||
public boolean isEndTurnRequested() {
|
||||
return endTurn;
|
||||
}
|
||||
|
||||
|
||||
public Turn copy() {
|
||||
return new Turn(this);
|
||||
}
|
||||
|
||||
|
||||
public String getValue(int turnNum) {
|
||||
StringBuilder sb = threadLocalBuilder.get();
|
||||
sb.append("[").append(turnNum)
|
||||
.append(":").append(currentPhase.getType())
|
||||
.append(":").append(currentPhase.getStep().getType())
|
||||
.append("]");
|
||||
.append(":").append(currentPhase.getType())
|
||||
.append(":").append(currentPhase.getStep().getType())
|
||||
.append("]");
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue