refactor methods to find cards in exile (#13967)

* refactor exile method names, add comments

* fix card effects checking exile with filter to process ObjectSourcePlayerPredicates

* fix card effects checking exile to respect range of influence
This commit is contained in:
xenohedron 2025-09-12 17:06:53 -04:00 committed by GitHub
parent 32af4a0671
commit 34c26f09c8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
60 changed files with 183 additions and 195 deletions

View file

@ -117,7 +117,7 @@ class AltairIbnLaAhadTokenEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Set<Card> cards = game
.getExile()
.getAllCards(game, source.getControllerId())
.getCardsOwned(game, source.getControllerId())
.stream()
.filter(card -> card.getCounters(game).containsKey(CounterType.MEMORY))
.filter(card -> card.isCreature(game))

View file

@ -11,6 +11,7 @@ import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.card.FaceDownPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.AshiokNightmareMuseToken;
@ -20,8 +21,6 @@ import mage.target.common.TargetNonlandPermanent;
import mage.util.CardUtil;
import java.util.UUID;
import mage.filter.predicate.card.FaceDownPredicate;
import mage.filter.predicate.card.OwnerIdPredicate;
/**
* @author TheElk801
@ -96,6 +95,10 @@ class AshiokNightmareMuseBounceEffect extends OneShotEffect {
class AshiokNightmareMuseCastEffect extends OneShotEffect {
private static final FilterCard filter = new FilterCard("face-up cards your opponents own from exile");
static {
filter.add(TargetController.OPPONENT.getOwnerPredicate());
filter.add(Predicates.not(FaceDownPredicate.instance));
}
AshiokNightmareMuseCastEffect() {
super(Outcome.Benefit);
@ -117,11 +120,10 @@ class AshiokNightmareMuseCastEffect extends OneShotEffect {
if (controller == null) {
return false;
}
// card is owned by an opponent and is face up
filter.add(Predicates.not(new OwnerIdPredicate(controller.getId())));
filter.add(Predicates.not(FaceDownPredicate.instance));
CardUtil.castMultipleWithAttributeForFree(
controller, source, game, new CardsImpl(game.getExile().getCards(filter, game)),
controller, source, game, new CardsImpl(
game.getExile().getCardsInRange(filter, controller.getId(), source, game)
),
StaticFilters.FILTER_CARD, 3
);
return true;

View file

@ -88,7 +88,7 @@ class BiotransferenceEffect extends ContinuousEffectImpl {
}
}
// in Exile
for (Card card : game.getState().getExile().getAllCards(game, source.getControllerId())) {
for (Card card : game.getState().getExile().getCardsOwned(game, source.getControllerId())) {
if (card.isCreature(game) && !card.isArtifact(game)) {
card.addCardType(game, CardType.ARTIFACT);
}

View file

@ -65,7 +65,7 @@ enum CandlekeepInspirationValue implements DynamicValue {
.getCards(game)
.stream(),
game.getExile()
.getAllCards(game, sourceAbility.getControllerId())
.getCardsOwned(game, sourceAbility.getControllerId())
.stream()
)
.filter(Objects::nonNull)

View file

@ -128,7 +128,7 @@ class CelestialDawnToWhiteEffect extends ContinuousEffectImpl {
}
}
// Exile
for (Card card : game.getExile().getAllCardsByRange(game, controller.getId())) {
for (Card card : game.getExile().getCardsInRange(game, controller.getId())) {
if (card.isOwnedBy(controller.getId())) {
setColor(card.getColor(game), game);
}

View file

@ -164,7 +164,7 @@ class ChissGoriaForgeTyrantAffinityEffect extends ContinuousEffectImpl {
return false;
}
for (Card card : game.getExile().getAllCardsByRange(game, source.getControllerId())) {
for (Card card : game.getExile().getCardsInRange(game, source.getControllerId())) {
if (morSet.contains(new MageObjectReference(card, game)) && card.isArtifact(game)) {
game.getState().addOtherAbility(card, new AffinityForArtifactsAbility());
}

View file

@ -92,14 +92,14 @@ public final class Conspiracy extends CardImpl {
}
}
// in Exile
for (Card card : game.getState().getExile().getAllCards(game)) {
if (card.isOwnedBy(controller.getId()) && card.isCreature(game)) {
for (Card card : game.getState().getExile().getCardsOwned(game, controller.getId())) {
if (card.isCreature(game)) {
setCreatureSubtype(card, subType, game);
}
}
// in Library (e.g. for Mystical Teachings)
for (Card card : controller.getLibrary().getCards(game)) {
if (card.isOwnedBy(controller.getId()) && card.isCreature(game)) {
if (card.isCreature(game)) {
setCreatureSubtype(card, subType, game);
}
}

View file

@ -56,7 +56,7 @@ enum CosmogoyfValue implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return game.getState().getExile().getAllCards(game, sourceAbility.getControllerId()).size();
return game.getState().getExile().getCardsOwned(game, sourceAbility.getControllerId()).size();
}
@Override

View file

@ -161,7 +161,7 @@ class TargetControlledSource extends TargetSource {
possibleTargets.add(card.getId());
}
// 108.4a If anything asks for the controller of a card that doesn't have one (because it's not a permanent or spell), use its owner instead.
for (Card card : game.getExile().getAllCards(game)) {
for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) {
if (Objects.equals(card.getOwnerId(), sourceControllerId)) {
possibleTargets.add(card.getId());
}

View file

@ -79,7 +79,7 @@ class DragonKamisEggEffect extends OneShotEffect {
}
Cards cards = new CardsImpl();
game.getExile()
.getAllCards(game, player.getId())
.getCardsOwned(game, player.getId())
.stream()
.filter(Objects::nonNull)
.filter(card -> card.getCounters(game).containsKey(CounterType.HATCHLING))

View file

@ -115,7 +115,7 @@ class DuneChanterContinuousEffect extends ContinuousEffectImpl {
}
}
// in exile
for (Card card : game.getState().getExile().getAllCards(game, controllerId)) {
for (Card card : game.getState().getExile().getCardsOwned(game, controllerId)) {
if (filterCard.match(card, controllerId, source, game) && !card.hasSubtype(subType, game)) {
game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType);
}
@ -174,5 +174,3 @@ class DuneChanterEffect extends OneShotEffect {
return true;
}
}

View file

@ -83,7 +83,7 @@ class DustOfMomentsEffect extends OneShotEffect {
permanent.addCounters(CounterType.TIME.createInstance(2), source, game);
}
}
for (Card card : game.getExile().getCards(filter.getCardFilter(), game)) {
for (Card card : game.getExile().getCardsInRange(filter.getCardFilter(), source.getControllerId(), source, game)) {
if (remove) {
card.removeCounters(CounterType.TIME.createInstance(2), source, game);
} else {

View file

@ -80,7 +80,7 @@ class EncroachingMycosynthEffect extends ContinuousEffectImpl {
}
}
// in Exile
for (Card card : game.getState().getExile().getAllCards(game, source.getControllerId())) {
for (Card card : game.getState().getExile().getCardsOwned(game, source.getControllerId())) {
if (card.isPermanent(game) && !card.isLand(game) && !card.isArtifact(game)) {
card.addCardType(game, CardType.ARTIFACT);
}

View file

@ -96,8 +96,8 @@ class EtrataTheSilencerEffect extends OneShotEffect {
if (card != null) {
card.addCounters(CounterType.HIT.createInstance(), source.getControllerId(), source, game);
}
int cardsFound = game.getExile().getAllCards(game).stream()
.filter(c -> c.getOwnerId().equals(player.getId()))
int cardsFound = game.getExile().getCardsOwned(game, player.getId())
.stream()
.filter(c -> c.getCounters(game).getCount(CounterType.HIT) > 0)
.mapToInt(x -> 1)
.sum();

View file

@ -85,7 +85,9 @@ class FalkenrathGorgerEffect extends ContinuousEffectImpl {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
if (controller == null) {
return false;
}
Map<UUID, MadnessAbility> usedMadnessAbilities = new HashMap<>();
// hand
for (Card card : controller.getHand().getCards(filter, game)) {
@ -96,19 +98,13 @@ class FalkenrathGorgerEffect extends ContinuousEffectImpl {
addMadnessToCard(game, card, usedMadnessAbilities);
}
// Exile
for (Card card : game.getExile().getAllCards(game)) {
if (filter.match(card, controller.getId(), source, game)) {
if (card.isOwnedBy(controller.getId())) {
for (Card card : game.getExile().getCardsOwned(filter, controller.getId(), source, game)) {
addMadnessToCard(game, card, usedMadnessAbilities);
}
}
}
madnessAbilities.clear();
madnessAbilities.putAll(usedMadnessAbilities);
return true;
}
return false;
}
private void addMadnessToCard(Game game, Card card, Map<UUID, MadnessAbility> usedMadnessAbilities) {

View file

@ -97,7 +97,7 @@ class FlamewarBrashVeteranEffect extends OneShotEffect {
}
Set<Card> cards = game
.getExile()
.getAllCards(game, source.getControllerId())
.getCardsOwned(game, source.getControllerId())
.stream()
.filter(card -> card.getCounters(game).containsKey(CounterType.INTEL))
.collect(Collectors.toSet());

View file

@ -2,12 +2,12 @@ package mage.cards.h;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.keyword.ScryEffect;
import mage.abilities.keyword.HexproofAbility;
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
@ -15,7 +15,6 @@ import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.predicate.mageobject.NamePredicate;
@ -93,9 +92,7 @@ class HedronAlignmentEffect extends OneShotEffect {
if (controller.getGraveyard().getCards(filterCard, controller.getId(), source, game).isEmpty()) {
return true;
}
Cards cardsToCheck = new CardsImpl();
cardsToCheck.addAllCards(game.getExile().getAllCards(game));
if (cardsToCheck.count(filterCard, controller.getId(), source, game) == 0) {
if (game.getExile().getCardsOwned(filterCard, controller.getId(), source, game).isEmpty()) {
return true;
}
controller.won(game);

View file

@ -87,7 +87,7 @@ class HenzieToolboxTorreGainBlitzEffect extends ContinuousEffectImpl {
controller.getLibrary().getCards(game).stream()
.filter(c -> filter.match(c, game))
.forEach(cardsToGainBlitz::add);
game.getExile().getAllCardsByRange(game, controller.getId()).stream()
game.getExile().getCardsInRange(game, controller.getId()).stream()
.filter(c -> filter.match(c, game))
.forEach(cardsToGainBlitz::add);
game.getCommanderCardsFromCommandZone(controller, CommanderCardType.ANY)

View file

@ -87,7 +87,7 @@ class HerigastEruptingNullkiteEffect extends ContinuousEffectImpl {
controller.getLibrary().getCards(game).stream()
.filter(c -> StaticFilters.FILTER_CARD_CREATURE.match(c, game))
.forEach(cardsToGainEmerge::add);
game.getExile().getAllCardsByRange(game, controller.getId()).stream()
game.getExile().getCardsInRange(game, controller.getId()).stream()
.filter(c -> StaticFilters.FILTER_CARD_CREATURE.match(c, game))
.forEach(cardsToGainEmerge::add);
game.getCommanderCardsFromCommandZone(controller, CommanderCardType.ANY)

View file

@ -59,7 +59,7 @@ enum HowlingGalefangCondition implements Condition {
public boolean apply(Game game, Ability source) {
return game
.getExile()
.getAllCards(game, source.getControllerId())
.getCardsOwned(game, source.getControllerId())
.stream()
.anyMatch(AdventureCard.class::isInstance);
}

View file

@ -77,13 +77,8 @@ enum HuskbursterSwarmValue implements DynamicValue {
}
return game
.getExile()
.getAllCards(game)
.stream()
.filter(card -> card.isCreature(game))
.map(Ownerable::getOwnerId)
.filter(sourceAbility::isControlledBy)
.mapToInt(x -> 1)
.sum()
.getCardsOwned(StaticFilters.FILTER_CARD_CREATURE, player.getId(), sourceAbility, game)
.size()
+ player
.getGraveyard()
.count(StaticFilters.FILTER_CARD_CREATURE, game);

View file

@ -18,10 +18,8 @@ import mage.target.TargetCard;
import mage.target.common.TargetCardInExile;
import mage.target.common.TargetOpponent;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author spjspj
@ -150,13 +148,7 @@ class KarnMinus1Effect extends OneShotEffect {
if (controller == null) {
return false;
}
Cards cards = new CardsImpl(game
.getExile()
.getCards(filter, game)
.stream()
.filter(Objects::nonNull)
.filter(card -> card.isOwnedBy(source.getControllerId()))
.collect(Collectors.toList()));
Cards cards = new CardsImpl(game.getExile().getCardsOwned(filter, controller.getId(), source, game));
Card card;
switch (cards.size()) {
case 0:
@ -173,6 +165,6 @@ class KarnMinus1Effect extends OneShotEffect {
if (card == null) {
return false;
}
return card != null && controller.moveCards(card, Zone.HAND, source, game);
return controller.moveCards(card, Zone.HAND, source, game);
}
}

View file

@ -131,12 +131,7 @@ class KayaOrzhovUsurperDamageEffect extends OneShotEffect {
if (controller == null || player == null) {
return false;
}
int count = 0;
for (Card card : game.getExile().getAllCards(game)) {
if (card != null && card.getOwnerId().equals(player.getId())) {
count += 1;
}
}
int count = game.getExile().getCardsOwned(game, player.getId()).size();
player.damage(count, source.getSourceId(), source, game);
controller.gainLife(count, game, source);
return true;

View file

@ -127,10 +127,8 @@ enum KianneDeanOfSubstanceValue implements DynamicValue {
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return game
.getExile()
.getAllCards(game)
.getCardsOwned(game, sourceAbility.getControllerId())
.stream()
.filter(Objects::nonNull)
.filter(card -> card.isOwnedBy(sourceAbility.getControllerId()))
.filter(card -> card.getCounters(game).containsKey(CounterType.STUDY))
.map(MageObject::getManaValue)
.distinct()
@ -155,10 +153,8 @@ enum KianneDeanOfSubstanceHint implements Hint {
@Override
public String getText(Game game, Ability ability) {
List<String> values = game.getExile()
.getAllCards(game)
.getCardsOwned(game, ability.getControllerId())
.stream()
.filter(Objects::nonNull)
.filter(card -> card.isOwnedBy(ability.getControllerId()))
.filter(card -> card.getCounters(game).containsKey(CounterType.STUDY))
.mapToInt(MageObject::getManaValue)
.distinct()
@ -166,7 +162,7 @@ enum KianneDeanOfSubstanceHint implements Hint {
.mapToObj(String::valueOf)
.collect(Collectors.toList());
return "Mana values of cards exiled with study counters: " + values.size()
+ (values.size() > 0 ? " (" + String.join(", ", values) + ')' : "");
+ (values.isEmpty() ? "" : " (" + String.join(", ", values) + ')');
}
@Override

View file

@ -125,7 +125,7 @@ class LivioOathswornSentinelReturnEffect extends OneShotEffect {
}
Set<Card> cards = game
.getExile()
.getAllCards(game)
.getCardsInRange(game, player.getId())
.stream()
.filter(Objects::nonNull)
.filter(card -> card.getCounters(game).containsKey(CounterType.AEGIS))

View file

@ -135,8 +135,7 @@ class MairsilThePretenderGainAbilitiesEffect extends ContinuousEffectImpl {
if (perm == null) {
return false;
}
for (Card card : game.getExile().getAllCards(game)) {
if (filter.match(card, game) && Objects.equals(card.getOwnerId(), perm.getControllerId())) {
for (Card card : game.getExile().getCardsOwned(filter, perm.getControllerId(), source, game)) {
for (Ability ability : card.getAbilities(game)) {
if (ability.isActivatedAbility()) {
ActivatedAbility copyAbility = (ActivatedAbility) ability.copy();
@ -145,7 +144,6 @@ class MairsilThePretenderGainAbilitiesEffect extends ContinuousEffectImpl {
}
}
}
}
return true;
}

View file

@ -96,7 +96,7 @@ class MaskwoodNexusEffect extends ContinuousEffectImpl {
.forEach(affectedCards::add);
// in Exile
game.getState().getExile().getAllCards(game, controller.getId()).stream()
game.getState().getExile().getCardsOwned(game, controller.getId()).stream()
.filter(card -> card.isOwnedBy(controller.getId()))
.forEach(affectedCards::add);

View file

@ -107,7 +107,7 @@ class EverythingIsColorlessEffect extends ContinuousEffectImpl {
}
// exile
affectedCards.addAll(game.getExile().getAllCardsByRange(game, controller.getId()));
affectedCards.addAll(game.getExile().getCardsInRange(game, controller.getId()));
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {

View file

@ -1,7 +1,5 @@
package mage.cards.o;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
@ -12,19 +10,20 @@ import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.common.FilterLandCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.card.FaceDownPredicate;
import mage.filter.predicate.card.OwnerIdPredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author LevelX2
@ -56,6 +55,11 @@ public final class OblivionSower extends CardImpl {
class OblivionSowerEffect extends OneShotEffect {
private static final FilterCard filter = new FilterLandCard();
static {
filter.add(Predicates.not(FaceDownPredicate.instance));
}
OblivionSowerEffect() {
super(Outcome.PutLandInPlay);
this.staticText = ", then you may put any number of land cards that player owns from exile onto the battlefield under your control";
@ -78,14 +82,12 @@ class OblivionSowerEffect extends OneShotEffect {
*/
Player controller = game.getPlayer(source.getControllerId());
Player targetPlayer = game.getPlayer(getTargetPointer().getFirst(game, source));
if (controller != null && targetPlayer != null) {
FilterLandCard filter = new FilterLandCard();
filter.add(new OwnerIdPredicate(targetPlayer.getId()));
filter.add(Predicates.not(FaceDownPredicate.instance));
Cards exiledCards = new CardsImpl();
exiledCards.addAllCards(game.getExile().getAllCards(game));
Cards exiledLands = new CardsImpl();
exiledLands.addAllCards(exiledCards.getCards(filter, controller.getId(), source, game));
if (controller == null || targetPlayer == null) {
return false;
}
Cards exiledLands = new CardsImpl(game.getExile()
.getCardsOwned(filter, targetPlayer.getId(), source, game)
);
if (!exiledLands.isEmpty() && controller.chooseUse(outcome, "Put lands into play?", source, game)) {
FilterCard filterToPlay = new FilterCard("land"
+ (exiledLands.size() > 1 ? "s" : "") + " from exile owned by "
@ -97,6 +99,4 @@ class OblivionSowerEffect extends OneShotEffect {
}
return true;
}
return false;
}
}

View file

@ -82,7 +82,7 @@ class PaintersServantEffect extends ContinuousEffectImpl {
}
// exile
affectedCards.addAll(game.getExile().getAllCardsByRange(game, controller.getId()));
affectedCards.addAll(game.getExile().getCardsInRange(game, controller.getId()));
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);

View file

@ -106,7 +106,7 @@ class AmazingSpiderManEffect extends ContinuousEffectImpl {
controller.getLibrary().getCards(game).stream()
.filter(c -> filter.match(c, game))
.forEach(cardsToGainAbility::add);
game.getExile().getAllCardsByRange(game, controller.getId()).stream()
game.getExile().getCardsInRange(game, controller.getId()).stream()
.filter(c -> filter.match(c, game))
.forEach(cardsToGainAbility::add);
game.getCommanderCardsFromCommandZone(controller, CommanderCardType.ANY).stream()

View file

@ -124,7 +124,7 @@ class RavenloftAdventurerLifeEffect extends OneShotEffect {
}
int count = game
.getExile()
.getAllCards(game, player.getId())
.getCardsOwned(game, player.getId())
.stream()
.filter(card -> card.getCounters(game).containsKey(CounterType.HIT))
.mapToInt(x -> 1)

View file

@ -74,7 +74,7 @@ class RayamiFirstOfTheFallenEffect extends ContinuousEffectImpl {
return false;
}
game.getExile()
.getAllCards(game)
.getCardsInRange(game, sourcePermanent.getControllerId())
.stream()
.filter(card1 -> card1.isCreature(game))
.filter(card -> card.getCounters(game).getCount(CounterType.BLOOD) > 0)

View file

@ -104,7 +104,7 @@ class RexCyberhoundContinuousEffect extends ContinuousEffectImpl {
filter.add(CounterType.BRAIN.getPredicate());
}
public RexCyberhoundContinuousEffect() {
RexCyberhoundContinuousEffect() {
super(Duration.WhileOnBattlefield, Layer.AbilityAddingRemovingEffects_6, SubLayer.NA, Outcome.AddAbility);
staticText = "{this} has all activated abilities of all cards in exile with brain counters on them";
addDependencyType(DependencyType.AddingAbility);
@ -125,7 +125,7 @@ class RexCyberhoundContinuousEffect extends ContinuousEffectImpl {
if (perm == null) {
return false;
}
for (Card card : game.getExile().getCards(filter, game)) {
for (Card card : game.getExile().getCardsInRange(filter, source.getControllerId(), source, game)) {
for (Ability ability : card.getAbilities(game)) {
if (ability.isActivatedAbility()) {
ActivatedAbility copyAbility = (ActivatedAbility) ability.copy();

View file

@ -85,7 +85,7 @@ enum RoseTylerValue implements DynamicValue {
.count(filter.getPermanentFilter(), sourceAbility.getControllerId(), sourceAbility, game)
+ game
.getExile()
.getAllCards(game, sourceAbility.getControllerId())
.getCardsOwned(game, sourceAbility.getControllerId())
.stream()
.filter(card -> card.isOwnedBy(sourceAbility.getControllerId()))
.mapToInt(card -> filter.getCardFilter().match(card, game) ? 1 : 0)

View file

@ -23,7 +23,6 @@ import mage.filter.predicate.Predicates;
import mage.game.Game;
import mage.players.Player;
import java.util.Optional;
import java.util.UUID;
/**
@ -69,7 +68,6 @@ enum SailorsBaneValue implements DynamicValue {
private static final FilterCard filter = new FilterCard();
static {
filter.add(TargetController.YOU.getOwnerPredicate());
filter.add(Predicates.or(
CardType.INSTANT.getPredicate(),
CardType.SORCERY.getPredicate(),
@ -79,15 +77,12 @@ enum SailorsBaneValue implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return Optional
.ofNullable(game.getPlayer(sourceAbility.getControllerId()))
.map(Player::getGraveyard)
.map(graveyard -> graveyard.count(filter, game))
.orElse(0)
+ game
.getExile()
.getCards(filter, game)
.size();
Player player = game.getPlayer(sourceAbility.getControllerId());
if (player == null) {
return 0;
}
return game.getExile().getCardsOwned(filter, player.getId(), sourceAbility, game).size()
+ player.getGraveyard().count(filter, player.getId(), sourceAbility, game);
}
@Override
@ -100,7 +95,7 @@ enum SailorsBaneValue implements DynamicValue {
return "";
}
private static final boolean checkAdventure(Card input, Game game) {
private static boolean checkAdventure(Card input, Game game) {
return input instanceof AdventureCard;
}
}

View file

@ -62,7 +62,7 @@ enum SeizeTheStormValue implements DynamicValue {
return player.getGraveyard().count(
StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game
) + game.getExile()
.getAllCards(game, sourceAbility.getControllerId())
.getCardsOwned(game, sourceAbility.getControllerId())
.stream()
.filter(card -> card.getAbilities(game).containsClass(FlashbackAbility.class))
.mapToInt(x -> 1).sum();

View file

@ -87,7 +87,7 @@ class SlimeAgainstHumanityEffect extends OneShotEffect {
int exileCount = game
.getState()
.getExile()
.getAllCards(game, source.getControllerId())
.getCardsOwned(game, source.getControllerId())
.stream()
.filter(card -> filter.match(card, game))
.mapToInt(x -> 1)

View file

@ -132,7 +132,7 @@ class TashaTheWitchQueenCastEffect extends OneShotEffect {
if (player == null) {
return false;
}
Cards cards = new CardsImpl(game.getExile().getCards(filter, game));
Cards cards = new CardsImpl(game.getExile().getCardsInRange(filter, source.getControllerId(), source, game));
return !cards.isEmpty() && CardUtil.castSpellWithAttributesForFree(player, source, game, cards, StaticFilters.FILTER_CARD);
}
}

View file

@ -87,14 +87,14 @@ class TeferiMageOfZhalfirAddFlashEffect extends ContinuousEffectImpl {
}
}
// in Exile
for (Card card : game.getState().getExile().getAllCards(game)) {
if (card.isOwnedBy(controller.getId()) && card.isCreature(game)) {
for (Card card : game.getState().getExile().getCardsOwned(game, controller.getId())) {
if (card.isCreature(game)) {
game.getState().addOtherAbility(card, FlashAbility.getInstance());
}
}
// in Library (e.g. for Mystical Teachings)
for (Card card : controller.getLibrary().getCards(game)) {
if (card.isOwnedBy(controller.getId()) && card.isCreature(game)) {
if (card.isCreature(game)) {
game.getState().addOtherAbility(card, FlashAbility.getInstance());
}
}

View file

@ -116,7 +116,7 @@ enum UlamogTheDefilerValue implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return game.getExile()
.getAllCardsByRange(game, sourceAbility.getControllerId())
.getCardsInRange(game, sourceAbility.getControllerId())
.stream()
.mapToInt(Card::getManaValue)
.max()

View file

@ -1,7 +1,5 @@
package mage.cards.w;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
@ -13,12 +11,13 @@ import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.Zone;
import mage.constants.SubType;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author LevelX2
@ -35,9 +34,10 @@ public final class WardenOfTheBeyond extends CardImpl {
// Vigilance
this.addAbility(VigilanceAbility.getInstance());
// Warden of the Beyond gets +2/+2 as long as an opponent owns a card in exile.
this.addAbility(new SimpleStaticAbility(
new ConditionalContinuousEffect(new BoostSourceEffect(2,2,Duration.WhileOnBattlefield), OpponentOwnsCardInExileCondition.instance,
new ConditionalContinuousEffect(new BoostSourceEffect(2, 2, Duration.WhileOnBattlefield), WardenOfTheBeyondCondition.instance,
"{this} gets +2/+2 as long as an opponent owns a card in exile")));
}
@ -51,7 +51,7 @@ public final class WardenOfTheBeyond extends CardImpl {
}
}
enum OpponentOwnsCardInExileCondition implements Condition {
enum WardenOfTheBeyondCondition implements Condition {
instance;
@ -59,7 +59,7 @@ enum OpponentOwnsCardInExileCondition implements Condition {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
for (Card card :game.getExile().getAllCards(game)) {
for (Card card : game.getExile().getCardsInRange(game, controller.getId())) {
if (controller.hasOpponent(card.getOwnerId(), game)) {
return true;
}

View file

@ -115,7 +115,7 @@ class ZethiArcaneBlademasterCastEffect extends OneShotEffect {
if (controller == null) {
return false;
}
Cards cards = new CardsImpl(game.getExile().getAllCards(game, source.getControllerId()));
Cards cards = new CardsImpl(game.getExile().getCardsOwned(game, source.getControllerId()));
cards.removeIf(uuid -> !game.getCard(uuid).getCounters(game).containsKey(CounterType.KICK));
if (cards.isEmpty()) {
return false;

View file

@ -147,7 +147,7 @@ public class ManifestTest extends CardTestPlayerBase {
runCode("after blink", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
if (cardAfterBlink == null) {
Assert.assertEquals("after blink card must keep in exile",
1, currentGame.getExile().getAllCardsByRange(currentGame, playerA.getId()).size());
1, currentGame.getExile().getCardsInRange(currentGame, playerA.getId()).size());
} else {
String realPermanentName = currentGame.getBattlefield().getAllPermanents()
.stream()

View file

@ -66,7 +66,7 @@ public class DuneChanterTest extends CardTestPlayerBase {
private static void checkExile(String info, Player player, Game game, int count) {
int amount = game
.getExile()
.getAllCards(game, player.getId())
.getCardsOwned(game, player.getId())
.stream()
.filter(c -> c.getSubtype(game).contains(SubType.DESERT))
.mapToInt(k -> 1)

View file

@ -888,7 +888,7 @@ public class TokenImagesTest extends CardTestPlayerBase {
// check face down card in exile
runCode("on face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
Card card = currentGame.getExile().getAllCards(currentGame, playerA.getId()).get(0);
Card card = currentGame.getExile().getCardsOwned(currentGame, playerA.getId()).get(0);
GameView gameView = getGameView(playerA);
CardView controllerCardView = gameView.getExile()
.stream()
@ -941,7 +941,7 @@ public class TokenImagesTest extends CardTestPlayerBase {
// check face down card
runCode("on face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
Card card = currentGame.getExile().getAllCards(currentGame, playerA.getId()).get(0);
Card card = currentGame.getExile().getCardsOwned(currentGame, playerA.getId()).get(0);
GameView gameView = getGameView(playerA);
CardView controllerCardView = gameView.getExile()
.stream()

View file

@ -74,7 +74,7 @@ public enum CardsInExileCount implements DynamicValue {
return playerIds.stream()
.map(game::getPlayer)
.filter(Objects::nonNull)
.map(player -> game.getExile().getAllCards(game, player.getId()))
.map(player -> game.getExile().getCardsOwned(game, player.getId()))
.flatMap(Collection::stream)
.filter(Objects::nonNull);
}

View file

@ -3,7 +3,6 @@ package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.cards.Card;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.players.Player;
@ -17,19 +16,12 @@ public enum InstantSorceryExileGraveyardCount implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
Player player = game.getPlayer(sourceAbility.getControllerId());
if (player != null) {
int exileCount = 0;
for (Card exiledCard : game.getExile().getAllCards(game)) {
if (exiledCard.getOwnerId().equals(player.getId()) && exiledCard.isInstantOrSorcery(game)) {
exileCount++;
}
}
return player.getGraveyard().count(
StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, game
) + exileCount;
}
if (player == null) {
return 0;
}
return game.getExile().getCardsOwned(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, player.getId(), sourceAbility, game).size()
+ player.getGraveyard().count(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, player.getId(), sourceAbility, game);
}
@Override
public InstantSorceryExileGraveyardCount copy() {

View file

@ -26,7 +26,7 @@ public enum TotalCardsExiledOwnedManaValue implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
int totalCMC = 0;
List<Card> cards = game.getExile().getAllCards(
List<Card> cards = game.getExile().getCardsOwned(
game,
sourceAbility.getControllerId()
);

View file

@ -99,7 +99,7 @@ public class WishEffect extends OneShotEffect {
return false;
}
Cards cards = controller.getSideboard();
List<Card> exile = game.getExile().getAllCards(game);
List<Card> exile = game.getExile().getCardsOwned(game, controller.getId());
boolean noTargets = cards.isEmpty() && (!alsoFromExile || exile.isEmpty());
if (noTargets) {
game.informPlayer(controller, "You have no cards outside the game" + (alsoFromExile ? " or in exile" : "") + '.');

View file

@ -98,7 +98,7 @@ public class AddCreatureSubTypeAllMultiZoneEffect extends ContinuousEffectImpl {
}
}
// in Exile
for (Card card : game.getState().getExile().getAllCards(game, controllerId)) {
for (Card card : game.getState().getExile().getCardsOwned(game, controllerId)) {
if (filterCard.match(card, controllerId, source, game) && !card.hasSubtype(subType, game)) {
game.getState().getCreateMageObjectAttribute(card, game).getSubtype().add(subType);
}

View file

@ -44,7 +44,7 @@ public class GainAbilityControlledSpellsEffect extends ContinuousEffectImpl {
return false;
}
for (Card card : game.getExile().getAllCardsByRange(game, source.getControllerId())) {
for (Card card : game.getExile().getCardsInRange(game, source.getControllerId())) {
if (filter.match(card, player.getId(), source, game)) {
game.getState().addOtherAbility(card, ability);
}

View file

@ -93,7 +93,7 @@ public class NextSpellCastHasAbilityEffect extends ContinuousEffectImpl {
discard(); // only one use
return false;
}
for (Card card : game.getExile().getAllCardsByRange(game, playerId)) {
for (Card card : game.getExile().getCardsInRange(game, playerId)) {
if (filter.match(card, playerId, source, game)) {
game.getState().addOtherAbility(card, ability);
}

View file

@ -79,7 +79,7 @@ public class ChoiceCreatureType extends ChoiceImpl {
});
// exile
game.getExile().getAllCards(game, playerId).forEach(card -> {
game.getExile().getCardsOwned(game, playerId).forEach(card -> {
list.addAll(card.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList()));
});
});

View file

@ -1,5 +1,6 @@
package mage.game;
import mage.abilities.Ability;
import mage.cards.Card;
import mage.filter.FilterCard;
import mage.util.Copyable;
@ -61,24 +62,33 @@ public class Exile implements Serializable, Copyable<Exile> {
return null;
}
/**
* Returns all cards in exile matching the filter. Use only for test framework.
* For card effects, instead use a method that checks owner or range of influence.
*/
@Deprecated
public List<Card> getCards(FilterCard filter, Game game) {
List<Card> allCards = getAllCards(game);
return allCards.stream().filter(card -> filter.match(card, game)).collect(Collectors.toList());
}
@Deprecated // TODO: must use related request due game range like getAllCardsByRange
/**
* Returns all cards in exile. Use only for test framework.
* For card effects, instead use a method that checks owner or range of influence.
*/
@Deprecated
public List<Card> getAllCards(Game game) {
return getAllCards(game, null);
return getCardsOwned(game, null);
}
/**
* Return exiled cards owned by a specific player. Use it in effects to find all cards in range.
* Returns all cards in exile owned by the specified player
*/
public List<Card> getAllCards(Game game, UUID fromPlayerId) {
public List<Card> getCardsOwned(Game game, UUID ownerId) {
List<Card> res = new ArrayList<>();
for (ExileZone exile : exileZones.values()) {
for (Card card : exile.getCards(game)) {
if (fromPlayerId == null || card.isOwnedBy(fromPlayerId)) {
if (ownerId == null || card.isOwnedBy(ownerId)) {
res.add(card);
}
}
@ -86,14 +96,37 @@ public class Exile implements Serializable, Copyable<Exile> {
return res;
}
public List<Card> getAllCardsByRange(Game game, UUID controllerId) {
/**
* Returns all cards in exile matching the filter, owned by the specified player
*/
public Set<Card> getCardsOwned(FilterCard filter, UUID playerId, Ability source, Game game) {
return getCardsOwned(game, playerId)
.stream()
.filter(card -> filter.match(card, playerId, source, game))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
/**
* Returns all cards in exile in range of the specified player
*/
public List<Card> getCardsInRange(Game game, UUID controllerId) {
List<Card> res = new ArrayList<>();
for (UUID playerId : game.getState().getPlayersInRange(controllerId, game)) {
res.addAll(getAllCards(game, playerId));
res.addAll(getCardsOwned(game, playerId));
}
return res;
}
/**
* Returns all cards in exile matching the filter, in range of the specified player
*/
public Set<Card> getCardsInRange(FilterCard filter, UUID playerId, Ability source, Game game) {
return getCardsInRange(game, playerId)
.stream()
.filter(card -> filter.match(card, playerId, source, game))
.collect(Collectors.toCollection(LinkedHashSet::new));
}
public boolean removeCard(Card card) {
for (ExileZone exile : exileZones.values()) {
if (exile.contains(card.getId())) {

View file

@ -48,7 +48,6 @@ public class KayaTheInexorableEmblem extends Emblem {
class KayaTheInexorableEmblemEffect extends OneShotEffect {
private static final FilterCard filter = new FilterOwnedCard();
private static final FilterCard filter2 = new FilterCard();
private static final Set<String> choices = new LinkedHashSet<>();
@ -94,7 +93,7 @@ class KayaTheInexorableEmblemEffect extends OneShotEffect {
cards.addAll(player.getGraveyard());
break;
case "Exile":
cards.addAllCards(game.getExile().getCards(filter, game));
cards.addAllCards(game.getExile().getCardsOwned(game, player.getId()));
break;
}
return CardUtil.castSpellWithAttributesForFree(player, source, game, cards, filter2);

View file

@ -134,7 +134,7 @@ public class TargetCard extends TargetObject {
protected static Set<UUID> getAllPossibleTargetInExile(Game game, Player player, UUID sourceControllerId, Ability source, FilterCard filter, boolean isNotTarget) {
Set<UUID> possibleTargets = new HashSet<>();
UUID sourceId = source != null ? source.getSourceId() : null;
for (Card card : game.getExile().getAllCardsByRange(game, sourceControllerId)) {
for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) {
if (filter.match(card, sourceControllerId, source, game)) {
possibleTargets.add(card.getId());
}

View file

@ -115,7 +115,7 @@ public class TargetSource extends TargetObject {
}
}
}
for (Card card : game.getExile().getAllCards(game)) {
for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) {
if (filter.match(card, sourceControllerId, source, game)) {
possibleTargets.add(card.getId());
}

View file

@ -52,7 +52,7 @@ public class TargetCardInExile extends TargetCard {
Set<UUID> possibleTargets = new HashSet<>();
if (zoneId == null) { // no specific exile zone
for (Card card : game.getExile().getAllCardsByRange(game, sourceControllerId)) {
for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) {
if (filter.match(card, sourceControllerId, source, game)) {
possibleTargets.add(card.getId());
}

View file

@ -66,7 +66,7 @@ public class TargetPermanentOrSuspendedCard extends TargetImpl {
possibleTargets.add(permanent.getId());
}
}
for (Card card : game.getExile().getAllCards(game)) {
for (Card card : game.getExile().getCardsInRange(game, sourceControllerId)) {
if (filter.match(card, sourceControllerId, source, game)) {
possibleTargets.add(card.getId());
}