foul-magics/Mage.Sets/src/mage/cards/k/KeeperOfTheDead.java
Oleg Agafonov c7a485b728 reworked AI, targeting and targets logic:
- refactor: simplified target implementation from a dozen canTarget, canChoose and possibleTargets methods to canTarget/possibleTargets only (part of #13638, #13766);
- refactor: fixed wrong target implementations in many cards (example: TargetCardInHand for opponent's hand, close #6210);
- AI: now human, AI and test players -- all use possibleTargets logic in most use cases instead filters or custom validation;
- AI: improved AI sims support for multiple targets abilities;
- AI: improved AI stability, freezes and targets errors in some use cases;
2025-08-04 23:56:23 +04:00

165 lines
5.6 KiB
Java

package mage.cards.k;
import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.FilterPlayer;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.ObjectSourcePlayer;
import mage.filter.predicate.ObjectSourcePlayerPredicate;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.TargetPlayer;
import java.util.Set;
import java.util.UUID;
/**
* @author spjspj
*/
public final class KeeperOfTheDead extends CardImpl {
private static final FilterPlayer filter = new FilterPlayer();
static {
filter.add(new KeeperOfDeadPredicate());
}
public KeeperOfTheDead(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}{B}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.WIZARD);
this.power = new MageInt(1);
this.toughness = new MageInt(2);
// {B}, {T}: Choose target opponent who had at least two fewer creature cards in their graveyard than you did as you activated this ability. Destroy target nonblack creature they control.
Ability ability = new SimpleActivatedAbility(new KeeperOfTheDeadEffect(), new TapSourceCost());
ability.addCost(new ManaCostsImpl<>("{B}"));
ability.addTarget(new TargetPlayer(1, 1, false, filter));
ability.addTarget(new KeeperOfTheDeadCreatureTarget());
this.addAbility(ability);
}
private KeeperOfTheDead(final KeeperOfTheDead card) {
super(card);
}
@Override
public KeeperOfTheDead copy() {
return new KeeperOfTheDead(this);
}
}
class KeeperOfDeadPredicate implements ObjectSourcePlayerPredicate<Player> {
@Override
public boolean apply(ObjectSourcePlayer<Player> input, Game game) {
Player targetPlayer = input.getObject();
Permanent sourceObject = input.getSource().getSourcePermanentIfItStillExists(game);
Player controller = null;
if (sourceObject != null) {
controller = game.getPlayer(sourceObject.getControllerId());
}
if (targetPlayer == null
|| controller == null
|| !controller.hasOpponent(targetPlayer.getId(), game)) {
return false;
}
int countGraveyardTargetPlayer = targetPlayer.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURES, game).size();
int countGraveyardController = controller.getGraveyard().getCards(StaticFilters.FILTER_CARD_CREATURES, game).size();
return countGraveyardController >= countGraveyardTargetPlayer + 2;
}
@Override
public String toString() {
return "opponent who had at least two fewer creature cards in their graveyard than you did as you activated this ability";
}
}
class KeeperOfTheDeadCreatureTarget extends TargetPermanent {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature that player controls");
static {
filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK)));
}
public KeeperOfTheDeadCreatureTarget() {
super(1, 1, filter);
}
private KeeperOfTheDeadCreatureTarget(final KeeperOfTheDeadCreatureTarget target) {
super(target);
}
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
Player needPlayer = game.getPlayerOrPlaneswalkerController(source.getFirstTarget());
if (needPlayer == null) {
// playable or not selected - use any
} else {
// filter by controller
possibleTargets.removeIf(id -> {
Permanent permanent = game.getPermanent(id);
return permanent == null
|| permanent.getId().equals(source.getFirstTarget())
|| !permanent.isControlledBy(needPlayer.getId());
});
}
return possibleTargets;
}
@Override
public KeeperOfTheDeadCreatureTarget copy() {
return new KeeperOfTheDeadCreatureTarget(this);
}
}
class KeeperOfTheDeadEffect extends OneShotEffect {
KeeperOfTheDeadEffect() {
super(Outcome.DestroyPermanent);
this.staticText = "Destroy target nonblack creature contolled by target opponent who had at least two fewer creature cards in their graveyard than you did as you activated this ability";
}
private KeeperOfTheDeadEffect(final KeeperOfTheDeadEffect effect) {
super(effect);
}
@Override
public KeeperOfTheDeadEffect copy() {
return new KeeperOfTheDeadEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player opponent = game.getPlayer(source.getTargets().get(0).getFirstTarget());
if (opponent != null) {
Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget());
if (creature != null) {
creature.destroy(source, game, false);
}
}
return true;
}
}