fix "any number of targets with total value X or less" (#12808)

* improve UX for Call of the Death-Dweller

* add new utility methods for targets of total value limit

* fix affected cards to use new standard methods

* adjust message

* remove incorrect check

* requested adjustments
This commit is contained in:
xenohedron 2024-09-09 00:18:29 -04:00 committed by GitHub
parent b10c79d737
commit 2f9ba6dba9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 530 additions and 337 deletions

View file

@ -1,27 +1,27 @@
package mage.cards.a;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
import mage.target.common.TargetCardInGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
* @author TheElk801
@ -91,7 +91,9 @@ class AncientBrassDragonTarget extends TargetCardInGraveyard {
private final int xValue;
AncientBrassDragonTarget(int xValue) {
super(0, Integer.MAX_VALUE, makeFilter(xValue), false);
super(0, Integer.MAX_VALUE, new FilterCreatureCard(
"creature cards with total mana value " + xValue + " or less from graveyards"
), false);
this.xValue = xValue;
}
@ -107,24 +109,27 @@ class AncientBrassDragonTarget extends TargetCardInGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
return card != null
&& this.getTargets()
.stream()
.map(game::getCard)
.mapToInt(Card::getManaValue)
.sum() + card.getManaValue() <= xValue;
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, xValue, game);
}
private static final FilterCard makeFilter(int xValue) {
FilterCard filter = new FilterCreatureCard(
"creature cards with total mana value "
+ xValue + " or less from graveyards"
);
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, xValue+1));
return filter;
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, xValue, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -9,7 +9,10 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.counter.AddCountersAllEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.FilterCard;
@ -21,8 +24,10 @@ import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInLibrary;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -110,14 +115,13 @@ class AoTheDawnSkyEffect extends OneShotEffect {
class AoTheDawnSkyTarget extends TargetCardInLibrary {
private static final FilterCard filter = new FilterPermanentCard("nonland permanent card");
private static final FilterCard filterStatic = new FilterPermanentCard("nonland permanent cards with total mana value 4 or less from your graveyard");
static {
filter.add(Predicates.not(CardType.LAND.getPredicate()));
filterStatic.add(Predicates.not(CardType.LAND.getPredicate()));
}
AoTheDawnSkyTarget() {
super(0, Integer.MAX_VALUE, filter);
super(0, Integer.MAX_VALUE, filterStatic);
}
private AoTheDawnSkyTarget(final AoTheDawnSkyTarget target) {
@ -130,19 +134,27 @@ class AoTheDawnSkyTarget extends TargetCardInLibrary {
}
@Override
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
if (!super.canTarget(playerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
return card != null
&& card.getManaValue()
+ this
.getTargets()
.stream()
.map(game::getCard)
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 4, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 4, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum() <= 4;
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -1,11 +1,14 @@
package mage.cards.c;
import mage.MageItem;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType;
@ -13,7 +16,6 @@ import mage.filter.FilterCard;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.filter.predicate.permanent.PermanentIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -21,11 +23,9 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -93,6 +93,14 @@ class CallOfTheDeathDwellerEffect extends OneShotEffect {
if (predicates.isEmpty()) {
return false;
}
if (predicates.size() == 1) {
Permanent permanent = game.getPermanent(cards.stream().findFirst().orElse(null));
if (permanent != null) {
permanent.addCounters(CounterType.DEATHTOUCH.createInstance(), source.getControllerId(), source, game);
permanent.addCounters(CounterType.MENACE.createInstance(), source.getControllerId(), source, game);
}
return true;
}
FilterPermanent filter = new FilterPermanent("creature to put a deathtouch counter on");
filter.add(Predicates.or(predicates));
Target target = new TargetPermanent(0, 1, filter, true);
@ -116,15 +124,11 @@ class CallOfTheDeathDwellerEffect extends OneShotEffect {
class CallOfTheDeathDwellerTarget extends TargetCardInYourGraveyard {
private static final FilterCard filter
private static final FilterCard filterStatic
= new FilterCreatureCard("creature cards with total mana value 3 or less from your graveyard");
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4));
}
CallOfTheDeathDwellerTarget() {
super(0, 2, filter, false);
super(0, 2, filterStatic, false);
}
private CallOfTheDeathDwellerTarget(final CallOfTheDeathDwellerTarget target) {
@ -138,15 +142,27 @@ class CallOfTheDeathDwellerTarget extends TargetCardInYourGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
return card != null &&
this.getTargets()
.stream()
.map(game::getCard)
.mapToInt(Card::getManaValue)
.sum() + card.getManaValue() <= 3;
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 3, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 3, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -18,13 +18,14 @@ import mage.filter.FilterPermanent;
import mage.filter.StaticFilters;
import mage.filter.common.FilterNonlandPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.TargetPermanent;
import mage.target.common.TargetCardInGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -70,11 +71,11 @@ public final class KairiTheSwirlingSky extends CardImpl {
class KairiTheSwirlingSkyTarget extends TargetPermanent {
private static final FilterPermanent filter
private static final FilterPermanent filterStatic
= new FilterNonlandPermanent("nonland permanents with total mana value 6 or less");
KairiTheSwirlingSkyTarget() {
super(0, Integer.MAX_VALUE, filter, false);
super(0, Integer.MAX_VALUE, filterStatic, false);
}
private KairiTheSwirlingSkyTarget(final KairiTheSwirlingSkyTarget target) {
@ -88,24 +89,27 @@ class KairiTheSwirlingSkyTarget extends TargetPermanent {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Permanent permanent = game.getPermanent(id);
if (permanent == null) {
return false;
}
int added = 0; // We need to prevent the target to be counted twice on revalidation.
if (!this.getTargets().contains(id)) {
added = permanent.getManaValue();// fresh target, adding its MV
}
return added +
this.getTargets()
.stream()
.map(game::getPermanent)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum() <= 6;
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 6, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 6, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -1,24 +1,29 @@
package mage.cards.l;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.search.SearchLibraryPutInGraveyardEffect;
import mage.abilities.keyword.SpreeAbility;
import mage.cards.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -81,15 +86,11 @@ class LivelyDirgeEffect extends OneShotEffect {
class LivelyDirgeTarget extends TargetCardInYourGraveyard {
private static final FilterCard filter
private static final FilterCard filterStatic
= new FilterCreatureCard("creature cards with total mana value 4 or less from your graveyard");
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 5));
}
LivelyDirgeTarget() {
super(0, 2, filter, true);
super(0, 2, filterStatic, true);
}
private LivelyDirgeTarget(final LivelyDirgeTarget target) {
@ -103,15 +104,26 @@ class LivelyDirgeTarget extends TargetCardInYourGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
return card != null &&
this.getTargets()
.stream()
.map(game::getCard)
.mapToInt(Card::getManaValue)
.sum() + card.getManaValue() <= 4;
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 4, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 4, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -1,21 +1,20 @@
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.effects.Effect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
*
@ -27,12 +26,8 @@ public final class MarchFromTheTomb extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{W}{B}");
// Return any number of target Ally creature cards with total converted mana cost of 8 or less from your graveyard to the battlefield.
Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect();
effect.setText("Return any number of target Ally creature cards with total mana value 8 or less from your graveyard to the battlefield");
this.getSpellAbility().addEffect(effect);
FilterCard filter = new FilterCreatureCard();
filter.add(SubType.ALLY.getPredicate());
this.getSpellAbility().addTarget(new MarchFromTheTombTarget(0, Integer.MAX_VALUE, filter));
this.getSpellAbility().addEffect(new ReturnFromGraveyardToBattlefieldTargetEffect());
this.getSpellAbility().addTarget(new MarchFromTheTombTarget());
}
private MarchFromTheTomb(final MarchFromTheTomb card) {
@ -47,8 +42,13 @@ public final class MarchFromTheTomb extends CardImpl {
class MarchFromTheTombTarget extends TargetCardInYourGraveyard {
public MarchFromTheTombTarget(int minNumTargets, int maxNumTargets, FilterCard filter) {
super(minNumTargets, maxNumTargets, filter);
private static final FilterCreatureCard filterStatic = new FilterCreatureCard("Ally creature cards with total mana value 8 or less from your graveyard");
static {
filterStatic.add(SubType.ALLY.getPredicate());
}
MarchFromTheTombTarget() {
super(0, Integer.MAX_VALUE, filterStatic);
}
private MarchFromTheTombTarget(final MarchFromTheTombTarget target) {
@ -56,40 +56,28 @@ class MarchFromTheTombTarget extends TargetCardInYourGraveyard {
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
int cmcLeft = 8;
for (UUID targetId : this.getTargets()) {
Card card = game.getCard(targetId);
if (card != null) {
cmcLeft -= card.getManaValue();
}
}
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
Set<UUID> leftPossibleTargets = new HashSet<>();
for (UUID targetId : possibleTargets) {
Card card = game.getCard(targetId);
if (card != null && card.getManaValue() <= cmcLeft) {
leftPossibleTargets.add(targetId);
}
}
withTargetName("any number of target Ally creature cards with total mana value of 8 or less (" + cmcLeft + " left) from your graveyard");
return leftPossibleTargets;
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 8, game);
}
@Override
public boolean canTarget(UUID playerId, UUID objectId, Ability source, Game game) {
if (super.canTarget(playerId, objectId, source, game)) {
int cmcLeft = 8;
for (UUID targetId : this.getTargets()) {
Card card = game.getCard(targetId);
if (card != null) {
cmcLeft -= card.getManaValue();
}
}
Card card = game.getCard(objectId);
return card != null && card.getManaValue() <= cmcLeft;
}
return false;
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 8, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
@Override

View file

@ -1,6 +1,7 @@
package mage.cards.m;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
@ -21,7 +22,10 @@ import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -80,6 +84,7 @@ class MoorlandRescuerEffect extends OneShotEffect {
player.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, source, game);
Card sourceCard = source.getSourceCardIfItStillExists(game);
if (sourceCard != null) {
game.processAction();
player.moveCards(sourceCard, Zone.EXILED, source, game);
}
return true;
@ -108,21 +113,28 @@ class MoorlandRescuerTarget extends TargetCardInYourGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
if (card == null) {
return false;
}
int powerSum = this
.getTargets()
.stream()
.map(game::getCard)
.map(Card::getPower)
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, m -> m.getPower().getValue(), xValue, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
m -> m.getPower().getValue(), xValue, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.map(MageObject::getPower)
.mapToInt(MageInt::getValue)
.sum();
return card.getPower().getValue() + powerSum <= xValue;
return super.getMessage(game) + " (selected total power " + selectedValue + ")";
}
private static FilterCard makeFilter(int xValue, Ability source, Game game) {

View file

@ -1,13 +1,13 @@
package mage.cards.n;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.MutatesSourceTriggeredAbility;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.keyword.DeathtouchAbility;
import mage.abilities.keyword.LifelinkAbility;
import mage.abilities.keyword.MutateAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -17,7 +17,10 @@ import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -63,11 +66,11 @@ public final class NethroiApexOfDeath extends CardImpl {
class NethroiApexOfDeathTarget extends TargetCardInYourGraveyard {
private static final FilterCard filter
private static final FilterCard filterStatic
= new FilterCreatureCard("creature cards with total power 10 or less from your graveyard");
NethroiApexOfDeathTarget() {
super(0, Integer.MAX_VALUE, filter);
super(0, Integer.MAX_VALUE, filterStatic);
}
private NethroiApexOfDeathTarget(final NethroiApexOfDeathTarget target) {
@ -81,20 +84,28 @@ class NethroiApexOfDeathTarget extends TargetCardInYourGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
if (card == null) {
return false;
}
int powerSum = this
.getTargets()
.stream()
.map(game::getCard)
.map(Card::getPower)
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, m -> m.getPower().getValue(), 10, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
m -> m.getPower().getValue(), 10, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.map(MageObject::getPower)
.mapToInt(MageInt::getValue)
.sum();
return card.getPower().getValue() + powerSum <= 10;
return super.getMessage(game) + " (selected total power " + selectedValue + ")";
}
}

View file

@ -1,9 +1,7 @@
package mage.cards.o;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@ -13,18 +11,22 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
import mage.abilities.keyword.HasteAbility;
import mage.cards.Card;
import mage.constants.*;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.HasteAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.targetadjustment.TargetAdjuster;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
*
@ -101,7 +103,7 @@ class OrcusPrinceOfUndeathTarget extends TargetCardInYourGraveyard {
private final int xValue;
public OrcusPrinceOfUndeathTarget(int xValue, FilterCreatureCard filter) {
OrcusPrinceOfUndeathTarget(int xValue, FilterCreatureCard filter) {
super(0, xValue, filter);
this.xValue = xValue;
}
@ -116,23 +118,29 @@ class OrcusPrinceOfUndeathTarget extends TargetCardInYourGraveyard {
return new OrcusPrinceOfUndeathTarget(this);
}
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, xValue, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
Set<UUID> possibleTargets = new HashSet<>();
int maxManaValue = this.xValue;
for (UUID targetId : this.getTargets()) {
Card card = game.getCard(targetId);
if (card != null) {
maxManaValue -= card.getManaValue();
}
}
for (UUID possibleTargetId : super.possibleTargets(sourceControllerId, source, game)) {
Card card = game.getCard(possibleTargetId);
if (card != null && card.getManaValue() <= maxManaValue) {
possibleTargets.add(possibleTargetId);
}
}
return possibleTargets;
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, xValue, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -1,9 +1,9 @@
package mage.cards.p;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileSpellEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.CardsImpl;
@ -15,7 +15,10 @@ import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -78,19 +81,18 @@ class PairODiceLostEffect extends OneShotEffect {
class PairODiceLostTarget extends TargetCardInYourGraveyard {
private static final FilterCard filter = new FilterCard(
"cards with total mana value X or less from your graveyard"
);
private final int value;
private final int xValue;
PairODiceLostTarget(int value) {
super(0, Integer.MAX_VALUE, filter, true);
this.value = value;
PairODiceLostTarget(int xValue) {
super(0, Integer.MAX_VALUE, new FilterCard(
"cards with total mana value " + xValue + " or less from your graveyard"
), true);
this.xValue = xValue;
}
private PairODiceLostTarget(final PairODiceLostTarget target) {
super(target);
this.value = target.value;
this.xValue = target.xValue;
}
@Override
@ -100,15 +102,27 @@ class PairODiceLostTarget extends TargetCardInYourGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
return card != null &&
this.getTargets()
.stream()
.map(game::getCard)
.mapToInt(Card::getManaValue)
.sum() + card.getManaValue() <= value;
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, xValue, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, xValue, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -1,18 +1,19 @@
package mage.cards.p;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.ComparisonType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -40,15 +41,11 @@ public final class PatchUp extends CardImpl {
class PatchUpTarget extends TargetCardInYourGraveyard {
private static final FilterCard filter
private static final FilterCard filterStatic
= new FilterCreatureCard("creature cards with total mana value 3 or less from your graveyard");
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 4));
}
PatchUpTarget() {
super(0, 3, filter, false);
super(0, 3, filterStatic, false);
}
private PatchUpTarget(final PatchUpTarget target) {
@ -62,15 +59,26 @@ class PatchUpTarget extends TargetCardInYourGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
return card != null &&
this.getTargets()
.stream()
.map(game::getCard)
.mapToInt(Card::getManaValue)
.sum() + card.getManaValue() <= 3;
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 3, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 3, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -5,15 +5,18 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DiesSourceTriggeredAbility;
import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
import mage.cards.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.target.common.TargetCardInLibrary;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -44,11 +47,11 @@ public final class ProteanHulk extends CardImpl {
class ProteanHulkTarget extends TargetCardInLibrary {
private static final FilterCard filter
private static final FilterCard filterStatic
= new FilterCreatureCard("creature cards with total mana value 6 or less");
ProteanHulkTarget() {
super(0, Integer.MAX_VALUE, filter);
super(0, Integer.MAX_VALUE, filterStatic);
}
private ProteanHulkTarget(final ProteanHulkTarget target) {
@ -61,16 +64,27 @@ class ProteanHulkTarget extends TargetCardInLibrary {
}
@Override
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
if (!super.canTarget(playerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
if (card == null) {
return false;
}
Cards cards = new CardsImpl(this.getTargets());
cards.add(id);
return cards.getCards(game).stream().filter(Objects::nonNull).mapToInt(MageObject::getManaValue).sum() <= 6;
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 6, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 6, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -18,10 +18,11 @@ import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterArtifactOrEnchantmentPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.TargetPermanent;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -62,14 +63,13 @@ public final class RampagingYaoGuai extends CardImpl {
}
}
//Based on Kairi, The Swirling Sky
class RampagingYaoGuaiTarget extends TargetPermanent {
private static final FilterPermanent filter
private static final FilterPermanent filterStatic
= new FilterArtifactOrEnchantmentPermanent("artifacts and/or enchantments with total mana value X or less");
RampagingYaoGuaiTarget() {
super(0, Integer.MAX_VALUE, filter, false);
super(0, Integer.MAX_VALUE, filterStatic, false);
}
private RampagingYaoGuaiTarget(final RampagingYaoGuaiTarget target) {
@ -83,23 +83,26 @@ class RampagingYaoGuaiTarget extends TargetPermanent {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Permanent permanent = game.getPermanent(id);
if (permanent == null) {
return false;
}
int added = 0; // We need to prevent the target to be counted twice on revalidation.
if (!this.getTargets().contains(id)) {
added = permanent.getManaValue();// fresh target, adding its MV
}
return added +
this.getTargets()
.stream()
.map(game::getPermanent)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum() <= GetXValue.instance.calculate(game, source, null);
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, GetXValue.instance.calculate(game, source, null), game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, GetXValue.instance.calculate(game, source, null), game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -1,23 +1,28 @@
package mage.cards.t;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.MillCardsControllerEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.CardsImpl;
import mage.constants.*;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -80,17 +85,16 @@ class TechnomancerEffect extends OneShotEffect {
class TechnomancerTarget extends TargetCardInYourGraveyard {
private static final FilterCard filter = new FilterCreatureCard(
private static final FilterCard filterStatic = new FilterCreatureCard(
"artifact creature cards with total mana value 6 or less from your graveyard"
);
static {
filter.add(CardType.ARTIFACT.getPredicate());
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 7));
filterStatic.add(CardType.ARTIFACT.getPredicate());
}
TechnomancerTarget() {
super(0, Integer.MAX_VALUE, filter, true);
super(0, Integer.MAX_VALUE, filterStatic, true);
}
private TechnomancerTarget(final TechnomancerTarget target) {
@ -104,15 +108,26 @@ class TechnomancerTarget extends TargetCardInYourGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
return card != null &&
this.getTargets()
.stream()
.map(game::getCard)
.mapToInt(Card::getManaValue)
.sum() + card.getManaValue() <= 6;
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 6, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 6, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -1,5 +1,6 @@
package mage.cards.t;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.SagaAbility;
import mage.abilities.effects.OneShotEffect;
@ -7,20 +8,23 @@ import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.LoseLifeSourceControllerEffect;
import mage.abilities.effects.common.MillCardsControllerEffect;
import mage.abilities.effects.common.continuous.AddCardTypeTargetEffect;
import mage.cards.*;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.counters.CounterType;
import mage.filter.FilterCard;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import mage.target.targetpointer.FixedTarget;
/**
* @author TheElk801
@ -38,8 +42,7 @@ public final class TheWarInHeaven extends CardImpl {
// I -- You draw three cards and you lose 3 life.
sagaAbility.addChapterEffect(
this, SagaChapter.CHAPTER_I,
new DrawCardSourceControllerEffect(3)
.setText("you draw three cards"),
new DrawCardSourceControllerEffect(3, true),
new LoseLifeSourceControllerEffect(3)
.concatBy("and")
);
@ -71,7 +74,7 @@ class TheWarInHeavenEffect extends OneShotEffect {
super(Outcome.Benefit);
staticText = "choose up to three target creature cards with total mana value 8 or less in your graveyard. "
+ "Return each of them to the battlefield with a necrodermis counter on it. "
+ "They\'re artifacts in addition to their other types";
+ "They're artifacts in addition to their other types";
}
private TheWarInHeavenEffect(final TheWarInHeavenEffect effect) {
@ -108,15 +111,11 @@ class TheWarInHeavenEffect extends OneShotEffect {
class TheWarInHeavenTarget extends TargetCardInYourGraveyard {
private static final FilterCard filter
private static final FilterCreatureCard filterStatic
= new FilterCreatureCard("creature cards with total mana value 8 or less from your graveyard");
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 9));
}
TheWarInHeavenTarget() {
super(0, 3, filter, false);
super(0, 3, filterStatic, false);
}
private TheWarInHeavenTarget(final TheWarInHeavenTarget target) {
@ -130,15 +129,26 @@ class TheWarInHeavenTarget extends TargetCardInYourGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
return card != null
&& this.getTargets()
.stream()
.map(game::getCard)
.mapToInt(Card::getManaValue)
.sum() + card.getManaValue() <= 8;
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 8, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 8, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -1,6 +1,7 @@
package mage.cards.t;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.ActivateAsSorceryActivatedAbility;
import mage.abilities.common.SimpleActivatedAbility;
@ -12,17 +13,17 @@ import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffec
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
import mage.abilities.effects.keyword.SurveilEffect;
import mage.abilities.keyword.VigilanceAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.StaticFilters;
import mage.filter.common.FilterArtifactCard;
import mage.filter.predicate.mageobject.ManaValuePredicate;
import mage.game.Game;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
/**
@ -74,16 +75,12 @@ public final class TocasiaDigSiteMentor extends CardImpl {
class TocasiaDigSiteMentorTarget extends TargetCardInYourGraveyard {
private static final FilterCard filter = new FilterArtifactCard(
private static final FilterArtifactCard filterStatic = new FilterArtifactCard(
"artifact cards with total mana value 10 or less from your graveyard"
);
static {
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, 11));
}
TocasiaDigSiteMentorTarget() {
super(0, Integer.MAX_VALUE, filter, false);
super(0, Integer.MAX_VALUE, filterStatic, false);
}
private TocasiaDigSiteMentorTarget(final TocasiaDigSiteMentorTarget target) {
@ -97,15 +94,26 @@ class TocasiaDigSiteMentorTarget extends TargetCardInYourGraveyard {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
if (!super.canTarget(controllerId, id, source, game)) {
return false;
}
Card card = game.getCard(id);
return card != null &&
this.getTargets()
.stream()
.map(game::getCard)
.mapToInt(Card::getManaValue)
.sum() + card.getManaValue() <= 10;
return super.canTarget(controllerId, id, source, game)
&& CardUtil.checkCanTargetTotalValueLimit(
this.getTargets(), id, MageObject::getManaValue, 10, game);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
return CardUtil.checkPossibleTargetsTotalValueLimit(this.getTargets(),
super.possibleTargets(sourceControllerId, source, game),
MageObject::getManaValue, 10, game);
}
@Override
public String getMessage(Game game) {
// shows selected total
int selectedValue = this.getTargets().stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage(game) + " (selected total mana value " + selectedValue + ")";
}
}

View file

@ -57,6 +57,7 @@ import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -1123,6 +1124,58 @@ public final class CardUtil {
return false;
}
/**
* For overriding `canTarget()` with usages such as "any number of target cards with total mana value X or less".
* Call this after super.canTarget() returns true.
*
* @param selectedTargets this.getTargets()
* @param checkTargetId id from canTarget
* @param valueMapper e.g. MageObject::getManaValue or m -> m.getPower().getValue()
* @param maxValue the maximum total value of the parameter
* @return true if the total value would not be exceeded by the target being checked.
*/
public static boolean checkCanTargetTotalValueLimit(Collection<UUID> selectedTargets, UUID checkTargetId,
ToIntFunction<MageObject> valueMapper, int maxValue,
Game game) {
MageObject checkTarget = game.getObject(checkTargetId);
if (checkTarget == null) {
return false;
}
return maxValue >= selectedTargets.stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(valueMapper)
.sum()
+ (selectedTargets.contains(checkTargetId) ? 0 : valueMapper.applyAsInt(checkTarget));
}
/**
* For overriding `possibleTargets()` with usages such as "any number of target cards with total mana value X or less".
*
* @param selectedTargets this.getTargets()
* @param possibleTargets super.possibleTargets()
* @param valueMapper e.g. MageObject::getManaValue or m -> m.getPower().getValue()
* @param maxValue the maximum total value of the parameter
* @return the set of possible targets that don't exceed the maximum total value.
*/
public static Set<UUID> checkPossibleTargetsTotalValueLimit(Collection<UUID> selectedTargets, Set<UUID> possibleTargets,
ToIntFunction<MageObject> valueMapper, int maxValue, Game game) {
int selectedValue = selectedTargets.stream()
.map(game::getObject)
.filter(Objects::nonNull)
.mapToInt(valueMapper)
.sum();
int remainingValue = maxValue - selectedValue;
Set<UUID> validTargets = new HashSet<>();
for (UUID id: possibleTargets) {
MageObject mageObject = game.getObject(id);
if (mageObject != null && valueMapper.applyAsInt(mageObject) <= remainingValue) {
validTargets.add(id);
}
}
return validTargets;
}
/**
* Put card to battlefield without resolve/ETB (for cheats and tests only)
*
@ -2325,6 +2378,6 @@ public final class CardUtil {
*/
public static boolean isInformationAbility(Ability ability) {
return !ability.getEffects().isEmpty()
&& ability.getEffects().stream().allMatch(e -> e instanceof InfoEffect);
&& ability.getEffects().stream().allMatch(InfoEffect.class::isInstance);
}
}