* 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:
LevelX2 2014-05-29 10:27:52 +02:00
parent dad109b88e
commit 8bd3109c87
19 changed files with 43 additions and 44 deletions

View file

@ -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

View file

@ -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) {

View file

@ -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);

View file

@ -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"));

View file

@ -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;

View file

@ -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);

View file

@ -110,15 +110,18 @@ 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 {
card = player.getLibrary().getFromTop(game);
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;

View file

@ -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) {

View file

@ -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();

View file

@ -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();

View file

@ -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;

View file

@ -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);
/**

View file

@ -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;