* From among cards exiled - fixed that some cards can cause game error, (NPE fix, example: Rod of Absorption);

This commit is contained in:
Oleg Agafonov 2023-03-31 23:11:44 +04:00
parent f5223b010b
commit 3030feaaf1
14 changed files with 109 additions and 75 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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<Permanent> permanents = cards.stream().map(game::getPermanent).filter(Objects::nonNull).collect(Collectors.toList());
if (permanents.isEmpty()) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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