forked from External/mage
Merge pull request #13892 from magefree/rework-targeting-and-possible-targets
Reworked targeting, possible targets and AI Test framework improves: - now game logs will show stack ability on push and on resolve (before any choices); - now game logs will show used choices made by cast/activate, setChoice, setMode and addTarget commands (not work for AI tests, part of #13832); - improved choice logic for modes and yes/not dialogs (now it's use a more strictly checks, use TestPlayer.MODE_SKIP to stop mode selection); - improved error logs and testable dialogs menu in cheat mode; Reworked AI, targeting and targets logic: - refactor: simplified target implementation from a dozen canTarget, canChoose and possibleTargets methods to canTarget/possibleTargets only (part of #13638, #13766); - refactor: fixed wrong target implementations in many cards (example: TargetCardInHand for opponent's hand, close #6210); - AI: now human, AI and test players -- all use possibleTargets logic in most use cases instead filters or custom validation; - AI: improved AI sims support for multiple targets abilities; - AI: improved AI stability, freezes and targets errors in some use cases; Additional fixes: - close #13840 - close #13865 - close #13857 - close #13650
This commit is contained in:
commit
f5004746e0
257 changed files with 2206 additions and 3518 deletions
|
|
@ -18,10 +18,8 @@ import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Comparator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -258,7 +256,7 @@ public class CardArea extends JPanel implements CardEventProducer {
|
||||||
this.reloaded = false;
|
this.reloaded = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void selectCards(List<UUID> selected) {
|
public void selectCards(Set<UUID> selected) {
|
||||||
for (Component component : cardArea.getComponents()) {
|
for (Component component : cardArea.getComponents()) {
|
||||||
if (component instanceof MageCard) {
|
if (component instanceof MageCard) {
|
||||||
MageCard mageCard = (MageCard) component;
|
MageCard mageCard = (MageCard) component;
|
||||||
|
|
@ -269,7 +267,7 @@ public class CardArea extends JPanel implements CardEventProducer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void markCards(List<UUID> marked) {
|
public void markCards(Set<UUID> marked) {
|
||||||
for (Component component : cardArea.getComponents()) {
|
for (Component component : cardArea.getComponents()) {
|
||||||
if (component instanceof MageCard) {
|
if (component instanceof MageCard) {
|
||||||
MageCard mageCard = (MageCard) component;
|
MageCard mageCard = (MageCard) component;
|
||||||
|
|
|
||||||
|
|
@ -100,11 +100,11 @@
|
||||||
cardArea.loadCards(showCards, bigCard, gameId);
|
cardArea.loadCards(showCards, bigCard, gameId);
|
||||||
if (options != null) {
|
if (options != null) {
|
||||||
if (options.containsKey("chosenTargets")) {
|
if (options.containsKey("chosenTargets")) {
|
||||||
java.util.List<UUID> chosenCards = (java.util.List<UUID>) options.get("chosenTargets");
|
java.util.Set<UUID> chosenCards = (java.util.Set<UUID>) options.get("chosenTargets");
|
||||||
cardArea.selectCards(chosenCards);
|
cardArea.selectCards(chosenCards);
|
||||||
}
|
}
|
||||||
if (options.containsKey("possibleTargets")) {
|
if (options.containsKey("possibleTargets")) {
|
||||||
java.util.List<UUID> choosableCards = (java.util.List<UUID>) options.get("possibleTargets");
|
java.util.Set<UUID> choosableCards = (java.util.Set<UUID>) options.get("possibleTargets");
|
||||||
cardArea.markCards(choosableCards);
|
cardArea.markCards(choosableCards);
|
||||||
}
|
}
|
||||||
if (options.containsKey("queryType") && options.get("queryType") == QueryType.PICK_ABILITY) {
|
if (options.containsKey("queryType") && options.get("queryType") == QueryType.PICK_ABILITY) {
|
||||||
|
|
|
||||||
|
|
@ -216,17 +216,9 @@ public final class GamePanel extends javax.swing.JPanel {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<UUID> getPossibleTargets() {
|
|
||||||
if (options != null && options.containsKey("possibleTargets")) {
|
|
||||||
return (List<UUID>) options.get("possibleTargets");
|
|
||||||
} else {
|
|
||||||
return Collections.emptyList();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Set<UUID> getChosenTargets() {
|
public Set<UUID> getChosenTargets() {
|
||||||
if (options != null && options.containsKey("chosenTargets")) {
|
if (options != null && options.containsKey("chosenTargets")) {
|
||||||
return new HashSet<>((List<UUID>) options.get("chosenTargets"));
|
return (Set<UUID>) options.get("chosenTargets");
|
||||||
} else {
|
} else {
|
||||||
return Collections.emptySet();
|
return Collections.emptySet();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
|
import mage.target.TargetPlayer;
|
||||||
import mage.target.common.TargetPermanentOrPlayer;
|
import mage.target.common.TargetPermanentOrPlayer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -76,6 +77,10 @@ abstract class BaseTestableDialog implements TestableDialog {
|
||||||
return createAnyTarget(min, max, false);
|
return createAnyTarget(min, max, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Target createPlayerTarget(int min, int max, boolean notTarget) {
|
||||||
|
return new TargetPlayer(min, max, notTarget);
|
||||||
|
}
|
||||||
|
|
||||||
private static Target createAnyTarget(int min, int max, boolean notTarget) {
|
private static Target createAnyTarget(int min, int max, boolean notTarget) {
|
||||||
return new TargetPermanentOrPlayer(min, max).withNotTarget(notTarget);
|
return new TargetPermanentOrPlayer(min, max).withNotTarget(notTarget);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -126,6 +126,19 @@ class ChooseTargetTestableDialog extends BaseTestableDialog {
|
||||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1-3", createImpossibleTarget(1, 3)).aiMustChoose(false, 0));
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1-3", createImpossibleTarget(1, 3)).aiMustChoose(false, 0));
|
||||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 2-3", createImpossibleTarget(2, 3)).aiMustChoose(false, 0));
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 2-3", createImpossibleTarget(2, 3)).aiMustChoose(false, 0));
|
||||||
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible max", createImpossibleTarget(0, Integer.MAX_VALUE)).aiMustChoose(false, 0));
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible max", createImpossibleTarget(0, Integer.MAX_VALUE)).aiMustChoose(false, 0));
|
||||||
|
//
|
||||||
|
// additional tests for 2 possible options limitation
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0", createPlayerTarget(0, 0, notTarget)).aiMustChoose(false, 0));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0-1", createPlayerTarget(0, 1, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0-2", createPlayerTarget(0, 2, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 0-5", createPlayerTarget(0, 5, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 1", createPlayerTarget(1, 1, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 1-2", createPlayerTarget(1, 2, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 1-5", createPlayerTarget(1, 5, notTarget)).aiMustChoose(true, 1));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 2", createPlayerTarget(2, 2, notTarget)).aiMustChoose(true, 2));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 2-5", createPlayerTarget(2, 5, notTarget)).aiMustChoose(true, 2));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 3", createPlayerTarget(3, 3, notTarget)).aiMustChoose(false, 0));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "player 3-5", createPlayerTarget(3, 5, notTarget)).aiMustChoose(false, 0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,7 @@ import mage.constants.Outcome;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -120,10 +117,8 @@ public class TestableDialogsRunner {
|
||||||
choice = prepareSelectDialogChoice(needGroup);
|
choice = prepareSelectDialogChoice(needGroup);
|
||||||
player.choose(Outcome.Benefit, choice, game);
|
player.choose(Outcome.Benefit, choice, game);
|
||||||
if (choice.getChoiceKey() != null) {
|
if (choice.getChoiceKey() != null) {
|
||||||
int needIndex = Integer.parseInt(choice.getChoiceKey());
|
int needRegNumber = Integer.parseInt(choice.getChoiceKey());
|
||||||
if (needIndex < this.dialogs.size()) {
|
needDialog = this.dialogs.getOrDefault(needRegNumber, null);
|
||||||
needDialog = this.dialogs.get(needIndex);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (needDialog == null) {
|
if (needDialog == null) {
|
||||||
|
|
@ -144,15 +139,20 @@ public class TestableDialogsRunner {
|
||||||
Choice choice = new ChoiceImpl(false);
|
Choice choice = new ChoiceImpl(false);
|
||||||
choice.setMessage("Choose dialogs group to run");
|
choice.setMessage("Choose dialogs group to run");
|
||||||
|
|
||||||
|
// use min reg number for groups
|
||||||
|
Map<String, Integer> groupNumber = new HashMap<>();
|
||||||
|
this.dialogs.values().forEach(dialog -> {
|
||||||
|
groupNumber.put(dialog.getGroup(), Math.min(groupNumber.getOrDefault(dialog.getGroup(), Integer.MAX_VALUE), dialog.getRegNumber()));
|
||||||
|
});
|
||||||
|
|
||||||
// main groups
|
// main groups
|
||||||
int recNumber = 0;
|
|
||||||
for (int i = 0; i < groups.size(); i++) {
|
for (int i = 0; i < groups.size(); i++) {
|
||||||
recNumber++;
|
|
||||||
String group = groups.get(i);
|
String group = groups.get(i);
|
||||||
|
Integer groupMinNumber = groupNumber.getOrDefault(group, 0);
|
||||||
choice.withItem(
|
choice.withItem(
|
||||||
String.valueOf(i),
|
String.valueOf(i),
|
||||||
String.format("%02d. %s", recNumber, group),
|
String.format("%02d. %s", groupMinNumber, group),
|
||||||
recNumber,
|
groupMinNumber,
|
||||||
ChoiceHintType.TEXT,
|
ChoiceHintType.TEXT,
|
||||||
String.join("<br>", group)
|
String.join("<br>", group)
|
||||||
);
|
);
|
||||||
|
|
@ -186,18 +186,15 @@ public class TestableDialogsRunner {
|
||||||
private Choice prepareSelectDialogChoice(String needGroup) {
|
private Choice prepareSelectDialogChoice(String needGroup) {
|
||||||
Choice choice = new ChoiceImpl(false);
|
Choice choice = new ChoiceImpl(false);
|
||||||
choice.setMessage("Choose game dialog to run from " + needGroup);
|
choice.setMessage("Choose game dialog to run from " + needGroup);
|
||||||
int recNumber = 0;
|
for (TestableDialog dialog : this.dialogs.values()) {
|
||||||
for (int i = 0; i < this.dialogs.size(); i++) {
|
|
||||||
TestableDialog dialog = this.dialogs.get(i);
|
|
||||||
if (!dialog.getGroup().equals(needGroup)) {
|
if (!dialog.getGroup().equals(needGroup)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
recNumber++;
|
|
||||||
String info = String.format("%s - %s - %s", dialog.getGroup(), dialog.getName(), dialog.getDescription());
|
String info = String.format("%s - %s - %s", dialog.getGroup(), dialog.getName(), dialog.getDescription());
|
||||||
choice.withItem(
|
choice.withItem(
|
||||||
String.valueOf(i),
|
String.valueOf(dialog.getRegNumber()),
|
||||||
String.format("%02d. %s", recNumber, info),
|
String.format("%02d. %s", dialog.getRegNumber(), info),
|
||||||
recNumber,
|
dialog.getRegNumber(),
|
||||||
ChoiceHintType.TEXT,
|
ChoiceHintType.TEXT,
|
||||||
String.join("<br>", info)
|
String.join("<br>", info)
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -405,13 +405,13 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
||||||
if (effect != null
|
if (effect != null
|
||||||
&& stackObject.getControllerId().equals(playerId)) {
|
&& stackObject.getControllerId().equals(playerId)) {
|
||||||
Target target = effect.getTarget();
|
Target target = effect.getTarget();
|
||||||
if (!target.isChoiceCompleted(getId(), (StackAbility) stackObject, game)) {
|
if (!target.isChoiceCompleted(getId(), (StackAbility) stackObject, game, null)) {
|
||||||
for (UUID targetId : target.possibleTargets(stackObject.getControllerId(), stackObject.getStackAbility(), game)) {
|
for (UUID targetId : target.possibleTargets(stackObject.getControllerId(), stackObject.getStackAbility(), game)) {
|
||||||
Game sim = game.createSimulationForAI();
|
Game sim = game.createSimulationForAI();
|
||||||
StackAbility newAbility = (StackAbility) stackObject.copy();
|
StackAbility newAbility = (StackAbility) stackObject.copy();
|
||||||
SearchEffect newEffect = getSearchEffect(newAbility);
|
SearchEffect newEffect = getSearchEffect(newAbility);
|
||||||
newEffect.getTarget().addTarget(targetId, newAbility, sim);
|
newEffect.getTarget().addTarget(targetId, newAbility, sim);
|
||||||
sim.getStack().push(newAbility);
|
sim.getStack().push(sim, newAbility);
|
||||||
SimulationNode2 newNode = new SimulationNode2(node, sim, depth, stackObject.getControllerId());
|
SimulationNode2 newNode = new SimulationNode2(node, sim, depth, stackObject.getControllerId());
|
||||||
node.children.add(newNode);
|
node.children.add(newNode);
|
||||||
newNode.getTargets().add(targetId);
|
newNode.getTargets().add(targetId);
|
||||||
|
|
@ -886,10 +886,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||||
if (!target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (!target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
for (UUID targetId : targets) {
|
for (UUID targetId : targets) {
|
||||||
target.addTarget(targetId, source, game);
|
target.addTarget(targetId, source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
targets.clear();
|
targets.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -906,10 +906,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||||
if (!target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (!target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
for (UUID targetId : targets) {
|
for (UUID targetId : targets) {
|
||||||
target.add(targetId, game);
|
target.add(targetId, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
targets.clear();
|
targets.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void simulateOptions(Game game) {
|
private void simulateOptions(Game game) {
|
||||||
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
List<ActivatedAbility> playables = game.getPlayer(playerId).getPlayable(game, isSimulatedPlayer);
|
||||||
for (ActivatedAbility ability : playables) {
|
for (ActivatedAbility ability : playables) {
|
||||||
if (ability.isManaAbility()) {
|
if (ability.isManaAbility()) {
|
||||||
|
|
@ -176,6 +176,10 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// remove invalid targets
|
||||||
|
// TODO: is it useless cause it already filtered before?
|
||||||
|
options.removeIf(option -> !option.getTargets().isChosen(game));
|
||||||
|
|
||||||
if (AI_SIMULATE_ALL_BAD_AND_GOOD_TARGETS) {
|
if (AI_SIMULATE_ALL_BAD_AND_GOOD_TARGETS) {
|
||||||
return options;
|
return options;
|
||||||
}
|
}
|
||||||
|
|
@ -315,7 +319,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
List<Ability> options = getPlayableOptions(ability, game);
|
List<Ability> options = getPlayableOptions(ability, game);
|
||||||
if (options.isEmpty()) {
|
if (options.isEmpty()) {
|
||||||
logger.debug("simulating -- triggered ability:" + ability);
|
logger.debug("simulating -- triggered ability:" + ability);
|
||||||
game.getStack().push(new StackAbility(ability, playerId));
|
game.getStack().push(game, new StackAbility(ability, playerId));
|
||||||
if (ability.activate(game, false) && ability.isUsesStack()) {
|
if (ability.activate(game, false) && ability.isUsesStack()) {
|
||||||
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||||
}
|
}
|
||||||
|
|
@ -337,9 +341,9 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
|
|
||||||
protected void addAbilityNode(SimulationNode2 parent, Ability ability, int depth, Game game) {
|
protected void addAbilityNode(SimulationNode2 parent, Ability ability, int depth, Game game) {
|
||||||
Game sim = game.createSimulationForAI();
|
Game sim = game.createSimulationForAI();
|
||||||
sim.getStack().push(new StackAbility(ability, playerId));
|
sim.getStack().push(sim, new StackAbility(ability, playerId));
|
||||||
if (ability.activate(sim, false) && ability.isUsesStack()) {
|
if (ability.activate(sim, false) && ability.isUsesStack()) {
|
||||||
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
sim.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||||
}
|
}
|
||||||
sim.applyEffects();
|
sim.applyEffects();
|
||||||
SimulationNode2 newNode = new SimulationNode2(parent, sim, depth, playerId);
|
SimulationNode2 newNode = new SimulationNode2(parent, sim, depth, playerId);
|
||||||
|
|
|
||||||
|
|
@ -142,17 +142,27 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||||
|
|
||||||
// nothing to choose, e.g. X=0
|
// nothing to choose, e.g. X=0
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, fromCards)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// default logic for any targets
|
|
||||||
PossibleTargetsSelector possibleTargetsSelector = new PossibleTargetsSelector(outcome, target, abilityControllerId, source, game);
|
PossibleTargetsSelector possibleTargetsSelector = new PossibleTargetsSelector(outcome, target, abilityControllerId, source, game);
|
||||||
possibleTargetsSelector.findNewTargets(fromCards);
|
possibleTargetsSelector.findNewTargets(fromCards);
|
||||||
|
|
||||||
|
// nothing to choose, e.g. no valid targets
|
||||||
|
if (!possibleTargetsSelector.hasAnyTargets()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can't choose
|
||||||
|
if (!possibleTargetsSelector.hasMinNumberOfTargets()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// good targets -- choose as much as possible
|
// good targets -- choose as much as possible
|
||||||
for (MageItem item : possibleTargetsSelector.getGoodTargets()) {
|
for (MageItem item : possibleTargetsSelector.getGoodTargets()) {
|
||||||
target.add(item.getId(), game);
|
target.add(item.getId(), game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, fromCards)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -226,7 +236,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
UUID abilityControllerId = target.getAffectedAbilityControllerId(getId());
|
||||||
|
|
||||||
// nothing to choose, e.g. X=0
|
// nothing to choose, e.g. X=0
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -238,6 +248,11 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// can't choose
|
||||||
|
if (!possibleTargetsSelector.hasMinNumberOfTargets()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
// KILL PRIORITY
|
// KILL PRIORITY
|
||||||
if (outcome == Outcome.Damage) {
|
if (outcome == Outcome.Damage) {
|
||||||
// opponent first
|
// opponent first
|
||||||
|
|
@ -251,7 +266,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
||||||
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
||||||
target.addTarget(item.getId(), leftLife, source, game);
|
target.addTarget(item.getId(), leftLife, source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -268,7 +283,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
||||||
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
if (leftLife > 0 && leftLife <= target.getAmountRemaining()) {
|
||||||
target.addTarget(item.getId(), leftLife, source, game);
|
target.addTarget(item.getId(), leftLife, source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -283,7 +298,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -303,7 +318,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
int leftLife = PossibleTargetsComparator.getLifeForDamage(item, game);
|
||||||
if (leftLife > 1) {
|
if (leftLife > 1) {
|
||||||
target.addTarget(item.getId(), Math.min(leftLife - 1, target.getAmountRemaining()), source, game);
|
target.addTarget(item.getId(), Math.min(leftLife - 1, target.getAmountRemaining()), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -322,7 +337,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
return !target.getTargets().isEmpty();
|
return !target.getTargets().isEmpty();
|
||||||
}
|
}
|
||||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -339,7 +354,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -355,7 +370,7 @@ public class ComputerPlayer extends PlayerImpl {
|
||||||
return !target.getTargets().isEmpty();
|
return !target.getTargets().isEmpty();
|
||||||
}
|
}
|
||||||
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
target.addTarget(item.getId(), target.getAmountRemaining(), source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -47,7 +47,6 @@ public class PossibleTargetsSelector {
|
||||||
// collect new valid targets
|
// collect new valid targets
|
||||||
List<MageItem> found = target.possibleTargets(abilityControllerId, source, game, fromTargetsList).stream()
|
List<MageItem> found = target.possibleTargets(abilityControllerId, source, game, fromTargetsList).stream()
|
||||||
.filter(id -> !target.contains(id))
|
.filter(id -> !target.contains(id))
|
||||||
.filter(id -> target.canTarget(abilityControllerId, id, source, game))
|
|
||||||
.map(id -> {
|
.map(id -> {
|
||||||
Player player = game.getPlayer(id);
|
Player player = game.getPlayer(id);
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
|
|
@ -137,6 +136,10 @@ public class PossibleTargetsSelector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<MageItem> getAny() {
|
||||||
|
return this.any;
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isMyItem(UUID abilityControllerId, MageItem item) {
|
public static boolean isMyItem(UUID abilityControllerId, MageItem item) {
|
||||||
if (item instanceof Player) {
|
if (item instanceof Player) {
|
||||||
return item.getId().equals(abilityControllerId);
|
return item.getId().equals(abilityControllerId);
|
||||||
|
|
@ -181,7 +184,12 @@ public class PossibleTargetsSelector {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean hasAnyTargets() {
|
public boolean hasAnyTargets() {
|
||||||
return !this.any.isEmpty();
|
return !this.any.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasMinNumberOfTargets() {
|
||||||
|
return this.target.getMinNumberOfTargets() == 0
|
||||||
|
|| this.any.size() >= this.target.getMinNumberOfTargets();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -121,7 +121,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (ability.isUsesStack()) {
|
if (ability.isUsesStack()) {
|
||||||
game.getStack().push(new StackAbility(ability, playerId));
|
game.getStack().push(game, new StackAbility(ability, playerId));
|
||||||
if (ability.activate(game, false)) {
|
if (ability.activate(game, false)) {
|
||||||
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
game.fireEvent(new GameEvent(GameEvent.EventType.TRIGGERED_ABILITY, ability.getId(), ability, ability.getControllerId()));
|
||||||
actionCount++;
|
actionCount++;
|
||||||
|
|
@ -187,8 +187,8 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
abort = true;
|
abort = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean chooseRandom(Target target, Game game) {
|
private boolean chooseRandom(Target target, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, game);
|
Set<UUID> possibleTargets = target.possibleTargets(playerId, source, game);
|
||||||
if (possibleTargets.isEmpty()) {
|
if (possibleTargets.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -233,7 +233,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Target target, Ability source, Game game) {
|
public boolean choose(Outcome outcome, Target target, Ability source, Game game) {
|
||||||
if (this.isHuman()) {
|
if (this.isHuman()) {
|
||||||
return chooseRandom(target, game);
|
return chooseRandom(target, source, game);
|
||||||
}
|
}
|
||||||
return super.choose(outcome, target, source, game);
|
return super.choose(outcome, target, source, game);
|
||||||
}
|
}
|
||||||
|
|
@ -241,7 +241,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Target target, Ability source, Game game, Map<String, Serializable> options) {
|
public boolean choose(Outcome outcome, Target target, Ability source, Game game, Map<String, Serializable> options) {
|
||||||
if (this.isHuman()) {
|
if (this.isHuman()) {
|
||||||
return chooseRandom(target, game);
|
return chooseRandom(target, source, game);
|
||||||
}
|
}
|
||||||
return super.choose(outcome, target, source, game, options);
|
return super.choose(outcome, target, source, game, options);
|
||||||
}
|
}
|
||||||
|
|
@ -252,7 +252,7 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Set<UUID> possibleTargets = target.possibleTargets(playerId, cards, source, game);
|
Set<UUID> possibleTargets = target.possibleTargets(playerId, source, game, cards);
|
||||||
if (possibleTargets.isEmpty()) {
|
if (possibleTargets.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ package mage.player.human;
|
||||||
import mage.MageIdentifier;
|
import mage.MageIdentifier;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.*;
|
import mage.abilities.*;
|
||||||
import mage.abilities.costs.VariableCost;
|
|
||||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||||
import mage.abilities.costs.common.TapSourceCost;
|
import mage.abilities.costs.common.TapSourceCost;
|
||||||
import mage.abilities.costs.mana.ManaCost;
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
|
|
@ -697,7 +696,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop on completed, e.g. X=0
|
// stop on completed, e.g. X=0
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -729,7 +728,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// continue to next target (example: auto-choose must fill min/max = 2 from 2 possible cards)
|
// continue to next target (example: auto-choose must fill min/max = 2 from 2 possible cards)
|
||||||
} else {
|
} else {
|
||||||
// manual choose
|
// manual choose
|
||||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||||
|
|
||||||
prepareForResponse(game);
|
prepareForResponse(game);
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
|
|
@ -747,9 +746,9 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (possibleTargets.contains(responseId) && target.canTarget(getId(), responseId, source, game)) {
|
if (possibleTargets.contains(responseId)) {
|
||||||
target.add(responseId, game);
|
target.add(responseId, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -794,7 +793,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
// manual choice
|
// manual choice
|
||||||
if (responseId == null) {
|
if (responseId == null) {
|
||||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||||
|
|
||||||
prepareForResponse(game);
|
prepareForResponse(game);
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
|
|
@ -814,11 +813,9 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
// add new target
|
// add new target
|
||||||
if (possibleTargets.contains(responseId)) {
|
if (possibleTargets.contains(responseId)) {
|
||||||
if (target.canTarget(abilityControllerId, responseId, source, game)) {
|
target.addTarget(responseId, source, game);
|
||||||
target.addTarget(responseId, source, game);
|
if (target.isChoiceCompleted(abilityControllerId, source, game, null)) {
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
return true;
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -864,13 +861,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
UUID abilityControllerId = target.getAffectedAbilityControllerId(this.getId());
|
||||||
|
|
||||||
while (canRespond()) {
|
while (canRespond()) {
|
||||||
|
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game, cards);
|
||||||
List<UUID> possibleTargets = new ArrayList<>();
|
|
||||||
for (UUID cardId : cards) {
|
|
||||||
if (target.canTarget(abilityControllerId, cardId, source, cards, game)) {
|
|
||||||
possibleTargets.add(cardId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
||||||
int count = cards.count(target.getFilter(), abilityControllerId, source, game);
|
int count = cards.count(target.getFilter(), abilityControllerId, source, game);
|
||||||
|
|
@ -880,7 +871,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
// if nothing to choose then show dialog (user must see non selectable items and click on any of them)
|
// if nothing to choose then show dialog (user must see non selectable items and click on any of them)
|
||||||
// TODO: need research - is it used?
|
// TODO: need research - is it used (test player and AI player don't see empty dialogs)?
|
||||||
if (required && possibleTargets.isEmpty()) {
|
if (required && possibleTargets.isEmpty()) {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
@ -894,7 +885,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
} else {
|
} else {
|
||||||
// manual choose
|
// manual choose
|
||||||
Map<String, Serializable> options = getOptions(target, null);
|
Map<String, Serializable> options = getOptions(target, null);
|
||||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||||
if (!possibleTargets.isEmpty()) {
|
if (!possibleTargets.isEmpty()) {
|
||||||
options.put("possibleTargets", (Serializable) possibleTargets);
|
options.put("possibleTargets", (Serializable) possibleTargets);
|
||||||
}
|
}
|
||||||
|
|
@ -916,9 +907,9 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (possibleTargets.contains(responseId) && target.canTarget(getId(), responseId, source, cards, game)) {
|
if (possibleTargets.contains(responseId)) {
|
||||||
target.add(responseId, game);
|
target.add(responseId, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -962,12 +953,8 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UUID> possibleTargets = new ArrayList<>();
|
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game, cards);
|
||||||
for (UUID cardId : cards) {
|
|
||||||
if (target.canTarget(abilityControllerId, cardId, source, cards, game)) {
|
|
||||||
possibleTargets.add(cardId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if nothing to choose then show dialog (user must see non-selectable items and click on any of them)
|
// if nothing to choose then show dialog (user must see non-selectable items and click on any of them)
|
||||||
if (possibleTargets.isEmpty()) {
|
if (possibleTargets.isEmpty()) {
|
||||||
required = false;
|
required = false;
|
||||||
|
|
@ -977,7 +964,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
if (responseId == null) {
|
if (responseId == null) {
|
||||||
Map<String, Serializable> options = getOptions(target, null);
|
Map<String, Serializable> options = getOptions(target, null);
|
||||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||||
|
|
||||||
if (!possibleTargets.isEmpty()) {
|
if (!possibleTargets.isEmpty()) {
|
||||||
options.put("possibleTargets", (Serializable) possibleTargets);
|
options.put("possibleTargets", (Serializable) possibleTargets);
|
||||||
|
|
@ -995,9 +982,9 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
if (responseId != null) {
|
if (responseId != null) {
|
||||||
if (target.contains(responseId)) { // if already included remove it
|
if (target.contains(responseId)) { // if already included remove it
|
||||||
target.remove(responseId);
|
target.remove(responseId);
|
||||||
} else if (target.canTarget(abilityControllerId, responseId, source, cards, game)) {
|
} else if (possibleTargets.contains(responseId)) {
|
||||||
target.addTarget(responseId, source, game);
|
target.addTarget(responseId, source, game);
|
||||||
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game, cards)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1054,9 +1041,9 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// 1. Select targets
|
// 1. Select targets
|
||||||
// TODO: rework to use existing chooseTarget instead custom select?
|
// TODO: rework to use existing chooseTarget instead custom select?
|
||||||
while (canRespond()) {
|
while (canRespond()) {
|
||||||
Set<UUID> possibleTargetIds = target.possibleTargets(abilityControllerId, source, game);
|
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game);
|
||||||
boolean required = target.isRequired(source.getSourceId(), game);
|
boolean required = target.isRequired(source.getSourceId(), game);
|
||||||
if (possibleTargetIds.isEmpty()
|
if (possibleTargets.isEmpty()
|
||||||
|| target.getSize() >= target.getMinNumberOfTargets()) {
|
|| target.getSize() >= target.getMinNumberOfTargets()) {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
@ -1065,12 +1052,6 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
// responseId is null if a choice couldn't be automatically made
|
// responseId is null if a choice couldn't be automatically made
|
||||||
if (responseId == null) {
|
if (responseId == null) {
|
||||||
List<UUID> possibleTargets = new ArrayList<>();
|
|
||||||
for (UUID targetId : possibleTargetIds) {
|
|
||||||
if (target.canTarget(abilityControllerId, targetId, source, game)) {
|
|
||||||
possibleTargets.add(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// if nothing to choose then show dialog (user must see non selectable items and click on any of them)
|
// if nothing to choose then show dialog (user must see non selectable items and click on any of them)
|
||||||
if (required && possibleTargets.isEmpty()) {
|
if (required && possibleTargets.isEmpty()) {
|
||||||
required = false;
|
required = false;
|
||||||
|
|
@ -1078,7 +1059,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
// selected
|
// selected
|
||||||
Map<String, Serializable> options = getOptions(target, null);
|
Map<String, Serializable> options = getOptions(target, null);
|
||||||
options.put("chosenTargets", (Serializable) target.getTargets());
|
options.put("chosenTargets", new HashSet<>(target.getTargets()));
|
||||||
if (!possibleTargets.isEmpty()) {
|
if (!possibleTargets.isEmpty()) {
|
||||||
options.put("possibleTargets", (Serializable) possibleTargets);
|
options.put("possibleTargets", (Serializable) possibleTargets);
|
||||||
}
|
}
|
||||||
|
|
@ -1087,7 +1068,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
String multiType = multiAmountType == MultiAmountType.DAMAGE ? " to divide %d damage" : " to distribute %d counters";
|
String multiType = multiAmountType == MultiAmountType.DAMAGE ? " to divide %d damage" : " to distribute %d counters";
|
||||||
String message = target.getMessage(game) + 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);
|
game.fireSelectTargetEvent(playerId, new MessageToClient(message, getRelatedObjectName(source, game)), possibleTargets, required, options);
|
||||||
}
|
}
|
||||||
waitForResponse(game);
|
waitForResponse(game);
|
||||||
|
|
||||||
|
|
@ -1098,9 +1079,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
if (target.contains(responseId)) {
|
if (target.contains(responseId)) {
|
||||||
// unselect
|
// unselect
|
||||||
target.remove(responseId);
|
target.remove(responseId);
|
||||||
} else if (possibleTargetIds.contains(responseId)
|
} else if (possibleTargets.contains(responseId) && target.getSize() < amountTotal) {
|
||||||
&& target.canTarget(abilityControllerId, responseId, source, game)
|
|
||||||
&& target.getSize() < amountTotal) {
|
|
||||||
// select
|
// select
|
||||||
target.addTarget(responseId, source, game);
|
target.addTarget(responseId, source, game);
|
||||||
}
|
}
|
||||||
|
|
@ -2094,32 +2073,33 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
TargetAttackingCreature target = new TargetAttackingCreature();
|
|
||||||
|
|
||||||
// TODO: add canRespond cycle?
|
// no need in cycle, cause parent selectBlockers used it already
|
||||||
if (!canRespond()) {
|
if (!canRespond()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID responseId = null;
|
UUID responseId = null;
|
||||||
|
|
||||||
prepareForResponse(game);
|
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
// possible attackers to block
|
// possible attackers to block
|
||||||
Set<UUID> attackers = target.possibleTargets(playerId, null, game);
|
TargetAttackingCreature target = new TargetAttackingCreature();
|
||||||
Permanent blocker = game.getPermanent(blockerId);
|
Permanent blocker = game.getPermanent(blockerId);
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> allAttackers = target.possibleTargets(playerId, null, game);
|
||||||
for (UUID attackerId : attackers) {
|
Set<UUID> possibleAttackersToBlock = new HashSet<>();
|
||||||
|
for (UUID attackerId : allAttackers) {
|
||||||
CombatGroup group = game.getCombat().findGroup(attackerId);
|
CombatGroup group = game.getCombat().findGroup(attackerId);
|
||||||
if (group != null && blocker != null && group.canBlock(blocker, game)) {
|
if (group != null && blocker != null && group.canBlock(blocker, game)) {
|
||||||
possibleTargets.add(attackerId);
|
possibleAttackersToBlock.add(attackerId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (possibleTargets.size() == 1) {
|
if (possibleAttackersToBlock.size() == 1) {
|
||||||
responseId = possibleTargets.stream().iterator().next();
|
// auto-choice
|
||||||
|
responseId = possibleAttackersToBlock.stream().iterator().next();
|
||||||
} else {
|
} else {
|
||||||
|
prepareForResponse(game);
|
||||||
game.fireSelectTargetEvent(playerId, new MessageToClient("Select attacker to block", getRelatedObjectName(blockerId, game)),
|
game.fireSelectTargetEvent(playerId, new MessageToClient("Select attacker to block", getRelatedObjectName(blockerId, game)),
|
||||||
possibleTargets, false, getOptions(target, null));
|
possibleAttackersToBlock, false, getOptions(target, null));
|
||||||
waitForResponse(game);
|
waitForResponse(game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2174,7 +2154,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return xValue;
|
return xValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -77,7 +77,7 @@ class AjanisChosenEffect extends OneShotEffect {
|
||||||
Permanent oldCreature = game.getPermanent(enchantment.getAttachedTo());
|
Permanent oldCreature = game.getPermanent(enchantment.getAttachedTo());
|
||||||
if (oldCreature != null) {
|
if (oldCreature != null) {
|
||||||
boolean canAttach = enchantment.getSpellAbility() == null
|
boolean canAttach = enchantment.getSpellAbility() == null
|
||||||
|| (!enchantment.getSpellAbility().getTargets().isEmpty() && enchantment.getSpellAbility().getTargets().get(0).canTarget(tokenPermanent.getId(), game));
|
|| (!enchantment.getSpellAbility().getTargets().isEmpty() && enchantment.getSpellAbility().getTargets().get(0).canTarget(tokenPermanent.getId(), source, game));
|
||||||
if (canAttach && controller.chooseUse(Outcome.Neutral, "Attach " + enchantment.getName() + " to the token ?", source, game)) {
|
if (canAttach && controller.chooseUse(Outcome.Neutral, "Attach " + enchantment.getName() + " to the token ?", source, game)) {
|
||||||
if (oldCreature.removeAttachment(enchantment.getId(), source, game)) {
|
if (oldCreature.removeAttachment(enchantment.getId(), source, game)) {
|
||||||
tokenPermanent.addAttachment(enchantment.getId(), source, game);
|
tokenPermanent.addAttachment(enchantment.getId(), source, game);
|
||||||
|
|
|
||||||
|
|
@ -108,8 +108,8 @@ class AncientBrassDragonTarget extends TargetCardInGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, xValue, game);
|
this.getTargets(), id, MageObject::getManaValue, xValue, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ class AnuridScavengerCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return this.getTargets().canChoose(controllerId, source, game);
|
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -134,8 +134,8 @@ class AoTheDawnSkyTarget extends TargetCardInLibrary {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, 4, game);
|
this.getTargets(), id, MageObject::getManaValue, 4, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class ArdentDustspeakerCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return this.getTargets().canChoose(controllerId, source, game);
|
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -67,7 +67,7 @@ class BackFromTheBrinkCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return this.getTargets().canChoose(controllerId, source, game);
|
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,7 @@ class BattleForBretagardTarget extends TargetPermanent {
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
|
|
||||||
Set<String> names = this.getTargets()
|
Set<String> names = this.getTargets()
|
||||||
.stream()
|
.stream()
|
||||||
.map(game::getPermanent)
|
.map(game::getPermanent)
|
||||||
|
|
@ -159,6 +160,7 @@ class BattleForBretagardTarget extends TargetPermanent {
|
||||||
Permanent permanent = game.getPermanent(uuid);
|
Permanent permanent = game.getPermanent(uuid);
|
||||||
return permanent == null || names.contains(permanent.getName());
|
return permanent == null || names.contains(permanent.getName());
|
||||||
});
|
});
|
||||||
|
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ class BattlefieldScroungerCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return this.getTargets().canChoose(controllerId, source, game);
|
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -66,12 +66,12 @@ class BeguilerOfWillsTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
Permanent permanent = game.getPermanent(id);
|
Permanent permanent = game.getPermanent(id);
|
||||||
int count = game.getBattlefield().countAll(this.filter, source.getControllerId(), game);
|
int count = game.getBattlefield().countAll(this.filter, source.getControllerId(), game);
|
||||||
|
|
||||||
if (permanent != null && permanent.getPower().getValue() <= count) {
|
if (permanent != null && permanent.getPower().getValue() <= count) {
|
||||||
return super.canTarget(controllerId, id, source, game);
|
return super.canTarget(playerId, id, source, game);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -49,18 +49,18 @@ class BlazingHopeTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
Permanent permanent = game.getPermanent(id);
|
Permanent permanent = game.getPermanent(id);
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
if (!isNotTarget()) {
|
if (!isNotTarget()) {
|
||||||
if (!permanent.canBeTargetedBy(game.getObject(source.getId()), controllerId, source, game)
|
if (!permanent.canBeTargetedBy(game.getObject(source.getId()), playerId, source, game)
|
||||||
|| !permanent.canBeTargetedBy(game.getObject(source), controllerId, source, game)) {
|
|| !permanent.canBeTargetedBy(game.getObject(source), playerId, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
if (controller != null && permanent.getPower().getValue() >= controller.getLife()) {
|
if (controller != null && permanent.getPower().getValue() >= controller.getLife()) {
|
||||||
return filter.match(permanent, controllerId, source, game);
|
return filter.match(permanent, playerId, source, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package mage.cards.b;
|
package mage.cards.b;
|
||||||
|
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.keyword.TheRingTemptsYouEffect;
|
import mage.abilities.effects.keyword.TheRingTemptsYouEffect;
|
||||||
|
|
@ -8,20 +7,20 @@ import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
import mage.filter.common.FilterOpponentsCreaturePermanent;
|
||||||
import mage.filter.predicate.Predicates;
|
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||||
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
|
||||||
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO: combine with Mutiny
|
||||||
|
*
|
||||||
* @author Susucr
|
* @author Susucr
|
||||||
*/
|
*/
|
||||||
public final class BreakingOfTheFellowship extends CardImpl {
|
public final class BreakingOfTheFellowship extends CardImpl {
|
||||||
|
|
@ -31,8 +30,8 @@ public final class BreakingOfTheFellowship extends CardImpl {
|
||||||
|
|
||||||
// Target creature an opponent controls deals damage equal to its power to another target creature that player controls.
|
// Target creature an opponent controls deals damage equal to its power to another target creature that player controls.
|
||||||
this.getSpellAbility().addEffect(new BreakingOfTheFellowshipEffect());
|
this.getSpellAbility().addEffect(new BreakingOfTheFellowshipEffect());
|
||||||
this.getSpellAbility().addTarget(new BreakingOfTheFellowshipFirstTarget());
|
this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||||
this.getSpellAbility().addTarget(new TargetPermanent(new FilterCreaturePermanent("another target creature that player controls")));
|
this.getSpellAbility().addTarget(new BreakingOfTheFellowshipSecondTarget());
|
||||||
|
|
||||||
// The Ring tempts you.
|
// The Ring tempts you.
|
||||||
this.getSpellAbility().addEffect(new TheRingTemptsYouEffect());
|
this.getSpellAbility().addEffect(new TheRingTemptsYouEffect());
|
||||||
|
|
@ -76,74 +75,45 @@ class BreakingOfTheFellowshipEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BreakingOfTheFellowshipFirstTarget extends TargetPermanent {
|
class BreakingOfTheFellowshipSecondTarget extends TargetPermanent {
|
||||||
|
|
||||||
public BreakingOfTheFellowshipFirstTarget() {
|
public BreakingOfTheFellowshipSecondTarget() {
|
||||||
super(1, 1, StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE, false);
|
super(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private BreakingOfTheFellowshipFirstTarget(final BreakingOfTheFellowshipFirstTarget target) {
|
private BreakingOfTheFellowshipSecondTarget(final BreakingOfTheFellowshipSecondTarget target) {
|
||||||
super(target);
|
super(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTarget(UUID id, Ability source, Game game, boolean skipEvent) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
super.addTarget(id, source, game, skipEvent);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
// Update the second target
|
|
||||||
UUID firstController = game.getControllerId(id);
|
|
||||||
if (firstController != null && source.getTargets().size() > 1) {
|
|
||||||
Player controllingPlayer = game.getPlayer(firstController);
|
|
||||||
TargetCreaturePermanent targetCreaturePermanent = (TargetCreaturePermanent) source.getTargets().get(1);
|
|
||||||
// Set a new filter to the second target with the needed restrictions
|
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature that player " + controllingPlayer.getName() + " controls");
|
|
||||||
filter.add(new ControllerIdPredicate(firstController));
|
|
||||||
filter.add(Predicates.not(new PermanentIdPredicate(id)));
|
|
||||||
targetCreaturePermanent.replaceFilter(filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
Permanent firstTarget = game.getPermanent(source.getFirstTarget());
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
if (firstTarget == null) {
|
||||||
if (super.canTarget(controllerId, id, source, game)) {
|
// playable or first target not yet selected
|
||||||
// can only target, if the controller has at least two targetable creatures
|
// use all
|
||||||
UUID controllingPlayerId = game.getControllerId(id);
|
if (possibleTargets.size() == 1) {
|
||||||
int possibleTargets = 0;
|
// workaround to make 1 target invalid
|
||||||
MageObject sourceObject = game.getObject(source.getId());
|
possibleTargets.clear();
|
||||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controllingPlayerId, game)) {
|
|
||||||
if (permanent.canBeTargetedBy(sourceObject, controllerId, source, game)) {
|
|
||||||
possibleTargets++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return possibleTargets > 1;
|
} else {
|
||||||
|
// real
|
||||||
|
// filter by same player
|
||||||
|
possibleTargets.removeIf(id -> {
|
||||||
|
Permanent permanent = game.getPermanent(id);
|
||||||
|
return permanent == null || !permanent.isControlledBy(firstTarget.getControllerId());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return false;
|
possibleTargets.removeIf(id -> firstTarget != null && firstTarget.getId().equals(id));
|
||||||
|
|
||||||
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
public BreakingOfTheFellowshipSecondTarget copy() {
|
||||||
if (super.canChoose(sourceControllerId, source, game)) {
|
return new BreakingOfTheFellowshipSecondTarget(this);
|
||||||
UUID controllingPlayerId = game.getControllerId(source.getSourceId());
|
|
||||||
for (UUID playerId : game.getOpponents(controllingPlayerId)) {
|
|
||||||
int possibleTargets = 0;
|
|
||||||
MageObject sourceObject = game.getObject(source);
|
|
||||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, playerId, game)) {
|
|
||||||
if (permanent.canBeTargetedBy(sourceObject, controllingPlayerId, source, game)) {
|
|
||||||
possibleTargets++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (possibleTargets > 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BreakingOfTheFellowshipFirstTarget copy() {
|
|
||||||
return new BreakingOfTheFellowshipFirstTarget(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -141,8 +141,8 @@ class CallOfTheDeathDwellerTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, 3, game);
|
this.getTargets(), id, MageObject::getManaValue, 3, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,5 @@
|
||||||
package mage.cards.c;
|
package mage.cards.c;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.ApprovingObject;
|
import mage.ApprovingObject;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
|
|
@ -17,7 +14,6 @@ import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.filter.common.FilterInstantOrSorceryCard;
|
import mage.filter.common.FilterInstantOrSorceryCard;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.game.stack.StackObject;
|
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
import mage.target.TargetCard;
|
import mage.target.TargetCard;
|
||||||
|
|
@ -25,6 +21,9 @@ import mage.target.TargetPermanent;
|
||||||
import mage.target.common.TargetPlayerOrPlaneswalker;
|
import mage.target.common.TargetPlayerOrPlaneswalker;
|
||||||
import mage.target.targetpointer.FixedTarget;
|
import mage.target.targetpointer.FixedTarget;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author jeffwadsworth
|
* @author jeffwadsworth
|
||||||
*/
|
*/
|
||||||
|
|
@ -111,47 +110,23 @@ class ChandraPyromasterTarget extends TargetPermanent {
|
||||||
super(target);
|
super(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
|
||||||
Player player = game.getPlayerOrPlaneswalkerController(source.getFirstTarget());
|
|
||||||
if (player == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UUID firstTarget = player.getId();
|
|
||||||
Permanent permanent = game.getPermanent(id);
|
|
||||||
if (firstTarget != null && permanent != null && permanent.isControlledBy(firstTarget)) {
|
|
||||||
return super.canTarget(id, source, game);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> availablePossibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
|
||||||
MageObject object = game.getObject(source);
|
|
||||||
|
|
||||||
for (StackObject item : game.getState().getStack()) {
|
Player needPlayer = game.getPlayerOrPlaneswalkerController(source.getFirstTarget());
|
||||||
if (item.getId().equals(source.getSourceId())) {
|
if (needPlayer == null) {
|
||||||
object = item;
|
// playable or not selected - use any
|
||||||
}
|
} else {
|
||||||
if (item.getSourceId().equals(source.getSourceId())) {
|
// filter by controller
|
||||||
object = item;
|
possibleTargets.removeIf(id -> {
|
||||||
}
|
Permanent permanent = game.getPermanent(id);
|
||||||
|
return permanent == null
|
||||||
|
|| permanent.getId().equals(source.getFirstTarget())
|
||||||
|
|| !permanent.isControlledBy(needPlayer.getId());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object instanceof StackObject) {
|
|
||||||
UUID playerId = ((StackObject) object).getStackAbility().getFirstTarget();
|
|
||||||
Player player = game.getPlayerOrPlaneswalkerController(playerId);
|
|
||||||
if (player != null) {
|
|
||||||
for (UUID targetId : availablePossibleTargets) {
|
|
||||||
Permanent permanent = game.getPermanent(targetId);
|
|
||||||
if (permanent != null && permanent.isControlledBy(player.getId())) {
|
|
||||||
possibleTargets.add(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -124,8 +124,8 @@ class ChaosMutationTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (!super.canTarget(controllerId, id, source, game)) {
|
if (!super.canTarget(playerId, id, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Permanent creature = game.getPermanent(id);
|
Permanent creature = game.getPermanent(id);
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,8 @@ class CloudsLimitBreakTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (!super.canTarget(controllerId, id, source, game)) {
|
if (!super.canTarget(playerId, id, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Permanent creature = game.getPermanent(id);
|
Permanent creature = game.getPermanent(id);
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,7 @@ class ConspiracyTheoristEffect extends OneShotEffect {
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
CardsImpl cards = new CardsImpl(discardedCards);
|
CardsImpl cards = new CardsImpl(discardedCards);
|
||||||
TargetCard target = new TargetCard(Zone.GRAVEYARD, new FilterCard("card to exile"));
|
TargetCard target = new TargetCard(Zone.GRAVEYARD, new FilterCard("card to exile"));
|
||||||
boolean validTarget = cards.stream()
|
boolean validTarget = cards.stream().anyMatch(card -> target.canTarget(card, source, game));
|
||||||
.anyMatch(card -> target.canTarget(card, game));
|
|
||||||
if (validTarget && controller.chooseUse(Outcome.Benefit, "Exile a card?", source, game)) {
|
if (validTarget && controller.chooseUse(Outcome.Benefit, "Exile a card?", source, game)) {
|
||||||
if (controller.choose(Outcome.Benefit, cards, target, source, game)) {
|
if (controller.choose(Outcome.Benefit, cards, target, source, game)) {
|
||||||
Card card = cards.get(target.getFirstTarget(), game);
|
Card card = cards.get(target.getFirstTarget(), game);
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import mage.abilities.keyword.InspiredAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.predicate.Predicates;
|
import mage.filter.predicate.Predicates;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
|
@ -35,8 +36,12 @@ public final class DaringThief extends CardImpl {
|
||||||
this.toughness = new MageInt(3);
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
// Inspired - Whenever Daring Thief becomes untapped, you may exchange control of target nonland permanent you control and target permanent an opponent controls that shares a card type with it.
|
// Inspired - Whenever Daring Thief becomes untapped, you may exchange control of target nonland permanent you control and target permanent an opponent controls that shares a card type with it.
|
||||||
Ability ability = new InspiredAbility(new ExchangeControlTargetEffect(Duration.EndOfGame,
|
Ability ability = new InspiredAbility(new ExchangeControlTargetEffect(
|
||||||
"you may exchange control of target nonland permanent you control and target permanent an opponent controls that shares a card type with it", false, true), true);
|
Duration.EndOfGame,
|
||||||
|
"you may exchange control of target nonland permanent you control and target permanent an opponent controls that shares a card type with it",
|
||||||
|
false,
|
||||||
|
true
|
||||||
|
), true);
|
||||||
ability.addTarget(new TargetControlledPermanentSharingOpponentPermanentCardType());
|
ability.addTarget(new TargetControlledPermanentSharingOpponentPermanentCardType());
|
||||||
ability.addTarget(new DaringThiefSecondTarget());
|
ability.addTarget(new DaringThiefSecondTarget());
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
@ -66,9 +71,10 @@ class TargetControlledPermanentSharingOpponentPermanentCardType extends TargetCo
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (super.canTarget(controllerId, id, source, game)) {
|
Set<CardType> cardTypes = getOpponentPermanentCardTypes(playerId, game);
|
||||||
Set<CardType> cardTypes = getOpponentPermanentCardTypes(controllerId, game);
|
|
||||||
|
if (super.canTarget(playerId, id, source, game)) {
|
||||||
Permanent permanent = game.getPermanent(id);
|
Permanent permanent = game.getPermanent(id);
|
||||||
for (CardType type : permanent.getCardType(game)) {
|
for (CardType type : permanent.getCardType(game)) {
|
||||||
if (cardTypes.contains(type)) {
|
if (cardTypes.contains(type)) {
|
||||||
|
|
@ -81,23 +87,21 @@ class TargetControlledPermanentSharingOpponentPermanentCardType extends TargetCo
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
// get all cardtypes from opponents permanents
|
|
||||||
Set<CardType> cardTypes = getOpponentPermanentCardTypes(sourceControllerId, game);
|
Set<CardType> cardTypes = getOpponentPermanentCardTypes(sourceControllerId, game);
|
||||||
|
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
MageObject targetSource = game.getObject(source);
|
MageObject targetSource = game.getObject(source);
|
||||||
if (targetSource != null) {
|
if (targetSource != null) {
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
||||||
if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
for (CardType type : permanent.getCardType(game)) {
|
||||||
for (CardType type : permanent.getCardType(game)) {
|
if (cardTypes.contains(type)) {
|
||||||
if (cardTypes.contains(type)) {
|
possibleTargets.add(permanent.getId());
|
||||||
possibleTargets.add(permanent.getId());
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return possibleTargets;
|
return keepValidPossibleTargets(possibleTargets, sourceControllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -119,58 +123,54 @@ class TargetControlledPermanentSharingOpponentPermanentCardType extends TargetCo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class DaringThiefSecondTarget extends TargetPermanent {
|
class DaringThiefSecondTarget extends TargetPermanent {
|
||||||
|
|
||||||
private Permanent firstTarget = null;
|
|
||||||
|
|
||||||
public DaringThiefSecondTarget() {
|
public DaringThiefSecondTarget() {
|
||||||
super();
|
super(StaticFilters.FILTER_OPPONENTS_PERMANENT);
|
||||||
this.filter = this.filter.copy();
|
|
||||||
filter.add(TargetController.OPPONENT.getControllerPredicate());
|
|
||||||
withTargetName("permanent an opponent controls that shares a card type with it");
|
withTargetName("permanent an opponent controls that shares a card type with it");
|
||||||
}
|
}
|
||||||
|
|
||||||
private DaringThiefSecondTarget(final DaringThiefSecondTarget target) {
|
private DaringThiefSecondTarget(final DaringThiefSecondTarget target) {
|
||||||
super(target);
|
super(target);
|
||||||
this.firstTarget = target.firstTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID id, Ability source, Game game) {
|
||||||
if (super.canTarget(id, source, game)) {
|
Permanent ownPermanent = game.getPermanent(source.getFirstTarget());
|
||||||
Permanent target1 = game.getPermanent(source.getFirstTarget());
|
Permanent possiblePermanent = game.getPermanent(id);
|
||||||
Permanent opponentPermanent = game.getPermanent(id);
|
if (ownPermanent == null || possiblePermanent == null) {
|
||||||
if (target1 != null && opponentPermanent != null) {
|
return false;
|
||||||
return target1.shareTypes(opponentPermanent, game);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return super.canTarget(id, source, game) && ownPermanent.shareTypes(possiblePermanent, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
if (firstTarget != null) {
|
|
||||||
MageObject targetSource = game.getObject(source);
|
Permanent ownPermanent = game.getPermanent(source.getFirstTarget());
|
||||||
if (targetSource != null) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
if (ownPermanent == null) {
|
||||||
if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
// playable or first target not yet selected
|
||||||
if (permanent.shareTypes(firstTarget, game)) {
|
// use all
|
||||||
possibleTargets.add(permanent.getId());
|
possibleTargets.add(permanent.getId());
|
||||||
}
|
} else {
|
||||||
}
|
// real
|
||||||
|
// filter by shared type
|
||||||
|
if (permanent.shareTypes(ownPermanent, game)) {
|
||||||
|
possibleTargets.add(permanent.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return possibleTargets;
|
possibleTargets.removeIf(id -> ownPermanent != null && ownPermanent.getId().equals(id));
|
||||||
|
|
||||||
|
return keepValidPossibleTargets(possibleTargets, sourceControllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
||||||
firstTarget = game.getPermanent(source.getFirstTarget());
|
// AI hint with better outcome
|
||||||
return super.chooseTarget(Outcome.Damage, playerId, source, game);
|
return super.chooseTarget(Outcome.GainControl, playerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,7 @@ class DeepCaverBatEffect extends OneShotEffect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TargetCard target = new TargetCardInHand(
|
TargetCard target = new TargetCard(0, 1, Zone.HAND, StaticFilters.FILTER_CARD_A_NON_LAND);
|
||||||
0, 1, StaticFilters.FILTER_CARD_A_NON_LAND
|
|
||||||
);
|
|
||||||
controller.choose(outcome, opponent.getHand(), target, source, game);
|
controller.choose(outcome, opponent.getHand(), target, source, game);
|
||||||
Card card = opponent.getHand().get(target.getFirstTarget(), game);
|
Card card = opponent.getHand().get(target.getFirstTarget(), game);
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
|
|
|
||||||
|
|
@ -137,49 +137,12 @@ class TargetControlledSource extends TargetSource {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canChoose(UUID sourceControllerId, Game game) {
|
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
||||||
int count = 0;
|
return canChooseFromPossibleTargets(sourceControllerId, source, game);
|
||||||
for (StackObject stackObject : game.getStack()) {
|
|
||||||
if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId())
|
|
||||||
&& Objects.equals(stackObject.getControllerId(), sourceControllerId)) {
|
|
||||||
count++;
|
|
||||||
if (count >= this.minNumberOfTargets) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(sourceControllerId, game)) {
|
|
||||||
if (Objects.equals(permanent.getControllerId(), sourceControllerId)) {
|
|
||||||
count++;
|
|
||||||
if (count >= this.minNumberOfTargets) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (Player player : game.getPlayers().values()) {
|
|
||||||
if (Objects.equals(player, game.getPlayer(sourceControllerId))) {
|
|
||||||
for (Card card : player.getGraveyard().getCards(game)) {
|
|
||||||
count++;
|
|
||||||
if (count >= this.minNumberOfTargets) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 108.4a If anything asks for the controller of a card that doesn't have one (because it's not a permanent or spell), use its owner instead.
|
|
||||||
for (Card card : game.getExile().getAllCards(game)) {
|
|
||||||
if (Objects.equals(card.getOwnerId(), sourceControllerId)) {
|
|
||||||
count++;
|
|
||||||
if (count >= this.minNumberOfTargets) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
for (StackObject stackObject : game.getStack()) {
|
for (StackObject stackObject : game.getStack()) {
|
||||||
if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId())
|
if (game.getState().getPlayersInRange(sourceControllerId, game).contains(stackObject.getControllerId())
|
||||||
|
|
@ -205,7 +168,7 @@ class TargetControlledSource extends TargetSource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return possibleTargets;
|
return keepValidPossibleTargets(possibleTargets, sourceControllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -90,10 +90,10 @@ class DistendedMindbenderEffect extends OneShotEffect {
|
||||||
TargetCard targetThreeOrLess = new TargetCard(1, Zone.HAND, filterThreeOrLess);
|
TargetCard targetThreeOrLess = new TargetCard(1, Zone.HAND, filterThreeOrLess);
|
||||||
TargetCard targetFourOrGreater = new TargetCard(1, Zone.HAND, filterFourOrGreater);
|
TargetCard targetFourOrGreater = new TargetCard(1, Zone.HAND, filterFourOrGreater);
|
||||||
Cards toDiscard = new CardsImpl();
|
Cards toDiscard = new CardsImpl();
|
||||||
if (controller.chooseTarget(Outcome.Benefit, opponent.getHand(), targetThreeOrLess, source, game)) {
|
if (controller.choose(Outcome.Benefit, opponent.getHand(), targetThreeOrLess, source, game)) {
|
||||||
toDiscard.addAll(targetThreeOrLess.getTargets());
|
toDiscard.addAll(targetThreeOrLess.getTargets());
|
||||||
}
|
}
|
||||||
if (controller.chooseTarget(Outcome.Benefit, opponent.getHand(), targetFourOrGreater, source, game)) {
|
if (controller.choose(Outcome.Benefit, opponent.getHand(), targetFourOrGreater, source, game)) {
|
||||||
toDiscard.addAll(targetFourOrGreater.getTargets());
|
toDiscard.addAll(targetFourOrGreater.getTargets());
|
||||||
}
|
}
|
||||||
opponent.discard(toDiscard, false, source, game);
|
opponent.discard(toDiscard, false, source, game);
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,26 @@
|
||||||
|
|
||||||
package mage.cards.d;
|
package mage.cards.d;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.cards.*;
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.cards.Cards;
|
||||||
|
import mage.cards.CardsImpl;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.filter.common.FilterArtifactCard;
|
import mage.filter.common.FilterArtifactCard;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.TargetEvent;
|
|
||||||
import mage.game.stack.StackObject;
|
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.TargetPlayer;
|
import mage.target.TargetPlayer;
|
||||||
import mage.target.common.TargetCardInGraveyard;
|
import mage.target.common.TargetCardInGraveyard;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author emerald000
|
* @author emerald000
|
||||||
*/
|
*/
|
||||||
public final class DrafnasRestoration extends CardImpl {
|
public final class DrafnasRestoration extends CardImpl {
|
||||||
|
|
@ -53,27 +54,33 @@ class DrafnasRestorationTarget extends TargetCardInGraveyard {
|
||||||
super(target);
|
super(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
|
||||||
Player targetPlayer = game.getPlayer(source.getFirstTarget());
|
|
||||||
return targetPlayer != null && targetPlayer.getGraveyard().contains(id) && super.canTarget(id, source, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
MageObject object = game.getObject(source);
|
|
||||||
if (object instanceof StackObject) {
|
Player controller = game.getPlayer(sourceControllerId);
|
||||||
Player targetPlayer = game.getPlayer(((StackObject) object).getStackAbility().getFirstTarget());
|
if (controller == null) {
|
||||||
if (targetPlayer != null) {
|
return possibleTargets;
|
||||||
for (Card card : targetPlayer.getGraveyard().getCards(filter, sourceControllerId, source, game)) {
|
|
||||||
if (source == null || source.getSourceId() == null || isNotTarget() || !game.replaceEvent(new TargetEvent(card, source.getSourceId(), sourceControllerId))) {
|
|
||||||
possibleTargets.add(card.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return possibleTargets;
|
|
||||||
|
Player targetPlayer = game.getPlayer(source.getFirstTarget());
|
||||||
|
game.getState().getPlayersInRange(sourceControllerId, game, true).stream()
|
||||||
|
.map(game::getPlayer)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.flatMap(player -> player.getGraveyard().getCards(filter, sourceControllerId, source, game).stream())
|
||||||
|
.forEach(card -> {
|
||||||
|
if (targetPlayer == null) {
|
||||||
|
// playable or not selected - use any
|
||||||
|
possibleTargets.add(card.getId());
|
||||||
|
} else {
|
||||||
|
// selected, filter by player
|
||||||
|
if (targetPlayer.getId().equals(card.getControllerOrOwnerId())) {
|
||||||
|
possibleTargets.add(card.getId());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return keepValidPossibleTargets(possibleTargets, sourceControllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,7 @@ class DreamsOfSteelAndOilEffect extends OneShotEffect {
|
||||||
filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate()));
|
filter.add(Predicates.or(CardType.ARTIFACT.getPredicate(), CardType.CREATURE.getPredicate()));
|
||||||
TargetCard target = new TargetCard(Zone.HAND, filter);
|
TargetCard target = new TargetCard(Zone.HAND, filter);
|
||||||
target.withNotTarget(true);
|
target.withNotTarget(true);
|
||||||
controller.chooseTarget(Outcome.Discard, opponent.getHand(), target, source, game);
|
controller.choose(Outcome.Discard, opponent.getHand(), target, source, game);
|
||||||
Card card = game.getCard(target.getFirstTarget());
|
Card card = game.getCard(target.getFirstTarget());
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
toExile.add(card);
|
toExile.add(card);
|
||||||
|
|
@ -81,7 +81,7 @@ class DreamsOfSteelAndOilEffect extends OneShotEffect {
|
||||||
filter.setMessage("artifact or creature card from " + opponent.getName() + "'s graveyard");
|
filter.setMessage("artifact or creature card from " + opponent.getName() + "'s graveyard");
|
||||||
target = new TargetCard(Zone.GRAVEYARD, filter);
|
target = new TargetCard(Zone.GRAVEYARD, filter);
|
||||||
target.withNotTarget(true);
|
target.withNotTarget(true);
|
||||||
controller.chooseTarget(Outcome.Exile, opponent.getGraveyard(), target, source, game);
|
controller.choose(Outcome.Exile, opponent.getGraveyard(), target, source, game);
|
||||||
card = game.getCard(target.getFirstTarget());
|
card = game.getCard(target.getFirstTarget());
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
toExile.add(card);
|
toExile.add(card);
|
||||||
|
|
|
||||||
|
|
@ -105,11 +105,10 @@ class RevivalExperimentTarget extends TargetCardInYourGraveyard {
|
||||||
return cardTypeAssigner.getRoleCount(cards, game) >= cards.size();
|
return cardTypeAssigner.getRoleCount(cards, game) >= cards.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, null, game));
|
possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, source, game));
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,7 @@ class EliteSpellbinderEffect extends OneShotEffect {
|
||||||
if (controller == null || opponent == null || opponent.getHand().isEmpty()) {
|
if (controller == null || opponent == null || opponent.getHand().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TargetCard target = new TargetCardInHand(
|
TargetCard target = new TargetCard(0, 1, Zone.HAND, StaticFilters.FILTER_CARD_A_NON_LAND);
|
||||||
0, 1, StaticFilters.FILTER_CARD_A_NON_LAND
|
|
||||||
);
|
|
||||||
controller.choose(outcome, opponent.getHand(), target, source, game);
|
controller.choose(outcome, opponent.getHand(), target, source, game);
|
||||||
Card card = opponent.getHand().get(target.getFirstTarget(), game);
|
Card card = opponent.getHand().get(target.getFirstTarget(), game);
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ class EnchantmentAlterationEffect extends OneShotEffect {
|
||||||
if (oldPermanent != null
|
if (oldPermanent != null
|
||||||
&& !oldPermanent.equals(permanentToBeAttachedTo)) {
|
&& !oldPermanent.equals(permanentToBeAttachedTo)) {
|
||||||
Target auraTarget = aura.getSpellAbility().getTargets().get(0);
|
Target auraTarget = aura.getSpellAbility().getTargets().get(0);
|
||||||
if (!auraTarget.canTarget(permanentToBeAttachedTo.getId(), game)) {
|
if (!auraTarget.canTarget(permanentToBeAttachedTo.getId(), source, game)) {
|
||||||
game.informPlayers(aura.getLogName() + " was not attched to " + permanentToBeAttachedTo.getLogName() + " because it's no legal target for the aura");
|
game.informPlayers(aura.getLogName() + " was not attched to " + permanentToBeAttachedTo.getLogName() + " because it's no legal target for the aura");
|
||||||
} else if (oldPermanent.removeAttachment(aura.getId(), source, game)) {
|
} else if (oldPermanent.removeAttachment(aura.getId(), source, game)) {
|
||||||
game.informPlayers(aura.getLogName() + " was unattached from " + oldPermanent.getLogName() + " and attached to " + permanentToBeAttachedTo.getLogName());
|
game.informPlayers(aura.getLogName() + " was unattached from " + oldPermanent.getLogName() + " and attached to " + permanentToBeAttachedTo.getLogName());
|
||||||
|
|
|
||||||
|
|
@ -58,7 +58,7 @@ enum EverythingComesToDustPredicate implements ObjectSourcePlayerPredicate<Perma
|
||||||
if (!p.isCreature(game)){
|
if (!p.isCreature(game)){
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
HashSet<MageObjectReference> set = CardUtil.getSourceCostsTag(game, input.getSource(), ConvokeAbility.convokingCreaturesKey, new HashSet<>(0));
|
HashSet<MageObjectReference> set = CardUtil.getSourceCostsTag(game, input.getSource(), ConvokeAbility.convokingCreaturesKey, new HashSet<>());
|
||||||
for (MageObjectReference mor : set){
|
for (MageObjectReference mor : set){
|
||||||
Permanent convoked = game.getPermanentOrLKIBattlefield(mor);
|
Permanent convoked = game.getPermanentOrLKIBattlefield(mor);
|
||||||
if (convoked.shareCreatureTypes(game, p)){
|
if (convoked.shareCreatureTypes(game, p)){
|
||||||
|
|
|
||||||
|
|
@ -8,9 +8,11 @@ import mage.cards.Cards;
|
||||||
import mage.cards.CardsImpl;
|
import mage.cards.CardsImpl;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.Zone;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
import mage.target.TargetCard;
|
||||||
import mage.target.common.TargetCardInHand;
|
import mage.target.common.TargetCardInHand;
|
||||||
import mage.target.common.TargetOpponent;
|
import mage.target.common.TargetOpponent;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
@ -65,9 +67,7 @@ class ExtractBrainEffect extends OneShotEffect {
|
||||||
if (controller == null || opponent == null || opponent.getHand().isEmpty() || xValue < 1) {
|
if (controller == null || opponent == null || opponent.getHand().isEmpty() || xValue < 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TargetCardInHand target = new TargetCardInHand(
|
TargetCard target = new TargetCard(Math.min(opponent.getHand().size(), xValue), Integer.MAX_VALUE, Zone.HAND, StaticFilters.FILTER_CARD);
|
||||||
Math.min(opponent.getHand().size(), xValue), StaticFilters.FILTER_CARD
|
|
||||||
);
|
|
||||||
opponent.choose(Outcome.Detriment, opponent.getHand(), target, source, game);
|
opponent.choose(Outcome.Detriment, opponent.getHand(), target, source, game);
|
||||||
Cards cards = new CardsImpl(target.getTargets());
|
Cards cards = new CardsImpl(target.getTargets());
|
||||||
controller.lookAtCards(source, null, cards, game);
|
controller.lookAtCards(source, null, cards, game);
|
||||||
|
|
|
||||||
|
|
@ -134,7 +134,6 @@ class FireballTargetCreatureOrPlayer extends TargetAnyTarget {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
possibleTargets.removeAll(getTargets());
|
|
||||||
for (UUID targetId : possibleTargets) {
|
for (UUID targetId : possibleTargets) {
|
||||||
TargetAnyTarget target = this.copy();
|
TargetAnyTarget target = this.copy();
|
||||||
target.clearChosen();
|
target.clearChosen();
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ class FrogkinKidnapperEffect extends OneShotEffect {
|
||||||
opponent.revealCards(source, opponent.getHand(), game);
|
opponent.revealCards(source, opponent.getHand(), game);
|
||||||
TargetCard target = new TargetCard(1, Zone.HAND, StaticFilters.FILTER_CARD_A_NON_LAND);
|
TargetCard target = new TargetCard(1, Zone.HAND, StaticFilters.FILTER_CARD_A_NON_LAND);
|
||||||
Cards toRansom = new CardsImpl();
|
Cards toRansom = new CardsImpl();
|
||||||
if (controller.chooseTarget(outcome, opponent.getHand(), target, source, game)) {
|
if (controller.choose(outcome, opponent.getHand(), target, source, game)) {
|
||||||
toRansom.addAll(target.getTargets());
|
toRansom.addAll(target.getTargets());
|
||||||
|
|
||||||
String exileName = "Ransomed (owned by " + opponent.getName() + ")";
|
String exileName = "Ransomed (owned by " + opponent.getName() + ")";
|
||||||
|
|
|
||||||
|
|
@ -9,13 +9,12 @@ import mage.abilities.keyword.EquipAbility;
|
||||||
import mage.abilities.keyword.TrampleAbility;
|
import mage.abilities.keyword.TrampleAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.AttachmentType;
|
import mage.constants.*;
|
||||||
import mage.constants.CardType;
|
|
||||||
import mage.constants.ComparisonType;
|
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.filter.FilterPermanent;
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterCreatureCard;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.filter.predicate.mageobject.ToughnessPredicate;
|
import mage.filter.predicate.mageobject.ToughnessPredicate;
|
||||||
|
import mage.target.TargetCard;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
|
||||||
|
|
@ -69,9 +69,10 @@ class GauntletsOfChaosFirstTarget extends TargetControlledPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (super.canTarget(controllerId, id, source, game)) {
|
Set<CardType> cardTypes = getOpponentPermanentCardTypes(playerId, game);
|
||||||
Set<CardType> cardTypes = getOpponentPermanentCardTypes(source.getSourceId(), controllerId, game);
|
|
||||||
|
if (super.canTarget(playerId, id, source, game)) {
|
||||||
Permanent permanent = game.getPermanent(id);
|
Permanent permanent = game.getPermanent(id);
|
||||||
for (CardType type : permanent.getCardType(game)) {
|
for (CardType type : permanent.getCardType(game)) {
|
||||||
if (cardTypes.contains(type)) {
|
if (cardTypes.contains(type)) {
|
||||||
|
|
@ -84,23 +85,21 @@ class GauntletsOfChaosFirstTarget extends TargetControlledPermanent {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
// get all cardtypes from opponents permanents
|
Set<CardType> cardTypes = getOpponentPermanentCardTypes(sourceControllerId, game);
|
||||||
Set<CardType> cardTypes = getOpponentPermanentCardTypes(source.getSourceId(), sourceControllerId, game);
|
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
MageObject targetSource = game.getObject(source);
|
MageObject targetSource = game.getObject(source);
|
||||||
if (targetSource != null) {
|
if (targetSource != null) {
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
||||||
if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
for (CardType type : permanent.getCardType(game)) {
|
||||||
for (CardType type : permanent.getCardType(game)) {
|
if (cardTypes.contains(type)) {
|
||||||
if (cardTypes.contains(type)) {
|
possibleTargets.add(permanent.getId());
|
||||||
possibleTargets.add(permanent.getId());
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return possibleTargets;
|
return keepValidPossibleTargets(possibleTargets, sourceControllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -108,7 +107,7 @@ class GauntletsOfChaosFirstTarget extends TargetControlledPermanent {
|
||||||
return new GauntletsOfChaosFirstTarget(this);
|
return new GauntletsOfChaosFirstTarget(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private EnumSet<CardType> getOpponentPermanentCardTypes(UUID sourceId, UUID sourceControllerId, Game game) {
|
private EnumSet<CardType> getOpponentPermanentCardTypes(UUID sourceControllerId, Game game) {
|
||||||
Player controller = game.getPlayer(sourceControllerId);
|
Player controller = game.getPlayer(sourceControllerId);
|
||||||
EnumSet<CardType> cardTypes = EnumSet.noneOf(CardType.class);
|
EnumSet<CardType> cardTypes = EnumSet.noneOf(CardType.class);
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
|
|
@ -125,8 +124,6 @@ class GauntletsOfChaosFirstTarget extends TargetControlledPermanent {
|
||||||
|
|
||||||
class GauntletsOfChaosSecondTarget extends TargetPermanent {
|
class GauntletsOfChaosSecondTarget extends TargetPermanent {
|
||||||
|
|
||||||
private Permanent firstTarget = null;
|
|
||||||
|
|
||||||
public GauntletsOfChaosSecondTarget() {
|
public GauntletsOfChaosSecondTarget() {
|
||||||
super();
|
super();
|
||||||
this.filter = this.filter.copy();
|
this.filter = this.filter.copy();
|
||||||
|
|
@ -136,44 +133,46 @@ class GauntletsOfChaosSecondTarget extends TargetPermanent {
|
||||||
|
|
||||||
private GauntletsOfChaosSecondTarget(final GauntletsOfChaosSecondTarget target) {
|
private GauntletsOfChaosSecondTarget(final GauntletsOfChaosSecondTarget target) {
|
||||||
super(target);
|
super(target);
|
||||||
this.firstTarget = target.firstTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID id, Ability source, Game game) {
|
||||||
if (super.canTarget(id, source, game)) {
|
Permanent ownPermanent = game.getPermanent(source.getFirstTarget());
|
||||||
Permanent target1 = game.getPermanent(source.getFirstTarget());
|
Permanent possiblePermanent = game.getPermanent(id);
|
||||||
Permanent opponentPermanent = game.getPermanent(id);
|
if (ownPermanent == null || possiblePermanent == null) {
|
||||||
if (target1 != null && opponentPermanent != null) {
|
return false;
|
||||||
return target1.shareTypes(opponentPermanent, game);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return super.canTarget(id, source, game) && ownPermanent.shareTypes(possiblePermanent, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
if (firstTarget != null) {
|
|
||||||
MageObject targetSource = game.getObject(source);
|
Permanent ownPermanent = game.getPermanent(source.getFirstTarget());
|
||||||
if (targetSource != null) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
if (ownPermanent == null) {
|
||||||
if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
// playable or first target not yet selected
|
||||||
if (permanent.shareTypes(firstTarget, game)) {
|
// use all
|
||||||
possibleTargets.add(permanent.getId());
|
possibleTargets.add(permanent.getId());
|
||||||
}
|
} else {
|
||||||
}
|
// real
|
||||||
|
// filter by shared type
|
||||||
|
if (permanent.shareTypes(ownPermanent, game)) {
|
||||||
|
possibleTargets.add(permanent.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return possibleTargets;
|
possibleTargets.removeIf(id -> ownPermanent != null && ownPermanent.getId().equals(id));
|
||||||
|
|
||||||
|
return keepValidPossibleTargets(possibleTargets, sourceControllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
||||||
firstTarget = game.getPermanent(source.getFirstTarget());
|
// AI hint with better outcome
|
||||||
return super.chooseTarget(Outcome.Damage, playerId, source, game);
|
return super.chooseTarget(Outcome.GainControl, playerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -82,7 +82,7 @@ class GhastlordOfFugueEffect extends OneShotEffect {
|
||||||
TargetCard target = new TargetCard(Zone.HAND, new FilterCard());
|
TargetCard target = new TargetCard(Zone.HAND, new FilterCard());
|
||||||
target.withNotTarget(true);
|
target.withNotTarget(true);
|
||||||
Card chosenCard = null;
|
Card chosenCard = null;
|
||||||
if (controller.chooseTarget(Outcome.Benefit, targetPlayer.getHand(), target, source, game)) {
|
if (controller.choose(Outcome.Benefit, targetPlayer.getHand(), target, source, game)) {
|
||||||
chosenCard = game.getCard(target.getFirstTarget());
|
chosenCard = game.getCard(target.getFirstTarget());
|
||||||
}
|
}
|
||||||
if (chosenCard != null) {
|
if (chosenCard != null) {
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
package mage.cards.g;
|
package mage.cards.g;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleActivatedAbility;
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
import mage.abilities.costs.common.TapSourceCost;
|
import mage.abilities.costs.common.TapSourceCost;
|
||||||
|
|
@ -12,15 +11,11 @@ import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.TargetController;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.game.Game;
|
import mage.filter.predicate.other.DamagedPlayerThisTurnPredicate;
|
||||||
import mage.game.permanent.Permanent;
|
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.watchers.common.PlayerDamagedBySourceWatcher;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -28,6 +23,12 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
public final class GiltspireAvenger extends CardImpl {
|
public final class GiltspireAvenger extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that dealt damage to you this turn");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(new DamagedPlayerThisTurnPredicate(TargetController.YOU));
|
||||||
|
}
|
||||||
|
|
||||||
public GiltspireAvenger(UUID ownerId, CardSetInfo setInfo) {
|
public GiltspireAvenger(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}{U}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{W}{U}");
|
||||||
this.subtype.add(SubType.HUMAN);
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
|
@ -41,7 +42,7 @@ public final class GiltspireAvenger extends CardImpl {
|
||||||
|
|
||||||
// {T}: Destroy target creature that dealt damage to you this turn.
|
// {T}: Destroy target creature that dealt damage to you this turn.
|
||||||
Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new TapSourceCost());
|
Ability ability = new SimpleActivatedAbility(new DestroyTargetEffect(), new TapSourceCost());
|
||||||
ability.addTarget(new GiltspireAvengerTarget());
|
ability.addTarget(new TargetPermanent(filter));
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -55,64 +56,3 @@ public final class GiltspireAvenger extends CardImpl {
|
||||||
return new GiltspireAvenger(this);
|
return new GiltspireAvenger(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class GiltspireAvengerTarget extends TargetPermanent {
|
|
||||||
|
|
||||||
public GiltspireAvengerTarget() {
|
|
||||||
super(1, 1, StaticFilters.FILTER_PERMANENT_CREATURE, false);
|
|
||||||
targetName = "creature that dealt damage to you this turn";
|
|
||||||
}
|
|
||||||
|
|
||||||
private GiltspireAvengerTarget(final GiltspireAvengerTarget target) {
|
|
||||||
super(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
|
||||||
PlayerDamagedBySourceWatcher watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, source.getControllerId());
|
|
||||||
if (watcher != null && watcher.hasSourceDoneDamage(id, game)) {
|
|
||||||
return super.canTarget(id, source, game);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
Set<UUID> availablePossibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
|
||||||
PlayerDamagedBySourceWatcher watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, sourceControllerId);
|
|
||||||
for (UUID targetId : availablePossibleTargets) {
|
|
||||||
Permanent permanent = game.getPermanent(targetId);
|
|
||||||
if (permanent != null && watcher != null && watcher.hasSourceDoneDamage(targetId, game)) {
|
|
||||||
possibleTargets.add(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
int remainingTargets = this.minNumberOfTargets - targets.size();
|
|
||||||
if (remainingTargets == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int count = 0;
|
|
||||||
MageObject targetSource = game.getObject(source);
|
|
||||||
PlayerDamagedBySourceWatcher watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, sourceControllerId);
|
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
|
||||||
if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)
|
|
||||||
&& watcher != null && watcher.hasSourceDoneDamage(permanent.getId(), game)) {
|
|
||||||
count++;
|
|
||||||
if (count >= remainingTargets) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public GiltspireAvengerTarget copy() {
|
|
||||||
return new GiltspireAvengerTarget(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,7 @@
|
||||||
package mage.cards.g;
|
package mage.cards.g;
|
||||||
|
|
||||||
import mage.MageObject;
|
|
||||||
import mage.MageObjectReference;
|
import mage.MageObjectReference;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.condition.common.SourceHasCounterCondition;
|
import mage.abilities.condition.common.SourceHasCounterCondition;
|
||||||
import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect;
|
import mage.abilities.decorator.ConditionalContinuousRuleModifyingEffect;
|
||||||
|
|
@ -11,11 +9,14 @@ import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect;
|
import mage.abilities.effects.common.DontUntapInControllersUntapStepSourceEffect;
|
||||||
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
||||||
import mage.abilities.effects.common.counter.RemoveCounterSourceEffect;
|
import mage.abilities.effects.common.counter.RemoveCounterSourceEffect;
|
||||||
|
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
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.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import mage.filter.StaticFilters;
|
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
|
@ -59,8 +60,6 @@ public final class GlyphOfDelusion extends CardImpl {
|
||||||
|
|
||||||
class GlyphOfDelusionSecondTarget extends TargetPermanent {
|
class GlyphOfDelusionSecondTarget extends TargetPermanent {
|
||||||
|
|
||||||
private Permanent firstTarget = null;
|
|
||||||
|
|
||||||
public GlyphOfDelusionSecondTarget() {
|
public GlyphOfDelusionSecondTarget() {
|
||||||
super();
|
super();
|
||||||
withTargetName("target creature that target Wall blocked this turn");
|
withTargetName("target creature that target Wall blocked this turn");
|
||||||
|
|
@ -68,33 +67,39 @@ class GlyphOfDelusionSecondTarget extends TargetPermanent {
|
||||||
|
|
||||||
private GlyphOfDelusionSecondTarget(final GlyphOfDelusionSecondTarget target) {
|
private GlyphOfDelusionSecondTarget(final GlyphOfDelusionSecondTarget target) {
|
||||||
super(target);
|
super(target);
|
||||||
this.firstTarget = target.firstTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
if (firstTarget != null) {
|
|
||||||
BlockedAttackerWatcher watcher = game.getState().getWatcher(BlockedAttackerWatcher.class);
|
BlockedAttackerWatcher watcher = game.getState().getWatcher(BlockedAttackerWatcher.class);
|
||||||
if (watcher != null) {
|
if (watcher == null) {
|
||||||
MageObject targetSource = game.getObject(source);
|
return possibleTargets;
|
||||||
if (targetSource != null) {
|
}
|
||||||
for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, sourceControllerId, source, game)) {
|
|
||||||
if (!targets.containsKey(creature.getId()) && creature.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
Permanent targetWall = game.getPermanent(source.getFirstTarget());
|
||||||
if (watcher.creatureHasBlockedAttacker(new MageObjectReference(creature, game), new MageObjectReference(firstTarget, game))) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
||||||
possibleTargets.add(creature.getId());
|
if (targetWall == null) {
|
||||||
}
|
// playable or first target not yet selected
|
||||||
}
|
// use all
|
||||||
}
|
possibleTargets.add(permanent.getId());
|
||||||
|
} else {
|
||||||
|
// real
|
||||||
|
// filter by blocked
|
||||||
|
if (watcher.creatureHasBlockedAttacker(new MageObjectReference(permanent, game), new MageObjectReference(targetWall, game))) {
|
||||||
|
possibleTargets.add(permanent.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return possibleTargets;
|
possibleTargets.removeIf(id -> targetWall != null && targetWall.getId().equals(id));
|
||||||
|
|
||||||
|
return keepValidPossibleTargets(possibleTargets, sourceControllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
||||||
firstTarget = game.getPermanent(source.getFirstTarget());
|
// AI hint with better outcome
|
||||||
return super.chooseTarget(Outcome.Tap, playerId, source, game);
|
return super.chooseTarget(Outcome.Tap, playerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -133,8 +138,7 @@ class GlyphOfDelusionEffect extends OneShotEffect {
|
||||||
effect.setTargetPointer(new FixedTarget(targetPermanent.getId(), game));
|
effect.setTargetPointer(new FixedTarget(targetPermanent.getId(), game));
|
||||||
game.addEffect(effect, source);
|
game.addEffect(effect, source);
|
||||||
|
|
||||||
BeginningOfUpkeepTriggeredAbility ability2 = new BeginningOfUpkeepTriggeredAbility(new RemoveCounterSourceEffect(CounterType.GLYPH.createInstance())
|
BeginningOfUpkeepTriggeredAbility ability2 = new BeginningOfUpkeepTriggeredAbility(new RemoveCounterSourceEffect(CounterType.GLYPH.createInstance()));
|
||||||
);
|
|
||||||
GainAbilityTargetEffect effect2 = new GainAbilityTargetEffect(ability2, Duration.Custom);
|
GainAbilityTargetEffect effect2 = new GainAbilityTargetEffect(ability2, Duration.Custom);
|
||||||
effect2.setTargetPointer(new FixedTarget(targetPermanent.getId(), game));
|
effect2.setTargetPointer(new FixedTarget(targetPermanent.getId(), game));
|
||||||
game.addEffect(effect2, source);
|
game.addEffect(effect2, source);
|
||||||
|
|
|
||||||
|
|
@ -81,8 +81,8 @@ class GoblinArtisansTarget extends TargetSpell {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (!super.canTarget(controllerId, id, source, game)) {
|
if (!super.canTarget(playerId, id, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
MageObjectReference sourceRef = new MageObjectReference(source.getSourceObject(game), game);
|
MageObjectReference sourceRef = new MageObjectReference(source.getSourceObject(game), game);
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,8 @@ class GracefulTakedownTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (!super.canTarget(controllerId, id, source, game)) {
|
if (!super.canTarget(playerId, id, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Permanent permanent = game.getPermanent(id);
|
Permanent permanent = game.getPermanent(id);
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ class GrimeGorgerTarget extends TargetCardInGraveyard {
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, null, game));
|
possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, source, game));
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -92,7 +92,7 @@ class GurzigostCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return this.getTargets().canChoose(controllerId, source, game);
|
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
package mage.cards.i;
|
package mage.cards.i;
|
||||||
|
|
||||||
import mage.MageItem;
|
|
||||||
import mage.ObjectColor;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleActivatedAbility;
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
import mage.abilities.costs.common.RevealTargetFromHandCost;
|
import mage.abilities.costs.common.RevealTargetFromHandCost;
|
||||||
|
|
@ -19,9 +17,9 @@ import mage.game.Game;
|
||||||
import mage.target.common.TargetCardInHand;
|
import mage.target.common.TargetCardInHand;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author TheElk801
|
* @author TheElk801
|
||||||
|
|
@ -67,51 +65,8 @@ class IlluminatedFolioTarget extends TargetCardInHand {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canChoose(sourceControllerId, source, game)
|
if (!super.canTarget(playerId, id, source, game)) {
|
||||||
&& !possibleTargets(sourceControllerId, source, game).isEmpty();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
|
||||||
if (this.getTargets().size() == 1) {
|
|
||||||
Card card = game.getCard(this.getTargets().get(0));
|
|
||||||
possibleTargets.removeIf(
|
|
||||||
uuid -> !game
|
|
||||||
.getCard(uuid)
|
|
||||||
.getColor(game)
|
|
||||||
.shares(card.getColor(game))
|
|
||||||
);
|
|
||||||
return possibleTargets;
|
|
||||||
}
|
|
||||||
if (possibleTargets.size() < 2) {
|
|
||||||
possibleTargets.clear();
|
|
||||||
return possibleTargets;
|
|
||||||
}
|
|
||||||
Set<Card> allTargets = possibleTargets
|
|
||||||
.stream()
|
|
||||||
.map(game::getCard)
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
possibleTargets.clear();
|
|
||||||
for (ObjectColor color : ObjectColor.getAllColors()) {
|
|
||||||
Set<Card> inColor = allTargets
|
|
||||||
.stream()
|
|
||||||
.filter(card -> card.getColor(game).shares(color))
|
|
||||||
.collect(Collectors.toSet());
|
|
||||||
if (inColor.size() > 1) {
|
|
||||||
inColor.stream().map(MageItem::getId).forEach(possibleTargets::add);
|
|
||||||
}
|
|
||||||
if (possibleTargets.size() == allTargets.size()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canTarget(UUID id, Game game) {
|
|
||||||
if (!super.canTarget(id, game)) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
List<UUID> targetList = this.getTargets();
|
List<UUID> targetList = this.getTargets();
|
||||||
|
|
@ -123,9 +78,17 @@ class IlluminatedFolioTarget extends TargetCardInHand {
|
||||||
&& targetList
|
&& targetList
|
||||||
.stream()
|
.stream()
|
||||||
.map(game::getCard)
|
.map(game::getCard)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
.anyMatch(c -> c.getColor(game).shares(card.getColor(game)));
|
.anyMatch(c -> c.getColor(game).shares(card.getColor(game)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
|
possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, source, game));
|
||||||
|
return possibleTargets;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public IlluminatedFolioTarget copy() {
|
public IlluminatedFolioTarget copy() {
|
||||||
return new IlluminatedFolioTarget(this);
|
return new IlluminatedFolioTarget(this);
|
||||||
|
|
|
||||||
|
|
@ -99,7 +99,7 @@ class ImpelledGiantCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return target.canChoose(controllerId, source, game);
|
return target.canChooseOrAlreadyChosen(controllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -79,9 +79,7 @@ class InvasionOfGobakhanEffect extends OneShotEffect {
|
||||||
if (controller == null || opponent == null || opponent.getHand().isEmpty()) {
|
if (controller == null || opponent == null || opponent.getHand().isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TargetCard target = new TargetCardInHand(
|
TargetCard target = new TargetCard(0, 1, Zone.HAND, StaticFilters.FILTER_CARD_A_NON_LAND);
|
||||||
0, 1, StaticFilters.FILTER_CARD_A_NON_LAND
|
|
||||||
);
|
|
||||||
controller.choose(outcome, opponent.getHand(), target, source, game);
|
controller.choose(outcome, opponent.getHand(), target, source, game);
|
||||||
Card card = opponent.getHand().get(target.getFirstTarget(), game);
|
Card card = opponent.getHand().get(target.getFirstTarget(), game);
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
|
|
|
||||||
|
|
@ -1,32 +1,27 @@
|
||||||
|
|
||||||
package mage.cards.i;
|
package mage.cards.i;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.keyword.TrampleAbility;
|
import mage.abilities.keyword.TrampleAbility;
|
||||||
import mage.cards.*;
|
import mage.cards.*;
|
||||||
import mage.constants.CardType;
|
import mage.constants.*;
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.constants.Outcome;
|
|
||||||
import mage.constants.SuperType;
|
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.filter.FilterCard;
|
import mage.filter.FilterCard;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
import mage.target.common.TargetCardInHand;
|
import mage.target.TargetCard;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public final class IwamoriOfTheOpenFist extends CardImpl {
|
public final class IwamoriOfTheOpenFist extends CardImpl {
|
||||||
|
|
||||||
public IwamoriOfTheOpenFist(UUID ownerId, CardSetInfo setInfo) {
|
public IwamoriOfTheOpenFist(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{G}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}");
|
||||||
this.supertype.add(SuperType.LEGENDARY);
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
this.subtype.add(SubType.HUMAN);
|
this.subtype.add(SubType.HUMAN);
|
||||||
this.subtype.add(SubType.MONK);
|
this.subtype.add(SubType.MONK);
|
||||||
|
|
@ -80,15 +75,11 @@ class IwamoriOfTheOpenFistEffect extends OneShotEffect {
|
||||||
Cards cards = new CardsImpl();
|
Cards cards = new CardsImpl();
|
||||||
for (UUID playerId : game.getOpponents(controller.getId())) {
|
for (UUID playerId : game.getOpponents(controller.getId())) {
|
||||||
Player opponent = game.getPlayer(playerId);
|
Player opponent = game.getPlayer(playerId);
|
||||||
Target target = new TargetCardInHand(filter);
|
Target target = new TargetCard(0, 1, Zone.HAND, filter).withChooseHint("put from hand to battlefield");
|
||||||
if (opponent != null && target.canChoose(opponent.getId(), source, game)) {
|
if (target.choose(Outcome.PutCreatureInPlay, opponent.getId(), source, game)) {
|
||||||
if (opponent.chooseUse(Outcome.PutCreatureInPlay, "Put a legendary creature card from your hand onto the battlefield?", source, game)) {
|
Card card = game.getCard(target.getFirstTarget());
|
||||||
if (target.chooseTarget(Outcome.PutCreatureInPlay, opponent.getId(), source, game)) {
|
if (card != null) {
|
||||||
Card card = game.getCard(target.getFirstTarget());
|
cards.add(card);
|
||||||
if (card != null) {
|
|
||||||
cards.add(card);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class JotunGruntCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return this.getTargets().canChoose(controllerId, source, game);
|
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,6 @@ import mage.filter.predicate.mageobject.NamePredicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.TargetCard;
|
import mage.target.TargetCard;
|
||||||
import mage.target.common.TargetCardInLibrary;
|
|
||||||
import mage.target.common.TargetCardInYourGraveyard;
|
import mage.target.common.TargetCardInYourGraveyard;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -62,28 +61,31 @@ class JourneyForTheElixirEffect extends OneShotEffect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player player = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
if (player == null) {
|
if (controller == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
TargetCardInLibrary targetCardInLibrary = new JourneyForTheElixirLibraryTarget();
|
|
||||||
player.searchLibrary(targetCardInLibrary, source, game);
|
// search your library and graveyard for 2 cards
|
||||||
Cards cards = new CardsImpl(targetCardInLibrary.getTargets());
|
Cards allCards = new CardsImpl();
|
||||||
TargetCard target = new JourneyForTheElixirGraveyardTarget(cards);
|
allCards.addAll(controller.getLibrary().getCardList());
|
||||||
player.choose(outcome, target, source, game);
|
allCards.addAll(controller.getGraveyard());
|
||||||
cards.addAll(target.getTargets());
|
TargetCard target = new JourneyForTheElixirTarget();
|
||||||
player.revealCards(source, cards, game);
|
if (controller.choose(Outcome.Benefit, allCards, target, source, game)) {
|
||||||
player.moveCards(cards, Zone.HAND, source, game);
|
Cards cards = new CardsImpl(target.getTargets());
|
||||||
player.shuffleLibrary(source, game);
|
controller.revealCards(source, cards, game);
|
||||||
return true;
|
controller.moveCards(cards, Zone.HAND, source, game);
|
||||||
|
controller.shuffleLibrary(source, game);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class JourneyForTheElixirLibraryTarget extends TargetCardInLibrary {
|
class JourneyForTheElixirTarget extends TargetCard {
|
||||||
|
|
||||||
private static final String name = "Jiang Yanggu";
|
private static final String name = "Jiang Yanggu";
|
||||||
private static final FilterCard filter
|
private static final FilterCard filter = new FilterCard("a basic land card and a card named Jiang Yanggu");
|
||||||
= new FilterCard("a basic land card and a card named Jiang Yanggu");
|
|
||||||
|
|
||||||
static {
|
static {
|
||||||
filter.add(Predicates.or(
|
filter.add(Predicates.or(
|
||||||
|
|
@ -95,17 +97,17 @@ class JourneyForTheElixirLibraryTarget extends TargetCardInLibrary {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
JourneyForTheElixirLibraryTarget() {
|
JourneyForTheElixirTarget() {
|
||||||
super(0, 2, filter);
|
super(2, 2, Zone.ALL, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private JourneyForTheElixirLibraryTarget(final JourneyForTheElixirLibraryTarget target) {
|
private JourneyForTheElixirTarget(final JourneyForTheElixirTarget target) {
|
||||||
super(target);
|
super(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public JourneyForTheElixirLibraryTarget copy() {
|
public JourneyForTheElixirTarget copy() {
|
||||||
return new JourneyForTheElixirLibraryTarget(this);
|
return new JourneyForTheElixirTarget(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -117,95 +119,35 @@ class JourneyForTheElixirLibraryTarget extends TargetCardInLibrary {
|
||||||
if (card == null) {
|
if (card == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (this.getTargets().isEmpty()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
Cards cards = new CardsImpl(this.getTargets());
|
Cards cards = new CardsImpl(this.getTargets());
|
||||||
if (card.isBasic(game)
|
boolean hasLand = cards
|
||||||
&& card.isLand(game)
|
|
||||||
&& cards
|
|
||||||
.getCards(game)
|
.getCards(game)
|
||||||
.stream()
|
.stream()
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.filter(c -> c.isBasic(game))
|
.filter(c -> c.isBasic(game))
|
||||||
.anyMatch(c -> c.isLand(game))) {
|
.anyMatch(c -> c.isLand(game));
|
||||||
return false;
|
boolean hasJiang = cards
|
||||||
}
|
|
||||||
if (name.equals(card.getName())
|
|
||||||
&& cards
|
|
||||||
.getCards(game)
|
.getCards(game)
|
||||||
.stream()
|
.stream()
|
||||||
.map(MageObject::getName)
|
.map(MageObject::getName)
|
||||||
.anyMatch(name::equals)) {
|
.anyMatch(name::equals);
|
||||||
return false;
|
|
||||||
|
if (!hasLand && card.isBasic(game) && card.isLand(game)) {
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class JourneyForTheElixirGraveyardTarget extends TargetCardInYourGraveyard {
|
if (!hasJiang && name.equals(card.getName())) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static final String name = "Jiang Yanggu";
|
return false;
|
||||||
private static final FilterCard filter
|
|
||||||
= new FilterCard("a basic land card and a card named Jiang Yanggu");
|
|
||||||
|
|
||||||
static {
|
|
||||||
filter.add(Predicates.or(
|
|
||||||
Predicates.and(
|
|
||||||
SuperType.BASIC.getPredicate(),
|
|
||||||
CardType.LAND.getPredicate()
|
|
||||||
),
|
|
||||||
new NamePredicate(name)
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Cards cards = new CardsImpl();
|
|
||||||
|
|
||||||
JourneyForTheElixirGraveyardTarget(Cards cards) {
|
|
||||||
super(0, Integer.MAX_VALUE, filter, true);
|
|
||||||
this.cards.addAll(cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
private JourneyForTheElixirGraveyardTarget(final JourneyForTheElixirGraveyardTarget target) {
|
|
||||||
super(target);
|
|
||||||
this.cards.addAll(target.cards);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public JourneyForTheElixirGraveyardTarget copy() {
|
|
||||||
return new JourneyForTheElixirGraveyardTarget(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
Cards alreadyTargeted = new CardsImpl(this.getTargets());
|
possibleTargets.removeIf(uuid -> !this.canTarget(sourceControllerId, uuid, source, game));
|
||||||
alreadyTargeted.addAll(cards);
|
|
||||||
boolean hasBasic = alreadyTargeted
|
|
||||||
.getCards(game)
|
|
||||||
.stream()
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.filter(c -> c.isLand(game))
|
|
||||||
.anyMatch(c -> c.isBasic(game));
|
|
||||||
possibleTargets.removeIf(uuid -> {
|
|
||||||
Card card = game.getCard(uuid);
|
|
||||||
return card != null
|
|
||||||
&& hasBasic
|
|
||||||
&& card.isLand(game)
|
|
||||||
&& card.isBasic(game);
|
|
||||||
});
|
|
||||||
boolean hasYanggu = alreadyTargeted
|
|
||||||
.getCards(game)
|
|
||||||
.stream()
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.map(MageObject::getName)
|
|
||||||
.anyMatch(name::equals);
|
|
||||||
possibleTargets.removeIf(uuid -> {
|
|
||||||
Card card = game.getCard(uuid);
|
|
||||||
return card != null
|
|
||||||
&& hasYanggu
|
|
||||||
&& name.equals(card.getName());
|
|
||||||
});
|
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -88,8 +88,8 @@ class KairiTheSwirlingSkyTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, 6, game);
|
this.getTargets(), id, MageObject::getManaValue, 6, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.k;
|
package mage.cards.k;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleActivatedAbility;
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
import mage.abilities.costs.common.TapSourceCost;
|
import mage.abilities.costs.common.TapSourceCost;
|
||||||
|
|
@ -15,10 +14,8 @@ import mage.filter.FilterOpponent;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.token.BeastToken4;
|
import mage.game.permanent.token.BeastToken4;
|
||||||
import mage.players.Player;
|
|
||||||
import mage.target.TargetPlayer;
|
import mage.target.TargetPlayer;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -35,7 +32,7 @@ public final class KeeperOfTheBeasts extends CardImpl {
|
||||||
this.power = new MageInt(1);
|
this.power = new MageInt(1);
|
||||||
this.toughness = new MageInt(2);
|
this.toughness = new MageInt(2);
|
||||||
|
|
||||||
// {G}, {tap}: Choose target opponent who controlled more creatures than you did as you activated this ability. Put a 2/2 green Beast creature token onto the battlefield.
|
// {G}, {T}: Choose target opponent who controlled more creatures than you did as you activated this ability. Put a 2/2 green Beast creature token onto the battlefield.
|
||||||
Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new BeastToken4()).setText("Choose target opponent who controlled more creatures than you did as you activated this ability. Create a 2/2 green Beast creature token."),
|
Ability ability = new SimpleActivatedAbility(new CreateTokenEffect(new BeastToken4()).setText("Choose target opponent who controlled more creatures than you did as you activated this ability. Create a 2/2 green Beast creature token."),
|
||||||
new ManaCostsImpl<>("{G}"));
|
new ManaCostsImpl<>("{G}"));
|
||||||
ability.addCost(new TapSourceCost());
|
ability.addCost(new TapSourceCost());
|
||||||
|
|
@ -65,42 +62,12 @@ class KeeperOfTheBeastsTarget extends TargetPlayer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> availablePossibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
int myCount = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, sourceControllerId, game);
|
||||||
int creaturesController = game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, sourceControllerId, game);
|
possibleTargets.removeIf(playerId -> game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, playerId, game) < myCount);
|
||||||
|
|
||||||
for (UUID targetId : availablePossibleTargets) {
|
|
||||||
if (game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, targetId, game) > creaturesController) {
|
|
||||||
possibleTargets.add(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
int count = 0;
|
|
||||||
MageObject targetSource = game.getObject(source);
|
|
||||||
Player controller = game.getPlayer(sourceControllerId);
|
|
||||||
if (controller != null && targetSource != null) {
|
|
||||||
for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
|
|
||||||
Player player = game.getPlayer(playerId);
|
|
||||||
if (player != null
|
|
||||||
&& game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, sourceControllerId, game)
|
|
||||||
< game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, playerId, game)
|
|
||||||
&& !player.hasLeft()
|
|
||||||
&& filter.match(player, sourceControllerId, source, game)
|
|
||||||
&& player.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
|
||||||
count++;
|
|
||||||
if (count >= this.minNumberOfTargets) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public KeeperOfTheBeastsTarget copy() {
|
public KeeperOfTheBeastsTarget copy() {
|
||||||
return new KeeperOfTheBeastsTarget(this);
|
return new KeeperOfTheBeastsTarget(this);
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
package mage.cards.k;
|
package mage.cards.k;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
import mage.ObjectColor;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleActivatedAbility;
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
import mage.abilities.costs.common.TapSourceCost;
|
import mage.abilities.costs.common.TapSourceCost;
|
||||||
|
|
@ -15,21 +12,23 @@ import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.filter.FilterPlayer;
|
import mage.filter.FilterPlayer;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.filter.predicate.ObjectSourcePlayer;
|
import mage.filter.predicate.ObjectSourcePlayer;
|
||||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||||
|
import mage.filter.predicate.Predicates;
|
||||||
|
import mage.filter.predicate.mageobject.ColorPredicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.game.stack.StackObject;
|
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.TargetPlayer;
|
import mage.target.TargetPlayer;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author spjspj
|
* @author spjspj
|
||||||
*/
|
*/
|
||||||
public final class KeeperOfTheDead extends CardImpl {
|
public final class KeeperOfTheDead extends CardImpl {
|
||||||
|
|
@ -95,48 +94,37 @@ class KeeperOfDeadPredicate implements ObjectSourcePlayerPredicate<Player> {
|
||||||
|
|
||||||
class KeeperOfTheDeadCreatureTarget extends TargetPermanent {
|
class KeeperOfTheDeadCreatureTarget extends TargetPermanent {
|
||||||
|
|
||||||
|
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonblack creature that player controls");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK)));
|
||||||
|
}
|
||||||
|
|
||||||
public KeeperOfTheDeadCreatureTarget() {
|
public KeeperOfTheDeadCreatureTarget() {
|
||||||
super(1, 1, new FilterCreaturePermanent("nonblack creature that player controls"), false);
|
super(1, 1, filter);
|
||||||
}
|
}
|
||||||
|
|
||||||
private KeeperOfTheDeadCreatureTarget(final KeeperOfTheDeadCreatureTarget target) {
|
private KeeperOfTheDeadCreatureTarget(final KeeperOfTheDeadCreatureTarget target) {
|
||||||
super(target);
|
super(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
|
||||||
UUID firstTarget = source.getFirstTarget();
|
|
||||||
Permanent permanent = game.getPermanent(id);
|
|
||||||
if (firstTarget != null && permanent != null && permanent.isControlledBy(firstTarget)) {
|
|
||||||
return super.canTarget(id, source, game);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> availablePossibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
|
||||||
MageObject object = game.getObject(source);
|
|
||||||
|
|
||||||
for (StackObject item : game.getState().getStack()) {
|
Player needPlayer = game.getPlayerOrPlaneswalkerController(source.getFirstTarget());
|
||||||
if (item.getId().equals(source.getSourceId())) {
|
if (needPlayer == null) {
|
||||||
object = item;
|
// playable or not selected - use any
|
||||||
}
|
} else {
|
||||||
if (item.getSourceId().equals(source.getSourceId())) {
|
// filter by controller
|
||||||
object = item;
|
possibleTargets.removeIf(id -> {
|
||||||
}
|
Permanent permanent = game.getPermanent(id);
|
||||||
|
return permanent == null
|
||||||
|
|| permanent.getId().equals(source.getFirstTarget())
|
||||||
|
|| !permanent.isControlledBy(needPlayer.getId());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object instanceof StackObject) {
|
|
||||||
UUID playerId = ((StackObject) object).getStackAbility().getFirstTarget();
|
|
||||||
for (UUID targetId : availablePossibleTargets) {
|
|
||||||
Permanent permanent = game.getPermanent(targetId);
|
|
||||||
if (permanent != null && StaticFilters.FILTER_PERMANENT_CREATURE_NON_BLACK.match(permanent, game) && permanent.isControlledBy(playerId)) {
|
|
||||||
possibleTargets.add(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,6 @@
|
||||||
|
|
||||||
package mage.cards.k;
|
package mage.cards.k;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleActivatedAbility;
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
import mage.abilities.costs.common.TapSourceCost;
|
import mage.abilities.costs.common.TapSourceCost;
|
||||||
|
|
@ -12,13 +10,11 @@ import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.filter.FilterOpponent;
|
import mage.filter.FilterOpponent;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.TargetPlayer;
|
import mage.target.TargetPlayer;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -67,43 +63,19 @@ class KeeperOfTheLightTarget extends TargetPlayer {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> availablePossibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
|
||||||
int lifeController = game.getPlayer(sourceControllerId).getLife();
|
|
||||||
|
|
||||||
for (UUID targetId : availablePossibleTargets) {
|
|
||||||
Player opponent = game.getPlayer(targetId);
|
|
||||||
if (opponent != null) {
|
|
||||||
int lifeOpponent = opponent.getLife();
|
|
||||||
if (lifeOpponent > lifeController) {
|
|
||||||
possibleTargets.add(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
int count = 0;
|
|
||||||
MageObject targetSource = game.getObject(source);
|
|
||||||
Player controller = game.getPlayer(sourceControllerId);
|
Player controller = game.getPlayer(sourceControllerId);
|
||||||
if (controller != null && targetSource != null) {
|
if (controller == null) {
|
||||||
for (UUID playerId : game.getState().getPlayersInRange(sourceControllerId, game)) {
|
return possibleTargets;
|
||||||
Player player = game.getPlayer(playerId);
|
|
||||||
if (player != null
|
|
||||||
&& controller.getLife() < player.getLife()
|
|
||||||
&& !player.hasLeft()
|
|
||||||
&& filter.match(player, sourceControllerId, source, game)
|
|
||||||
&& player.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
|
||||||
count++;
|
|
||||||
if (count >= this.minNumberOfTargets) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
possibleTargets.removeIf(playerId -> {
|
||||||
|
Player player = game.getPlayer(playerId);
|
||||||
|
return player == null || player.getLife() >= controller.getLife();
|
||||||
|
});
|
||||||
|
|
||||||
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class KeldonBattlewagonCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return target.canChoose(controllerId, source, game);
|
return target.canChooseOrAlreadyChosen(controllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -7,15 +7,14 @@ import mage.abilities.effects.common.continuous.BoostAllEffect;
|
||||||
import mage.abilities.keyword.EquipAbility;
|
import mage.abilities.keyword.EquipAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.*;
|
||||||
import mage.constants.Duration;
|
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.constants.SuperType;
|
|
||||||
import mage.filter.FilterPermanent;
|
import mage.filter.FilterPermanent;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.filter.common.FilterCreatureCard;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.target.TargetCard;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.stack.Spell;
|
import mage.game.stack.Spell;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
import mage.target.TargetCard;
|
||||||
import mage.target.common.TargetCardInGraveyard;
|
import mage.target.common.TargetCardInGraveyard;
|
||||||
import mage.target.common.TargetCardInHand;
|
import mage.target.common.TargetCardInHand;
|
||||||
import mage.target.common.TargetCardInLibrary;
|
import mage.target.common.TargetCardInLibrary;
|
||||||
|
|
@ -107,9 +108,9 @@ class KotoseTheSilentSpiderEffect extends OneShotEffect {
|
||||||
cards.addAll(targetCardInGraveyard.getTargets());
|
cards.addAll(targetCardInGraveyard.getTargets());
|
||||||
|
|
||||||
filter.setMessage("cards named " + card.getName() + " from " + opponent.getName() + "'s hand");
|
filter.setMessage("cards named " + card.getName() + " from " + opponent.getName() + "'s hand");
|
||||||
TargetCardInHand targetCardInHand = new TargetCardInHand(0, Integer.MAX_VALUE, filter);
|
TargetCard targetCard = new TargetCard(0, Integer.MAX_VALUE, Zone.HAND, filter);
|
||||||
controller.choose(outcome, opponent.getHand(), targetCardInHand, source, game);
|
controller.choose(outcome, opponent.getHand(), targetCard, source, game);
|
||||||
cards.addAll(targetCardInHand.getTargets());
|
cards.addAll(targetCard.getTargets());
|
||||||
|
|
||||||
filter.setMessage("cards named " + card.getName() + " from " + opponent.getName() + "'s library");
|
filter.setMessage("cards named " + card.getName() + " from " + opponent.getName() + "'s library");
|
||||||
TargetCardInLibrary target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter);
|
TargetCardInLibrary target = new TargetCardInLibrary(0, Integer.MAX_VALUE, filter);
|
||||||
|
|
|
||||||
|
|
@ -130,8 +130,8 @@ class LagrellaTheMagpieTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (!super.canTarget(controllerId, id, source, game)) {
|
if (!super.canTarget(playerId, id, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Permanent creature = game.getPermanent(id);
|
Permanent creature = game.getPermanent(id);
|
||||||
|
|
|
||||||
|
|
@ -69,7 +69,7 @@ class LethalSchemeEffect extends OneShotEffect {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
HashSet<MageObjectReference> convokingCreatures = CardUtil.getSourceCostsTag(game, source,
|
HashSet<MageObjectReference> convokingCreatures = CardUtil.getSourceCostsTag(game, source,
|
||||||
ConvokeAbility.convokingCreaturesKey, new HashSet<>(0));
|
ConvokeAbility.convokingCreaturesKey, new HashSet<>());
|
||||||
Set<AbstractMap.SimpleEntry<UUID, Permanent>> playerPermanentsPairs =
|
Set<AbstractMap.SimpleEntry<UUID, Permanent>> playerPermanentsPairs =
|
||||||
convokingCreatures
|
convokingCreatures
|
||||||
.stream()
|
.stream()
|
||||||
|
|
|
||||||
|
|
@ -103,8 +103,8 @@ class LivelyDirgeTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, 4, game);
|
this.getTargets(), id, MageObject::getManaValue, 4, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ class LobotomyEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExile
|
||||||
TargetCard target = new TargetCard(Zone.HAND, filter);
|
TargetCard target = new TargetCard(Zone.HAND, filter);
|
||||||
target.withNotTarget(true);
|
target.withNotTarget(true);
|
||||||
Card chosenCard = null;
|
Card chosenCard = null;
|
||||||
if (controller.chooseTarget(Outcome.Benefit, targetPlayer.getHand(), target, source, game)) {
|
if (controller.choose(Outcome.Benefit, targetPlayer.getHand(), target, source, game)) {
|
||||||
chosenCard = game.getCard(target.getFirstTarget());
|
chosenCard = game.getCard(target.getFirstTarget());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,6 @@ import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author weirddan455
|
* @author weirddan455
|
||||||
*/
|
*/
|
||||||
public final class LongRest extends CardImpl {
|
public final class LongRest extends CardImpl {
|
||||||
|
|
@ -67,19 +66,22 @@ class LongRestTarget extends TargetCardInYourGraveyard {
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
Set<Integer> manaValues = new HashSet<>();
|
|
||||||
|
Set<Integer> usedManaValues = new HashSet<>();
|
||||||
for (UUID targetId : this.getTargets()) {
|
for (UUID targetId : this.getTargets()) {
|
||||||
Card card = game.getCard(targetId);
|
Card card = game.getCard(targetId);
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
manaValues.add(card.getManaValue());
|
usedManaValues.add(card.getManaValue());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (UUID possibleTargetId : super.possibleTargets(sourceControllerId, source, game)) {
|
for (UUID possibleTargetId : super.possibleTargets(sourceControllerId, source, game)) {
|
||||||
Card card = game.getCard(possibleTargetId);
|
Card card = game.getCard(possibleTargetId);
|
||||||
if (card != null && !manaValues.contains(card.getManaValue())) {
|
if (card != null && !usedManaValues.contains(card.getManaValue())) {
|
||||||
possibleTargets.add(possibleTargetId);
|
possibleTargets.add(possibleTargetId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,8 +56,8 @@ class MarchFromTheTombTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, 8, game);
|
this.getTargets(), id, MageObject::getManaValue, 8, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -86,8 +86,8 @@ class ModifyMemoryTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (!super.canTarget(controllerId, id, source, game)) {
|
if (!super.canTarget(playerId, id, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Permanent creature = game.getPermanent(id);
|
Permanent creature = game.getPermanent(id);
|
||||||
|
|
|
||||||
|
|
@ -112,8 +112,8 @@ class MoorlandRescuerTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, m -> m.getPower().getValue(), xValue, game);
|
this.getTargets(), id, m -> m.getPower().getValue(), xValue, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package mage.cards.m;
|
package mage.cards.m;
|
||||||
|
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
|
@ -9,18 +8,16 @@ import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.filter.predicate.Predicates;
|
|
||||||
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
|
||||||
import mage.filter.predicate.permanent.PermanentIdPredicate;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* TODO: combine with BreakingOfTheFellowship
|
||||||
|
*
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public final class Mutiny extends CardImpl {
|
public final class Mutiny extends CardImpl {
|
||||||
|
|
@ -30,7 +27,8 @@ public final class Mutiny extends CardImpl {
|
||||||
|
|
||||||
// Target creature an opponent controls deals damage equal to its power to another target creature that player controls.
|
// Target creature an opponent controls deals damage equal to its power to another target creature that player controls.
|
||||||
this.getSpellAbility().addEffect(new MutinyEffect());
|
this.getSpellAbility().addEffect(new MutinyEffect());
|
||||||
this.getSpellAbility().addTarget(new MutinyFirstTarget(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
|
this.getSpellAbility().addTarget(new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE));
|
||||||
|
this.getSpellAbility().addTarget(new MutinySecondTarget());
|
||||||
this.getSpellAbility().addTarget(new TargetPermanent(new FilterCreaturePermanent("another target creature that player controls")));
|
this.getSpellAbility().addTarget(new TargetPermanent(new FilterCreaturePermanent("another target creature that player controls")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -72,74 +70,45 @@ class MutinyEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MutinyFirstTarget extends TargetPermanent {
|
class MutinySecondTarget extends TargetPermanent {
|
||||||
|
|
||||||
public MutinyFirstTarget(FilterCreaturePermanent filter) {
|
public MutinySecondTarget() {
|
||||||
super(1, 1, filter, false);
|
super(StaticFilters.FILTER_OPPONENTS_PERMANENT_CREATURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private MutinyFirstTarget(final MutinyFirstTarget target) {
|
private MutinySecondTarget(final MutinySecondTarget target) {
|
||||||
super(target);
|
super(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addTarget(UUID id, Ability source, Game game, boolean skipEvent) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
super.addTarget(id, source, game, skipEvent);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
// Update the second target
|
|
||||||
UUID firstController = game.getControllerId(id);
|
|
||||||
if (firstController != null && source.getTargets().size() > 1) {
|
|
||||||
Player controllingPlayer = game.getPlayer(firstController);
|
|
||||||
TargetCreaturePermanent targetCreaturePermanent = (TargetCreaturePermanent) source.getTargets().get(1);
|
|
||||||
// Set a new filter to the second target with the needed restrictions
|
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature that player " + controllingPlayer.getName() + " controls");
|
|
||||||
filter.add(new ControllerIdPredicate(firstController));
|
|
||||||
filter.add(Predicates.not(new PermanentIdPredicate(id)));
|
|
||||||
targetCreaturePermanent.replaceFilter(filter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
Permanent firstTarget = game.getPermanent(source.getFirstTarget());
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
if (firstTarget == null) {
|
||||||
if (super.canTarget(controllerId, id, source, game)) {
|
// playable or first target not yet selected
|
||||||
// can only target, if the controller has at least two targetable creatures
|
// use all
|
||||||
UUID controllingPlayerId = game.getControllerId(id);
|
if (possibleTargets.size() == 1) {
|
||||||
int possibleTargets = 0;
|
// workaround to make 1 target invalid
|
||||||
MageObject sourceObject = game.getObject(source.getId());
|
possibleTargets.clear();
|
||||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, controllingPlayerId, game)) {
|
|
||||||
if (permanent.canBeTargetedBy(sourceObject, controllerId, source, game)) {
|
|
||||||
possibleTargets++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return possibleTargets > 1;
|
} else {
|
||||||
|
// real
|
||||||
|
// filter by same player
|
||||||
|
possibleTargets.removeIf(id -> {
|
||||||
|
Permanent permanent = game.getPermanent(id);
|
||||||
|
return permanent == null || !permanent.isControlledBy(firstTarget.getControllerId());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return false;
|
possibleTargets.removeIf(id -> firstTarget != null && firstTarget.getId().equals(id));
|
||||||
|
|
||||||
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
public MutinySecondTarget copy() {
|
||||||
if (super.canChoose(sourceControllerId, source, game)) {
|
return new MutinySecondTarget(this);
|
||||||
UUID controllingPlayerId = game.getControllerId(source.getSourceId());
|
|
||||||
for (UUID playerId : game.getOpponents(controllingPlayerId)) {
|
|
||||||
int possibleTargets = 0;
|
|
||||||
MageObject sourceObject = game.getObject(source);
|
|
||||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, playerId, game)) {
|
|
||||||
if (permanent.canBeTargetedBy(sourceObject, controllingPlayerId, source, game)) {
|
|
||||||
possibleTargets++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (possibleTargets > 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public MutinyFirstTarget copy() {
|
|
||||||
return new MutinyFirstTarget(this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -22,10 +22,7 @@ import mage.target.common.TargetCardInYourGraveyard;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.Collection;
|
import java.util.*;
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -113,7 +110,10 @@ class NethergoyfTarget extends TargetCardInYourGraveyard {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Check that exiling all the possible cards would have >= 4 different card types
|
// Check that exiling all the possible cards would have >= 4 different card types
|
||||||
return metCondition(this.possibleTargets(sourceControllerId, source, game), game);
|
Set<UUID> idsToCheck = new HashSet<>();
|
||||||
|
idsToCheck.addAll(this.getTargets());
|
||||||
|
idsToCheck.addAll(this.possibleTargets(sourceControllerId, source, game));
|
||||||
|
return metCondition(idsToCheck, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Set<CardType> typesAmongSelection(Collection<UUID> cardsIds, Game game) {
|
private static Set<CardType> typesAmongSelection(Collection<UUID> cardsIds, Game game) {
|
||||||
|
|
|
||||||
|
|
@ -83,8 +83,8 @@ class NethroiApexOfDeathTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, m -> m.getPower().getValue(), 10, game);
|
this.getTargets(), id, m -> m.getPower().getValue(), 10, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,7 @@ class NicolBolasDragonGodPlusOneEffect extends OneShotEffect {
|
||||||
TargetPermanent targetPermanent = new TargetControlledPermanent();
|
TargetPermanent targetPermanent = new TargetControlledPermanent();
|
||||||
targetPermanent.withNotTarget(true);
|
targetPermanent.withNotTarget(true);
|
||||||
targetPermanent.setTargetController(opponentId);
|
targetPermanent.setTargetController(opponentId);
|
||||||
if (!targetPermanent.possibleTargets(opponentId, game).isEmpty()) {
|
if (!targetPermanent.possibleTargets(opponentId, source, game).isEmpty()) {
|
||||||
possibleTargetTypes.add(targetPermanent);
|
possibleTargetTypes.add(targetPermanent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,7 @@ class NivmagusElementalCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return this.getTargets().canChoose(controllerId, source, game);
|
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package mage.cards.n;
|
package mage.cards.n;
|
||||||
|
|
||||||
|
import mage.MageObjectReference;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.common.FightTargetsEffect;
|
import mage.abilities.effects.common.FightTargetsEffect;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
|
|
@ -15,7 +16,9 @@ import mage.game.permanent.Permanent;
|
||||||
import mage.game.stack.Spell;
|
import mage.game.stack.Spell;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.common.TargetControlledCreaturePermanent;
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
|
import mage.watchers.common.BlockedAttackerWatcher;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -54,50 +57,27 @@ class TargetCreatureWithLessPowerPermanent extends TargetPermanent {
|
||||||
super(target);
|
super(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
int maxPower = Integer.MIN_VALUE; // get the most powerful controlled creature that can be targeted
|
|
||||||
Card sourceCard = game.getCard(source.getSourceId());
|
|
||||||
if (sourceCard == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURES, sourceControllerId, game)) {
|
|
||||||
if (permanent.getPower().getValue() > maxPower && permanent.canBeTargetedBy(sourceCard, sourceControllerId, source, game)) {
|
|
||||||
maxPower = permanent.getPower().getValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// now check, if another creature has less power and can be targeted
|
|
||||||
FilterCreaturePermanent checkFilter = new FilterCreaturePermanent();
|
|
||||||
checkFilter.add(new PowerPredicate(ComparisonType.FEWER_THAN, maxPower));
|
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(checkFilter, sourceControllerId, source, game)) {
|
|
||||||
if (permanent.canBeTargetedBy(sourceCard, sourceControllerId, source, game)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Spell spell = game.getStack().getSpell(source.getSourceId());
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
if (spell != null) {
|
|
||||||
Permanent firstTarget = getPermanentFromFirstTarget(spell.getSpellAbility(), game);
|
Permanent firstPermanent = game.getPermanent(source.getFirstTarget());
|
||||||
if (firstTarget != null) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
||||||
int power = firstTarget.getPower().getValue();
|
if (firstPermanent == null) {
|
||||||
// overwrite the filter with the power predicate
|
// playable or first target not yet selected
|
||||||
filter = new FilterCreaturePermanent("creature with power less than " + power);
|
// use all
|
||||||
filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, power));
|
possibleTargets.add(permanent.getId());
|
||||||
|
} else {
|
||||||
|
// real
|
||||||
|
// filter by power
|
||||||
|
if (firstPermanent.getPower().getValue() > permanent.getPower().getValue()) {
|
||||||
|
possibleTargets.add(permanent.getId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.possibleTargets(sourceControllerId, source, game);
|
possibleTargets.removeIf(id -> firstPermanent != null && firstPermanent.getId().equals(id));
|
||||||
}
|
|
||||||
|
|
||||||
private Permanent getPermanentFromFirstTarget(Ability source, Game game) {
|
return keepValidPossibleTargets(possibleTargets, sourceControllerId, source, game);
|
||||||
Permanent firstTarget = null;
|
|
||||||
if (source.getTargets().size() == 2) {
|
|
||||||
firstTarget = game.getPermanent(source.getTargets().get(0).getFirstTarget());
|
|
||||||
}
|
|
||||||
return firstTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,8 @@ public final class OKagachiVengefulKami extends CardImpl {
|
||||||
ability.addTarget(new TargetPermanent(filter));
|
ability.addTarget(new TargetPermanent(filter));
|
||||||
ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster());
|
ability.setTargetAdjuster(new ThatPlayerControlsTargetAdjuster());
|
||||||
ability.withInterveningIf(KagachiVengefulKamiCondition.instance);
|
ability.withInterveningIf(KagachiVengefulKamiCondition.instance);
|
||||||
this.addAbility(ability);
|
|
||||||
|
this.addAbility(ability, new OKagachiVengefulKamiWatcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
private OKagachiVengefulKami(final OKagachiVengefulKami card) {
|
private OKagachiVengefulKami(final OKagachiVengefulKami card) {
|
||||||
|
|
|
||||||
|
|
@ -9,12 +9,12 @@ import mage.abilities.keyword.EquipAbility;
|
||||||
import mage.abilities.keyword.TrampleAbility;
|
import mage.abilities.keyword.TrampleAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.AttachmentType;
|
import mage.constants.*;
|
||||||
import mage.constants.CardType;
|
|
||||||
import mage.constants.ComparisonType;
|
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||||
|
import mage.filter.common.FilterCreatureCard;
|
||||||
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.filter.predicate.mageobject.PowerPredicate;
|
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||||
|
import mage.target.TargetCard;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -24,7 +24,7 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
public final class ONaginata extends CardImpl {
|
public final class ONaginata extends CardImpl {
|
||||||
|
|
||||||
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature with power 3 or greater");
|
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature with power 3 or greater");
|
||||||
|
|
||||||
static {
|
static {
|
||||||
filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 2));
|
filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 2));
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.Zone;
|
||||||
import mage.filter.FilterCard;
|
import mage.filter.FilterCard;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
@ -81,7 +82,7 @@ class OildeepGearhulkEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
|
|
||||||
controller.lookAtCards(targetPlayer.getName() + " Hand", targetPlayer.getHand(), game);
|
controller.lookAtCards(targetPlayer.getName() + " Hand", targetPlayer.getHand(), game);
|
||||||
TargetCard chosenCard = new TargetCardInHand(0, 1, new FilterCard("card to discard"));
|
TargetCard chosenCard = new TargetCard(0, 1, Zone.HAND, new FilterCard("card to discard"));
|
||||||
if (!controller.choose(Outcome.Discard, targetPlayer.getHand(), chosenCard, source, game)) {
|
if (!controller.choose(Outcome.Discard, targetPlayer.getHand(), chosenCard, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -119,8 +119,8 @@ class OrcusPrinceOfUndeathTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, xValue, game);
|
this.getTargets(), id, MageObject::getManaValue, xValue, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -101,8 +101,8 @@ class PairODiceLostTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, xValue, game);
|
this.getTargets(), id, MageObject::getManaValue, xValue, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ class PatchUpTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, 3, game);
|
this.getTargets(), id, MageObject::getManaValue, 3, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -105,7 +105,7 @@ class PhenomenonInvestigatorsReturnCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return this.getTargets().canChoose(controllerId, source, game);
|
return canChooseOrAlreadyChosen(ability, source, controllerId, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -94,8 +94,8 @@ class PhoenixWardenOfFireTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, 6, game
|
this.getTargets(), id, MageObject::getManaValue, 6, game
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@ class PrimordialMistCost extends CostImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
public boolean canPay(Ability ability, Ability source, UUID controllerId, Game game) {
|
||||||
return target.canChoose(controllerId, source, game);
|
return target.canChooseOrAlreadyChosen(controllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -64,8 +64,8 @@ class ProteanHulkTarget extends TargetCardInLibrary {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, 6, game);
|
this.getTargets(), id, MageObject::getManaValue, 6, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,36 +1,36 @@
|
||||||
package mage.cards.p;
|
package mage.cards.p;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.TriggeredAbilityImpl;
|
import mage.abilities.TriggeredAbilityImpl;
|
||||||
import mage.abilities.effects.common.ExileTargetEffect;
|
import mage.abilities.effects.common.ExileTargetEffect;
|
||||||
import mage.abilities.keyword.MonstrosityAbility;
|
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.abilities.keyword.FlyingAbility;
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
|
import mage.abilities.keyword.MonstrosityAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.common.FilterArtifactOrEnchantmentPermanent;
|
import mage.filter.common.FilterArtifactOrEnchantmentPermanent;
|
||||||
|
import mage.game.Controllable;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
|
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Jmlundeen
|
* @author Jmlundeen
|
||||||
*/
|
*/
|
||||||
public final class ProtectorOfTheWastes extends CardImpl {
|
public final class ProtectorOfTheWastes extends CardImpl {
|
||||||
|
|
||||||
public ProtectorOfTheWastes(UUID ownerId, CardSetInfo setInfo) {
|
public ProtectorOfTheWastes(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}");
|
||||||
|
|
||||||
this.subtype.add(SubType.DRAGON);
|
this.subtype.add(SubType.DRAGON);
|
||||||
this.power = new MageInt(5);
|
this.power = new MageInt(5);
|
||||||
this.toughness = new MageInt(5);
|
this.toughness = new MageInt(5);
|
||||||
|
|
@ -76,36 +76,19 @@ class ProtectorOfTheWastesTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
if (!super.canTarget(id, source, game)) {
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
return false;
|
|
||||||
}
|
Set<UUID> usedControllers = this.getTargets().stream()
|
||||||
Permanent permanent = game.getPermanent(id);
|
|
||||||
if (permanent == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return this.getTargets().stream()
|
|
||||||
.map(game::getPermanent)
|
.map(game::getPermanent)
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.noneMatch(perm -> !perm.getId().equals(permanent.getId())
|
.map(Controllable::getControllerId)
|
||||||
&& perm.isControlledBy(permanent.getControllerId()));
|
.collect(Collectors.toSet());
|
||||||
}
|
possibleTargets.removeIf(id -> {
|
||||||
|
Permanent permanent = game.getPermanent(id);
|
||||||
|
return permanent == null || usedControllers.contains(permanent.getControllerId());
|
||||||
|
});
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
|
||||||
MageObject targetSource = game.getObject(source);
|
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
|
||||||
boolean validTarget = this.getTargets().stream()
|
|
||||||
.map(game::getPermanent)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.noneMatch(perm -> !perm.getId().equals(permanent.getId()) && perm.isControlledBy(permanent.getControllerId()));
|
|
||||||
if (validTarget) {
|
|
||||||
if (notTarget || permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
|
||||||
possibleTargets.add(permanent.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,21 +1,18 @@
|
||||||
|
|
||||||
package mage.cards.p;
|
package mage.cards.p;
|
||||||
|
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
|
||||||
import mage.abilities.effects.common.continuous.ExchangeControlTargetEffect;
|
import mage.abilities.effects.common.continuous.ExchangeControlTargetEffect;
|
||||||
|
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.TargetController;
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.predicate.Predicates;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.common.TargetControlledPermanent;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
@ -33,7 +30,7 @@ public final class PucasMischief extends CardImpl {
|
||||||
|
|
||||||
// At the beginning of your upkeep, you may exchange control of target nonland permanent you control and target nonland permanent an opponent controls with an equal or lesser converted mana cost.
|
// At the beginning of your upkeep, you may exchange control of target nonland permanent you control and target nonland permanent an opponent controls with an equal or lesser converted mana cost.
|
||||||
Ability ability = new BeginningOfUpkeepTriggeredAbility(new ExchangeControlTargetEffect(Duration.EndOfGame, rule, false, true), true);
|
Ability ability = new BeginningOfUpkeepTriggeredAbility(new ExchangeControlTargetEffect(Duration.EndOfGame, rule, false, true), true);
|
||||||
ability.addTarget(new TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent());
|
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_NON_LAND));
|
||||||
ability.addTarget(new PucasMischiefSecondTarget());
|
ability.addTarget(new PucasMischiefSecondTarget());
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
|
@ -49,89 +46,53 @@ public final class PucasMischief extends CardImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent extends TargetControlledPermanent {
|
|
||||||
|
|
||||||
public TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent() {
|
|
||||||
super();
|
|
||||||
this.filter = this.filter.copy();
|
|
||||||
filter.add(Predicates.not(CardType.LAND.getPredicate()));
|
|
||||||
withTargetName("nonland permanent you control");
|
|
||||||
}
|
|
||||||
|
|
||||||
private TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent(final TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent target) {
|
|
||||||
super(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
|
||||||
MageObject targetSource = game.getObject(source);
|
|
||||||
if (targetSource != null) {
|
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
|
||||||
if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
|
||||||
possibleTargets.add(permanent.getId());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent copy() {
|
|
||||||
return new TargetControlledPermanentWithCMCGreaterOrLessThanOpponentPermanent(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class PucasMischiefSecondTarget extends TargetPermanent {
|
class PucasMischiefSecondTarget extends TargetPermanent {
|
||||||
|
|
||||||
private Permanent firstTarget = null;
|
|
||||||
|
|
||||||
public PucasMischiefSecondTarget() {
|
public PucasMischiefSecondTarget() {
|
||||||
super();
|
super(StaticFilters.FILTER_OPPONENTS_PERMANENT_NON_LAND);
|
||||||
this.filter = this.filter.copy();
|
|
||||||
filter.add(TargetController.OPPONENT.getControllerPredicate());
|
|
||||||
filter.add(Predicates.not(CardType.LAND.getPredicate()));
|
|
||||||
withTargetName("permanent an opponent controls with an equal or lesser mana value");
|
withTargetName("permanent an opponent controls with an equal or lesser mana value");
|
||||||
}
|
}
|
||||||
|
|
||||||
private PucasMischiefSecondTarget(final PucasMischiefSecondTarget target) {
|
private PucasMischiefSecondTarget(final PucasMischiefSecondTarget target) {
|
||||||
super(target);
|
super(target);
|
||||||
this.firstTarget = target.firstTarget;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID id, Ability source, Game game) {
|
||||||
if (super.canTarget(id, source, game)) {
|
Permanent ownPermanent = game.getPermanent(source.getFirstTarget());
|
||||||
Permanent target1 = game.getPermanent(source.getFirstTarget());
|
Permanent possiblePermanent = game.getPermanent(id);
|
||||||
Permanent opponentPermanent = game.getPermanent(id);
|
if (ownPermanent == null || possiblePermanent == null) {
|
||||||
if (target1 != null && opponentPermanent != null) {
|
return false;
|
||||||
return target1.getManaValue() >= opponentPermanent.getManaValue();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return false;
|
return super.canTarget(id, source, game) && ownPermanent.getManaValue() >= possiblePermanent.getManaValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
Set<UUID> possibleTargets = new HashSet<>();
|
||||||
if (firstTarget != null) {
|
|
||||||
MageObject targetSource = game.getObject(source);
|
Permanent ownPermanent = game.getPermanent(source.getFirstTarget());
|
||||||
if (targetSource != null) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
if (ownPermanent == null) {
|
||||||
if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)) {
|
// playable or first target not yet selected
|
||||||
if (firstTarget.getManaValue() >= permanent.getManaValue()) {
|
// use all
|
||||||
possibleTargets.add(permanent.getId());
|
possibleTargets.add(permanent.getId());
|
||||||
}
|
} else {
|
||||||
}
|
// real
|
||||||
|
// filter by cmc
|
||||||
|
if (ownPermanent.getManaValue() >= permanent.getManaValue()) {
|
||||||
|
possibleTargets.add(permanent.getId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return possibleTargets;
|
possibleTargets.removeIf(id -> ownPermanent != null && ownPermanent.getId().equals(id));
|
||||||
|
|
||||||
|
return keepValidPossibleTargets(possibleTargets, sourceControllerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
||||||
firstTarget = game.getPermanent(source.getFirstTarget());
|
// AI hint with better outcome
|
||||||
return super.chooseTarget(Outcome.GainControl, playerId, source, game);
|
return super.chooseTarget(Outcome.GainControl, playerId, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,26 +109,11 @@ class QueenKaylaBinKroogTarget extends TargetCard {
|
||||||
return new QueenKaylaBinKroogTarget(this);
|
return new QueenKaylaBinKroogTarget(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canTarget(UUID playerId, UUID id, Ability ability, Game game) {
|
|
||||||
if (!super.canTarget(playerId, id, ability, game)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Card card = game.getCard(id);
|
|
||||||
return card != null && 1 <= card.getManaValue() && card.getManaValue() <= 3 && this
|
|
||||||
.getTargets()
|
|
||||||
.stream()
|
|
||||||
.map(game::getCard)
|
|
||||||
.filter(Objects::nonNull)
|
|
||||||
.mapToInt(MageObject::getManaValue)
|
|
||||||
.noneMatch(x -> card.getManaValue() == x);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
Set<Integer> manaValues = this
|
|
||||||
|
Set<Integer> usedManaValues = this
|
||||||
.getTargets()
|
.getTargets()
|
||||||
.stream()
|
.stream()
|
||||||
.map(game::getCard)
|
.map(game::getCard)
|
||||||
|
|
@ -137,8 +122,9 @@ class QueenKaylaBinKroogTarget extends TargetCard {
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
possibleTargets.removeIf(uuid -> {
|
possibleTargets.removeIf(uuid -> {
|
||||||
Card card = game.getCard(uuid);
|
Card card = game.getCard(uuid);
|
||||||
return card != null && manaValues.contains(card.getManaValue());
|
return card == null || usedManaValues.contains(card.getManaValue());
|
||||||
});
|
});
|
||||||
|
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ class RaiseTheDraugrTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (!super.canTarget(controllerId, id, source, game)) {
|
if (!super.canTarget(playerId, id, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getTargets().isEmpty()) {
|
if (getTargets().isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -82,8 +82,8 @@ class RampagingYaoGuaiTarget extends TargetPermanent {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, MageObject::getManaValue, GetXValue.instance.calculate(game, source, null), game);
|
this.getTargets(), id, MageObject::getManaValue, GetXValue.instance.calculate(game, source, null), game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ class RasputinDreamweaverWatcher extends Watcher {
|
||||||
filter.add(TappedPredicate.UNTAPPED);
|
filter.add(TappedPredicate.UNTAPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Set<UUID> startedUntapped = new HashSet<>(0);
|
private final Set<UUID> startedUntapped = new HashSet<>();
|
||||||
|
|
||||||
RasputinDreamweaverWatcher() {
|
RasputinDreamweaverWatcher() {
|
||||||
super(WatcherScope.GAME);
|
super(WatcherScope.GAME);
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.r;
|
package mage.cards.r;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.TransformIntoSourceTriggeredAbility;
|
import mage.abilities.common.TransformIntoSourceTriggeredAbility;
|
||||||
import mage.abilities.common.WerewolfBackTriggeredAbility;
|
import mage.abilities.common.WerewolfBackTriggeredAbility;
|
||||||
|
|
@ -15,13 +14,11 @@ import mage.constants.SubType;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.game.stack.StackObject;
|
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.target.common.TargetOpponentOrPlaneswalker;
|
import mage.target.common.TargetOpponentOrPlaneswalker;
|
||||||
import mage.target.targetpointer.EachTargetPointer;
|
import mage.target.targetpointer.EachTargetPointer;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -100,54 +97,30 @@ class RavagerOfTheFellsEffect extends OneShotEffect {
|
||||||
class RavagerOfTheFellsTarget extends TargetPermanent {
|
class RavagerOfTheFellsTarget extends TargetPermanent {
|
||||||
|
|
||||||
RavagerOfTheFellsTarget() {
|
RavagerOfTheFellsTarget() {
|
||||||
super(0, 1, StaticFilters.FILTER_PERMANENT_CREATURE, false);
|
super(0, 1, StaticFilters.FILTER_PERMANENT_CREATURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private RavagerOfTheFellsTarget(final RavagerOfTheFellsTarget target) {
|
private RavagerOfTheFellsTarget(final RavagerOfTheFellsTarget target) {
|
||||||
super(target);
|
super(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
|
||||||
Player player = game.getPlayerOrPlaneswalkerController(source.getFirstTarget());
|
|
||||||
if (player == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UUID firstTarget = player.getId();
|
|
||||||
Permanent permanent = game.getPermanent(id);
|
|
||||||
if (firstTarget != null && permanent != null && permanent.isControlledBy(firstTarget)) {
|
|
||||||
return super.canTarget(id, source, game);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
||||||
Set<UUID> availablePossibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
Set<UUID> possibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
|
||||||
MageObject object = game.getObject(source);
|
|
||||||
|
|
||||||
for (StackObject item : game.getState().getStack()) {
|
Player needPlayer = game.getPlayerOrPlaneswalkerController(source.getFirstTarget());
|
||||||
if (item.getId().equals(source.getSourceId())) {
|
if (needPlayer == null) {
|
||||||
object = item;
|
// playable or not selected - use any
|
||||||
}
|
} else {
|
||||||
if (item.getSourceId().equals(source.getSourceId())) {
|
// filter by controller
|
||||||
object = item;
|
possibleTargets.removeIf(id -> {
|
||||||
}
|
Permanent permanent = game.getPermanent(id);
|
||||||
|
return permanent == null
|
||||||
|
|| permanent.getId().equals(source.getFirstTarget())
|
||||||
|
|| !permanent.isControlledBy(needPlayer.getId());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (object instanceof StackObject) {
|
|
||||||
UUID playerId = ((StackObject) object).getStackAbility().getFirstTarget();
|
|
||||||
Player player = game.getPlayerOrPlaneswalkerController(playerId);
|
|
||||||
if (player != null) {
|
|
||||||
for (UUID targetId : availablePossibleTargets) {
|
|
||||||
Permanent permanent = game.getPermanent(targetId);
|
|
||||||
if (permanent != null && permanent.isControlledBy(player.getId())) {
|
|
||||||
possibleTargets.add(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
return possibleTargets;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -85,7 +85,7 @@ class ReapIntellectEffect extends OneShotEffect {
|
||||||
int xCost = Math.min(CardUtil.getSourceCostsTag(game, source, "X", 0), targetPlayer.getHand().size());
|
int xCost = Math.min(CardUtil.getSourceCostsTag(game, source, "X", 0), targetPlayer.getHand().size());
|
||||||
TargetCard target = new TargetCard(0, xCost, Zone.HAND, filterNonLands);
|
TargetCard target = new TargetCard(0, xCost, Zone.HAND, filterNonLands);
|
||||||
target.withNotTarget(true);
|
target.withNotTarget(true);
|
||||||
controller.chooseTarget(Outcome.Benefit, targetPlayer.getHand(), target, source, game);
|
controller.choose(Outcome.Benefit, targetPlayer.getHand(), target, source, game);
|
||||||
for (UUID cardId : target.getTargets()) {
|
for (UUID cardId : target.getTargets()) {
|
||||||
Card chosenCard = game.getCard(cardId);
|
Card chosenCard = game.getCard(cardId);
|
||||||
if (chosenCard != null) {
|
if (chosenCard != null) {
|
||||||
|
|
|
||||||
|
|
@ -1,20 +1,15 @@
|
||||||
|
|
||||||
package mage.cards.r;
|
package mage.cards.r;
|
||||||
|
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.effects.common.ExileTargetEffect;
|
import mage.abilities.effects.common.ExileTargetEffect;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.filter.StaticFilters;
|
import mage.constants.TargetController;
|
||||||
import mage.game.Game;
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.filter.predicate.other.DamagedPlayerThisTurnPredicate;
|
||||||
import mage.target.TargetPermanent;
|
import mage.target.TargetPermanent;
|
||||||
import mage.watchers.common.PlayerDamagedBySourceWatcher;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -22,12 +17,18 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
public final class Reciprocate extends CardImpl {
|
public final class Reciprocate extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature that dealt damage to you this turn");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(new DamagedPlayerThisTurnPredicate(TargetController.YOU));
|
||||||
|
}
|
||||||
|
|
||||||
public Reciprocate(UUID ownerId, CardSetInfo setInfo) {
|
public Reciprocate(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}");
|
||||||
|
|
||||||
// Exile target creature that dealt damage to you this turn.
|
// Exile target creature that dealt damage to you this turn.
|
||||||
this.getSpellAbility().addEffect(new ExileTargetEffect());
|
this.getSpellAbility().addEffect(new ExileTargetEffect());
|
||||||
this.getSpellAbility().addTarget(new ReciprocateTarget());
|
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Reciprocate(final Reciprocate card) {
|
private Reciprocate(final Reciprocate card) {
|
||||||
|
|
@ -40,66 +41,3 @@ public final class Reciprocate extends CardImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ReciprocateTarget extends TargetPermanent {
|
|
||||||
|
|
||||||
public ReciprocateTarget() {
|
|
||||||
super(1, 1, StaticFilters.FILTER_PERMANENT_CREATURE, false);
|
|
||||||
targetName = "creature that dealt damage to you this turn";
|
|
||||||
}
|
|
||||||
|
|
||||||
private ReciprocateTarget(final ReciprocateTarget target) {
|
|
||||||
super(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canTarget(UUID id, Ability source, Game game) {
|
|
||||||
PlayerDamagedBySourceWatcher watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, source.getControllerId());
|
|
||||||
if (watcher != null && watcher.hasSourceDoneDamage(id, game)) {
|
|
||||||
return super.canTarget(id, source, game);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
Set<UUID> availablePossibleTargets = super.possibleTargets(sourceControllerId, source, game);
|
|
||||||
Set<UUID> possibleTargets = new HashSet<>();
|
|
||||||
PlayerDamagedBySourceWatcher watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, sourceControllerId);
|
|
||||||
for (UUID targetId : availablePossibleTargets) {
|
|
||||||
Permanent permanent = game.getPermanent(targetId);
|
|
||||||
if (permanent != null && watcher != null && watcher.hasSourceDoneDamage(targetId, game)) {
|
|
||||||
possibleTargets.add(targetId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return possibleTargets;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean canChoose(UUID sourceControllerId, Ability source, Game game) {
|
|
||||||
int remainingTargets = this.minNumberOfTargets - targets.size();
|
|
||||||
if (remainingTargets == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
int count = 0;
|
|
||||||
MageObject targetSource = game.getObject(source);
|
|
||||||
if (targetSource != null) {
|
|
||||||
PlayerDamagedBySourceWatcher watcher = game.getState().getWatcher(PlayerDamagedBySourceWatcher.class, sourceControllerId);
|
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, sourceControllerId, source, game)) {
|
|
||||||
if (!targets.containsKey(permanent.getId()) && permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game)
|
|
||||||
&& watcher != null && watcher.hasSourceDoneDamage(permanent.getId(), game)) {
|
|
||||||
count++;
|
|
||||||
if (count >= remainingTargets) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public ReciprocateTarget copy() {
|
|
||||||
return new ReciprocateTarget(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.Zone;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
|
|
@ -68,7 +69,7 @@ class ReckonerShakedownEffect extends OneShotEffect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
player.revealCards(source, player.getHand(), game);
|
player.revealCards(source, player.getHand(), game);
|
||||||
TargetCard target = new TargetCardInHand(0, 1, StaticFilters.FILTER_CARD_A_NON_LAND);
|
TargetCard target = new TargetCard(0, 1, Zone.HAND, StaticFilters.FILTER_CARD_A_NON_LAND);
|
||||||
controller.choose(Outcome.Discard, player.getHand(), target, source, game);
|
controller.choose(Outcome.Discard, player.getHand(), target, source, game);
|
||||||
Card card = game.getCard(target.getFirstTarget());
|
Card card = game.getCard(target.getFirstTarget());
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ class RetetherEffect extends OneShotEffect {
|
||||||
for (Ability ability : aura.getAbilities()) {
|
for (Ability ability : aura.getAbilities()) {
|
||||||
if (ability instanceof SpellAbility) {
|
if (ability instanceof SpellAbility) {
|
||||||
for (Target abilityTarget : ability.getTargets()) {
|
for (Target abilityTarget : ability.getTargets()) {
|
||||||
if (abilityTarget.possibleTargets(controller.getId(), game).contains(permanent.getId())) {
|
if (abilityTarget.possibleTargets(controller.getId(), source, game).contains(permanent.getId())) {
|
||||||
target = abilityTarget.copy();
|
target = abilityTarget.copy();
|
||||||
break auraLegalitySearch;
|
break auraLegalitySearch;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,8 @@ class ReturnFromExtinctionTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
if (!super.canTarget(controllerId, id, source, game)) {
|
if (!super.canTarget(playerId, id, source, game)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (getTargets().isEmpty()) {
|
if (getTargets().isEmpty()) {
|
||||||
|
|
|
||||||
|
|
@ -61,8 +61,8 @@ class ReunionOfTheHouseTarget extends TargetCardInYourGraveyard {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
public boolean canTarget(UUID playerId, UUID id, Ability source, Game game) {
|
||||||
return super.canTarget(controllerId, id, source, game)
|
return super.canTarget(playerId, id, source, game)
|
||||||
&& CardUtil.checkCanTargetTotalValueLimit(
|
&& CardUtil.checkCanTargetTotalValueLimit(
|
||||||
this.getTargets(), id, m -> m.getPower().getValue(), 10, game);
|
this.getTargets(), id, m -> m.getPower().getValue(), 10, game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue