implement [MH3] Nethergoyf, refactor targets usages by game param (#12267)

This commit is contained in:
Susucre 2024-05-21 13:34:38 +02:00 committed by GitHub
parent 88b6f4036f
commit 754b382e78
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
62 changed files with 592 additions and 285 deletions

View file

@ -1147,7 +1147,7 @@ public class CardView extends SimpleCardView {
// from normal targets
for (Target target : targets) {
if (target.isChosen()) {
if (target.isChosen(game)) {
newTargets.addAll(target.getTargets());
}
}

View file

@ -34,7 +34,6 @@ import mage.target.Targets;
import mage.util.RandomUtil;
import org.apache.log4j.Logger;
import java.io.File;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;
@ -392,7 +391,7 @@ public class ComputerPlayer6 extends ComputerPlayer {
if (effect != null
&& stackObject.getControllerId().equals(playerId)) {
Target target = effect.getTarget();
if (!target.doneChoosing()) {
if (!target.doneChoosing(game)) {
for (UUID targetId : target.possibleTargets(stackObject.getControllerId(), stackObject.getStackAbility(), game)) {
Game sim = game.createSimulationForAI();
StackAbility newAbility = (StackAbility) stackObject.copy();
@ -740,10 +739,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
if (targets.isEmpty()) {
return super.chooseTarget(outcome, cards, target, source, game);
}
if (!target.doneChoosing()) {
if (!target.doneChoosing(game)) {
for (UUID targetId : targets) {
target.addTarget(targetId, source, game);
if (target.doneChoosing()) {
if (target.doneChoosing(game)) {
targets.clear();
return true;
}
@ -758,10 +757,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
if (targets.isEmpty()) {
return super.choose(outcome, cards, target, source, game);
}
if (!target.doneChoosing()) {
if (!target.doneChoosing(game)) {
for (UUID targetId : targets) {
target.add(targetId, game);
if (target.doneChoosing()) {
if (target.doneChoosing(game)) {
targets.clear();
return true;
}

View file

@ -10,7 +10,6 @@ import mage.abilities.costs.mana.ManaCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.costs.mana.VariableManaCost;
import mage.abilities.effects.Effect;
import mage.cards.Card;
import mage.game.Game;
import mage.game.combat.Combat;
import mage.game.events.GameEvent;
@ -149,7 +148,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
}
newAbility.adjustTargets(game);
// add the different possible target option for the specific X value
if (!newAbility.getTargets().getUnchosen().isEmpty()) {
if (!newAbility.getTargets().getUnchosen(game).isEmpty()) {
addTargetOptions(options, newAbility, targetNum, game);
}
}

View file

@ -174,7 +174,7 @@ public class ComputerPlayer extends PlayerImpl {
for (int i = unplayable.size() - 1; i >= 0; i--) {
if (target.canTarget(abilityControllerId, unplayable.values().toArray(new Card[0])[i].getId(), null, game)) {
target.add(unplayable.values().toArray(new Card[0])[i].getId(), game);
if (target.isChosen()) {
if (target.isChosen(game)) {
return true;
}
}
@ -184,7 +184,7 @@ public class ComputerPlayer extends PlayerImpl {
for (int i = 0; i < hand.size(); i++) {
if (target.canTarget(abilityControllerId, hand.toArray(new UUID[0])[i], null, game)) {
target.add(hand.toArray(new UUID[0])[i], game);
if (target.isChosen()) {
if (target.isChosen(game)) {
return true;
}
}
@ -252,12 +252,12 @@ public class ComputerPlayer extends PlayerImpl {
}
// add the target
target.add(permanent.getId(), game);
if (target.doneChoosing()) {
if (target.doneChoosing(game)) {
return true;
}
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetCardInHand
@ -269,7 +269,7 @@ public class ComputerPlayer extends PlayerImpl {
cards.add(card);
}
}
while ((outcome.isGood() ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen())
while ((outcome.isGood() ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen(game))
&& !cards.isEmpty()) {
Card pick = pickTarget(abilityControllerId, cards, outcome, target, null, game);
if (pick != null) {
@ -277,7 +277,7 @@ public class ComputerPlayer extends PlayerImpl {
cards.remove(pick);
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetAnyTarget) {
@ -410,7 +410,7 @@ public class ComputerPlayer extends PlayerImpl {
// exile cost workaround: exile is bad, but exile from graveyard in most cases is good (more exiled -- more good things you get, e.g. delve's pay)
boolean isRealGood = outcome.isGood() || outcome == Outcome.Exile;
while ((isRealGood ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen())
while ((isRealGood ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen(game))
&& !cards.isEmpty()) {
Card pick = pickTarget(abilityControllerId, cards, outcome, target, null, game);
if (pick != null) {
@ -421,7 +421,7 @@ public class ComputerPlayer extends PlayerImpl {
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetCardInGraveyard
@ -437,7 +437,7 @@ public class ComputerPlayer extends PlayerImpl {
// exile cost workaround: exile is bad, but exile from graveyard in most cases is good (more exiled -- more good things you get, e.g. delve's pay)
boolean isRealGood = outcome.isGood() || outcome == Outcome.Exile;
while ((isRealGood ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen())
while ((isRealGood ? target.getTargets().size() < target.getMaxNumberOfTargets() : !target.isChosen(game))
&& !cards.isEmpty()) {
Card pick = pickTarget(abilityControllerId, cards, outcome, target, null, game);
if (pick != null) {
@ -448,7 +448,7 @@ public class ComputerPlayer extends PlayerImpl {
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetCardInYourGraveyard
@ -460,7 +460,7 @@ public class ComputerPlayer extends PlayerImpl {
Card card = pickTarget(abilityControllerId, cards, outcome, target, null, game);
if (card != null && alreadyTargeted != null && !alreadyTargeted.contains(card.getId())) {
target.add(card.getId(), game);
if (target.isChosen()) {
if (target.isChosen(game)) {
return true;
}
}
@ -485,7 +485,7 @@ public class ComputerPlayer extends PlayerImpl {
Card card = pickTarget(abilityControllerId, cards, outcome, target, null, game);
if (card != null && alreadyTargeted != null && !alreadyTargeted.contains(card.getId())) {
target.add(card.getId(), game);
if (target.isChosen()) {
if (target.isChosen(game)) {
return true;
}
}
@ -517,14 +517,14 @@ public class ComputerPlayer extends PlayerImpl {
if (target.getOriginalTarget() instanceof TargetPermanentOrSuspendedCard) {
Cards cards = new CardsImpl(possibleTargets);
List<Card> possibleCards = new ArrayList<>(cards.getCards(game));
while (!target.isChosen() && !possibleCards.isEmpty()) {
while (!target.isChosen(game) && !possibleCards.isEmpty()) {
Card pick = pickTarget(abilityControllerId, possibleCards, outcome, target, null, game);
if (pick != null) {
target.addTarget(pick.getId(), null, game);
possibleCards.remove(pick);
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetCard
@ -537,14 +537,14 @@ public class ComputerPlayer extends PlayerImpl {
}
}
}
while (!target.isChosen() && !cardsInCommandZone.isEmpty()) {
while (!target.isChosen(game) && !cardsInCommandZone.isEmpty()) {
Card pick = pickTarget(abilityControllerId, cardsInCommandZone, outcome, target, null, game);
if (pick != null) {
target.addTarget(pick.getId(), null, game);
cardsInCommandZone.remove(pick);
}
}
return target.isChosen();
return target.isChosen(game);
}
throw new IllegalStateException("Target wasn't handled in computer's choose method: " + target.getClass().getCanonicalName());
@ -641,7 +641,7 @@ public class ComputerPlayer extends PlayerImpl {
}
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetDiscard
@ -650,7 +650,7 @@ public class ComputerPlayer extends PlayerImpl {
// good
Cards cards = new CardsImpl(possibleTargets);
List<Card> cardsInHand = new ArrayList<>(cards.getCards(game));
while (!target.isChosen()
while (!target.isChosen(game)
&& !cardsInHand.isEmpty()
&& target.getMaxNumberOfTargets() > target.getTargets().size()) {
Card card = pickBestCard(cardsInHand, null, target, source, game);
@ -658,7 +658,7 @@ public class ComputerPlayer extends PlayerImpl {
if (target.canTarget(abilityControllerId, card.getId(), source, game)) {
target.addTarget(card.getId(), source, game);
cardsInHand.remove(card);
if (target.isChosen()) {
if (target.isChosen(game)) {
return true;
}
}
@ -671,7 +671,7 @@ public class ComputerPlayer extends PlayerImpl {
if (possibleTargets.contains(card.getId())
&& target.canTarget(abilityControllerId, card.getId(), source, game)) {
target.addTarget(card.getId(), source, game);
if (target.isChosen()) {
if (target.isChosen(game)) {
return true;
}
}
@ -681,7 +681,7 @@ public class ComputerPlayer extends PlayerImpl {
if (possibleTargets.contains(card.getId())
&& target.canTarget(abilityControllerId, card.getId(), source, game)) {
target.addTarget(card.getId(), source, game);
if (target.isChosen()) {
if (target.isChosen(game)) {
return true;
}
}
@ -707,7 +707,7 @@ public class ComputerPlayer extends PlayerImpl {
}
}
}
return target.isChosen();
return target.isChosen(game);
}
@ -749,7 +749,7 @@ public class ComputerPlayer extends PlayerImpl {
target.addTarget(permanent.getId(), source, game);
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetCreatureOrPlayer) {
@ -956,14 +956,14 @@ public class ComputerPlayer extends PlayerImpl {
if (target.getOriginalTarget() instanceof TargetCardInYourGraveyard) {
List<Card> cards = new ArrayList<>(game.getPlayer(abilityControllerId).getGraveyard().getCards((FilterCard) target.getFilter(), game));
while (!target.isChosen() && !cards.isEmpty()) {
while (!target.isChosen(game) && !cards.isEmpty()) {
Card card = pickTarget(abilityControllerId, cards, outcome, target, source, game);
if (card != null) {
target.addTarget(card.getId(), source, game);
cards.remove(card); // pickTarget don't remove cards (only on second+ tries)
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetSpell
@ -1034,7 +1034,7 @@ public class ComputerPlayer extends PlayerImpl {
if (target.getOriginalTarget() instanceof TargetDefender) {
UUID randomDefender = RandomUtil.randomFromCollection(possibleTargets);
target.addTarget(randomDefender, source, game);
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetCardInASingleGraveyard) {
@ -1042,14 +1042,14 @@ public class ComputerPlayer extends PlayerImpl {
for (Player player : game.getPlayers().values()) {
cards.addAll(player.getGraveyard().getCards(game));
}
while (!target.isChosen() && !cards.isEmpty()) {
while (!target.isChosen(game) && !cards.isEmpty()) {
Card pick = pickTarget(abilityControllerId, cards, outcome, target, source, game);
if (pick != null) {
target.addTarget(pick.getId(), source, game);
cards.remove(pick); // pickTarget don't remove cards (only on second+ tries)
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetCardInExile) {
@ -1070,14 +1070,14 @@ public class ComputerPlayer extends PlayerImpl {
cards.add(card);
}
}
while (!target.isChosen() && !cards.isEmpty()) {
while (!target.isChosen(game) && !cards.isEmpty()) {
Card pick = pickTarget(abilityControllerId, cards, outcome, target, source, game);
if (pick != null) {
target.addTarget(pick.getId(), source, game);
cards.remove(pick); // pickTarget don't remove cards (only on second+ tries)
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetActivatedAbility) {
@ -1088,22 +1088,22 @@ public class ComputerPlayer extends PlayerImpl {
stackObjects.add(stackObject);
}
}
while (!target.isChosen() && !stackObjects.isEmpty()) {
while (!target.isChosen(game) && !stackObjects.isEmpty()) {
StackObject pick = stackObjects.get(0);
if (pick != null) {
target.addTarget(pick.getId(), source, game);
stackObjects.remove(0);
}
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetActivatedOrTriggeredAbility) {
Iterator<UUID> iterator = target.possibleTargets(source.getControllerId(), source, game).iterator();
while (!target.isChosen() && iterator.hasNext()) {
while (!target.isChosen(game) && iterator.hasNext()) {
target.addTarget(iterator.next(), source, game);
}
return target.isChosen();
return target.isChosen(game);
}
if (target.getOriginalTarget() instanceof TargetCardInGraveyardBattlefieldOrStack) {
@ -1121,14 +1121,14 @@ public class ComputerPlayer extends PlayerImpl {
if (target.getOriginalTarget() instanceof TargetPermanentOrSuspendedCard) {
Cards cards = new CardsImpl(possibleTargets);
List<Card> possibleCards = new ArrayList<>(cards.getCards(game));
while (!target.isChosen() && !possibleCards.isEmpty()) {
while (!target.isChosen(game) && !possibleCards.isEmpty()) {
Card pick = pickTarget(abilityControllerId, possibleCards, outcome, target, source, game);
if (pick != null) {
target.addTarget(pick.getId(), source, game);
possibleCards.remove(pick);
}
}
return target.isChosen();
return target.isChosen(game);
}
throw new IllegalStateException("Target wasn't handled in computer's chooseTarget method: " + target.getClass().getCanonicalName());
@ -2063,7 +2063,7 @@ public class ComputerPlayer extends PlayerImpl {
// we still use playerId when getting cards even if they don't control the search
List<Card> cardChoices = new ArrayList<>(cards.getCards(target.getFilter(), playerId, source, game));
while (!target.doneChoosing()) {
while (!target.doneChoosing(game)) {
Card card = pickTarget(abilityControllerId, cardChoices, outcome, target, source, game);
if (card != null) {
target.addTarget(card.getId(), source, game);
@ -2094,7 +2094,7 @@ public class ComputerPlayer extends PlayerImpl {
}
List<Card> cardChoices = new ArrayList<>(cards.getCards(target.getFilter(), abilityControllerId, source, game));
while (!target.doneChoosing()) {
while (!target.doneChoosing(game)) {
Card card = pickTarget(abilityControllerId, cardChoices, outcome, target, source, game);
if (card != null) {
target.add(card.getId(), game);

View file

@ -674,7 +674,7 @@ public class HumanPlayer extends PlayerImpl {
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), possibleTargetIds, required, getOptions(target, options));
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(game), getRelatedObjectName(source, game)), possibleTargetIds, required, getOptions(target, options));
}
waitForResponse(game);
responseId = getFixedResponseUUID(game);
@ -696,7 +696,7 @@ public class HumanPlayer extends PlayerImpl {
if (target instanceof TargetPermanent) {
if (((TargetPermanent) target).canTarget(abilityControllerId, responseId, source, game, false)) {
target.add(responseId, game);
if (target.doneChoosing()) {
if (target.doneChoosing(game)) {
return true;
}
}
@ -708,7 +708,7 @@ public class HumanPlayer extends PlayerImpl {
target.remove(responseId);
} else {
target.addTarget(responseId, (Ability) object, game);
if (target.doneChoosing()) {
if (target.doneChoosing(game)) {
return true;
}
}
@ -718,7 +718,7 @@ public class HumanPlayer extends PlayerImpl {
target.remove(responseId);
} else {
target.addTarget(responseId, null, game);
if (target.doneChoosing()) {
if (target.doneChoosing(game)) {
return true;
}
}
@ -775,7 +775,7 @@ public class HumanPlayer extends PlayerImpl {
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)),
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(game), getRelatedObjectName(source, game)),
possibleTargetIds, required, getOptions(target, options));
}
waitForResponse(game);
@ -792,7 +792,7 @@ public class HumanPlayer extends PlayerImpl {
if (possibleTargetIds.contains(responseId)) {
if (target.canTarget(abilityControllerId, responseId, source, game)) {
target.addTarget(responseId, source, game);
if (target.doneChoosing()) {
if (target.doneChoosing(game)) {
return true;
}
}
@ -873,7 +873,7 @@ public class HumanPlayer extends PlayerImpl {
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage()), cards, required, options);
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage(game)), cards, required, options);
}
waitForResponse(game);
@ -886,7 +886,7 @@ public class HumanPlayer extends PlayerImpl {
} else {
if (target.canTarget(abilityControllerId, responseId, source, cards, game)) {
target.add(responseId, game);
if (target.doneChoosing()) {
if (target.doneChoosing(game)) {
return true;
}
}
@ -956,7 +956,7 @@ public class HumanPlayer extends PlayerImpl {
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), cards, required, options);
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage(game), getRelatedObjectName(source, game)), cards, required, options);
}
waitForResponse(game);
@ -968,7 +968,7 @@ public class HumanPlayer extends PlayerImpl {
target.remove(responseId);
} else if (target.canTarget(abilityControllerId, responseId, source, cards, game)) {
target.addTarget(responseId, source, game);
if (target.doneChoosing()) {
if (target.doneChoosing(game)) {
return true;
}
}
@ -1051,7 +1051,7 @@ public class HumanPlayer extends PlayerImpl {
prepareForResponse(game);
if (!isExecutingMacro()) {
String multiType = multiAmountType == MultiAmountType.DAMAGE ? " to divide %d damage" : " to distribute %d counters";
String message = target.getMessage() + String.format(multiType, amountTotal);
String message = target.getMessage(game) + String.format(multiType, amountTotal);
game.fireSelectTargetEvent(playerId, new MessageToClient(message, getRelatedObjectName(source, game)), possibleTargetIds, required, options);
}
waitForResponse(game);

View file

@ -115,7 +115,7 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect {
TargetPermanent target = new TargetPermanent(filter);
target.withNotTarget(true);
if (target.canChoose(controller.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
while (!target.isChosen(game) && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
} else {

View file

@ -1,6 +1,5 @@
package mage.cards.b;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageAllEffect;
@ -17,8 +16,9 @@ import mage.target.Target;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author Plopman
*/
public final class BurningOfXinye extends CardImpl {
@ -53,7 +53,7 @@ class BurningOfXinyeEffect extends OneShotEffect{
staticText = "You destroy four lands you control, then target opponent destroys four lands they control";
}
public BurningOfXinyeEffect ( BurningOfXinyeEffect effect ) {
private BurningOfXinyeEffect(final BurningOfXinyeEffect effect) {
super(effect);
}
@ -82,7 +82,7 @@ class BurningOfXinyeEffect extends OneShotEffect{
Target target = new TargetControlledPermanent(amount, amount, filter, true);
if (amount > 0 && target.canChoose(player.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(player.getId(), source, game) && player.canRespond()) {
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
player.choose(Outcome.DestroyPermanent, target, source, game);
}

View file

@ -1,7 +1,6 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
@ -17,11 +16,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.choices.Choice;
import mage.choices.ChoiceCreatureType;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
@ -30,8 +25,9 @@ import mage.target.common.TargetCreaturePermanent;
import mage.target.common.TargetOpponent;
import mage.util.CardUtil;
import java.util.UUID;
/**
*
* @author L_J
*/
public final class CallousOppressor extends CardImpl {
@ -118,7 +114,7 @@ class CallousOppressorChooseCreatureTypeEffect extends OneShotEffect {
if (controller != null) {
TargetOpponent target = new TargetOpponent(true);
if (target.canChoose(controller.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
while (!target.isChosen(game) && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
} else {

View file

@ -1,9 +1,6 @@
package mage.cards.c;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
@ -21,8 +18,11 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetControlledPermanent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
*
* @author jeffwadsworth
*/
public final class Cataclysm extends CardImpl {
@ -68,7 +68,7 @@ class CataclysmEffect extends OneShotEffect {
Target target4 = new TargetControlledPermanent(1, 1, new FilterControlledLandPermanent(), true);
if (target1.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target1.isChosen() && target1.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target1.isChosen(game) && target1.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target1, source, game);
}
Permanent artifact = game.getPermanent(target1.getFirstTarget());
@ -79,7 +79,7 @@ class CataclysmEffect extends OneShotEffect {
}
if (target2.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target2.isChosen() && target2.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target2.isChosen(game) && target2.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target2, source, game);
}
Permanent creature = game.getPermanent(target2.getFirstTarget());
@ -90,7 +90,7 @@ class CataclysmEffect extends OneShotEffect {
}
if (target3.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target3.isChosen() && target3.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target3.isChosen(game) && target3.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target3, source, game);
}
Permanent enchantment = game.getPermanent(target3.getFirstTarget());
@ -101,7 +101,7 @@ class CataclysmEffect extends OneShotEffect {
}
if (target4.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target4.isChosen() && target4.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target4.isChosen(game) && target4.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target4, source, game);
}
Permanent land = game.getPermanent(target4.getFirstTarget());

View file

@ -101,7 +101,7 @@ class CataclysmicGearhulkEffect extends OneShotEffect {
Target target4 = new TargetControlledPermanent(1, 1, filterPlaneswalker, true);
if (target1.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target1.isChosen() && target1.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target1.isChosen(game) && target1.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target1, source, game);
}
Permanent artifact = game.getPermanent(target1.getFirstTarget());
@ -112,7 +112,7 @@ class CataclysmicGearhulkEffect extends OneShotEffect {
}
if (target2.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target2.isChosen() && target2.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target2.isChosen(game) && target2.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target2, source, game);
}
Permanent creature = game.getPermanent(target2.getFirstTarget());
@ -123,7 +123,7 @@ class CataclysmicGearhulkEffect extends OneShotEffect {
}
if (target3.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target3.isChosen() && target3.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target3.isChosen(game) && target3.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target3, source, game);
}
Permanent enchantment = game.getPermanent(target3.getFirstTarget());
@ -134,7 +134,7 @@ class CataclysmicGearhulkEffect extends OneShotEffect {
}
if (target4.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target4.isChosen() && target4.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target4.isChosen(game) && target4.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target4, source, game);
}
Permanent planeswalker = game.getPermanent(target4.getFirstTarget());

View file

@ -1,9 +1,6 @@
package mage.cards.c;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
@ -26,6 +23,10 @@ import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetControlledPermanent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
public final class CatchRelease extends SplitCard {
public CatchRelease(UUID ownerId, CardSetInfo setInfo) {
@ -87,7 +88,7 @@ class ReleaseSacrificeEffect extends OneShotEffect {
Target target5 = new TargetControlledPermanent(1, 1, new FilterControlledPlaneswalkerPermanent(), true);
if (target1.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target1.isChosen() && target1.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target1.isChosen(game) && target1.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target1, source, game);
}
Permanent artifact = game.getPermanent(target1.getFirstTarget());
@ -98,7 +99,7 @@ class ReleaseSacrificeEffect extends OneShotEffect {
}
if (target2.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target2.isChosen() && target2.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target2.isChosen(game) && target2.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target2, source, game);
}
Permanent creature = game.getPermanent(target2.getFirstTarget());
@ -109,7 +110,7 @@ class ReleaseSacrificeEffect extends OneShotEffect {
}
if (target3.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target3.isChosen() && target3.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target3.isChosen(game) && target3.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target3, source, game);
}
Permanent enchantment = game.getPermanent(target3.getFirstTarget());
@ -120,7 +121,7 @@ class ReleaseSacrificeEffect extends OneShotEffect {
}
if (target4.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target4.isChosen() && target4.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target4.isChosen(game) && target4.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target4, source, game);
}
Permanent land = game.getPermanent(target4.getFirstTarget());
@ -131,7 +132,7 @@ class ReleaseSacrificeEffect extends OneShotEffect {
}
if (target5.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target5.isChosen() && target5.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target5.isChosen(game) && target5.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target5, source, game);
}
Permanent planeswalker = game.getPermanent(target5.getFirstTarget());

View file

@ -1,7 +1,6 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealsDamageToAPlayerTriggeredAbility;
@ -10,7 +9,10 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates;
import mage.game.Game;
@ -20,8 +22,9 @@ import mage.target.Target;
import mage.target.common.TargetControlledPermanent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
*
* @author L_J
*/
public final class Clambassadors extends CardImpl {
@ -82,7 +85,7 @@ class ClambassadorsEffect extends OneShotEffect {
if (controller != null) {
Target target = new TargetControlledPermanent(1, 1, filter, true);
if (target.canChoose(controller.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
while (!target.isChosen(game) && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
}

View file

@ -19,19 +19,16 @@ import mage.constants.TargetController;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPlayer;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetSacrifice;
import java.util.Objects;
import java.util.UUID;
/**
*
* @author anonymous
*/
public final class CurseOfTheCabal extends CardImpl {
@ -87,7 +84,7 @@ class CurseOfTheCabalSacrificeEffect extends OneShotEffect {
}
Target target = new TargetSacrifice(amount, StaticFilters.FILTER_CONTROLLED_PERMANENT);
if (target.canChoose(targetPlayer.getId(), source, game)) {
while (!target.isChosen()
while (!target.isChosen(game)
&& target.canChoose(targetPlayer.getId(), source, game) && targetPlayer.canRespond()) {
targetPlayer.choose(Outcome.Sacrifice, target, source, game);
}

View file

@ -18,7 +18,6 @@ import mage.target.common.TargetSacrifice;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class DevourFlesh extends CardImpl {
@ -65,7 +64,7 @@ class DevourFleshSacrificeEffect extends OneShotEffect {
}
if (game.getBattlefield().count(TargetSacrifice.makeFilter(StaticFilters.FILTER_PERMANENT_CREATURE), player.getId(), source, game) > 0) {
Target target = new TargetSacrifice(StaticFilters.FILTER_PERMANENT_CREATURE);
while (player.canRespond() && !target.isChosen() && target.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target.isChosen(game) && target.canChoose(player.getId(), source, game)) {
player.choose(Outcome.Sacrifice, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());

View file

@ -1,10 +1,6 @@
package mage.cards.d;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
@ -22,6 +18,10 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetControlledPermanent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
* @author nantuko
*/
@ -68,7 +68,7 @@ class DivineReckoningEffect extends OneShotEffect {
if (player != null) {
Target target = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true);
if (target.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target.isChosen() && target.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target.isChosen(game) && target.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.Benefit, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());

View file

@ -108,7 +108,7 @@ class DrainPowerEffect extends OneShotEffect {
FilterLandPermanent filter2 = new FilterLandPermanent("land you control to tap for mana (remaining: " + permList.size() + ')');
filter2.add(new PermanentReferenceInCollectionPredicate(permList, game));
target = new TargetPermanent(1, 1, filter2, true);
while (!target.isChosen() && target.canChoose(targetPlayer.getId(), source, game) && targetPlayer.canRespond()) {
while (!target.isChosen(game) && target.canChoose(targetPlayer.getId(), source, game) && targetPlayer.canRespond()) {
targetPlayer.chooseTarget(Outcome.Neutral, target, source, game);
}
permanent = game.getPermanent(target.getFirstTarget());

View file

@ -20,7 +20,6 @@ import mage.target.common.TargetSacrifice;
import java.util.UUID;
/**
*
* @author fireshoes
*/
public final class EntrapmentManeuver extends CardImpl {
@ -67,7 +66,7 @@ class EntrapmentManeuverSacrificeEffect extends OneShotEffect {
}
if (game.getBattlefield().count(TargetSacrifice.makeFilter(StaticFilters.FILTER_ATTACKING_CREATURE), player.getId(), source, game) > 0) {
Target target = new TargetSacrifice(StaticFilters.FILTER_ATTACKING_CREATURE);
while (player.canRespond() && !target.isChosen() && target.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target.isChosen(game) && target.canChoose(player.getId(), source, game)) {
player.choose(Outcome.Sacrifice, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());

View file

@ -68,7 +68,7 @@ class EunuchsIntriguesEffect extends OneShotEffect {
filter.add(new ControllerIdPredicate(player.getId()));
Target target = new TargetPermanent(1, 1, filter, true);
if (target.canChoose(player.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(player.getId(), source, game) && player.canRespond()) {
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());

View file

@ -33,7 +33,6 @@ import java.util.Objects;
import java.util.UUID;
/**
*
* @author notgreat
*/
public final class FabricationFoundry extends CardImpl {
@ -96,6 +95,7 @@ enum ArtifactSpellOrActivatedAbilityCondition implements Condition {
return object != null && object.isArtifact(game) && !source.isActivated();
}
}
//Cost based on Kozilek, The Great Distortion and CrewAbility
class ExileTargetsTotalManaValueCost extends CostImpl {
private static final FilterPermanent filter = new FilterControlledArtifactPermanent("one or more other artifacts you control with total mana value X");
@ -103,6 +103,7 @@ class ExileTargetsTotalManaValueCost extends CostImpl {
static {
filter.add(AnotherPredicate.instance);
}
public ExileTargetsTotalManaValueCost() {
this.text = "Exile one or more other artifacts you control with total mana value X";
}
@ -125,7 +126,7 @@ class ExileTargetsTotalManaValueCost extends CostImpl {
int sum = 0;
Target target = new TargetPermanent(1, Integer.MAX_VALUE, filter, true) {
@Override
public String getMessage() {
public String getMessage(Game game) {
// shows selected mana value
int selectedPower = this.targets.keySet().stream()
.map(game::getPermanent)
@ -136,7 +137,7 @@ class ExileTargetsTotalManaValueCost extends CostImpl {
if (selectedPower >= minX) {
extraInfo = HintUtils.prepareText(extraInfo, Color.GREEN);
}
return super.getMessage() + " " + extraInfo;
return super.getMessage(game) + " " + extraInfo;
}
};
if (!target.choose(Outcome.Exile, controllerId, source.getSourceId(), source, game)) {

View file

@ -129,7 +129,7 @@ class FalseOrdersUnblockEffect extends OneShotEffect {
TargetPermanent target = new TargetPermanent(filter);
target.withNotTarget(true);
if (target.canChoose(controller.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
while (!target.isChosen(game) && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
} else {

View file

@ -143,9 +143,9 @@ class FireballTargetCreatureOrPlayer extends TargetAnyTarget {
chosen = true;
}
if (!target.isChosen()) {
if (!target.isChosen(game)) {
Iterator<UUID> it2 = possibleTargets.iterator();
while (it2.hasNext() && !target.isChosen()) {
while (it2.hasNext() && !target.isChosen(game)) {
UUID nextTargetId = it2.next();
target.addTarget(nextTargetId, source, game, true);
@ -155,7 +155,7 @@ class FireballTargetCreatureOrPlayer extends TargetAnyTarget {
}
}
if (target.isChosen()) {
if (target.isChosen(game)) {
options.add(target);
}
}

View file

@ -1,9 +1,7 @@
package mage.cards.g;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ContinuousEffect;
@ -21,8 +19,9 @@ import mage.target.common.TargetAnyTarget;
import mage.target.common.TargetOpponent;
import mage.target.targetpointer.FixedTarget;
import java.util.UUID;
/**
*
* @author L_J
*/
public final class GoblinFestival extends CardImpl {
@ -72,7 +71,7 @@ class GoblinFestivalChangeControlEffect extends OneShotEffect {
if (sourcePermanent != null) {
Target target = new TargetOpponent(true);
if (target.canChoose(controller.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
while (!target.isChosen(game) && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
}

View file

@ -68,7 +68,7 @@ class GoblinWarCryEffect extends OneShotEffect {
filter.add(new ControllerIdPredicate(player.getId()));
Target target = new TargetPermanent(1, 1, filter, true);
if (target.canChoose(player.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(player.getId(), source, game) && player.canRespond()) {
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());

View file

@ -1,7 +1,6 @@
package mage.cards.i;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
@ -17,8 +16,9 @@ import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author TheElk801
*/
public final class ImperialEdict extends CardImpl {
@ -67,7 +67,7 @@ class ImperialEdictEffect extends OneShotEffect {
filter.add(new ControllerIdPredicate(player.getId()));
Target target = new TargetPermanent(1, 1, filter, true);
if (target.canChoose(player.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(player.getId(), source, game) && player.canRespond()) {
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());

View file

@ -1,10 +1,6 @@
package mage.cards.j;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
@ -15,11 +11,7 @@ import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.common.FilterControlledLandPermanent;
import mage.game.Game;
@ -31,8 +23,12 @@ import mage.target.TargetPermanent;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetOpponent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.UUID;
/**
*
* @author L_J
*/
public final class JalumGrifter extends CardImpl {
@ -95,7 +91,7 @@ class JalumGrifterEffect extends OneShotEffect {
Target target = new TargetControlledPermanent(2, 2, new FilterControlledLandPermanent(), true);
if (target.canChoose(controller.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
while (!target.isChosen(game) && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
}

View file

@ -91,7 +91,7 @@ class LegateLaniusCaesarsAceSacrificeEffect extends OneShotEffect {
continue;
}
TargetSacrifice target = new TargetSacrifice(numTargets, filter);
while (!target.isChosen() && target.canChoose(playerId, source, game) && player.canRespond()) {
while (!target.isChosen(game) && target.canChoose(playerId, source, game) && player.canRespond()) {
player.choose(Outcome.Sacrifice, target, source, game);
}
perms.addAll(target.getTargets());

View file

@ -1,8 +1,5 @@
package mage.cards.l;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
@ -11,11 +8,7 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.*;
import mage.filter.FilterPlayer;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.other.PlayerIdPredicate;
@ -25,8 +18,11 @@ import mage.players.Player;
import mage.target.TargetPlayer;
import mage.target.targetpointer.FixedTarget;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
*
* @author L_J
*/
public final class LoxodonPeacekeeper extends CardImpl {
@ -105,7 +101,7 @@ class LoxodonPeacekeeperEffect extends OneShotEffect {
}
TargetPlayer target = new TargetPlayer(1, 1, true, filter);
if (target.canChoose(controller.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
while (!target.isChosen(game) && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
} else {

View file

@ -43,7 +43,7 @@ public final class LunarHatchling extends CardImpl {
// Escape-{4}{G}{U}, Exile a land you control, Exile five other cards from your graveyard.
CostsImpl<Cost> additionalCost = new CostsImpl();
additionalCost.add(new ExileTargetCost(new TargetControlledPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_A_LAND)));
this.addAbility(new EscapeAbility(this, "{4}{G}{U}", 5, additionalCost));
this.addAbility(new EscapeAbility(this, "{4}{G}{U}", additionalCost, 5));
}
private LunarHatchling(final LunarHatchling card) {

View file

@ -0,0 +1,128 @@
package mage.cards.n;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.CostsImpl;
import mage.abilities.costs.common.ExileFromGraveCost;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.CardTypesInGraveyardCount;
import mage.abilities.effects.common.continuous.SetBasePowerToughnessPlusOneSourceEffect;
import mage.abilities.hint.HintUtils;
import mage.abilities.keyword.EscapeAbility;
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.predicate.mageobject.AnotherPredicate;
import mage.game.Game;
import mage.target.common.TargetCardInYourGraveyard;
import mage.util.CardUtil;
import java.awt.*;
import java.util.Collection;
import java.util.Objects;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* @author Susucr
*/
public final class Nethergoyf extends CardImpl {
private static final DynamicValue powerValue = CardTypesInGraveyardCount.YOU;
public Nethergoyf(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{B}");
this.subtype.add(SubType.LHURGOYF);
this.power = new MageInt(0);
this.toughness = new MageInt(1);
// Nethergoyf'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)));
// Escape--{2}{B}, Exile any number of other cards from your graveyard with four or more card types among them.
CostsImpl<Cost> additionalCost = new CostsImpl();
additionalCost.add(new ExileFromGraveCost(
new NethergoyfTarget(),
"exile any number of other cards from your graveyard with four or more card types among them")
);
this.addAbility(new EscapeAbility(this, "{2}{B}", additionalCost));
}
private Nethergoyf(final Nethergoyf card) {
super(card);
}
@Override
public Nethergoyf copy() {
return new Nethergoyf(this);
}
}
class NethergoyfTarget extends TargetCardInYourGraveyard {
private static final FilterCard filter = new FilterCard("other cards from your graveyard with four or more card types among them");
static {
filter.add(AnotherPredicate.instance);
}
NethergoyfTarget() {
super(1, Integer.MAX_VALUE, filter, true);
}
private NethergoyfTarget(final NethergoyfTarget target) {
super(target);
}
@Override
public NethergoyfTarget copy() {
return new NethergoyfTarget(this);
}
@Override
public boolean isChosen(Game game) {
return super.isChosen(game) && metCondition(this.getTargets(), game);
}
@Override
public String getMessage(Game game) {
String text = "Select " + CardUtil.addArticle(targetName);
Set<CardType> types = typesAmongSelection(this.getTargets(), game);
text += " (selected " + this.getTargets().size() + " cards; card types: ";
text += HintUtils.prepareText(
types.size() + " of 4",
types.size() >= 4 ? Color.GREEN : Color.RED
);
text += " [" + types.stream().map(CardType::toString).collect(Collectors.joining(", ")) + "])";
return text;
}
@Override
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
if (!super.canChoose(sourceControllerId, source, game)) {
return false;
}
// Check that exiling all the possible cards would have >= 4 different card types
return metCondition(this.possibleTargets(sourceControllerId, source, game), game);
}
private static Set<CardType> typesAmongSelection(Collection<UUID> cardsIds, Game game) {
return cardsIds
.stream()
.map(game::getCard)
.filter(Objects::nonNull)
.flatMap(c -> c.getCardType(game).stream())
.collect(Collectors.toSet());
}
private static boolean metCondition(Collection<UUID> cardsIds, Game game) {
return typesAmongSelection(cardsIds, game).size() >= 4;
}
}

View file

@ -70,7 +70,7 @@ enum NotOfThisWorldCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
StackObject sourceSpell = game.getStack().getStackObject(source.getSourceId());
if (sourceSpell == null || !sourceSpell.getStackAbility().getTargets().isChosen()) {
if (sourceSpell == null || !sourceSpell.getStackAbility().getTargets().isChosen(game)) {
return false;
}
StackObject objectToCounter = game.getStack().getStackObject(sourceSpell.getStackAbility().getTargets().getFirstTarget());

View file

@ -12,12 +12,9 @@ import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.Predicates;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetSacrifice;
import mage.target.targetpointer.FixedTarget;
@ -88,7 +85,7 @@ class PlaguecrafterEffect extends OneShotEffect {
}
TargetSacrifice target = new TargetSacrifice(StaticFilters.FILTER_CONTROLLED_PERMANENT_CREATURE_OR_PLANESWALKER);
if (target.canChoose(player.getId(), source, game)) {
while (!target.isChosen() && player.canRespond()) {
while (!target.isChosen(game) && player.canRespond()) {
player.choose(Outcome.Sacrifice, target, source, game);
}
perms.addAll(target.getTargets());

View file

@ -1,9 +1,6 @@
package mage.cards.r;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
@ -18,8 +15,11 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetControlledPermanent;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
*
* @author spjspj
*/
public final class RaziasPurification extends CardImpl {
@ -63,7 +63,7 @@ class RaziasPurificationEffect extends OneShotEffect {
if (player != null && target1.canChoose(player.getId(), source, game)) {
int chosenPermanents = 0;
while (player.canRespond() && !target1.isChosen() && target1.canChoose(player.getId(), source, game) && chosenPermanents < 3) {
while (player.canRespond() && !target1.isChosen(game) && target1.canChoose(player.getId(), source, game) && chosenPermanents < 3) {
player.chooseTarget(Outcome.Benefit, target1, source, game);
for (UUID targetId : target1.getTargets()) {
Permanent p = game.getPermanent(targetId);

View file

@ -69,7 +69,7 @@ class ReignOfThePitEffect extends OneShotEffect {
if (player != null) {
TargetSacrifice target = new TargetSacrifice(StaticFilters.FILTER_PERMANENT_CREATURE);
if (target.canChoose(player.getId(), source, game)) {
while (!target.isChosen() && player.canRespond()) {
while (!target.isChosen(game) && player.canRespond()) {
player.choose(Outcome.Sacrifice, target, source, game);
}
perms.addAll(target.getTargets());

View file

@ -147,14 +147,14 @@ class RiskyMoveFlipCoinEffect extends OneShotEffect {
Target target2 = new TargetOpponent(true);
if (target1.canChoose(controller.getId(), source, game)) {
while (!target1.isChosen()
while (!target1.isChosen(game)
&& target1.canChoose(controller.getId(), source, game)
&& controller.canRespond()) {
controller.chooseTarget(outcome, target1, source, game);
}
}
if (target2.canChoose(controller.getId(), source, game)) {
while (!target2.isChosen()
while (!target2.isChosen(game)
&& target2.canChoose(controller.getId(), source, game)
&& controller.canRespond()) {
controller.chooseTarget(outcome, target2, source, game);

View file

@ -1,7 +1,6 @@
package mage.cards.r;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageEverythingEffect;
@ -11,18 +10,17 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetSacrifice;
import java.util.UUID;
/**
*
* @author L_J
*/
public final class Rupture extends CardImpl {
@ -68,7 +66,7 @@ class RuptureEffect extends OneShotEffect {
int power = 0;
TargetSacrifice target = new TargetSacrifice(StaticFilters.FILTER_PERMANENT_CREATURE);
if (target.canChoose(player.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(player.getId(), source, game) && player.canRespond()) {
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
player.choose(Outcome.Sacrifice, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());

View file

@ -1,7 +1,6 @@
package mage.cards.s;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
@ -13,16 +12,14 @@ import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.counters.CounterType;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetSacrifice;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class Smokestack extends CardImpl {
@ -75,7 +72,7 @@ class SmokestackEffect extends OneShotEffect {
//A spell or ability could have removed the only legal target this player
//had, if thats the case this ability should fizzle.
if (target.canChoose(activePlayer.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(activePlayer.getId(), source, game) && activePlayer.canRespond()) {
while (!target.isChosen(game) && target.canChoose(activePlayer.getId(), source, game) && activePlayer.canRespond()) {
activePlayer.choose(Outcome.Sacrifice, target, source, game);
}

View file

@ -1,7 +1,6 @@
package mage.cards.s;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
@ -22,8 +21,9 @@ import mage.players.Player;
import mage.target.TargetPlayer;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author L_J
*/
public final class SpyNetwork extends CardImpl {
@ -109,7 +109,7 @@ class SpyNetworkFaceDownEffect extends OneShotEffect {
if (target.canChoose(controller.getId(), source, game)) {
while (controller.chooseUse(outcome, "Look at a face down creature controlled by " + player.getLogName() + "?", source, game)) {
target.clearChosen();
while (!target.isChosen() && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
while (!target.isChosen(game) && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
controller.chooseTarget(outcome, target, source, game);
}
Permanent faceDownCreature = game.getPermanent(target.getFirstTarget());

View file

@ -1,6 +1,5 @@
package mage.cards.s;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
@ -16,8 +15,9 @@ import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetControlledCreaturePermanent;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public final class SunkenHope extends CardImpl {
@ -66,7 +66,7 @@ class SunkenHopeReturnToHandEffect extends OneShotEffect {
Target target = new TargetControlledCreaturePermanent().withNotTarget(true);
if (target.canChoose(player.getId(), source, game)) {
while (player.canRespond() && !target.isChosen()
while (player.canRespond() && !target.isChosen(game)
&& target.canChoose(player.getId(), source, game)) {
player.chooseTarget(Outcome.ReturnToHand, target, source, game);
}

View file

@ -1,20 +1,19 @@
package mage.cards.t;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.common.SagaAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.mana.BasicManaEffect;
import mage.abilities.effects.common.DamageAllEffect;
import mage.abilities.effects.mana.BasicManaEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SagaChapter;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.common.FilterCreaturePermanent;
@ -24,11 +23,11 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetSacrifice;
import java.util.UUID;
/**
*
* @author TheElk801
*/
public final class TheFirstEruption extends CardImpl {
@ -101,7 +100,7 @@ class TheFirstEruptionEffect extends OneShotEffect {
Target target = new TargetSacrifice(filter);
boolean sacrificed = false;
if (target.canChoose(controller.getId(), source, game)) {
while (controller.canRespond() && !target.isChosen() && target.canChoose(controller.getId(), source, game)) {
while (controller.canRespond() && !target.isChosen(game) && target.canChoose(controller.getId(), source, game)) {
controller.choose(Outcome.Sacrifice, target, source, game);
}

View file

@ -443,7 +443,7 @@ class UrzaAcademyHeadmasterRandomEffect extends OneShotEffect {
}
source.addTarget(target);
}
if (target == null || target.isChosen()) {
if (target == null || target.isChosen(game)) {
for (Effect effect : effects) {
if (effect instanceof ContinuousEffect) {
game.addEffect((ContinuousEffect) effect, source);

View file

@ -134,7 +134,7 @@ class VodalianWarMachineWatcher extends Watcher {
for (Cost cost : ability.getCosts()) {
if (cost instanceof TapTargetCost && cost.isPaid()) {
TapTargetCost tapCost = (TapTargetCost) cost;
if (tapCost.getTarget().isChosen()) {
if (tapCost.getTarget().isChosen(game)) {
MageObjectReference mor = new MageObjectReference(sourcePermanent.getId(), sourcePermanent.getZoneChangeCounter(game), game);
Set<MageObjectReference> toAdd;
if (tappedMerfolkIds.get(mor) == null) {

View file

@ -10,15 +10,12 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SetTargetPointer;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.FoodToken;
import mage.players.Player;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.common.TargetSacrifice;
import java.util.ArrayList;
@ -26,7 +23,6 @@ import java.util.List;
import java.util.UUID;
/**
*
* @author Susucr
*/
public final class VoraciousFellBeast extends CardImpl {
@ -91,7 +87,7 @@ class VoraciousFellBeastEffect extends OneShotEffect {
TargetSacrifice target = new TargetSacrifice(StaticFilters.FILTER_PERMANENT_CREATURE);
if (target.canChoose(player.getId(), source, game)) {
while (!target.isChosen() && player.canRespond()) {
while (!target.isChosen(game) && player.canRespond()) {
player.choose(Outcome.Sacrifice, target, source, game);
}
perms.addAll(target.getTargets());

View file

@ -1,16 +1,15 @@
package mage.cards.w;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.constants.SubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
@ -20,8 +19,9 @@ import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponent;
import java.util.UUID;
/**
*
* @author TheElk801
*/
public final class WeiAssassins extends CardImpl {
@ -77,7 +77,7 @@ class WeiAssassinsEffect extends OneShotEffect {
filter.add(new ControllerIdPredicate(player.getId()));
Target target = new TargetPermanent(1, 1, filter, true);
if (target.canChoose(player.getId(), source, game)) {
while (!target.isChosen() && target.canChoose(player.getId(), source, game) && player.canRespond()) {
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());

View file

@ -18,8 +18,6 @@ import mage.filter.common.FilterControlledPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetSacrifice;
import java.util.*;
@ -119,7 +117,7 @@ class WorldQuellerEffect extends OneShotEffect {
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
Player player2 = game.getPlayer(playerId);
if (player2 != null && target.canChoose(playerId, source, game)) {
while (player2.canRespond() && !target.isChosen() && target.canChoose(playerId, source, game)) {
while (player2.canRespond() && !target.isChosen(game) && target.canChoose(playerId, source, game)) {
player2.choose(Outcome.Sacrifice, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());

View file

@ -42,6 +42,7 @@ public final class ModernHorizons3 extends ExpansionSet {
cards.add(new SetCardInfo("Laelia, the Blade Reforged", 281, Rarity.RARE, mage.cards.l.LaeliaTheBladeReforged.class));
cards.add(new SetCardInfo("Meltdown", 282, Rarity.UNCOMMON, mage.cards.m.Meltdown.class));
cards.add(new SetCardInfo("Mountain", 307, Rarity.LAND, mage.cards.basiclands.Mountain.class, FULL_ART_BFZ_VARIOUS));
cards.add(new SetCardInfo("Nethergoyf", 103, Rarity.MYTHIC, mage.cards.n.Nethergoyf.class));
cards.add(new SetCardInfo("Null Elemental Blast", 12, Rarity.UNCOMMON, mage.cards.n.NullElementalBlast.class));
cards.add(new SetCardInfo("Nulldrifter", 13, Rarity.RARE, mage.cards.n.Nulldrifter.class));
cards.add(new SetCardInfo("Orim's Chant", 265, Rarity.RARE, mage.cards.o.OrimsChant.class));

View file

@ -0,0 +1,193 @@
package org.mage.test.cards.single.mh3;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
/**
* @author Susucr
*/
public class NethergoyfTest extends CardTestPlayerBaseWithAIHelps {
/**
* {@link mage.cards.n.Nethergoyf Nethergoyf} {B}
* Creature Lhurgoyf
* Nethergoyfs power is equal to the number of card types among cards in your graveyard and its toughness is equal to that number plus 1.
* Escape{2}{B}, Exile any number of other cards from your graveyard with four or more card types among them. (You may cast this card from your graveyard for its escape cost.)
* * / 1+*
*/
private static final String nethergoyf = "Nethergoyf";
@Test
public void test_Escape_Two_DualTypes() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Memnite"); // Creature Artifact
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
checkPlayableAbility("can escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nethergoyf + " with Escape");
setChoice(playerA, "Memnite^Bitterblossom"); // cards exiled for escape cost
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 2);
}
@Test
public void test_AI_Escape_Two_DualTypes() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Memnite"); // Creature Artifact
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
checkPlayableAbility("can escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", true);
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 2);
}
@Test
public void test_Escape_MoreCardsThanNeeded() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Memnite", 5); // Creature Artifact
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
checkPlayableAbility("can escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nethergoyf + " with Escape");
setChoice(playerA, "Memnite^Memnite^Memnite^Memnite^Bitterblossom"); // cards exiled for escape cost: Exile all the Memnite but one.
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 2, 3); // 1 Memnite in graveyard
assertExileCount(playerA, 5);
assertGraveyardCount(playerA, 1);
}
@Test
public void test_AI_Escape_MoreCardsThanNeeded() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
addCard(Zone.GRAVEYARD, playerA, "Memnite", 5); // Creature Artifact
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 6); // It is weird, but AI likes to choose all to be exiled, even though the Outcome is Exile (so detriment)
assertGraveyardCount(playerA, 0);
}
@Test
public void test_CantEscape_Without4TypesInGraveyard() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Taiga"); // Land
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
checkPlayableAbility("can't escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", false);
// 3 types from other cards in graveyard, Nethergoyf can't escape
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, 3);
}
@Test
public void test_AI_CantEscape_Without4TypesInGraveyard() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
addCard(Zone.GRAVEYARD, playerA, "Taiga"); // Land
addCard(Zone.GRAVEYARD, playerA, "Bitterblossom"); // Tribal Enchantment
checkPlayableAbility("can't escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", false);
// 3 types from other cards in graveyard, Nethergoyf can't escape
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, 3);
}
@Test
public void test_DynamicGameType() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 7);
addCard(Zone.GRAVEYARD, playerA, "Taiga"); // Land
addCard(Zone.GRAVEYARD, playerA, "Grist, the Hunger Tide"); // Planeswalker, is a Creature if not in play
// Nonland permanents you control are artifacts in addition to their other types.
// The same is true for permanent spells you control and nonland permanent cards you own that arent on the battlefield.
addCard(Zone.HAND, playerA, "Encroaching Mycosynth");
checkPlayableAbility("1: can't escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", false);
// 3 types from other cards in graveyard, Nethergoyf can't escape
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Encroaching Mycosynth", true);
// After Mycosynth in play, Grist is now an Artifact in addition to its other types
checkPlayableAbility("2: can", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", true);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nethergoyf + " with Escape");
setChoice(playerA, "Taiga^Grist, the Hunger Tide"); // cards exiled for escape cost
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 2);
}
@Test
public void test_AI_DynamicGameType() {
setStrictChooseMode(true);
addCard(Zone.GRAVEYARD, playerA, nethergoyf);
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 7);
addCard(Zone.GRAVEYARD, playerA, "Taiga"); // Land
addCard(Zone.GRAVEYARD, playerA, "Grist, the Hunger Tide"); // Planeswalker, is a Creature if not in play
// Nonland permanents you control are artifacts in addition to their other types.
// The same is true for permanent spells you control and nonland permanent cards you own that arent on the battlefield.
addCard(Zone.HAND, playerA, "Encroaching Mycosynth");
aiPlayStep(1, PhaseStep.PRECOMBAT_MAIN, playerA);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, nethergoyf, 1);
assertPowerToughness(playerA, nethergoyf, 0, 1);
assertExileCount(playerA, 2);
}
}

View file

@ -2001,8 +2001,8 @@ public class TestPlayer implements Player {
return "Ability: null";
}
private String getInfo(Target o) {
return "Target: " + (o != null ? o.getClass().getSimpleName() + ": " + o.getMessage() : "null");
private String getInfo(Target o, Game game) {
return "Target: " + (o != null ? o.getClass().getSimpleName() + ": " + o.getMessage(game) : "null");
}
private void assertAliasSupportInChoices(boolean methodSupportAliases) {
@ -2167,7 +2167,7 @@ public class TestPlayer implements Player {
}
// ignore player select
if (target.getMessage().equals("Select a starting player")) {
if (target.getMessage(game).equals("Select a starting player")) {
return computerPlayer.choose(outcome, target, source, game, options);
}
@ -2317,7 +2317,7 @@ public class TestPlayer implements Player {
// apply only on ALL targets or revert
if (usedChoices.size() > 0) {
if (target.isChosen()) {
if (target.isChosen(game)) {
// remove all used choices
for (int i = choices.size(); i >= 0; i--) {
if (usedChoices.contains(i)) {
@ -2369,7 +2369,7 @@ public class TestPlayer implements Player {
}
}
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target));
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.choose(outcome, target, source, game, options);
}
@ -2690,19 +2690,19 @@ public class TestPlayer implements Player {
message = this.getName() + " - Targets list was setup by addTarget with " + targets + ", but not used"
+ "\nCard: " + source.getSourceObject(game)
+ "\nAbility: " + source.getClass().getSimpleName() + " (" + source.getRule() + ")"
+ "\nTarget: " + target.getClass().getSimpleName() + " (" + target.getMessage() + ")"
+ "\nTarget: " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
+ "\nYou must implement target class support in TestPlayer, \"filter instanceof\", or setup good targets";
} else {
message = this.getName() + " - Targets list was setup by addTarget with " + targets + ", but not used"
+ "\nCard: unknown source"
+ "\nAbility: unknown source"
+ "\nTarget: " + target.getClass().getSimpleName() + " (" + target.getMessage() + ")"
+ "\nTarget: " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
+ "\nYou must implement target class support in TestPlayer, \"filter instanceof\", or setup good targets";
}
Assert.fail(message);
}
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target));
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.chooseTarget(outcome, target, source, game);
}
@ -2748,7 +2748,7 @@ public class TestPlayer implements Player {
LOGGER.warn("Wrong target");
}
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target));
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.chooseTarget(outcome, cards, target, source, game);
}
@ -4134,7 +4134,7 @@ public class TestPlayer implements Player {
assertWrongChoiceUsage(choices.size() > 0 ? choices.get(0) : "empty list");
}
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target));
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.choose(outcome, cards, target, source, game);
}
@ -4207,7 +4207,7 @@ public class TestPlayer implements Player {
}
}
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target));
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
return computerPlayer.chooseTargetAmount(outcome, target, source, game);
}

View file

@ -67,7 +67,7 @@ public class CollectEvidenceCost extends CostImpl {
// TODO: require target to have minimum selected total mana value (requires refactor)
Target target = new TargetCardInYourGraveyard(1, Integer.MAX_VALUE) {
@Override
public String getMessage() {
public String getMessage(Game game) {
// shows selected mana value
int totalMV = this
.getTargets()
@ -76,7 +76,7 @@ public class CollectEvidenceCost extends CostImpl {
.filter(Objects::nonNull)
.mapToInt(MageObject::getManaValue)
.sum();
return super.getMessage() + HintUtils.prepareText(
return super.getMessage(game) + HintUtils.prepareText(
" (selected mana value " + totalMV + " of " + amount + ")",
totalMV >= amount ? Color.GREEN : Color.RED
);

View file

@ -31,7 +31,7 @@ public class ExileTargetCost extends CostImpl {
this.text = "exile " + target.getDescription();
}
public ExileTargetCost(ExileTargetCost cost) {
protected ExileTargetCost(ExileTargetCost cost) {
super(cost);
for (Permanent permanent : cost.permanents) {
this.permanents.add(permanent.copy());

View file

@ -75,7 +75,7 @@ public class RollPlanarDieEffect extends OneShotEffect {
}
boolean done = false;
while (controller.canRespond() && effect != null && !done) {
if (target != null && !target.isChosen() && target.canChoose(controller.getId(), source, game)) {
if (target != null && !target.isChosen(game) && target.canChoose(controller.getId(), source, game)) {
controller.chooseTarget(Outcome.Benefit, target, source, game);
source.addTarget(target);
}

View file

@ -27,6 +27,7 @@ public class SacrificeAllEffect extends OneShotEffect {
/**
* Each player sacrifices a permanent
*
* @param filter can be generic, will automatically add article and necessary sacrifice predicates
*/
public SacrificeAllEffect(FilterPermanent filter) {
@ -35,6 +36,7 @@ public class SacrificeAllEffect extends OneShotEffect {
/**
* Each player sacrifices N permanents
*
* @param filter can be generic, will automatically add necessary sacrifice predicates
*/
public SacrificeAllEffect(int amount, FilterPermanent filter) {
@ -43,6 +45,7 @@ public class SacrificeAllEffect extends OneShotEffect {
/**
* Each player sacrifices X permanents
*
* @param filter can be generic, will automatically add necessary sacrifice predicates
*/
public SacrificeAllEffect(DynamicValue amount, FilterPermanent filter) {
@ -91,7 +94,7 @@ public class SacrificeAllEffect extends OneShotEffect {
continue;
}
TargetSacrifice target = new TargetSacrifice(numTargets, filter);
while (!target.isChosen() && target.canChoose(player.getId(), source, game) && player.canRespond()) {
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
player.choose(Outcome.Sacrifice, target, source, game);
}
perms.addAll(target.getTargets());

View file

@ -64,7 +64,7 @@ public class SacrificeEffect extends OneShotEffect {
continue;
}
TargetSacrifice target = new TargetSacrifice(amount, filter);
while (!target.isChosen() && target.canChoose(player.getId(), source, game) && player.canRespond()) {
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
player.choose(Outcome.Sacrifice, target, source, game);
}
for (UUID targetId : target.getTargets()) {

View file

@ -151,7 +151,7 @@ class CrewCost extends CostImpl {
}
Target target = new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, filter, true) {
@Override
public String getMessage() {
public String getMessage(Game game) {
// shows selected power
int selectedPower = this.targets.keySet().stream()
.map(game::getPermanent)
@ -162,7 +162,7 @@ class CrewCost extends CostImpl {
if (selectedPower >= value) {
extraInfo = HintUtils.prepareText(extraInfo, Color.GREEN);
}
return super.getMessage() + " " + extraInfo;
return super.getMessage(game) + " " + extraInfo;
}
};

View file

@ -32,10 +32,14 @@ public class EscapeAbility extends SpellAbility {
private final String staticText;
public EscapeAbility(Card card, String manaCost, int exileCount) {
this(card, manaCost, exileCount, new CostsImpl<>());
this(card, manaCost, new CostsImpl<>(), exileCount);
}
public EscapeAbility(Card card, String manaCost, int exileCount, Costs<Cost> additionalCosts) {
public EscapeAbility(Card card, String manaCost, Costs<Cost> additionalCost) {
this(card, manaCost, additionalCost, 0);
}
public EscapeAbility(Card card, String manaCost, Costs<Cost> additionalCosts, int exileCount) {
super(card.getSpellAbility());
this.newId();
this.setCardName(card.getName() + " with Escape");
@ -45,17 +49,22 @@ public class EscapeAbility extends SpellAbility {
this.clearManaCosts();
this.clearManaCostsToPay();
String text = "Escape&mdash;" + manaCost;
this.addCost(new ManaCostsImpl<>(manaCost));
for (Cost cost : additionalCosts) {
text += ", " + CardUtil.getTextWithFirstCharUpperCase(cost.getText());
this.addCost(cost.copy().setText("")); // hide additional cost text from rules
}
text += ", Exile " + CardUtil.numberToText(exileCount) + " other cards from your graveyard."
+ " <i>(You may cast this card from your graveyard for its escape cost.)</i>";
if (exileCount > 0) {
this.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(exileCount, filter), "")); // hide additional cost text from rules
}
String text = "Escape&mdash;" + manaCost;
for (Cost cost : additionalCosts) {
text += ", " + CardUtil.getTextWithFirstCharUpperCase(cost.getText());
}
if (exileCount > 0) {
text += ", Exile " + CardUtil.numberToText(exileCount) + " other cards from your graveyard";
}
text += ". <i>(You may cast this card from your graveyard for its escape cost.)</i>";
this.staticText = text;
}

View file

@ -177,7 +177,7 @@ class OfferingAsThoughEffect extends AsThoughEffectImpl {
&& player.chooseUse(Outcome.Benefit, "Offer a " + filter.getMessage() + " to cast " + spellToCast.getName() + '?', source, game)) {
Target target = new TargetSacrifice(filter);
player.choose(Outcome.Sacrifice, target, source, game);
if (!target.isChosen()) {
if (!target.isChosen(game)) {
return false;
}
game.getState().setValue("offering_" + card.getId(), true);

View file

@ -113,7 +113,7 @@ class SaddleCost extends CostImpl {
public boolean pay(Ability ability, Game game, Ability source, UUID controllerId, boolean noMana, Cost costToPay) {
Target target = new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, filter, true) {
@Override
public String getMessage() {
public String getMessage(Game game) {
// shows selected power
int selectedPower = this.targets.keySet().stream()
.map(game::getPermanent)
@ -125,7 +125,7 @@ class SaddleCost extends CostImpl {
if (selectedPower >= value) {
extraInfo = HintUtils.prepareText(extraInfo, Color.GREEN);
}
return super.getMessage() + " " + extraInfo;
return super.getMessage(game) + " " + extraInfo;
}
};

View file

@ -4349,14 +4349,14 @@ public abstract class PlayerImpl implements Player, Serializable {
List<Ability> options = new ArrayList<>();
if (ability.isModal()) {
addModeOptions(options, ability, game);
} else if (!ability.getTargets().getUnchosen().isEmpty()) {
} else if (!ability.getTargets().getUnchosen(game).isEmpty()) {
// TODO: Handle other variable costs than mana costs
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
addVariableXOptions(options, ability, 0, game);
} else {
addTargetOptions(options, ability, 0, game);
}
} else if (!ability.getCosts().getTargets().getUnchosen().isEmpty()) {
} else if (!ability.getCosts().getTargets().getUnchosen(game).isEmpty()) {
addCostTargetOptions(options, ability, 0, game);
}
@ -4371,13 +4371,13 @@ public abstract class PlayerImpl implements Player, Serializable {
newOption.getModes().clearSelectedModes();
newOption.getModes().addSelectedMode(mode.getId());
newOption.getModes().setActiveMode(mode);
if (!newOption.getTargets().getUnchosen().isEmpty()) {
if (!newOption.getTargets().getUnchosen(game).isEmpty()) {
if (!newOption.getManaCosts().getVariableCosts().isEmpty()) {
addVariableXOptions(options, newOption, 0, game);
} else {
addTargetOptions(options, newOption, 0, game);
}
} else if (!newOption.getCosts().getTargets().getUnchosen().isEmpty()) {
} else if (!newOption.getCosts().getTargets().getUnchosen(game).isEmpty()) {
addCostTargetOptions(options, newOption, 0, game);
} else {
options.add(newOption);
@ -4390,7 +4390,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
protected void addTargetOptions(List<Ability> options, Ability option, int targetNum, Game game) {
for (Target target : option.getTargets().getUnchosen().get(targetNum).getTargetOptions(option, game)) {
for (Target target : option.getTargets().getUnchosen(game).get(targetNum).getTargetOptions(option, game)) {
Ability newOption = option.copy();
if (target instanceof TargetAmount) {
for (UUID targetId : target.getTargets()) {

View file

@ -19,9 +19,9 @@ import java.util.UUID;
*/
public interface Target extends Serializable {
boolean isChosen();
boolean isChosen(Game game);
boolean doneChoosing();
boolean doneChoosing(Game game);
void clearChosen();
@ -98,7 +98,10 @@ public interface Target extends Serializable {
*/
String getDescription();
String getMessage();
/**
* @return message displayed on choosing targets (can be dynamically changed on more target selected)
*/
String getMessage(Game game);
/**
* @return single target name

View file

@ -44,12 +44,12 @@ public abstract class TargetAmount extends TargetImpl {
}
@Override
public boolean isChosen() {
return doneChoosing();
public boolean isChosen(Game game) {
return doneChoosing(game);
}
@Override
public boolean doneChoosing() {
public boolean doneChoosing(Game game) {
return amountWasSet
&& (remainingAmount == 0
|| (getMinNumberOfTargets() < getMaxNumberOfTargets()
@ -104,7 +104,7 @@ public abstract class TargetAmount extends TargetImpl {
if (!amountWasSet) {
setAmount(source, game);
}
chosen = isChosen();
chosen = isChosen(game);
while (remainingAmount > 0) {
if (!player.canRespond()) {
return chosen;
@ -112,7 +112,7 @@ public abstract class TargetAmount extends TargetImpl {
if (!getTargetController(game, playerId).chooseTargetAmount(outcome, this, source, game)) {
return chosen;
}
chosen = isChosen();
chosen = isChosen(game);
}
return chosen;
}

View file

@ -143,7 +143,7 @@ public abstract class TargetImpl implements Target {
}
@Override
public String getMessage() {
public String getMessage(Game game) {
// UI choose message
String suffix = "";
if (this.chooseHint != null) {
@ -215,7 +215,7 @@ public abstract class TargetImpl implements Target {
}
@Override
public boolean isChosen() {
public boolean isChosen(Game game) {
if (getMaxNumberOfTargets() == 0 && getNumberOfTargets() == 0) {
return true;
}
@ -223,7 +223,7 @@ public abstract class TargetImpl implements Target {
}
@Override
public boolean doneChoosing() {
public boolean doneChoosing(Game game) {
return getMaxNumberOfTargets() != 0 && targets.size() == getMaxNumberOfTargets();
}
@ -332,7 +332,7 @@ public abstract class TargetImpl implements Target {
return chosen;
}
chosen = targets.size() >= getNumberOfTargets();
} while (!isChosen() && !doneChoosing());
} while (!isChosen(game) && !doneChoosing(game));
return chosen;
}
@ -375,7 +375,7 @@ public abstract class TargetImpl implements Target {
}
}
chosen = targets.size() >= getNumberOfTargets();
} while (!isChosen() && !doneChoosing());
} while (!isChosen(game) && !doneChoosing(game));
return chosen;
}

View file

@ -37,8 +37,8 @@ public class Targets extends ArrayList<Target> implements Copyable<Targets> {
return this;
}
public List<Target> getUnchosen() {
return stream().filter(target -> !target.isChosen()).collect(Collectors.toList());
public List<Target> getUnchosen(Game game) {
return stream().filter(target -> !target.isChosen(game)).collect(Collectors.toList());
}
public void clearChosen() {
@ -47,8 +47,8 @@ public class Targets extends ArrayList<Target> implements Copyable<Targets> {
}
}
public boolean isChosen() {
return stream().allMatch(Target::isChosen);
public boolean isChosen(Game game) {
return stream().allMatch(t -> t.isChosen(game));
}
public boolean choose(Outcome outcome, UUID playerId, UUID sourceId, Ability source, Game game) {
@ -56,8 +56,8 @@ public class Targets extends ArrayList<Target> implements Copyable<Targets> {
if (!canChoose(playerId, source, game)) {
return false;
}
while (!isChosen()) {
Target target = this.getUnchosen().get(0);
while (!isChosen(game)) {
Target target = this.getUnchosen(game).get(0);
if (!target.choose(outcome, playerId, sourceId, source, game)) {
return false;
}
@ -73,8 +73,8 @@ public class Targets extends ArrayList<Target> implements Copyable<Targets> {
}
//int state = game.bookmarkState();
while (!isChosen()) {
Target target = this.getUnchosen().get(0);
while (!isChosen(game)) {
Target target = this.getUnchosen(game).get(0);
UUID targetController = playerId;
// some targets can have controller different than ability controller
@ -97,7 +97,7 @@ public class Targets extends ArrayList<Target> implements Copyable<Targets> {
return false;
}
// Check if there are some rules for targets are violated, if so reset the targets and start again
if (this.getUnchosen().isEmpty()
if (this.getUnchosen(game).isEmpty()
&& game.replaceEvent(new GameEvent(GameEvent.EventType.TARGETS_VALID, source.getSourceId(), source, source.getControllerId()), source)) {
//game.restoreState(state, "Targets");
clearChosen();

View file

@ -85,7 +85,7 @@ public class TargetCardInLibrary extends TargetCard {
return chosen;
}
chosen = targets.size() >= getMinNumberOfTargets();
} while (!isChosen() && !doneChoosing());
} while (!isChosen(game) && !doneChoosing(game));
return chosen;
}