- Improved TargetCardInASingleGraveyard to restrict all targets after the 1st to the same graveyard as the first chosen target

- Added slight documentation to Target and TargetCard
- Minor cleanup to Unlicensedhearse
This commit is contained in:
Alex Vasile 2022-05-25 10:57:13 -06:00
parent 82708e4273
commit eb63ea1e32
4 changed files with 86 additions and 17 deletions

View file

@ -39,13 +39,15 @@ enum UnlicensedHearseValue implements DynamicValue {
if (unlicensedHearse == null) {
return 0;
}
// use the source card, not the source object of the ability to grab the correct zcc
ExileZone cardsExiledWithUnlicensedHearse
= game.getExile().getExileZone(
CardUtil.getExileZoneId(game, unlicensedHearse.getId(), unlicensedHearse.getZoneChangeCounter(game)));
ExileZone cardsExiledWithUnlicensedHearse = game.getExile().getExileZone(
CardUtil.getExileZoneId(game, unlicensedHearse.getId(), unlicensedHearse.getZoneChangeCounter(game))
);
if (cardsExiledWithUnlicensedHearse == null) {
return 0;
}
return cardsExiledWithUnlicensedHearse.size();
}
@ -72,8 +74,7 @@ public final class UnlicensedHearse extends CardImpl {
this.toughness = new MageInt(0);
// {T}: Exile up to two target cards from a single graveyard.
Ability ability
= new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new TapSourceCost());
Ability ability = new SimpleActivatedAbility(new ExileTargetForSourceEffect(), new TapSourceCost());
ability.addTarget(new TargetCardInASingleGraveyard(0, 2, StaticFilters.FILTER_CARD_CARDS));
this.addAbility(ability);
@ -81,8 +82,9 @@ public final class UnlicensedHearse extends CardImpl {
this.addAbility(
new SimpleStaticAbility(
Zone.ALL,
new SetPowerToughnessSourceEffect(UnlicensedHearseValue.instance, Duration.EndOfGame))
.addHint(hint));
new SetPowerToughnessSourceEffect(UnlicensedHearseValue.instance, Duration.EndOfGame)
).addHint(hint)
);
// Crew 2
this.addAbility(new CrewAbility(2));

View file

@ -36,6 +36,14 @@ public interface Target extends Serializable {
// methods for targets
boolean canChoose(UUID sourceControllerId, Ability source, Game game);
/**
* Returns a set of all possible targets that match the criteria of the implemented Target class.
*
* @param sourceControllerId UUID of the ability's controller
* @param source Ability which requires the targets
* @param game Current game
* @return Set of the UUIDs of possible targets
*/
Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game);
boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game);

View file

@ -51,7 +51,8 @@ public class TargetCard extends TargetObject {
}
/**
* Checks if there are enough {@link Card} that can be chosen.
* Checks if there are enough {@link Card cards} in the appropriate zone that the player can choose from among them
* or if they are autochosen since there are fewer than the minimum number.
*
* @param sourceControllerId - controller of the target event source
* @param source
@ -160,6 +161,7 @@ public class TargetCard extends TargetObject {
switch (zone) {
case HAND:
for (Card card : player.getHand().getCards(filter, sourceControllerId, source, game)) {
// TODO: Why for sourceId == null?
if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) {
possibleTargets.add(card.getId());
}

View file

@ -2,12 +2,16 @@ package mage.target.common;
import mage.abilities.Ability;
import mage.cards.Card;
import mage.constants.CommanderCardType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.game.events.TargetEvent;
import mage.players.Player;
import mage.target.TargetCard;
import java.util.UUID;
import java.util.*;
import java.util.stream.Collectors;
/**
* @author LevelX2
@ -20,22 +24,75 @@ public class TargetCardInASingleGraveyard extends TargetCard {
super(minNumTargets, maxNumTargets, Zone.GRAVEYARD, filter.copy().withMessage(filter.getMessage() + " from a single graveyard"));
}
public TargetCardInASingleGraveyard(final TargetCardInASingleGraveyard target) {
private TargetCardInASingleGraveyard(final TargetCardInASingleGraveyard target) {
super(target);
}
@Override
public boolean canTarget(UUID id, Ability source, Game game) {
UUID firstTarget = this.getFirstTarget();
if (firstTarget != null) {
Card card = game.getCard(firstTarget);
Card targetCard = game.getCard(id);
if (card == null || targetCard == null
|| !card.isOwnedBy(targetCard.getOwnerId())) {
return false;
if (firstTarget == null) {
return false;
}
Card card = game.getCard(firstTarget);
Card targetCard = game.getCard(id);
if (card == null || targetCard == null || !card.isOwnedBy(targetCard.getOwnerId())) {
return false;
}
return super.canTarget(id, source, game);
}
/**
* Set of UUIDs of all possible targets
*
* @param sourceControllerId UUID of the ability's controller
* @param source Ability which requires the targets
* @param game Current game
* @return Set of the UUIDs of possible targets
*/
@Override
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
Set<UUID> possibleTargets = new HashSet<>();
UUID sourceId = source != null ? source.getSourceId() : null;
UUID controllerOfFirstTarget = null;
// If any targets have been chosen, get the UUID of the owner in order to limit the targets to that owner's graveyard
if (!targets.isEmpty()) {
for (UUID cardInGraveyardId : targets.keySet()) {
Card targetCard = game.getCard(cardInGraveyardId);
if (targetCard == null) {
continue;
}
UUID ownerOfCardId = targetCard.getOwnerId();
controllerOfFirstTarget = ownerOfCardId;
break; // Only need the first UUID since they will all be the same
}
}
return super.canTarget(id, source, game);
for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
// If the playerId of this iteration is not the same as that of any existing target, then continue
// All cards must be from the same player's graveyard.
if (controllerOfFirstTarget != null && !playerId.equals(controllerOfFirstTarget)) {
continue;
}
Player player = game.getPlayer(playerId);
if (player == null) {
continue;
}
for (Card card : player.getGraveyard().getCards(filter, sourceControllerId, source, game)) {
// TODO: Why for sourceId == null?
if (sourceId == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, sourceId, sourceControllerId))) {
possibleTargets.add(card.getId());
}
}
}
return possibleTargets;
}
@Override