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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

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;