mirror of
https://github.com/correl/mage.git
synced 2025-03-13 01:09:53 -09:00
* Fixed some game locking loops if a player concedes while resolving an effect (e.g. Scry, Discard). Some changes to game log for info about moving cards to library.
This commit is contained in:
parent
dad109b88e
commit
8bd3109c87
19 changed files with 43 additions and 44 deletions
Mage.Sets/src/mage/sets
alarareborn
journeyintonyx
mirage
returntoravnica
seventhedition
zendikar
Mage/src/mage
abilities
costs/common
effects/common
LookLibraryControllerEffect.javaReturnFromExileEffect.javaRevealLibraryPutIntoHandEffect.javaScryEffect.java
discard
keyword
cards
players
|
@ -167,6 +167,9 @@ class CascadeEffect extends OneShotEffect<CascadeEffect> {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Card card;
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
ExileZone exile = game.getExile().createZone(source.getSourceId(), player.getName() + " Cascade");
|
||||
Card stackCard = game.getCard(targetPointer.getFirst(game, source));
|
||||
if (stackCard == null) {
|
||||
|
@ -179,7 +182,7 @@ class CascadeEffect extends OneShotEffect<CascadeEffect> {
|
|||
break;
|
||||
}
|
||||
|
||||
card.moveToExile(exile.getId(), exile.getName(), source.getId(), game);
|
||||
player.moveCardToExileWithInfo(card, source.getId(), exile.getName(), source.getSourceId(), game, Zone.LIBRARY);
|
||||
} while (card.getCardType().contains(CardType.LAND) || card.getManaCost().convertedManaCost() >= sourceCost);
|
||||
|
||||
if (card != null) {
|
||||
|
@ -192,7 +195,7 @@ class CascadeEffect extends OneShotEffect<CascadeEffect> {
|
|||
while (exile.size() > 0) {
|
||||
card = exile.getRandom(game);
|
||||
exile.remove(card.getId());
|
||||
card.moveToZone(Zone.LIBRARY, source.getId(), game, false);
|
||||
player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.EXILED, false, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -42,12 +42,10 @@ import mage.constants.Rarity;
|
|||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
import mage.game.Game;
|
||||
|
|
|
@ -98,7 +98,7 @@ class PainfulMemoriesEffect extends OneShotEffect<PainfulMemoriesEffect> {
|
|||
if (you.choose(Outcome.Benefit, targetPlayer.getHand(), target, game)) {
|
||||
Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
return targetPlayer.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.HAND, true);
|
||||
return targetPlayer.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.HAND, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -311,13 +311,13 @@ class JaceArchitectOfThoughtEffect2 extends OneShotEffect<JaceArchitectOfThought
|
|||
Card card = cardsToLibrary.get(targetCard.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
cardsToLibrary.remove(card);
|
||||
player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, false);
|
||||
player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, false, false);
|
||||
}
|
||||
target.clearChosen();
|
||||
}
|
||||
if (cardsToLibrary.size() == 1) {
|
||||
Card card = cardsToLibrary.get(cardsToLibrary.iterator().next(), game);
|
||||
player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, false);
|
||||
player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, false, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -99,14 +99,13 @@ class AgonizingMemoriesEffect extends OneShotEffect<AgonizingMemoriesEffect> {
|
|||
}
|
||||
|
||||
private void chooseCardInHandAndPutOnTopOfLibrary(Game game, Ability source, Player you, Player targetPlayer) {
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
if (targetPlayer.getHand().size() > 0) {
|
||||
TargetCard target = new TargetCard(Zone.PICK, new FilterCard("card to put on the top of library (last chosen will be on top)"));
|
||||
target.setRequired(true);
|
||||
if (you.choose(Outcome.Benefit, targetPlayer.getHand(), target, game)) {
|
||||
Card card = targetPlayer.getHand().get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
targetPlayer.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.HAND, true);
|
||||
targetPlayer.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.HAND, true, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,7 +110,7 @@ class GomazoaEffect extends OneShotEffect<GomazoaEffect> {
|
|||
List<UUID> players = new ArrayList<>();
|
||||
Permanent gomazoa = game.getPermanent(source.getSourceId());
|
||||
if (gomazoa != null) {
|
||||
controller.moveCardToLibraryWithInfo(gomazoa, source.getSourceId(), game, Zone.BATTLEFIELD, true);
|
||||
controller.moveCardToLibraryWithInfo(gomazoa, source.getSourceId(), game, Zone.BATTLEFIELD, true, true);
|
||||
players.add(gomazoa.getOwnerId());
|
||||
}
|
||||
|
||||
|
@ -123,7 +123,7 @@ class GomazoaEffect extends OneShotEffect<GomazoaEffect> {
|
|||
players.add(blockedByGomazoa.getOwnerId());
|
||||
Player owner = game.getPlayer(blockedByGomazoa.getOwnerId());
|
||||
if (owner != null) {
|
||||
owner.moveCardToLibraryWithInfo(blockedByGomazoa, source.getSourceId(), game, Zone.BATTLEFIELD, true);
|
||||
owner.moveCardToLibraryWithInfo(blockedByGomazoa, source.getSourceId(), game, Zone.BATTLEFIELD, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ public class PutTopCardOfYourLibraryToGraveyardCost extends CostImpl<PutTopCardO
|
|||
if (player != null && player.getLibrary().size() >= numberOfCards) {
|
||||
int i = 0;
|
||||
paid = true;
|
||||
while (i < numberOfCards) {
|
||||
while (player.isInGame() && i < numberOfCards) {
|
||||
Card card = player.getLibrary().removeFromTop(game);
|
||||
if (card != null) {
|
||||
// all cards must reach the graveyard to pay the costs
|
||||
|
|
|
@ -165,7 +165,7 @@ public class LookLibraryControllerEffect extends OneShotEffect<LookLibraryContro
|
|||
case LIBRARY:
|
||||
TargetCard target = new TargetCard(Zone.PICK, new FilterCard(this.getPutBackText()));
|
||||
target.setRequired(true);
|
||||
while (cards.size() > 1) {
|
||||
while (player.isInGame() && cards.size() > 1) {
|
||||
player.choose(Outcome.Neutral, cards, target, game);
|
||||
Card card = cards.get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
|
|
|
@ -102,7 +102,7 @@ public class ReturnFromExileEffect extends OneShotEffect<ReturnFromExileEffect>
|
|||
controller.moveCardToGraveyardWithInfo(card, source.getSourceId(), game, Zone.EXILED);
|
||||
break;
|
||||
case LIBRARY:
|
||||
controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.EXILED, true);
|
||||
controller.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.EXILED, true, true);
|
||||
break;
|
||||
default:
|
||||
card.moveToZone(zone, source.getSourceId(), game, tapped);
|
||||
|
|
|
@ -98,7 +98,7 @@ public class RevealLibraryPutIntoHandEffect extends OneShotEffect<RevealLibraryP
|
|||
}
|
||||
}
|
||||
|
||||
while (cards.size() > 1) {
|
||||
while (player.isInGame() && cards.size() > 1) {
|
||||
Card card;
|
||||
if (anyOrder) {
|
||||
TargetCard target = new TargetCard(Zone.PICK, new FilterCard("card to put on the bottom of your library"));
|
||||
|
|
|
@ -28,13 +28,13 @@
|
|||
|
||||
package mage.abilities.effects.common;
|
||||
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
|
@ -81,37 +81,32 @@ public class ScryEffect extends OneShotEffect<ScryEffect> {
|
|||
}
|
||||
TargetCard target1 = new TargetCard(Zone.LIBRARY, filter1);
|
||||
// move cards to the bottom of the library
|
||||
while (cards.size() > 0 && player.choose(Outcome.Detriment, cards, target1, game)) {
|
||||
while (player.isInGame() && cards.size() > 0 && player.choose(Outcome.Detriment, cards, target1, game)) {
|
||||
Card card = cards.get(target1.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
cards.remove(card);
|
||||
card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false);
|
||||
player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, false, false);
|
||||
}
|
||||
target1.clearChosen();
|
||||
}
|
||||
// move cards to the top of the library
|
||||
int onBottom = scryNumber - cards.size();
|
||||
if (cards.size() > 1) {
|
||||
TargetCard target2 = new TargetCard(Zone.LIBRARY, filter2);
|
||||
target2.setRequired(true);
|
||||
while (cards.size() > 1) {
|
||||
while (player.isInGame() && cards.size() > 1) {
|
||||
player.choose(Outcome.Benefit, cards, target2, game);
|
||||
Card card = cards.get(target2.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
cards.remove(card);
|
||||
card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
|
||||
player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, true, false);
|
||||
}
|
||||
target2.clearChosen();
|
||||
}
|
||||
}
|
||||
if (cards.size() == 1) {
|
||||
Card card = cards.get(cards.iterator().next(), game);
|
||||
card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
|
||||
player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.LIBRARY, true, false);
|
||||
}
|
||||
game.informPlayers(new StringBuilder(player.getName()).append(" puts ")
|
||||
.append(onBottom).append(onBottom == 1 ?" card":" cards")
|
||||
.append(" on the bottom of his or her library (scry ")
|
||||
.append(scryNumber).append(")").toString());
|
||||
game.fireEvent(new GameEvent(GameEvent.EventType.SCRY, source.getControllerId(), source.getSourceId(), source.getControllerId()));
|
||||
player.setTopCardRevealed(revealed);
|
||||
return true;
|
||||
|
|
|
@ -82,7 +82,7 @@ public class DiscardEachPlayerEffect extends OneShotEffect<DiscardEachPlayerEffe
|
|||
int numberOfCardsToDiscard = Math.min(amount.calculate(game, source), player.getHand().size());
|
||||
Cards cards = new CardsImpl();
|
||||
if (randomDiscard) {
|
||||
while (cards.size() < numberOfCardsToDiscard) {
|
||||
while (player.isInGame() && cards.size() < numberOfCardsToDiscard) {
|
||||
Card card = player.getHand().getRandom(game);
|
||||
if (!cards.contains(card.getId())) {
|
||||
cards.add(card);
|
||||
|
|
|
@ -110,6 +110,9 @@ class CascadeEffect extends OneShotEffect<CascadeEffect> {
|
|||
public boolean apply(Game game, Ability source) {
|
||||
Card card;
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
ExileZone exile = game.getExile().createZone(source.getSourceId(), player.getName() + " Cascade");
|
||||
int sourceCost = game.getCard(source.getSourceId()).getManaCost().convertedManaCost();
|
||||
do {
|
||||
|
@ -117,8 +120,8 @@ class CascadeEffect extends OneShotEffect<CascadeEffect> {
|
|||
if (card == null) {
|
||||
break;
|
||||
}
|
||||
card.moveToExile(exile.getId(), exile.getName(), source.getId(), game);
|
||||
} while (card.getCardType().contains(CardType.LAND) || card.getManaCost().convertedManaCost() >= sourceCost);
|
||||
player.moveCardToExileWithInfo(card, source.getId(), exile.getName(), source.getSourceId(), game, Zone.LIBRARY);
|
||||
} while (player.isInGame() && card.getCardType().contains(CardType.LAND) || card.getManaCost().convertedManaCost() >= sourceCost);
|
||||
|
||||
if (card != null) {
|
||||
if (player.chooseUse(outcome, "Use cascade effect on " + card.getName() + "?", game)) {
|
||||
|
@ -130,7 +133,7 @@ class CascadeEffect extends OneShotEffect<CascadeEffect> {
|
|||
while (exile.size() > 0) {
|
||||
card = exile.getRandom(game);
|
||||
exile.remove(card.getId());
|
||||
card.moveToZone(Zone.LIBRARY, source.getId(), game, false);
|
||||
player.moveCardToLibraryWithInfo(card, source.getSourceId(), game, Zone.EXILED, false, false);
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
|
@ -141,7 +141,7 @@ class HideawayExileEffect extends OneShotEffect<HideawayExileEffect> {
|
|||
if (cards.size() > 0) {
|
||||
TargetCard target2 = new TargetCard(Zone.PICK, filter2);
|
||||
target2.setRequired(true);
|
||||
while (cards.size() > 1) {
|
||||
while (player.isInGame() && cards.size() > 1) {
|
||||
player.choose(Outcome.Benefit, cards, target2, game);
|
||||
Card card = cards.get(target2.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
|
|
|
@ -201,7 +201,7 @@ public class KickerAbility extends StaticAbility<KickerAbility> implements Optio
|
|||
this.resetKicker();
|
||||
for (OptionalAdditionalCost kickerCost: kickerCosts) {
|
||||
boolean again = true;
|
||||
while (again) {
|
||||
while (player.isInGame() && again) {
|
||||
String times = "";
|
||||
if (kickerCost.isRepeatable()) {
|
||||
int activated = kickerCost.getActivateCount();
|
||||
|
|
|
@ -114,7 +114,7 @@ public class ReplicateAbility extends StaticAbility<ReplicateAbility> implements
|
|||
this.resetReplicate();
|
||||
|
||||
boolean again = true;
|
||||
while (again) {
|
||||
while (player.isInGame() && again) {
|
||||
String times = "";
|
||||
if (additionalCost.isRepeatable()) {
|
||||
int activated = additionalCost.getActivateCount();
|
||||
|
|
|
@ -104,7 +104,7 @@ public class Sets extends HashMap<String, ExpansionSet> {
|
|||
|
||||
int count = 0;
|
||||
int tries = 0;
|
||||
List<Card> cardPool = new ArrayList<Card>();
|
||||
List<Card> cardPool = new ArrayList<>();
|
||||
while (count < cardsCount) {
|
||||
CardInfo cardInfo = cards.get(rnd.nextInt(cards.size()));
|
||||
Card card = cardInfo != null ? cardInfo.getCard() : null;
|
||||
|
|
|
@ -439,9 +439,10 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
* @param game
|
||||
* @param fromZone if null, this info isn't postet
|
||||
* @param toTop to the top of the library else to the bottom
|
||||
* @param withName show the card name in the log
|
||||
* @return
|
||||
*/
|
||||
boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean toTop);
|
||||
boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean toTop, boolean withName);
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -563,7 +563,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
|
|||
public void discardToMax(Game game) {
|
||||
if (hand.size() > this.maxHandSize) {
|
||||
game.informPlayers(new StringBuilder(getName()).append(" discards down to ").append(this.maxHandSize).append(this.maxHandSize == 1?" hand card":" hand cards").toString());
|
||||
while (hand.size() > this.maxHandSize) {
|
||||
while (isInGame() && hand.size() > this.maxHandSize) {
|
||||
TargetDiscard target = new TargetDiscard(playerId);
|
||||
target.setTargetName(new StringBuilder(" card to discard (").append(hand.size() - this.maxHandSize).append(" in total)").toString());
|
||||
choose(Outcome.Discard, target, null, game);
|
||||
|
@ -607,7 +607,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
|
|||
return;
|
||||
}
|
||||
int numDiscarded = 0;
|
||||
while (numDiscarded < amount) {
|
||||
while (isInGame() && numDiscarded < amount) {
|
||||
if (hand.size() == 0) {
|
||||
break;
|
||||
}
|
||||
|
@ -713,7 +713,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
|
|||
} else {
|
||||
TargetCard target = new TargetCard(Zone.PICK, new FilterCard("card to put on the bottom of your library"));
|
||||
target.setRequired(true);
|
||||
while (cards.size() > 1) {
|
||||
while (isInGame() && cards.size() > 1) {
|
||||
this.choose(Outcome.Neutral, cards, target, game);
|
||||
Card chosenCard = cards.get(target.getFirstTarget(), game);
|
||||
if (chosenCard != null) {
|
||||
|
@ -1130,7 +1130,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
|
|||
filter.add(Predicates.not(new PermanentIdPredicate(permanent.getId())));
|
||||
}
|
||||
// while targets left and there is still allowed to untap
|
||||
while (leftForUntap.size() > 0 && numberToUntap > 0) {
|
||||
while (isInGame() && 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) {
|
||||
|
@ -1180,7 +1180,7 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
|
|||
}
|
||||
}
|
||||
|
||||
} while (playerCanceledSelection);
|
||||
} while (isInGame() && playerCanceledSelection);
|
||||
|
||||
// show in log which permanents were selected to untap
|
||||
for(Permanent permanent :selectedToUntap) {
|
||||
|
@ -2237,11 +2237,11 @@ public abstract class PlayerImpl<T extends PlayerImpl<T>> implements Player, Ser
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean toTop) {
|
||||
public boolean moveCardToLibraryWithInfo(Card card, UUID sourceId, Game game, Zone fromZone, boolean toTop, boolean withName) {
|
||||
boolean result = false;
|
||||
if (card.moveToZone(Zone.LIBRARY, sourceId, game, toTop)) {
|
||||
game.informPlayers(new StringBuilder(this.getName())
|
||||
.append(" puts ").append(card.getName()).append(" ")
|
||||
.append(" puts ").append(withName ? card.getName():"a card").append(" ")
|
||||
.append(fromZone != null ? new StringBuilder("from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(" "):"")
|
||||
.append("to the ").append(toTop ? "top":"bottom").append(" of his or her library").toString());
|
||||
result = true;
|
||||
|
|
Loading…
Add table
Reference in a new issue