diff --git a/Mage.Sets/src/mage/cards/u/UnlicensedHearse.java b/Mage.Sets/src/mage/cards/u/UnlicensedHearse.java index 98a4b4aa89d..00b837ff489 100644 --- a/Mage.Sets/src/mage/cards/u/UnlicensedHearse.java +++ b/Mage.Sets/src/mage/cards/u/UnlicensedHearse.java @@ -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)); diff --git a/Mage/src/main/java/mage/target/Target.java b/Mage/src/main/java/mage/target/Target.java index 244fda9f259..bcd3a870570 100644 --- a/Mage/src/main/java/mage/target/Target.java +++ b/Mage/src/main/java/mage/target/Target.java @@ -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 possibleTargets(UUID sourceControllerId, Ability source, Game game); boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game); diff --git a/Mage/src/main/java/mage/target/TargetCard.java b/Mage/src/main/java/mage/target/TargetCard.java index c2dfee9e968..752a1572aa8 100644 --- a/Mage/src/main/java/mage/target/TargetCard.java +++ b/Mage/src/main/java/mage/target/TargetCard.java @@ -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()); } diff --git a/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java b/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java index 88e0f66386e..b6fe654667c 100644 --- a/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java +++ b/Mage/src/main/java/mage/target/common/TargetCardInASingleGraveyard.java @@ -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 possibleTargets(UUID sourceControllerId, Ability source, Game game) { + Set 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