From 3030feaaf1be56be82dad51f11ea47dbaf1b99d5 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Fri, 31 Mar 2023 23:11:44 +0400 Subject: [PATCH] * From among cards exiled - fixed that some cards can cause game error, (NPE fix, example: Rod of Absorption); --- .../src/mage/cards/c/ColfenorsPlans.java | 4 +- .../mage/cards/j/JelevaNephaliasScourge.java | 6 +- .../src/mage/cards/k/KahoMinamoHistorian.java | 11 +++- Mage.Sets/src/mage/cards/k/KarnLiberated.java | 38 ++++++------ .../src/mage/cards/k/KheruMindEater.java | 4 +- .../src/mage/cards/k/KingNarfisBetrayal.java | 6 +- .../src/mage/cards/l/LegionsInitiative.java | 6 +- Mage.Sets/src/mage/cards/l/LivingLore.java | 12 ++-- .../src/mage/cards/m/MoonringMirror.java | 62 ++++++++++--------- .../src/mage/cards/n/NightveilSpecter.java | 4 +- .../src/mage/cards/o/OlagLudevicsHubris.java | 9 ++- .../src/mage/cards/r/RodOfAbsorption.java | 9 ++- .../src/mage/cards/s/ShellOfTheLastKappa.java | 7 ++- .../src/mage/cards/t/TheaterOfHorrors.java | 6 +- 14 files changed, 109 insertions(+), 75 deletions(-) diff --git a/Mage.Sets/src/mage/cards/c/ColfenorsPlans.java b/Mage.Sets/src/mage/cards/c/ColfenorsPlans.java index 8c461e68ae..e6632e2481 100644 --- a/Mage.Sets/src/mage/cards/c/ColfenorsPlans.java +++ b/Mage.Sets/src/mage/cards/c/ColfenorsPlans.java @@ -117,8 +117,8 @@ class ColfenorsPlansPlayCardEffect extends AsThoughEffectImpl { @Override public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { if (affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId) == Zone.EXILED) { - ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - return zone != null && zone.contains(objectId); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); + return exileZone != null && exileZone.contains(objectId); } return false; } diff --git a/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java b/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java index 30f8158937..4e01e76db8 100644 --- a/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java +++ b/Mage.Sets/src/mage/cards/j/JelevaNephaliasScourge.java @@ -13,6 +13,7 @@ import mage.cards.Cards; import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.StaticFilters; +import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -125,10 +126,11 @@ class JelevaNephaliasCastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller == null) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); + if (controller == null || exileZone == null) { return false; } - Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source))); + Cards cards = new CardsImpl(exileZone); return CardUtil.castSpellWithAttributesForFree( controller, source, game, cards, StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY diff --git a/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java b/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java index 3cd51456f0..dee4d00c63 100644 --- a/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java +++ b/Mage.Sets/src/mage/cards/k/KahoMinamoHistorian.java @@ -16,6 +16,7 @@ import mage.cards.CardsImpl; import mage.constants.*; import mage.filter.FilterCard; import mage.filter.predicate.mageobject.ManaValuePredicate; +import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; import mage.target.common.TargetCardInLibrary; @@ -121,10 +122,16 @@ class KahoMinamoHistorianCastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source))); - if (controller == null || cards.isEmpty()) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); + if (controller == null || exileZone == null) { return false; } + + Cards cards = new CardsImpl(exileZone); + if (cards.isEmpty()) { + return false; + } + FilterCard filter = new FilterCard(); filter.add(new ManaValuePredicate(ComparisonType.EQUAL_TO, source.getManaCostsToPay().getX())); return CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, filter); diff --git a/Mage.Sets/src/mage/cards/k/KarnLiberated.java b/Mage.Sets/src/mage/cards/k/KarnLiberated.java index d83d596df4..fc84e14dbb 100644 --- a/Mage.Sets/src/mage/cards/k/KarnLiberated.java +++ b/Mage.Sets/src/mage/cards/k/KarnLiberated.java @@ -186,27 +186,29 @@ class KarnLiberatedDelayedEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); - if (controller != null) { - ExileZone exile = game.getExile().getExileZone(exileId); - if (exile != null) { - // Creatures put onto the battlefield due to Karn's ability will have been under their controller's control continuously - // since the beginning of the first turn. They can attack and their activated abilities with {T} in the cost can be activated. - Cards cards = new CardsImpl(exile); // needed because putOntoTheBattlefield removes from exile - if (!cards.isEmpty()) { - controller.moveCards(cards, Zone.BATTLEFIELD, source, game); - for (Card card : cards.getCards(game)) { - if (card != null) { - Permanent permanent = game.getPermanent(card.getId()); - if (permanent != null) { - ((PermanentImpl) permanent).removeSummoningSickness(); - } - } - } + ExileZone exileZone = game.getExile().getExileZone(exileId); + if (controller == null || exileZone == null) { + return false; + } + + // Creatures put onto the battlefield due to Karn's ability will have been under their controller's control continuously + // since the beginning of the first turn. They can attack and their activated abilities with {T} in the cost can be activated. + Cards cards = new CardsImpl(exileZone); // needed because putOntoTheBattlefield removes from exile + if (cards.isEmpty()) { + return false; + } + + controller.moveCards(cards, Zone.BATTLEFIELD, source, game); + for (Card card : cards.getCards(game)) { + if (card != null) { + Permanent permanent = game.getPermanent(card.getId()); + if (permanent != null) { + ((PermanentImpl) permanent).removeSummoningSickness(); } } - return true; } - return false; + + return true; } @Override diff --git a/Mage.Sets/src/mage/cards/k/KheruMindEater.java b/Mage.Sets/src/mage/cards/k/KheruMindEater.java index 7a722dd559..325fc63f25 100644 --- a/Mage.Sets/src/mage/cards/k/KheruMindEater.java +++ b/Mage.Sets/src/mage/cards/k/KheruMindEater.java @@ -121,8 +121,8 @@ class KheruMindEaterEffect extends AsThoughEffectImpl { public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { Card card = game.getCard(objectId); if (affectedControllerId.equals(source.getControllerId()) && card != null && game.getState().getZone(card.getId()) == Zone.EXILED) { - ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - return zone != null && zone.contains(card.getId()); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); + return exileZone != null && exileZone.contains(card.getId()); } return false; } diff --git a/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java b/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java index 68ccae8e71..18c05ed036 100644 --- a/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java +++ b/Mage.Sets/src/mage/cards/k/KingNarfisBetrayal.java @@ -143,9 +143,9 @@ class KingNarfisBetrayalSecondEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - if (zone != null) { - for (Card card : zone.getCards(game)) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); + if (exileZone != null) { + for (Card card : exileZone.getCards(game)) { CardUtil.makeCardPlayable(game, source, card, Duration.EndOfTurn, true); } } diff --git a/Mage.Sets/src/mage/cards/l/LegionsInitiative.java b/Mage.Sets/src/mage/cards/l/LegionsInitiative.java index e3ffe081f0..eee0411ff0 100644 --- a/Mage.Sets/src/mage/cards/l/LegionsInitiative.java +++ b/Mage.Sets/src/mage/cards/l/LegionsInitiative.java @@ -136,11 +136,11 @@ class LegionsInitiativeReturnFromExileEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - ExileZone exile = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source, -1)); - if (player == null || exile == null || exile.isEmpty()) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source, -1)); + if (player == null || exileZone == null || exileZone.isEmpty()) { return false; } - Cards cards = new CardsImpl(exile); + Cards cards = new CardsImpl(exileZone); player.moveCards(cards, Zone.BATTLEFIELD, source, game); List permanents = cards.stream().map(game::getPermanent).filter(Objects::nonNull).collect(Collectors.toList()); if (permanents.isEmpty()) { diff --git a/Mage.Sets/src/mage/cards/l/LivingLore.java b/Mage.Sets/src/mage/cards/l/LivingLore.java index 7856013733..23c74fecd5 100644 --- a/Mage.Sets/src/mage/cards/l/LivingLore.java +++ b/Mage.Sets/src/mage/cards/l/LivingLore.java @@ -156,11 +156,11 @@ class LivingLoreCastEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source, -2)); - return controller != null - && exileZone != null - && !exileZone.isEmpty() - && CardUtil.castSpellWithAttributesForFree( - controller, source, game, new CardsImpl(exileZone), StaticFilters.FILTER_CARD - ); + if (controller == null || exileZone == null || exileZone.isEmpty()) { + return false; + } + + return CardUtil.castSpellWithAttributesForFree(controller, source, game, + new CardsImpl(exileZone), StaticFilters.FILTER_CARD); } } diff --git a/Mage.Sets/src/mage/cards/m/MoonringMirror.java b/Mage.Sets/src/mage/cards/m/MoonringMirror.java index 0a1a4febde..4e17d3b307 100644 --- a/Mage.Sets/src/mage/cards/m/MoonringMirror.java +++ b/Mage.Sets/src/mage/cards/m/MoonringMirror.java @@ -1,29 +1,26 @@ package mage.cards.m; -import java.util.HashSet; -import java.util.Set; -import java.util.UUID; import mage.MageObject; import mage.abilities.Ability; import mage.abilities.common.BeginningOfUpkeepTriggeredAbility; import mage.abilities.common.DrawCardControllerTriggeredAbility; import mage.abilities.effects.OneShotEffect; -import mage.cards.Card; -import mage.cards.CardImpl; -import mage.cards.CardSetInfo; -import mage.cards.Cards; -import mage.cards.CardsImpl; +import mage.cards.*; import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.TargetController; import mage.constants.Zone; +import mage.game.ExileZone; import mage.game.Game; import mage.players.Player; import mage.util.CardUtil; +import java.util.HashSet; +import java.util.Set; +import java.util.UUID; + /** - * * @author LevelX2 */ public final class MoonringMirror extends CardImpl { @@ -31,7 +28,7 @@ public final class MoonringMirror extends CardImpl { protected static final String VALUE_PREFIX = "ExileZones"; public MoonringMirror(UUID ownerId, CardSetInfo setInfo) { - super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{5}"); + super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{5}"); // Whenever you draw a card, exile the top card of your library face down. this.addAbility(new DrawCardControllerTriggeredAbility(new MoonringMirrorExileEffect(), false)); @@ -110,26 +107,35 @@ class MoonringMirrorEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); MageObject sourceObject = source.getSourceObject(game); - if (controller != null && sourceObject != null) { - UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); - Cards cardsToHand = null; - if (game.getExile().getExileZone(exileZoneId) != null && !game.getExile().getExileZone(exileZoneId).isEmpty()) { - cardsToHand = new CardsImpl(game.getExile().getExileZone(exileZoneId)); - } - for (Card card : controller.getHand().getCards(game)) { + if (controller == null || sourceObject == null) { + return false; + } + + UUID exileZoneId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter()); + ExileZone exileZone = game.getExile().getExileZone(exileZoneId); + + Cards cardsToHand = null; + if (exileZone != null && !exileZone.isEmpty()) { + cardsToHand = new CardsImpl(exileZone); + } + + // hand + for (Card card : controller.getHand().getCards(game)) { + card.setFaceDown(true, game); + } + controller.moveCardsToExile(controller.getHand().getCards(game), source, game, false, exileZoneId, sourceObject.getIdName()); + + if (cardsToHand != null) { + controller.moveCards(cardsToHand.getCards(game), Zone.HAND, source, game, false, true, false, null); + } + + exileZone = game.getExile().getExileZone(exileZoneId); + if (exileZone != null && !exileZone.isEmpty()) { + for (Card card : game.getExile().getExileZone(exileZoneId).getCards(game)) { card.setFaceDown(true, game); } - controller.moveCardsToExile(controller.getHand().getCards(game), source, game, false, exileZoneId, sourceObject.getIdName()); - if (cardsToHand != null) { - controller.moveCards(cardsToHand.getCards(game), Zone.HAND, source, game, false, true, false, null); - } - if (game.getExile().getExileZone(exileZoneId) != null) { - for (Card card : game.getExile().getExileZone(exileZoneId).getCards(game)) { - card.setFaceDown(true, game); - } - } - return true; } - return false; + + return true; } } diff --git a/Mage.Sets/src/mage/cards/n/NightveilSpecter.java b/Mage.Sets/src/mage/cards/n/NightveilSpecter.java index 2e1aad0f47..6090839515 100644 --- a/Mage.Sets/src/mage/cards/n/NightveilSpecter.java +++ b/Mage.Sets/src/mage/cards/n/NightveilSpecter.java @@ -126,8 +126,8 @@ class NightveilSpecterEffect extends AsThoughEffectImpl { objectId = theCard.getMainCard().getId();// for split cards if (affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId) == Zone.EXILED) { - ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - return zone != null && zone.contains(objectId); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); + return exileZone != null && exileZone.contains(objectId); } return false; } diff --git a/Mage.Sets/src/mage/cards/o/OlagLudevicsHubris.java b/Mage.Sets/src/mage/cards/o/OlagLudevicsHubris.java index fda1d84b23..7e2f6815f6 100644 --- a/Mage.Sets/src/mage/cards/o/OlagLudevicsHubris.java +++ b/Mage.Sets/src/mage/cards/o/OlagLudevicsHubris.java @@ -9,6 +9,7 @@ import mage.abilities.effects.common.CopyEffect; import mage.cards.*; import mage.constants.*; import mage.filter.StaticFilters; +import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.permanent.Permanent; @@ -67,11 +68,17 @@ class OlagLudevicsHubrisEffect extends ReplacementEffectImpl { @Override public boolean replaceEvent(GameEvent event, Ability source, Game game) { - Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getExileZoneId(game, source))); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (exileZone == null) { + return false; + } + + Cards cards = new CardsImpl(exileZone); cards.removeIf(uuid -> !game.getCard(uuid).isCreature(game)); if (cards.isEmpty()) { return false; } + Card copyFromCard = getCard(cards, source, game); if (copyFromCard == null) { return false; diff --git a/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java b/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java index 4da3c94390..ecc73b156c 100644 --- a/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java +++ b/Mage.Sets/src/mage/cards/r/RodOfAbsorption.java @@ -15,6 +15,7 @@ import mage.constants.Duration; import mage.constants.Outcome; import mage.constants.Zone; import mage.filter.StaticFilters; +import mage.game.ExileZone; import mage.game.Game; import mage.game.events.GameEvent; import mage.game.events.ZoneChangeEvent; @@ -167,10 +168,16 @@ class RodOfAbsorptionCastEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { Player player = game.getPlayer(source.getControllerId()); - Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getExileZoneId(game, source))); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (exileZone == null || exileZone.isEmpty()) { + return false; + } + + Cards cards = new CardsImpl(exileZone); if (player == null || cards.isEmpty()) { return false; } + CardUtil.castMultipleWithAttributeForFree( player, source, game, cards, StaticFilters.FILTER_CARD, Integer.MAX_VALUE, new RodOfAbsorptionTracker(source.getManaCostsToPay().getX()) diff --git a/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java b/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java index 36b9ceda84..c86501e7c0 100644 --- a/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java +++ b/Mage.Sets/src/mage/cards/s/ShellOfTheLastKappa.java @@ -16,6 +16,7 @@ import mage.filter.StaticFilters; import mage.filter.predicate.ObjectSourcePlayer; import mage.filter.predicate.ObjectSourcePlayerPredicate; import mage.filter.predicate.Predicates; +import mage.game.ExileZone; import mage.game.Game; import mage.game.permanent.Permanent; import mage.game.stack.Spell; @@ -132,10 +133,12 @@ class ShellOfTheLastKappaCastEffect extends OneShotEffect { public boolean apply(Game game, Ability source) { Player controller = game.getPlayer(source.getControllerId()); Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId()); - if (controller == null || sourcePermanent == null) { + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getExileZoneId(game, source)); + if (controller == null || sourcePermanent == null || exileZone == null) { return false; } - Cards cards = new CardsImpl(game.getExile().getExileZone(CardUtil.getExileZoneId(game, source))); + + Cards cards = new CardsImpl(exileZone); return CardUtil.castSpellWithAttributesForFree(controller, source, game, cards, StaticFilters.FILTER_CARD); } } diff --git a/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java b/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java index 6fb986eb58..8cd90df81c 100644 --- a/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java +++ b/Mage.Sets/src/mage/cards/t/TheaterOfHorrors.java @@ -126,9 +126,9 @@ class TheaterOfHorrorsCastEffect extends AsThoughEffectImpl { && watcher.getAllOppLifeLost(source.getControllerId(), game) > 0 && affectedControllerId.equals(source.getControllerId()) && game.getState().getZone(objectId) == Zone.EXILED) { - ExileZone zone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); - return zone != null - && zone.contains(objectId); + ExileZone exileZone = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)); + return exileZone != null + && exileZone.contains(objectId); } return false; }