add some new cardutil stuff

This commit is contained in:
theelk801 2024-09-26 18:49:00 -04:00
parent c07b9680bd
commit b9b69586dc
3 changed files with 111 additions and 129 deletions

View file

@ -1,7 +1,6 @@
package mage.cards.c;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.Condition;
@ -19,9 +18,6 @@ import mage.game.Game;
import mage.game.permanent.token.Construct4Token;
import mage.util.CardUtil;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
/**
@ -66,16 +62,11 @@ enum ChromeReplicatorCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
Map<String, Integer> nameMap = new HashMap<>();
return game
.getBattlefield()
.getActivePermanents(
filter, source.getControllerId(), source, game
).stream()
.filter(Objects::nonNull)
.map(MageObject::getName)
.filter(Objects::nonNull)
.filter(s -> !s.isEmpty())
.anyMatch(s -> nameMap.compute(s, CardUtil::setOrIncrementValue) >= 2);
return CardUtil
.checkAnyPairs(
game.getBattlefield().getActivePermanents(
filter, source.getControllerId(), source, game
), (p1, p2) -> p1.sharesName(p2, game)
);
}
}

View file

@ -6,22 +6,21 @@ import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.DiscardTargetCost;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.common.FilterNonlandCard;
import mage.filter.predicate.mageobject.NamePredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInHand;
import mage.util.CardUtil;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author LevelX2
@ -39,9 +38,9 @@ public final class SphinxOfTheChimes extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// Discard two nonland cards with the same name: Draw four cards.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(4), new DiscardTwoNonlandCardsWithTheSameNameCost());
this.addAbility(ability);
this.addAbility(new SimpleActivatedAbility(
new DrawCardSourceControllerEffect(4), new DiscardTargetCost(new SphinxOfTheChimesTarget())
));
}
private SphinxOfTheChimes(final SphinxOfTheChimes card) {
@ -54,122 +53,54 @@ public final class SphinxOfTheChimes extends CardImpl {
}
}
class DiscardTwoNonlandCardsWithTheSameNameCost extends DiscardTargetCost {
class SphinxOfTheChimesTarget extends TargetCardInHand {
public DiscardTwoNonlandCardsWithTheSameNameCost() {
super(new TargetTwoNonLandCardsWithSameNameInHand());
private static final FilterCard filter = new FilterNonlandCard("nonland cards with the same name");
public SphinxOfTheChimesTarget() {
super(2, 2, filter);
}
private DiscardTwoNonlandCardsWithTheSameNameCost(final DiscardTwoNonlandCardsWithTheSameNameCost cost) {
super(cost);
}
@Override
public DiscardTwoNonlandCardsWithTheSameNameCost copy() {
return new DiscardTwoNonlandCardsWithTheSameNameCost(this);
}
}
class TargetTwoNonLandCardsWithSameNameInHand extends TargetCardInHand {
public TargetTwoNonLandCardsWithSameNameInHand() {
super(2, 2, new FilterNonlandCard("nonland cards with the same name"));
}
private TargetTwoNonLandCardsWithSameNameInHand(final TargetTwoNonLandCardsWithSameNameInHand target) {
private SphinxOfTheChimesTarget(final SphinxOfTheChimesTarget target) {
super(target);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Game game) {
Set<UUID> newPossibleTargets = new HashSet<>();
Set<UUID> possibleTargets = new HashSet<>();
Player player = game.getPlayer(sourceControllerId);
if (player == null) {
return newPossibleTargets;
}
for (Card card : player.getHand().getCards(filter, game)) {
possibleTargets.add(card.getId());
}
Cards cardsToCheck = new CardsImpl();
cardsToCheck.addAll(possibleTargets);
if (targets.size() == 1) {
// first target is laready chosen, now only targets with the same name are selectable
for (Map.Entry<UUID, Integer> entry : targets.entrySet()) {
Card chosenCard = cardsToCheck.get(entry.getKey(), game);
if (chosenCard != null) {
for (UUID cardToCheck : cardsToCheck) {
if (!cardToCheck.equals(chosenCard.getId()) && chosenCard.getName().equals(game.getCard(cardToCheck).getName())) {
newPossibleTargets.add(cardToCheck);
}
}
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
switch (this.getTargets().size()) {
case 0:
if (possibleTargets.size() < 2) {
possibleTargets.clear();
break;
}
}
} else {
for (UUID cardToCheck : cardsToCheck) {
Card card = game.getCard(cardToCheck);
if (card != null) {
String nameToSearch = CardUtil.getCardNameForSameNameSearch(card);
FilterCard nameFilter = new FilterCard();
nameFilter.add(new NamePredicate(nameToSearch));
if (cardsToCheck.count(nameFilter, game) > 1) {
newPossibleTargets.add(cardToCheck);
}
}
}
Set<UUID> set = CardUtil
.streamPairsWithMap(
possibleTargets,
(u1, u2) -> game.getCard(u1).sharesName(game.getCard(u2), game)
? Stream.of(u1, u2)
: Stream.<UUID>empty()
)
.flatMap(Function.identity())
.collect(Collectors.toSet());
possibleTargets.clear();
possibleTargets.addAll(set);
break;
case 1:
possibleTargets.removeIf(
uuid -> !game
.getCard(uuid)
.sharesName(game.getCard(this.getTargets().get(0)), game)
);
break;
default:
possibleTargets.clear();
}
return newPossibleTargets;
return possibleTargets;
}
@Override
public boolean canChoose(UUID sourceControllerId, Game game) {
Cards cardsToCheck = new CardsImpl();
Player player = game.getPlayer(sourceControllerId);
if (player == null) {
return false;
}
for (Card card : player.getHand().getCards(filter, game)) {
cardsToCheck.add(card.getId());
}
int possibleCards = 0;
for (Card card : cardsToCheck.getCards(game)) {
String nameToSearch = CardUtil.getCardNameForSameNameSearch(card);
FilterCard nameFilter = new FilterCard();
nameFilter.add(new NamePredicate(nameToSearch));
if (cardsToCheck.count(nameFilter, game) > 1) {
++possibleCards;
}
}
return possibleCards > 0;
}
@Override
public boolean canTarget(UUID id, Game game) {
if (super.canTarget(id, game)) {
Card card = game.getCard(id);
if (card != null) {
if (targets.size() == 1) {
Card card2 = game.getCard(targets.entrySet().iterator().next().getKey());
return CardUtil.haveSameNames(card2, card);
} else {
String nameToSearch = CardUtil.getCardNameForSameNameSearch(card);
FilterCard nameFilter = new FilterCard();
nameFilter.add(new NamePredicate(nameToSearch));
Player player = game.getPlayer(card.getOwnerId());
return player != null && player.getHand().getCards(nameFilter, game).size() > 1;
}
}
}
return false;
}
@Override
public TargetTwoNonLandCardsWithSameNameInHand copy() {
return new TargetTwoNonLandCardsWithSameNameInHand(this);
public SphinxOfTheChimesTarget copy() {
return new SphinxOfTheChimesTarget(this);
}
}

View file

@ -56,6 +56,8 @@ import java.net.URLDecoder;
import java.net.URLEncoder;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.BiPredicate;
import java.util.function.ToIntFunction;
import java.util.stream.Collectors;
import java.util.stream.Stream;
@ -2167,6 +2169,64 @@ public final class CardUtil {
return stream.filter(clazz::isInstance).map(clazz::cast).filter(Objects::nonNull);
}
private static class IntPairIterator implements Iterator<AbstractMap.SimpleImmutableEntry<Integer, Integer>> {
private final int amount;
private int firstCounter = 0;
private int secondCounter = 1;
IntPairIterator(int amount) {
this.amount = amount;
}
@Override
public boolean hasNext() {
return firstCounter + 1 < amount;
}
@Override
public AbstractMap.SimpleImmutableEntry<Integer, Integer> next() {
AbstractMap.SimpleImmutableEntry<Integer, Integer> value
= new AbstractMap.SimpleImmutableEntry(firstCounter, secondCounter);
secondCounter++;
if (secondCounter == amount) {
firstCounter++;
secondCounter = firstCounter + 1;
}
return value;
}
public int getMax() {
// amount choose 2
return (amount * amount - amount) / 2;
}
}
public static <T> boolean checkAnyPairs(Collection<T> collection, BiPredicate<T, T> predicate) {
return streamPairsWithMap(collection, (t1, t2) -> predicate.test(t1, t2)).anyMatch(x -> x);
}
public static <T, U> Stream<U> streamPairsWithMap(
Collection<T> collection,
BiFunction<T, T, U> function) {
if (collection.size() < 2) {
return Stream.empty();
}
List<T> list;
if (collection instanceof List) {
list = (List<T>) collection;
} else {
list = new ArrayList<>(collection);
}
IntPairIterator it = new IntPairIterator(list.size());
return Stream
.generate(it::next)
.limit(it.getMax())
.map(pair -> function.apply(
list.get(pair.getKey()),
list.get(pair.getValue())
));
}
public static void AssertNoControllerOwnerPredicates(Target target) {
List<Predicate> list = new ArrayList<>();
Predicates.collectAllComponents(target.getFilter().getPredicates(), target.getFilter().getExtraPredicates(), list);