mirror of
https://github.com/correl/mage.git
synced 2025-04-13 01:01:11 -09:00
Fix NullPointerException when a random discard effect is applied to a player with empty hand.
This commit is contained in:
parent
81108be346
commit
3d59c57b0e
1 changed files with 35 additions and 33 deletions
|
@ -216,14 +216,14 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
protected boolean topCardRevealed = false;
|
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
|
// 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.
|
// They neither expire immediately nor last indefinitely.
|
||||||
protected boolean reachedNextTurnAfterLeaving = false;
|
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)
|
// 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 UUID castSourceIdWithAlternateMana;
|
||||||
protected ManaCosts castSourceIdManaCosts;
|
protected ManaCosts castSourceIdManaCosts;
|
||||||
|
|
||||||
// indicates that the player is in mana payment phase
|
// indicates that the player is in mana payment phase
|
||||||
protected boolean payManaMode = false;
|
protected boolean payManaMode = false;
|
||||||
|
|
||||||
|
@ -369,7 +369,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
this.canPaySacrificeCost = player.canPaySacrificeCost();
|
this.canPaySacrificeCost = player.canPaySacrificeCost();
|
||||||
this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife();
|
this.loseByZeroOrLessLife = player.canLoseByZeroOrLessLife();
|
||||||
this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard();
|
this.canPlayCardsFromGraveyard = player.canPlayCardsFromGraveyard();
|
||||||
this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts());
|
this.alternativeSourceCosts.addAll(player.getAlternativeSourceCosts());
|
||||||
|
|
||||||
this.topCardRevealed = player.isTopCardRevealed();
|
this.topCardRevealed = player.isTopCardRevealed();
|
||||||
this.playersUnderYourControl.clear();
|
this.playersUnderYourControl.clear();
|
||||||
|
@ -444,7 +444,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
this.payManaMode = false;
|
this.payManaMode = false;
|
||||||
this.setLife(game.getLife(), game);
|
this.setLife(game.getLife(), game);
|
||||||
this.setReachedNextTurnAfterLeaving(false);
|
this.setReachedNextTurnAfterLeaving(false);
|
||||||
|
|
||||||
this.castSourceIdWithAlternateMana = null;
|
this.castSourceIdWithAlternateMana = null;
|
||||||
this.castSourceIdManaCosts = 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
|
* returns true if the player has the control itself - false if the player is controlled by another player
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public boolean isGameUnderControl() {
|
public boolean isGameUnderControl() {
|
||||||
|
@ -704,8 +704,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
if (random) {
|
if (random) {
|
||||||
for (int i = 0; i < amount; i++) {
|
for (int i = 0; i < amount; i++) {
|
||||||
Card card = this.getHand().getRandom(game);
|
Card card = this.getHand().getRandom(game);
|
||||||
discardedCards.add(card);
|
if(card != null) {
|
||||||
discard(card, source, game);
|
discardedCards.add(card);
|
||||||
|
discard(card, source, game);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
TargetDiscard target = new TargetDiscard(amount, amount, new FilterCard(CardUtil.numberToText(amount, "a") + " card" + (amount > 1 ? "s" : "")), playerId);
|
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);
|
attachedToPlayer.removeAttachment(permanent, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (permanent.getPairedCard() != null) {
|
if (permanent.getPairedCard() != null) {
|
||||||
Permanent pairedCard = game.getPermanent(permanent.getPairedCard());
|
Permanent pairedCard = game.getPermanent(permanent.getPairedCard());
|
||||||
|
@ -876,7 +878,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
while (isInGame() && cards.size() > 1) {
|
while (isInGame() && cards.size() > 1) {
|
||||||
this.choose(Outcome.Neutral, cards, target, game);
|
this.choose(Outcome.Neutral, cards, target, game);
|
||||||
UUID targetObjectId = target.getFirstTarget();
|
UUID targetObjectId = target.getFirstTarget();
|
||||||
cards.remove(targetObjectId);
|
cards.remove(targetObjectId);
|
||||||
moveObjectToLibrary(targetObjectId, source.getSourceId(), game, true, false);
|
moveObjectToLibrary(targetObjectId, source.getSourceId(), game, true, false);
|
||||||
target.clearChosen();
|
target.clearChosen();
|
||||||
}
|
}
|
||||||
|
@ -917,7 +919,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
return castSourceIdManaCosts;
|
return castSourceIdManaCosts;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInPayManaMode() {
|
public boolean isInPayManaMode() {
|
||||||
return payManaMode;
|
return payManaMode;
|
||||||
|
@ -947,7 +949,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
spellAbility.getManaCosts().add(alternateCosts.copy());
|
spellAbility.getManaCosts().add(alternateCosts.copy());
|
||||||
spellAbility.getManaCostsToPay().clear();
|
spellAbility.getManaCostsToPay().clear();
|
||||||
spellAbility.getManaCostsToPay().add(alternateCosts.copy());
|
spellAbility.getManaCostsToPay().add(alternateCosts.copy());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setCastSourceIdWithAlternateMana(null, null);
|
setCastSourceIdWithAlternateMana(null, null);
|
||||||
if (spell.activate(game, noMana)) {
|
if (spell.activate(game, noMana)) {
|
||||||
|
@ -1528,7 +1530,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLogName() {
|
public String getLogName() {
|
||||||
return GameLog.getColoredPlayerName(name);
|
return GameLog.getColoredPlayerName(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -1669,11 +1671,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
sourceControllerId = ((Spell) source).getControllerId();
|
sourceControllerId = ((Spell) source).getControllerId();
|
||||||
} else if (source instanceof Card) {
|
} else if (source instanceof Card) {
|
||||||
sourceAbilities = ((Card) source).getAbilities(game);
|
sourceAbilities = ((Card) source).getAbilities(game);
|
||||||
sourceControllerId = ((Card) source).getOwnerId();
|
sourceControllerId = ((Card) source).getOwnerId();
|
||||||
} else if (source instanceof CommandObject){
|
} else if (source instanceof CommandObject){
|
||||||
sourceControllerId = ((CommandObject) source).getControllerId();
|
sourceControllerId = ((CommandObject) source).getControllerId();
|
||||||
sourceAbilities = ((CommandObject) source).getAbilities();
|
sourceAbilities = ((CommandObject) source).getAbilities();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sourceAbilities = ((Permanent) source).getAbilities(game);
|
sourceAbilities = ((Permanent) source).getAbilities(game);
|
||||||
sourceControllerId = ((Permanent) source).getControllerId();
|
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)) {
|
if (game.getContinuousEffects().asThough(card.getId(), AsThoughEffectType.PLAY_FROM_NON_HAND_ZONE, this.getId(), game)) {
|
||||||
for (Ability ability : card.getAbilities()) {
|
for (Ability ability : card.getAbilities()) {
|
||||||
if (ability.getZone().match(Zone.HAND)) {
|
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 (ability instanceof ActivatedAbility) {
|
||||||
if (((ActivatedAbility) ability).canActivate(playerId, game)) {
|
if (((ActivatedAbility) ability).canActivate(playerId, game)) {
|
||||||
playable.add(ability);
|
playable.add(ability);
|
||||||
|
@ -2537,7 +2539,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
private boolean shouldSkipGettingPlayable(Game game) {
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
for (Entry<PhaseStep, Step.StepPart> phaseStep : silentPhaseSteps.entrySet()) {
|
for (Entry<PhaseStep, Step.StepPart> phaseStep : silentPhaseSteps.entrySet()) {
|
||||||
|
@ -2841,16 +2843,16 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
cardList.addAll(cards.getCards(game));
|
cardList.addAll(cards.getCards(game));
|
||||||
return moveCards(cardList, fromZone, toZone, source, game);
|
return moveCards(cardList, fromZone, toZone, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(Card card, Zone fromZone, Zone toZone, Ability source, Game game) {
|
public boolean moveCards(Card card, Zone fromZone, Zone toZone, Ability source, Game game) {
|
||||||
ArrayList<Card> cardList = new ArrayList<>();
|
ArrayList<Card> cardList = new ArrayList<>();
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
cardList.add(card);
|
cardList.add(card);
|
||||||
}
|
}
|
||||||
return moveCards(cardList, fromZone, toZone, source, game);
|
return moveCards(cardList, fromZone, toZone, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCards(List<Card> cards, Zone fromZone, Zone toZone, Ability source, Game game) {
|
public boolean moveCards(List<Card> cards, Zone fromZone, Zone toZone, Ability source, Game game) {
|
||||||
if (cards.isEmpty()) {
|
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));
|
game.fireEvent(new ZoneChangeGroupEvent(cards, source == null ? null : source.getSourceId(), this.getId(), fromZone, toZone));
|
||||||
switch(toZone) {
|
switch(toZone) {
|
||||||
case EXILED:
|
case EXILED:
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
for(Card card: cards) {
|
for(Card card: cards) {
|
||||||
result |= moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, fromZone, true);
|
result |= moveCardToExileWithInfo(card, null, "", source == null ? null : source.getSourceId(), game, fromZone, true);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
case GRAVEYARD:
|
case GRAVEYARD:
|
||||||
return moveCardsToGraveyardWithInfo(cards, source, game, fromZone);
|
return moveCardsToGraveyardWithInfo(cards, source, game, fromZone);
|
||||||
case HAND:
|
case HAND:
|
||||||
result = false;
|
result = false;
|
||||||
|
@ -2874,9 +2876,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
return result;
|
return result;
|
||||||
default:
|
default:
|
||||||
throw new UnsupportedOperationException("to Zone not supported yet");
|
throw new UnsupportedOperationException("to Zone not supported yet");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardToHandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) {
|
public boolean moveCardToHandWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) {
|
||||||
return this.moveCardToHandWithInfo(card, sourceId, game, fromZone, true);
|
return this.moveCardToHandWithInfo(card, sourceId, game, fromZone, true);
|
||||||
|
@ -2898,7 +2900,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
default:
|
default:
|
||||||
sb.append(fromZone != null ? new StringBuilder(" from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(" ") : "");
|
sb.append(fromZone != null ? new StringBuilder(" from ").append(fromZone.toString().toLowerCase(Locale.ENGLISH)).append(" ") : "");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
sb.append(card.getOwnerId().equals(this.getId()) ? "into his or her hand" : "into its owner's hand");
|
sb.append(card.getOwnerId().equals(this.getId()) ? "into his or her hand" : "into its owner's hand");
|
||||||
game.informPlayers(sb.toString());
|
game.informPlayers(sb.toString());
|
||||||
}
|
}
|
||||||
|
@ -2906,10 +2908,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardsToGraveyardWithInfo(List<Card> allCards, Ability source, Game game, Zone fromZone) {
|
public boolean moveCardsToGraveyardWithInfo(List<Card> allCards, Ability source, Game game, Zone fromZone) {
|
||||||
boolean result = true;
|
boolean result = true;
|
||||||
UUID sourceId = source == null ? null : source.getSourceId();
|
UUID sourceId = source == null ? null : source.getSourceId();
|
||||||
while (!allCards.isEmpty()) {
|
while (!allCards.isEmpty()) {
|
||||||
// identify cards from one owner
|
// identify cards from one owner
|
||||||
|
@ -2943,10 +2945,10 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
target.setRequired(true);
|
target.setRequired(true);
|
||||||
while (choosingPlayer.isInGame() && cards.size() > 1) {
|
while (choosingPlayer.isInGame() && cards.size() > 1) {
|
||||||
choosingPlayer.chooseTarget(Outcome.Neutral, cards, target, source, game);
|
choosingPlayer.chooseTarget(Outcome.Neutral, cards, target, source, game);
|
||||||
UUID targetObjectId = target.getFirstTarget();
|
UUID targetObjectId = target.getFirstTarget();
|
||||||
Card card = cards.get(targetObjectId, game);
|
Card card = cards.get(targetObjectId, game);
|
||||||
cards.remove(targetObjectId);
|
cards.remove(targetObjectId);
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone);
|
result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone);
|
||||||
}
|
}
|
||||||
target.clearChosen();
|
target.clearChosen();
|
||||||
|
@ -2959,11 +2961,11 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone);
|
result &= choosingPlayer.moveCardToGraveyardWithInfo(card, sourceId, game, fromZone);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) {
|
public boolean moveCardToGraveyardWithInfo(Card card, UUID sourceId, Game game, Zone fromZone) {
|
||||||
boolean result = false;
|
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) {
|
public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped) {
|
||||||
return this.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, tapped, false);
|
return this.putOntoBattlefieldWithInfo(card, game, fromZone, sourceId, tapped, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped, boolean facedown) {
|
public boolean putOntoBattlefieldWithInfo(Card card, Game game, Zone fromZone, UUID sourceId, boolean tapped, boolean facedown) {
|
||||||
boolean result = false;
|
boolean result = false;
|
||||||
|
|
Loading…
Add table
Reference in a new issue