From 3d59c57b0e4f36ce1c003b7b374630951dda2c19 Mon Sep 17 00:00:00 2001 From: LoneFox Date: Thu, 11 Jun 2015 14:23:55 +0300 Subject: [PATCH] Fix NullPointerException when a random discard effect is applied to a player with empty hand. --- Mage/src/mage/players/PlayerImpl.java | 68 ++++++++++++++------------- 1 file changed, 35 insertions(+), 33 deletions(-) diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index a9fb708017..79702411d2 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -216,14 +216,14 @@ public abstract class PlayerImpl implements Player, Serializable { protected boolean topCardRevealed = false; // 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. + // or until a specific point in that turn will last until that turn would have begun. // They neither expire immediately nor last indefinitely. protected boolean reachedNextTurnAfterLeaving = false; // indicates that the spell with the set sourceId can be cast with an alternate mana costs (can also be no mana costs) protected UUID castSourceIdWithAlternateMana; protected ManaCosts castSourceIdManaCosts; - + // indicates that the player is in mana payment phase protected boolean payManaMode = false; @@ -369,7 +369,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.canPaySacrificeCost = player.canPaySacrificeCost(); this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife(); this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard(); - this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts()); + this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts()); this.topCardRevealed = player.isTopCardRevealed(); this.playersUnderYourControl.clear(); @@ -444,7 +444,7 @@ public abstract class PlayerImpl implements Player, Serializable { this.payManaMode = false; this.setLife(game.getLife(), game); this.setReachedNextTurnAfterLeaving(false); - + this.castSourceIdWithAlternateMana = null; this.castSourceIdManaCosts = null; } @@ -575,7 +575,7 @@ public abstract class PlayerImpl implements Player, Serializable { /** * returns true if the player has the control itself - false if the player is controlled by another player - * @return + * @return */ @Override public boolean isGameUnderControl() { @@ -704,8 +704,10 @@ public abstract class PlayerImpl implements Player, Serializable { if (random) { for (int i = 0; i < amount; i++) { Card card = this.getHand().getRandom(game); - discardedCards.add(card); - discard(card, source, game); + if(card != null) { + discardedCards.add(card); + discard(card, source, game); + } } } else { TargetDiscard target = new TargetDiscard(amount, amount, new FilterCard(CardUtil.numberToText(amount, "a") + " card" + (amount > 1 ? "s" : "")), playerId); @@ -802,7 +804,7 @@ public abstract class PlayerImpl implements Player, Serializable { attachedToPlayer.removeAttachment(permanent, game); } } - + } if (permanent.getPairedCard() != null) { Permanent pairedCard = game.getPermanent(permanent.getPairedCard()); @@ -876,7 +878,7 @@ public abstract class PlayerImpl implements Player, Serializable { while (isInGame() && cards.size() > 1) { this.choose(Outcome.Neutral, cards, target, game); UUID targetObjectId = target.getFirstTarget(); - cards.remove(targetObjectId); + cards.remove(targetObjectId); moveObjectToLibrary(targetObjectId, source.getSourceId(), game, true, false); target.clearChosen(); } @@ -917,7 +919,7 @@ public abstract class PlayerImpl implements Player, Serializable { return castSourceIdManaCosts; } - + @Override public boolean isInPayManaMode() { return payManaMode; @@ -947,7 +949,7 @@ public abstract class PlayerImpl implements Player, Serializable { spellAbility.getManaCosts().add(alternateCosts.copy()); spellAbility.getManaCostsToPay().clear(); spellAbility.getManaCostsToPay().add(alternateCosts.copy()); - } + } } setCastSourceIdWithAlternateMana(null, null); if (spell.activate(game, noMana)) { @@ -1528,7 +1530,7 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public String getLogName() { - return GameLog.getColoredPlayerName(name); + return GameLog.getColoredPlayerName(name); } @Override @@ -1669,11 +1671,11 @@ public abstract class PlayerImpl implements Player, Serializable { sourceControllerId = ((Spell) source).getControllerId(); } else if (source instanceof Card) { sourceAbilities = ((Card) source).getAbilities(game); - sourceControllerId = ((Card) source).getOwnerId(); + sourceControllerId = ((Card) source).getOwnerId(); } else if (source instanceof CommandObject){ sourceControllerId = ((CommandObject) source).getControllerId(); sourceAbilities = ((CommandObject) source).getAbilities(); - } + } } else { sourceAbilities = ((Permanent) source).getAbilities(game); sourceControllerId = ((Permanent) source).getControllerId(); @@ -2426,7 +2428,7 @@ public abstract class PlayerImpl implements Player, Serializable { if (game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NON_HAND_ZONE, this.getId(), game)) { for (Ability ability : card.getAbilities()) { if (ability.getZone().match(Zone.HAND)) { - ability.setControllerId(this.getId()); // controller must be set for case owner != caster + ability.setControllerId(this.getId()); // controller must be set for case owner != caster if (ability instanceof ActivatedAbility) { if (((ActivatedAbility) ability).canActivate(playerId, game)) { playable.add(ability); @@ -2537,7 +2539,7 @@ public abstract class PlayerImpl implements Player, Serializable { * @return */ private boolean shouldSkipGettingPlayable(Game game) { - if (game.getStep() == null) { // happens at the start of the game + if (game.getStep() == null) { // happens at the start of the game return true; } for (Entry phaseStep : silentPhaseSteps.entrySet()) { @@ -2841,16 +2843,16 @@ public abstract class PlayerImpl implements Player, Serializable { cardList.addAll(cards.getCards(game)); return moveCards(cardList, fromZone, toZone, source, game); } - + @Override public boolean moveCards(Card card, Zone fromZone, Zone toZone, Ability source, Game game) { ArrayList cardList = new ArrayList<>(); if (card != null) { cardList.add(card); } - return moveCards(cardList, fromZone, toZone, source, game); + return moveCards(cardList, fromZone, toZone, source, game); } - + @Override public boolean moveCards(List cards, Zone fromZone, Zone toZone, Ability source, Game game) { if (cards.isEmpty()) { @@ -2858,13 +2860,13 @@ public abstract class PlayerImpl implements Player, Serializable { } game.fireEvent(new ZoneChangeGroupEvent(cards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone)); switch(toZone) { - case EXILED: + case EXILED: boolean result = false; for(Card card: cards) { result |= moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, fromZone, true); } - return result; - case GRAVEYARD: + return result; + case GRAVEYARD: return moveCardsToGraveyardWithInfo(cards, source, game, fromZone); case HAND: result = false; @@ -2874,9 +2876,9 @@ public abstract class PlayerImpl implements Player, Serializable { return result; default: throw new UnsupportedOperationException("to Zone not supported yet"); - } + } } - + @Override public boolean moveCardToHandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) { return this.moveCardToHandWithInfo(card, sourceId, game, fromZone, true); @@ -2898,7 +2900,7 @@ public abstract class PlayerImpl implements Player, Serializable { default: sb.append(fromZone != null ? new StringBuilder(" from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(" ") : ""); break; - } + } sb.append(card.getOwnerId().equals(this.getId()) ? "into his or her hand" : "into its owner's hand"); game.informPlayers(sb.toString()); } @@ -2906,10 +2908,10 @@ public abstract class PlayerImpl implements Player, Serializable { } return result; } - + @Override public boolean moveCardsToGraveyardWithInfo(List allCards, Ability source, Game game, Zone fromZone) { - boolean result = true; + boolean result = true; UUID sourceId = source == null ? null : source.getSourceId(); while (!allCards.isEmpty()) { // identify cards from one owner @@ -2943,10 +2945,10 @@ public abstract class PlayerImpl implements Player, Serializable { target.setRequired(true); while (choosingPlayer.isInGame() && cards.size() > 1) { choosingPlayer.chooseTarget(Outcome.Neutral, cards, target, source, game); - UUID targetObjectId = target.getFirstTarget(); + UUID targetObjectId = target.getFirstTarget(); Card card = cards.get(targetObjectId, game); - cards.remove(targetObjectId); - if (card != null) { + cards.remove(targetObjectId); + if (card != null) { result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone); } target.clearChosen(); @@ -2959,11 +2961,11 @@ public abstract class PlayerImpl implements Player, Serializable { result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone); } } - } + } } return result; } - + @Override public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) { boolean result = false; @@ -3045,7 +3047,7 @@ public abstract class PlayerImpl implements Player, Serializable { public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped) { return this.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, tapped, false); } - + @Override public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped, boolean facedown) { boolean result = false;