mirror of
https://github.com/magefree/mage.git
synced 2025-12-28 22:42:03 -08:00
* Rollback - Fixed that UI is locked after a accepted rollback request (fixes #1158).
This commit is contained in:
parent
eccc367920
commit
efb5e06e57
159 changed files with 231 additions and 204 deletions
|
|
@ -60,7 +60,7 @@ public class AddManaAnyColorAttachedControllerEffect extends ManaEffect {
|
|||
if (player != null) {
|
||||
ChoiceColor choice = new ChoiceColor();
|
||||
while (!player.choose(outcome, choice, game)) {
|
||||
if (!player.isInGame()) {
|
||||
if (!player.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
|
|||
Mana mana = new Mana();
|
||||
int amountOfManaLeft = amount.calculate(game, source, this);
|
||||
|
||||
while (amountOfManaLeft > 0 && player.isInGame()) {
|
||||
while (amountOfManaLeft > 0 && player.canRespond()) {
|
||||
for (ColoredManaSymbol coloredManaSymbol: manaSymbols) {
|
||||
int number = player.getAmount(0, amountOfManaLeft, new StringBuilder("How many ").append(coloredManaSymbol.name()).append(" mana?").toString(), game);
|
||||
if (number > 0) {
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ public class ChooseColorEffect extends OneShotEffect {
|
|||
ChoiceColor choice = new ChoiceColor();
|
||||
while (!choice.isChosen()) {
|
||||
controller.choose(outcome, choice, game);
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public class ChooseCreatureTypeEffect extends OneShotEffect {
|
|||
typeChoice.setMessage("Choose creature type");
|
||||
typeChoice.setChoices(CardRepository.instance.getCreatureTypes());
|
||||
while (!controller.choose(outcome, typeChoice, game)) {
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ public class ChooseModeEffect extends OneShotEffect {
|
|||
choice.setMessage(choiceMessage);
|
||||
choice.getChoices().addAll(modes);
|
||||
while (!choice.isChosen()) {
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
controller.choose(Outcome.Neutral, choice, game);
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ public class DynamicManaEffect extends BasicManaEffect {
|
|||
for (int i = 0; i < count; i++) {
|
||||
if (!choiceColor.isChosen()) {
|
||||
while (!controller.choose(Outcome.Benefit, choiceColor, game)) {
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
return computedMana;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,7 +94,7 @@ public class NameACardEffect extends OneShotEffect {
|
|||
}
|
||||
cardChoice.clearChoice();
|
||||
while (!controller.choose(Outcome.Detriment, cardChoice, game)) {
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ public class SacrificeAllEffect extends OneShotEffect {
|
|||
int numTargets = Math.min(amount.calculate(game, source, this), game.getBattlefield().countAll(filter, player.getId(), game));
|
||||
TargetControlledPermanent target = new TargetControlledPermanent(numTargets, numTargets, filter, true);
|
||||
if (target.canChoose(player.getId(), game)) {
|
||||
while (!target.isChosen() && player.isInGame()) {
|
||||
while (!target.isChosen() && player.canRespond()) {
|
||||
player.choose(Outcome.Sacrifice, target, source.getSourceId(), game);
|
||||
}
|
||||
perms.addAll(target.getTargets());
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ public class SacrificeEffect extends OneShotEffect{
|
|||
// A spell or ability could have removed the only legal target this player
|
||||
// had, if thats the case this ability should fizzle.
|
||||
if (amount > 0 && target.canChoose(source.getSourceId(), player.getId(), game)) {
|
||||
while (!target.isChosen() && target.canChoose(player.getId(), game) && player.isInGame()) {
|
||||
while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) {
|
||||
player.chooseTarget(Outcome.Sacrifice, target, source, game);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class BecomesChosenNonWallCreatureTypeTargetEffect extends OneShotEffect
|
|||
types.remove("Wall");
|
||||
typeChoice.setChoices(types);
|
||||
while (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
|
||||
if (!player.isInGame()) {
|
||||
if (!player.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ public class BecomesColorOrColorsTargetEffect extends OneShotEffect {
|
|||
}
|
||||
ChoiceColor choiceColor = new ChoiceColor();
|
||||
controller.choose(Outcome.Benefit, choiceColor, game);
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
if (!game.isSimulation())
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ public class BecomesColorSourceEffect extends ContinuousEffectImpl {
|
|||
ChoiceColor choice = new ChoiceColor();
|
||||
while(!choice.isChosen()) {
|
||||
controller.choose(Outcome.PutManaInPool, choice, game);
|
||||
if(!controller.isInGame()) {
|
||||
if(!controller.canRespond()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,7 +83,7 @@ public class BecomesColorTargetEffect extends ContinuousEffectImpl {
|
|||
ChoiceColor choice = new ChoiceColor();
|
||||
while (!choice.isChosen()) {
|
||||
controller.choose(Outcome.PutManaInPool, choice, game);
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class GainProtectionFromColorSourceEffect extends GainAbilitySourceEffect
|
|||
colorChoice.setMessage("Choose color for protection ability");
|
||||
while (!colorChoice.isChosen()) {
|
||||
controller.choose(outcome, colorChoice, game);
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
discard();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -73,7 +73,7 @@ public class GainProtectionFromColorTargetEffect extends GainAbilityTargetEffect
|
|||
choice.clearChoice();
|
||||
while (!choice.isChosen()) {
|
||||
controller.choose(Outcome.Protect, choice, game);
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ public class FatesealEffect extends OneShotEffect {
|
|||
target1.setRequired(false);
|
||||
// move cards to the bottom of the library
|
||||
while (cards.size() > 0 && controller.choose(Outcome.Detriment, cards, target1, game)) {
|
||||
if (!controller.isInGame() || !opponent.isInGame()) {
|
||||
if (!controller.canRespond() || !opponent.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
Card card = cards.get(target1.getFirstTarget(), game);
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ public class ScryEffect extends OneShotEffect {
|
|||
TargetCard target1 = new TargetCard(Zone.LIBRARY, filter1);
|
||||
target1.setRequired(false);
|
||||
// move cards to the bottom of the library
|
||||
while (player.isInGame() && cards.size() > 0 && player.choose(Outcome.Detriment, cards, target1, game)) {
|
||||
while (player.canRespond() && cards.size() > 0 && player.choose(Outcome.Detriment, cards, target1, game)) {
|
||||
Card card = cards.get(target1.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
cards.remove(card);
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ class AnnihilatorEffect extends OneShotEffect {
|
|||
int amount = Math.min(count, game.getBattlefield().countAll(filter, player.getId(), game));
|
||||
Target target = new TargetControlledPermanent(amount, amount, filter, false);
|
||||
if (target.canChoose(player.getId(), game)) {
|
||||
while (!target.isChosen() && target.canChoose(player.getId(), game) && player.isInGame()) {
|
||||
while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) {
|
||||
player.choose(Outcome.Sacrifice, target, source.getSourceId(), game);
|
||||
}
|
||||
for (int idx = 0; idx < target.getTargets().size(); idx++) {
|
||||
|
|
|
|||
|
|
@ -214,7 +214,7 @@ class ConvokeEffect extends OneShotEffect {
|
|||
chooseManaType.setMessage("Choose mana color to reduce from " + perm.getName());
|
||||
while (!chooseManaType.isChosen()) {
|
||||
controller.choose(Outcome.Benefit, chooseManaType, game);
|
||||
if (!controller.isInGame()) {
|
||||
if (!controller.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -209,7 +209,7 @@ public class KickerAbility extends StaticAbility implements OptionalAdditionalSo
|
|||
this.resetKicker(game, ability);
|
||||
for (OptionalAdditionalCost kickerCost : kickerCosts) {
|
||||
boolean again = true;
|
||||
while (player.isInGame() && again) {
|
||||
while (player.canRespond() && again) {
|
||||
String times = "";
|
||||
if (kickerCost.isRepeatable()) {
|
||||
int activatedCount = getKickedCounter(game, ability);
|
||||
|
|
|
|||
|
|
@ -113,7 +113,7 @@ public class ReplicateAbility extends StaticAbility implements OptionalAdditiona
|
|||
this.resetReplicate();
|
||||
|
||||
boolean again = true;
|
||||
while (player.isInGame() && again) {
|
||||
while (player.canRespond() && again) {
|
||||
String times = "";
|
||||
if (additionalCost.isRepeatable()) {
|
||||
int numActivations = additionalCost.getActivateCount();
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class RippleEffect extends OneShotEffect {
|
|||
target1.setRequired(false);
|
||||
|
||||
// choose cards to play for free
|
||||
while (player.isInGame() && cards.count(sameNameFilter, game) > 0 && player.choose(Outcome.PlayForFree, cards, target1, game)) {
|
||||
while (player.canRespond() && cards.count(sameNameFilter, game) > 0 && player.choose(Outcome.PlayForFree, cards, target1, game)) {
|
||||
Card card = cards.get(target1.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
player.cast(card.getSpellAbility(), game, true);
|
||||
|
|
|
|||
|
|
@ -733,7 +733,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
GameEvent event = new GameEvent(GameEvent.EventType.PLAY_TURN, null, null, extraTurn.getPlayerId());
|
||||
if (!replaceEvent(event)) {
|
||||
Player extraPlayer = this.getPlayer(extraTurn.getPlayerId());
|
||||
if (extraPlayer != null && extraPlayer.isInGame()) {
|
||||
if (extraPlayer != null && extraPlayer.canRespond()) {
|
||||
state.setExtraTurn(true);
|
||||
state.setTurnId(extraTurn.getId());
|
||||
if (!this.isSimulation()) {
|
||||
|
|
@ -1176,7 +1176,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
}
|
||||
player = getPlayer(state.getPlayerList().get());
|
||||
state.setPriorityPlayerId(player.getId());
|
||||
while (!player.isPassed() && player.isInGame() && !isPaused() && !gameOver(null)) {
|
||||
while (!player.isPassed() && player.canRespond() && !isPaused() && !gameOver(null)) {
|
||||
if (!resuming) {
|
||||
// 603.3. Once an ability has triggered, its controller puts it on the stack as an object thats not a card the next time a player would receive priority
|
||||
checkStateAndTriggered();
|
||||
|
|
@ -1270,7 +1270,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
|
||||
protected boolean allPassed() {
|
||||
for (Player player : state.getPlayers().values()) {
|
||||
if (!player.isPassed() && player.isInGame()) {
|
||||
if (!player.isPassed() && player.canRespond()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -236,7 +236,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
if (!game.getPlayer(game.getActivePlayerId()).getAvailableAttackers(game).isEmpty()) {
|
||||
player.selectAttackers(game, attackerId);
|
||||
}
|
||||
if (game.isPaused() || game.gameOver(null)) {
|
||||
if (game.isPaused() || game.gameOver(null) || game.executingRollback()) {
|
||||
return;
|
||||
}
|
||||
// because of possible undo during declare attackers it's neccassary to call here the methods with "game.getCombat()." to get the valid combat object!!!
|
||||
|
|
@ -393,7 +393,7 @@ public class Combat implements Serializable, Copyable<Combat> {
|
|||
}
|
||||
while (choose) {
|
||||
controller.selectBlockers(game, defenderId);
|
||||
if (game.isPaused() || game.gameOver(null)) {
|
||||
if (game.isPaused() || game.gameOver(null) || game.executingRollback()) {
|
||||
return;
|
||||
}
|
||||
if (!this.checkBlockRestrictions(defender, game)) {
|
||||
|
|
|
|||
|
|
@ -434,7 +434,7 @@ public class CombatGroup implements Serializable, Copyable<CombatGroup> {
|
|||
Player player = game.getPlayer(playerId);
|
||||
List<UUID> blockerList = new ArrayList<>(blockers);
|
||||
blockerOrder.clear();
|
||||
while (true && player.isInGame()) {
|
||||
while (true && player.canRespond()) {
|
||||
if (blockerList.size() == 1) {
|
||||
blockerOrder.add(blockerList.get(0));
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ public abstract class StackObjImpl implements StackObject {
|
|||
newTarget.clearChosen();
|
||||
}
|
||||
}
|
||||
} while (targetController.isInGame() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1));
|
||||
} while (targetController.canRespond() && (targetId.equals(newTarget.getFirstTarget()) || newTarget.getTargets().size() != 1));
|
||||
// choose a new target
|
||||
} else {
|
||||
// build a target definition with exactly one possible target to select that replaces old target
|
||||
|
|
@ -235,7 +235,7 @@ public abstract class StackObjImpl implements StackObject {
|
|||
newTarget.addTarget(tempTarget.getFirstTarget(), target.getTargetAmount(targetId), ability, game, false);
|
||||
}
|
||||
}
|
||||
} while (again && targetController.isInGame());
|
||||
} while (again && targetController.canRespond());
|
||||
}
|
||||
} // keep the target
|
||||
else {
|
||||
|
|
|
|||
|
|
@ -25,15 +25,13 @@
|
|||
* 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.util.UUID;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -63,7 +61,7 @@ public class DeclareBlockersStep extends Step {
|
|||
public void beginStep(Game game, UUID activePlayerId) {
|
||||
super.beginStep(game, activePlayerId);
|
||||
game.getCombat().selectBlockers(game);
|
||||
if (!game.isPaused()) {
|
||||
if (!game.isPaused() && !game.executingRollback()) {
|
||||
game.getCombat().acceptBlockers(game);
|
||||
game.getCombat().damageAssignmentOrder(game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -163,6 +163,9 @@ public abstract class Phase implements Serializable {
|
|||
currentStep = step;
|
||||
if (!game.getState().getTurnMods().skipStep(activePlayerId, currentStep.getType())) {
|
||||
playStep(game);
|
||||
if (game.executingRollback()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -203,13 +206,13 @@ public abstract class Phase implements Serializable {
|
|||
if (!currentStep.skipStep(game, activePlayerId)) {
|
||||
game.getState().increaseStepNum();
|
||||
prePriority(game, activePlayerId);
|
||||
if (!game.isPaused() && !game.gameOver(null)) {
|
||||
if (!game.isPaused() && !game.gameOver(null) && !game.executingRollback()) {
|
||||
currentStep.priority(game, activePlayerId, false);
|
||||
if (game.executingRollback()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!game.isPaused() && !game.gameOver(null)) {
|
||||
if (!game.isPaused() && !game.gameOver(null) && !game.executingRollback()) {
|
||||
postPriority(game, activePlayerId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -214,6 +214,14 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
*/
|
||||
boolean isInGame();
|
||||
|
||||
/**
|
||||
* Player is still active in game (has not left, lost or won the game) and
|
||||
* no abort state is given.
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
boolean canRespond();
|
||||
|
||||
/**
|
||||
* Called if other player left the game
|
||||
*
|
||||
|
|
|
|||
|
|
@ -1399,7 +1399,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
filter.add(Predicates.not(new PermanentIdPredicate(permanent.getId())));
|
||||
}
|
||||
// while targets left and there is still allowed to untap
|
||||
while (isInGame() && leftForUntap.size() > 0 && numberToUntap > 0) {
|
||||
while (canRespond() && leftForUntap.size() > 0 && numberToUntap > 0) {
|
||||
// player has to select the permanent he wants to untap for this restriction
|
||||
Ability ability = handledEntry.getKey().getValue().iterator().next();
|
||||
if (ability != null) {
|
||||
|
|
@ -1449,7 +1449,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
} while (isInGame() && playerCanceledSelection);
|
||||
} while (canRespond() && playerCanceledSelection);
|
||||
|
||||
if (!game.isSimulation()) {
|
||||
// show in log which permanents were selected to untap
|
||||
|
|
@ -2017,6 +2017,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
return !hasQuit() && !hasLost() && !hasWon() && !hasLeft();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRespond() { // abort is checked here to get out of player requests
|
||||
return !hasQuit() && !hasLost() && !hasWon() && !hasLeft() && !abort;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasWon() {
|
||||
if (!this.loses) {
|
||||
|
|
@ -3014,7 +3019,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
|||
if (chooseOrder) {
|
||||
TargetCard target = new TargetCard(fromZone, new FilterCard("card to put on the top of your graveyard (last one chosen will be topmost)"));
|
||||
target.setRequired(true);
|
||||
while (choosingPlayer.isInGame() && cards.size() > 1) {
|
||||
while (choosingPlayer.canRespond() && cards.size() > 1) {
|
||||
choosingPlayer.chooseTarget(Outcome.Neutral, cards, target, source, game);
|
||||
UUID targetObjectId = target.getFirstTarget();
|
||||
Card card = cards.get(targetObjectId, game);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue