mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
cleanup DelayedTriggeredAbility on player leave
This commit is contained in:
parent
6cc3c5384a
commit
8db7a4d307
4 changed files with 88 additions and 35 deletions
|
|
@ -0,0 +1,67 @@
|
|||
package org.mage.test.multiplayer;
|
||||
|
||||
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.Test;
|
||||
import org.mage.test.serverside.base.CardTestMultiPlayerBase;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
|
||||
/**
|
||||
* @author Susucr
|
||||
*/
|
||||
public class DelayedTriggerLastingAfterControllerLeavesTest extends CardTestMultiPlayerBase {
|
||||
|
||||
@Override
|
||||
protected Game createNewGameAndPlayers() throws GameException, FileNotFoundException {
|
||||
Game game = new FreeForAll(MultiplayerAttackOption.MULTIPLE, RangeOfInfluence.ALL, MulliganType.GAME_DEFAULT.getMulligan(0), 40, 7);
|
||||
// Player order: A -> D -> C -> B
|
||||
playerA = createPlayer(game, "PlayerA");
|
||||
playerB = createPlayer(game, "PlayerB");
|
||||
playerC = createPlayer(game, "PlayerC");
|
||||
playerD = createPlayer(game, "PlayerD");
|
||||
return game;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_UntilYourNextTurn_AfterLeave() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
// +1: Until your next turn, whenever a creature an opponent controls attacks, it gets -1/-0 until end of turn.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Jace, Architect of Thought");
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerD, "Grizzly Bears");
|
||||
addCard(Zone.BATTLEFIELD, playerD, "Elite Vanguard");
|
||||
addCard(Zone.BATTLEFIELD, playerC, "Grizzly Bears");
|
||||
addCard(Zone.BATTLEFIELD, playerC, "Elite Vanguard");
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1:");
|
||||
|
||||
attack(2, playerD, "Grizzly Bears", playerB);
|
||||
attack(2, playerD, "Elite Vanguard", playerB);
|
||||
setChoice(playerA, "Until your next turn"); // order trigger
|
||||
|
||||
checkLife("2: after D attack affected by Delayed triggers", 2, PhaseStep.POSTCOMBAT_MAIN, playerB, 40 - 2);
|
||||
concede(2, PhaseStep.END_TURN, playerA);
|
||||
|
||||
attack(3, playerC, "Grizzly Bears", playerB);
|
||||
attack(3, playerC, "Elite Vanguard", playerB);
|
||||
|
||||
checkLife("3: after C attack affected by Delayed triggers", 3, PhaseStep.POSTCOMBAT_MAIN, playerB, 40 - 2 - 4);
|
||||
// No trigger, as triggers from leaved players don't trigger
|
||||
|
||||
attack(5, playerD, "Grizzly Bears", playerB);
|
||||
attack(5, playerD, "Elite Vanguard", playerB);
|
||||
|
||||
setStopAt(5, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
||||
assertLife(playerB, 40 - 2 - 4 - 4);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,12 +27,10 @@ public class DelayedTriggeredAbilities extends AbilitiesImpl<DelayedTriggeredAbi
|
|||
// TODO: add same integrity checks as TriggeredAbilities?!
|
||||
for (Iterator<DelayedTriggeredAbility> it = this.iterator(); it.hasNext(); ) {
|
||||
DelayedTriggeredAbility ability = it.next();
|
||||
if (ability.getDuration() == Duration.Custom) {
|
||||
if (ability.isInactive(game)) {
|
||||
it.remove();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (!ability.checkEventType(event, game)) {
|
||||
continue;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.abilities.effects.Effect;
|
|||
import mage.constants.Duration;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -64,6 +65,11 @@ public abstract class DelayedTriggeredAbility extends TriggeredAbilityImpl {
|
|||
}
|
||||
|
||||
public boolean isInactive(Game game) {
|
||||
return false;
|
||||
// discard as soon as possible for leaved player
|
||||
// 800.4d. If an object that would be owned by a player who has left the game would be created in any zone, it isn't created.
|
||||
// If a triggered ability that would be controlled by a player who has left the game would be put onto the stack, it isn't put on the stack.
|
||||
Player player = game.getPlayer(getControllerId());
|
||||
boolean canDelete = player == null || (!player.isInGame() && player.hasReachedNextTurnAfterLeaving());
|
||||
return canDelete;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -310,22 +310,12 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
return false;
|
||||
}
|
||||
|
||||
boolean canDelete;
|
||||
Player player = game.getPlayer(startingControllerId);
|
||||
|
||||
// discard on start of turn for leaved player
|
||||
// 800.4i When a player leaves the game, any continuous effects with durations that last until that player's next turn
|
||||
// or until a specific point in that turn will last until that turn would have begun.
|
||||
// They neither expire immediately nor last indefinitely.
|
||||
switch (duration) {
|
||||
case UntilYourNextTurn:
|
||||
case UntilEndOfYourNextTurn:
|
||||
canDelete = player == null || (!player.isInGame() && player.hasReachedNextTurnAfterLeaving());
|
||||
break;
|
||||
default:
|
||||
canDelete = false;
|
||||
}
|
||||
|
||||
boolean canDelete = player == null || (!player.isInGame() && player.hasReachedNextTurnAfterLeaving());
|
||||
if (canDelete) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -333,28 +323,20 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
|
|||
// discard on another conditions (start of your turn)
|
||||
switch (duration) {
|
||||
case UntilYourNextTurn:
|
||||
if (player != null && player.isInGame()) {
|
||||
return this.isYourNextTurn(game);
|
||||
}
|
||||
break;
|
||||
case UntilYourNextEndStep:
|
||||
if (player != null && player.isInGame()) {
|
||||
return this.isYourNextEndStep(game);
|
||||
}
|
||||
break;
|
||||
case UntilEndCombatOfYourNextTurn:
|
||||
if (player != null && player.isInGame()) {
|
||||
return this.isEndCombatOfYourNextTurn(game);
|
||||
}
|
||||
break;
|
||||
case UntilYourNextUpkeepStep:
|
||||
if (player != null && player.isInGame()) {
|
||||
return this.isYourNextUpkeepStep(game);
|
||||
case UntilEndOfYourNextTurn:
|
||||
// cleanup handled by ContinuousEffectsList::removeEndOfTurnEffects
|
||||
// TODO: should those be aligned to all be handled in the same place?
|
||||
return false;
|
||||
default: // Should handle all the duration that do pass the first switch.
|
||||
throw new IllegalStateException("Missing case for isInactive's Duration:" + duration);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return canDelete;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue