Number of card types in graveyards - added card hint for cards like Tarmogoyf

This commit is contained in:
Oleg Agafonov 2025-02-07 07:37:27 +04:00
parent 4a960995f7
commit 25e4930bd5
94 changed files with 245 additions and 306 deletions

View file

@ -3,14 +3,16 @@ package mage.abilities.dynamicvalue.common;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.effects.Effect;
import mage.abilities.hint.Hint;
import mage.abilities.hint.ValueHint;
import mage.cards.Card;
import mage.constants.CardType;
import mage.game.Game;
import mage.game.permanent.PermanentToken;
import mage.players.Player;
import java.util.Collection;
import java.util.Objects;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
@ -20,16 +22,18 @@ public enum CardTypesInGraveyardCount implements DynamicValue {
YOU("your graveyard"),
ALL("all graveyards"),
OPPONENTS("your opponents' graveyards");
private final String message;
private final CardTypesInGraveyardHint hint;
CardTypesInGraveyardCount(String message) {
this.message = "the number of card types among cards in " + message;
this.hint = new CardTypesInGraveyardHint(this);
}
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
return getStream(game, sourceAbility)
.filter(card -> !card.isCopy() && !(card instanceof PermanentToken))
return getGraveyardCards(game, sourceAbility)
.map(card -> card.getCardType(game))
.flatMap(Collection::stream)
.distinct()
@ -52,16 +56,16 @@ public enum CardTypesInGraveyardCount implements DynamicValue {
return message;
}
private final Stream<Card> getStream(Game game, Ability ability) {
public Hint getHint() {
return hint;
}
public Stream<Card> getGraveyardCards(Game game, Ability ability) {
Collection<UUID> playerIds;
switch (this) {
case YOU:
Player player = game.getPlayer(ability.getControllerId());
return player == null
? null : player
.getGraveyard()
.getCards(game)
.stream();
playerIds = Collections.singletonList(ability.getControllerId());
break;
case OPPONENTS:
playerIds = game.getOpponents(ability.getControllerId());
break;
@ -69,13 +73,47 @@ public enum CardTypesInGraveyardCount implements DynamicValue {
playerIds = game.getState().getPlayersInRange(ability.getControllerId(), game);
break;
default:
return null;
throw new IllegalArgumentException("Wrong code usage: miss implementation for " + this);
}
return playerIds.stream()
.map(game::getPlayer)
.filter(Objects::nonNull)
.map(Player::getGraveyard)
.map(graveyard -> graveyard.getCards(game))
.flatMap(Collection::stream);
.flatMap(Collection::stream)
.filter(Objects::nonNull)
.filter(card -> !card.isCopy() && !(card instanceof PermanentToken));
}
}
class CardTypesInGraveyardHint implements Hint {
CardTypesInGraveyardCount value;
CardTypesInGraveyardHint(CardTypesInGraveyardCount value) {
this.value = value;
}
private CardTypesInGraveyardHint(final CardTypesInGraveyardHint hint) {
this.value = hint.value;
}
@Override
public String getText(Game game, Ability ability) {
Stream<Card> stream = this.value.getGraveyardCards(game, ability);
List<String> types = stream
.map(card -> card.getCardType(game))
.flatMap(Collection::stream)
.distinct()
.map(CardType::toString)
.sorted()
.collect(Collectors.toList());
return "Card types in " + this.value.getMessage() + ": " + types.size()
+ (types.size() > 0 ? " (" + String.join(", ", types) + ')' : "");
}
@Override
public CardTypesInGraveyardHint copy() {
return new CardTypesInGraveyardHint(this);
}
}

View file

@ -52,7 +52,7 @@ public class ConditionTrueHint implements Hint {
}
@Override
public Hint copy() {
public ConditionTrueHint copy() {
return new ConditionTrueHint(this);
}
}

View file

@ -30,7 +30,7 @@ public class StaticHint implements Hint {
}
@Override
public Hint copy() {
public StaticHint copy() {
return new StaticHint(this);
}
}

View file

@ -1,79 +0,0 @@
package mage.abilities.hint.common;
import mage.abilities.Ability;
import mage.abilities.hint.Hint;
import mage.cards.Card;
import mage.constants.CardType;
import mage.game.Game;
import mage.players.Player;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author JayDi85
*/
public enum CardTypesInGraveyardHint implements Hint {
YOU("your graveyard"),
ALL("all graveyards"),
OPPONENTS("your opponents' graveyards");
private final String message;
CardTypesInGraveyardHint(String message) {
this.message = message;
}
@Override
public String getText(Game game, Ability ability) {
Stream<Card> stream = getStream(game, ability);
if (stream == null) {
return null;
}
List<String> types = stream
.map(card -> card.getCardType(game))
.flatMap(Collection::stream)
.distinct()
.map(CardType::toString)
.sorted()
.collect(Collectors.toList());
return "Card types in " + this.message + ": " + types.size()
+ (types.size() > 0 ? " (" + String.join(", ", types) + ')' : "");
}
@Override
public Hint copy() {
return this;
}
private final Stream<Card> getStream(Game game, Ability ability) {
Collection<UUID> playerIds;
switch (this) {
case YOU:
Player player = game.getPlayer(ability.getControllerId());
return player == null
? null : player
.getGraveyard()
.getCards(game)
.stream();
case OPPONENTS:
playerIds = game.getOpponents(ability.getControllerId());
break;
case ALL:
playerIds = game.getState().getPlayersInRange(ability.getControllerId(), game);
break;
default:
return null;
}
return playerIds.stream()
.map(game::getPlayer)
.filter(Objects::nonNull)
.map(Player::getGraveyard)
.map(graveyard -> graveyard.getCards(game))
.flatMap(Collection::stream);
}
}

View file

@ -1,19 +1,19 @@
package mage.abilities.hint.common;
import mage.abilities.Ability;
import mage.abilities.condition.common.CountersOnPermanentsCondition;
import mage.abilities.hint.Hint;
import mage.counters.Counter;
import mage.counters.CounterType;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.abilities.condition.common.CountersOnPermanentsCondition;
import mage.util.CardUtil;
/**
* A hint which keeps track of how many counters of a specific type there are
* among some type of permanents
*
*
* @author alexander-novo
*/
public class CountersOnPermanentsHint implements Hint {
@ -23,6 +23,10 @@ public class CountersOnPermanentsHint implements Hint {
// Which counter type to count
public final CounterType counterType;
public CountersOnPermanentsHint(CountersOnPermanentsCondition condition) {
this(condition.filter, condition.counterType);
}
/**
* @param filter Which permanents to consider counters on
* @param counterType Which counter type to count
@ -32,12 +36,9 @@ public class CountersOnPermanentsHint implements Hint {
this.counterType = counterType;
}
/**
* Copy parameters from a {@link CountersOnPermanentsCondition}
*/
public CountersOnPermanentsHint(CountersOnPermanentsCondition condition) {
this.filter = condition.filter;
this.counterType = condition.counterType;
public CountersOnPermanentsHint(final CountersOnPermanentsHint hint) {
this.filter = hint.filter.copy();
this.counterType = hint.counterType;
}
@Override
@ -56,7 +57,7 @@ public class CountersOnPermanentsHint implements Hint {
}
@Override
public Hint copy() {
return this;
public CountersOnPermanentsHint copy() {
return new CountersOnPermanentsHint(this);
}
}

View file

@ -14,8 +14,6 @@ import mage.constants.Zone;
*/
public final class ConsumingBlobOozeToken extends TokenImpl {
private static final DynamicValue powerValue = CardTypesInGraveyardCount.YOU;
public ConsumingBlobOozeToken() {
super("Ooze Token", "green Ooze creature token with \"This creature's power is equal to the number of card types among cards in your graveyard and its toughness is equal to that number plus 1.\"");
cardType.add(CardType.CREATURE);
@ -26,7 +24,9 @@ public final class ConsumingBlobOozeToken extends TokenImpl {
toughness = new MageInt(1);
// This creature's power is equal to the number of card types among cards in your graveyard and its toughness is equal to that number plus 1.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetBasePowerToughnessPlusOneSourceEffect(powerValue)));
this.addAbility(new SimpleStaticAbility(Zone.ALL,
new SetBasePowerToughnessPlusOneSourceEffect(CardTypesInGraveyardCount.YOU)
).addHint(CardTypesInGraveyardCount.YOU.getHint()));
}
private ConsumingBlobOozeToken(final ConsumingBlobOozeToken token) {

View file

@ -24,7 +24,9 @@ public final class TarmogoyfToken extends TokenImpl {
toughness = new MageInt(1);
// Tarmogoyf's power is equal to the number of card types among cards in all graveyards and its toughness is equal to that number plus 1.
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetBasePowerToughnessPlusOneSourceEffect(CardTypesInGraveyardCount.ALL)));
this.addAbility(new SimpleStaticAbility(Zone.ALL,
new SetBasePowerToughnessPlusOneSourceEffect(CardTypesInGraveyardCount.ALL)
).addHint(CardTypesInGraveyardCount.ALL.getHint()));
}
private TarmogoyfToken(final TarmogoyfToken token) {