mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
other: reworked target selection: (#13638)
- WIP: AI and multi targets, human and X=0 use cases, human and impossible targets use cases;
- improved stability and shared logic (related to #13606, #11134, #11666, continue from a53eb66b58, close #13617, close #13613);
- improved test logs and debug info to show more target info on errors;
- improved test framework to support multiple addTarget calls;
- improved test framework to find bad commands order for targets (related to #11666);
- fixed game freezes on auto-choice usages with disconnected or under control players (related to #11285);
- gui, game: fixed that player doesn't mark avatar as selected/green in "up to" targeting;
- gui, game: fixed small font in some popup messages on big screens (related to #969);
- gui, game: added min targets info for target selection dialog;
- for devs: added new cheat option to call and test any game dialog (define own dialogs, targets, etc in HumanDialogsTester);
- for devs: now tests require complete an any or up to target selection by addTarget + TestPlayer.TARGET_SKIP or setChoice + TestPlayer.CHOICE_SKIP (if not all max/possible targets used);
- for devs: added detail targets info for activate/trigger/cast, can be useful to debug unit tests, auto-choose or AI (see DebugUtil.GAME_SHOW_CHOOSE_TARGET_LOGS)
This commit is contained in:
parent
80d62727e1
commit
133e4fe425
84 changed files with 2737 additions and 743 deletions
|
|
@ -326,6 +326,13 @@ public class TestCardRenderDialog extends MageDialog {
|
||||||
possibleTargets.add(playerYou.getId());
|
possibleTargets.add(playerYou.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// chosen target
|
||||||
|
Set<UUID> chosenTargets = null;
|
||||||
|
if (false) { // TODO: add GUI's checkbox for checkPlayerAsChosen
|
||||||
|
chosenTargets = new LinkedHashSet<>();
|
||||||
|
chosenTargets.add(playerYou.getId());
|
||||||
|
}
|
||||||
|
|
||||||
// player's panel
|
// player's panel
|
||||||
if (this.player == null) {
|
if (this.player == null) {
|
||||||
// create new panel
|
// create new panel
|
||||||
|
|
@ -345,7 +352,7 @@ public class TestCardRenderDialog extends MageDialog {
|
||||||
.findFirst()
|
.findFirst()
|
||||||
.orElse(null);
|
.orElse(null);
|
||||||
this.player.init(this.game.getId(), playerYou.getId(), isMe, this.bigCard, 0);
|
this.player.init(this.game.getId(), playerYou.getId(), isMe, this.bigCard, 0);
|
||||||
this.player.update(gameView, currentPlayerView, possibleTargets);
|
this.player.update(gameView, currentPlayerView, possibleTargets, chosenTargets);
|
||||||
this.player.sizePlayerPanel(smallMode);
|
this.player.sizePlayerPanel(smallMode);
|
||||||
|
|
||||||
// update CARDS
|
// update CARDS
|
||||||
|
|
|
||||||
|
|
@ -216,6 +216,22 @@ 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() {
|
||||||
|
if (options != null && options.containsKey("chosenTargets")) {
|
||||||
|
return new HashSet<>((List<UUID>) options.get("chosenTargets"));
|
||||||
|
} else {
|
||||||
|
return Collections.emptySet();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public CardView findCard(UUID id) {
|
public CardView findCard(UUID id) {
|
||||||
return this.allCardsIndex.getOrDefault(id, null);
|
return this.allCardsIndex.getOrDefault(id, null);
|
||||||
}
|
}
|
||||||
|
|
@ -642,7 +658,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
||||||
// see test render dialog for refresh commands order
|
// see test render dialog for refresh commands order
|
||||||
playPanel.getPlayerPanel().fullRefresh(GUISizeHelper.playerPanelGuiScale);
|
playPanel.getPlayerPanel().fullRefresh(GUISizeHelper.playerPanelGuiScale);
|
||||||
playPanel.init(player, bigCard, gameId, player.getPriorityTimeLeftSecs());
|
playPanel.init(player, bigCard, gameId, player.getPriorityTimeLeftSecs());
|
||||||
playPanel.update(lastGameData.game, player, lastGameData.targets);
|
playPanel.update(lastGameData.game, player, lastGameData.targets, lastGameData.getChosenTargets());
|
||||||
playPanel.getPlayerPanel().sizePlayerPanel(false);
|
playPanel.getPlayerPanel().sizePlayerPanel(false);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -1170,7 +1186,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
players.get(player.getPlayerId()).update(lastGameData.game, player, lastGameData.targets);
|
players.get(player.getPlayerId()).update(lastGameData.game, player, lastGameData.targets, lastGameData.getChosenTargets());
|
||||||
if (player.getPlayerId().equals(playerId)) {
|
if (player.getPlayerId().equals(playerId)) {
|
||||||
skipButtons.updateFromPlayer(player);
|
skipButtons.updateFromPlayer(player);
|
||||||
}
|
}
|
||||||
|
|
@ -1786,12 +1802,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
||||||
needZone = (Zone) lastGameData.options.get("targetZone");
|
needZone = (Zone) lastGameData.options.get("targetZone");
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UUID> needChosen;
|
Set<UUID> needChosen = lastGameData.getChosenTargets();
|
||||||
if (lastGameData.options != null && lastGameData.options.containsKey("chosenTargets")) {
|
|
||||||
needChosen = (List<UUID>) lastGameData.options.get("chosenTargets");
|
|
||||||
} else {
|
|
||||||
needChosen = new ArrayList<>();
|
|
||||||
}
|
|
||||||
|
|
||||||
Set<UUID> needSelectable;
|
Set<UUID> needSelectable;
|
||||||
if (lastGameData.targets != null) {
|
if (lastGameData.targets != null) {
|
||||||
|
|
@ -2021,7 +2032,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
||||||
private void prepareSelectableWindows(
|
private void prepareSelectableWindows(
|
||||||
Collection<CardInfoWindowDialog> windows,
|
Collection<CardInfoWindowDialog> windows,
|
||||||
Set<UUID> needSelectable,
|
Set<UUID> needSelectable,
|
||||||
List<UUID> needChosen,
|
Set<UUID> needChosen,
|
||||||
PlayableObjectsList needPlayable
|
PlayableObjectsList needPlayable
|
||||||
) {
|
) {
|
||||||
// lookAt or reveals windows clean up on next priority, so users can see dialogs, but xmage can't restore it
|
// lookAt or reveals windows clean up on next priority, so users can see dialogs, but xmage can't restore it
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ public class PlayAreaPanel extends javax.swing.JPanel {
|
||||||
|
|
||||||
// data init
|
// data init
|
||||||
init(player, bigCard, gameId, priorityTime);
|
init(player, bigCard, gameId, priorityTime);
|
||||||
update(null, player, null);
|
update(null, player, null, null);
|
||||||
playerPanel.sizePlayerPanel(isSmallMode());
|
playerPanel.sizePlayerPanel(isSmallMode());
|
||||||
|
|
||||||
// init popup menu (must run after data init)
|
// init popup menu (must run after data init)
|
||||||
|
|
@ -510,8 +510,8 @@ public class PlayAreaPanel extends javax.swing.JPanel {
|
||||||
this.isMe = player.getControlled();
|
this.isMe = player.getControlled();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void update(GameView game, PlayerView player, Set<UUID> possibleTargets) {
|
public final void update(GameView game, PlayerView player, Set<UUID> possibleTargets, Set<UUID> chosenTargets) {
|
||||||
this.playerPanel.update(game, player, possibleTargets);
|
this.playerPanel.update(game, player, possibleTargets, chosenTargets);
|
||||||
this.battlefieldPanel.update(player.getBattlefield());
|
this.battlefieldPanel.update(player.getBattlefield());
|
||||||
if (this.allowViewHandCardsMenuItem != null) {
|
if (this.allowViewHandCardsMenuItem != null) {
|
||||||
this.allowViewHandCardsMenuItem.setSelected(player.getUserData().isAllowRequestHandToAll());
|
this.allowViewHandCardsMenuItem.setSelected(player.getUserData().isAllowRequestHandToAll());
|
||||||
|
|
|
||||||
|
|
@ -247,7 +247,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
||||||
.orElse(0);
|
.orElse(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void update(GameView game, PlayerView player, Set<UUID> possibleTargets) {
|
public void update(GameView game, PlayerView player, Set<UUID> possibleTargets, Set<UUID> chosenTargets) {
|
||||||
this.player = player;
|
this.player = player;
|
||||||
int pastLife = player.getLife();
|
int pastLife = player.getLife();
|
||||||
if (playerLives != null) {
|
if (playerLives != null) {
|
||||||
|
|
@ -427,6 +427,12 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
||||||
this.btnPlayer.setBorder(YELLOW_BORDER);
|
this.btnPlayer.setBorder(YELLOW_BORDER);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// selected targeting (draw as priority)
|
||||||
|
if (chosenTargets != null && chosenTargets.contains(this.playerId)) {
|
||||||
|
this.avatar.setBorder(GREEN_BORDER); // TODO: use diff green color for chosen targeting and current priority?
|
||||||
|
this.btnPlayer.setBorder(GREEN_BORDER);
|
||||||
|
}
|
||||||
|
|
||||||
update(player.getManaPool());
|
update(player.getManaPool());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import mage.client.draft.DraftPanel;
|
||||||
import mage.client.game.GamePanel;
|
import mage.client.game.GamePanel;
|
||||||
import mage.client.plugins.impl.Plugins;
|
import mage.client.plugins.impl.Plugins;
|
||||||
import mage.client.util.DeckUtil;
|
import mage.client.util.DeckUtil;
|
||||||
|
import mage.client.util.GUISizeHelper;
|
||||||
import mage.client.util.IgnoreList;
|
import mage.client.util.IgnoreList;
|
||||||
import mage.client.util.audio.AudioManager;
|
import mage.client.util.audio.AudioManager;
|
||||||
import mage.client.util.object.SaveObjectUtil;
|
import mage.client.util.object.SaveObjectUtil;
|
||||||
|
|
@ -22,9 +23,12 @@ import mage.util.DebugUtil;
|
||||||
import mage.view.*;
|
import mage.view.*;
|
||||||
import mage.view.ChatMessage.MessageType;
|
import mage.view.ChatMessage.MessageType;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
import org.mage.card.arcane.ManaSymbols;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import java.awt.*;
|
||||||
import java.awt.event.KeyEvent;
|
import java.awt.event.KeyEvent;
|
||||||
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -203,11 +207,7 @@ public class CallbackClientImpl implements CallbackClient {
|
||||||
case SERVER_MESSAGE: {
|
case SERVER_MESSAGE: {
|
||||||
if (callback.getData() != null) {
|
if (callback.getData() != null) {
|
||||||
ChatMessage message = (ChatMessage) callback.getData();
|
ChatMessage message = (ChatMessage) callback.getData();
|
||||||
if (message.getColor() == ChatMessage.MessageColor.RED) {
|
showMessageDialog(null, message.getMessage(), "Server message");
|
||||||
JOptionPane.showMessageDialog(null, message.getMessage(), "Server message", JOptionPane.WARNING_MESSAGE);
|
|
||||||
} else {
|
|
||||||
JOptionPane.showMessageDialog(null, message.getMessage(), "Server message", JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -401,7 +401,7 @@ public class CallbackClientImpl implements CallbackClient {
|
||||||
case SHOW_USERMESSAGE: {
|
case SHOW_USERMESSAGE: {
|
||||||
List<String> messageData = (List<String>) callback.getData();
|
List<String> messageData = (List<String>) callback.getData();
|
||||||
if (messageData.size() == 2) {
|
if (messageData.size() == 2) {
|
||||||
JOptionPane.showMessageDialog(null, messageData.get(1), messageData.get(0), JOptionPane.WARNING_MESSAGE);
|
showMessageDialog(null, messageData.get(1), messageData.get(0));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -420,8 +420,7 @@ public class CallbackClientImpl implements CallbackClient {
|
||||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||||
if (panel != null) {
|
if (panel != null) {
|
||||||
JOptionPane.showMessageDialog(panel, message.getMessage(), "Game message",
|
showMessageDialog(panel, message.getMessage(), "Game message");
|
||||||
JOptionPane.INFORMATION_MESSAGE);
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -510,6 +509,18 @@ public class CallbackClientImpl implements CallbackClient {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Show modal message box, so try to use it only for critical errors or global message. As less as possible.
|
||||||
|
*/
|
||||||
|
private void showMessageDialog(Component parentComponent, String message, String title) {
|
||||||
|
// convert to html
|
||||||
|
// message - supported
|
||||||
|
// title - not supported
|
||||||
|
message = ManaSymbols.replaceSymbolsWithHTML(message, ManaSymbols.Type.DIALOG);
|
||||||
|
message = GUISizeHelper.textToHtmlWithSize(message, GUISizeHelper.dialogFont);
|
||||||
|
JOptionPane.showMessageDialog(parentComponent, message, title, JOptionPane.INFORMATION_MESSAGE);
|
||||||
|
}
|
||||||
|
|
||||||
private ActionData appendJsonEvent(String name, UUID gameId, Object value) {
|
private ActionData appendJsonEvent(String name, UUID gameId, Object value) {
|
||||||
Session session = SessionHandler.getSession();
|
Session session = SessionHandler.getSession();
|
||||||
if (session.isJsonLogActive()) {
|
if (session.isJsonLogActive()) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import mage.client.util.gui.GuiDisplayUtil;
|
||||||
import org.mage.card.arcane.CardRenderer;
|
import org.mage.card.arcane.CardRenderer;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
|
import javax.swing.plaf.FontUIResource;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
|
@ -108,7 +109,7 @@ public final class GUISizeHelper {
|
||||||
|
|
||||||
// app - frame/window title
|
// app - frame/window title
|
||||||
// nimbus's LaF limited to static title size, so font can't be too big (related code in SynthInternalFrameTitlePane, BasicInternalFrameTitlePane)
|
// nimbus's LaF limited to static title size, so font can't be too big (related code in SynthInternalFrameTitlePane, BasicInternalFrameTitlePane)
|
||||||
UIManager.put("InternalFrame.titleFont", dialogFont.deriveFont(Font.BOLD, Math.min(17, 0.8f * dialogFont.getSize())));
|
UIManager.put("InternalFrame.titleFont", new FontUIResource(dialogFont.deriveFont(Font.BOLD, Math.min(17, 0.8f * dialogFont.getSize()))));
|
||||||
|
|
||||||
// app - tables
|
// app - tables
|
||||||
tableFont = new java.awt.Font("Arial", 0, dialogFontSize);
|
tableFont = new java.awt.Font("Arial", 0, dialogFontSize);
|
||||||
|
|
@ -150,7 +151,11 @@ public final class GUISizeHelper {
|
||||||
cardTooltipLargeImageHeight = 30 * tooltipFontSize;
|
cardTooltipLargeImageHeight = 30 * tooltipFontSize;
|
||||||
cardTooltipLargeTextWidth = Math.max(150, 20 * tooltipFontSize - 50);
|
cardTooltipLargeTextWidth = Math.max(150, 20 * tooltipFontSize - 50);
|
||||||
cardTooltipLargeTextHeight = Math.max(100, 12 * tooltipFontSize - 20);
|
cardTooltipLargeTextHeight = Math.max(100, 12 * tooltipFontSize - 20);
|
||||||
UIManager.put("ToolTip.font", cardTooltipFont);
|
UIManager.put("ToolTip.font", new FontUIResource(cardTooltipFont));
|
||||||
|
|
||||||
|
// app - information boxes (only title, text controls by content)
|
||||||
|
// TODO: doesn't work
|
||||||
|
//UIManager.put("OptionPane.titleFont", new FontUIResource(dialogFont.deriveFont(Font.BOLD, Math.min(17, 1.3f * dialogFont.getSize()))));
|
||||||
|
|
||||||
// game - player panel
|
// game - player panel
|
||||||
playerPanelGuiScale = (float) (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GUI_PLAYER_PANEL_SIZE, 14) / 14.0);
|
playerPanelGuiScale = (float) (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_GUI_PLAYER_PANEL_SIZE, 14) / 14.0);
|
||||||
|
|
|
||||||
|
|
@ -474,7 +474,7 @@ public final class GuiDisplayUtil {
|
||||||
public static void refreshThemeSettings() {
|
public static void refreshThemeSettings() {
|
||||||
// apply Nimbus's look and fill
|
// apply Nimbus's look and fill
|
||||||
// possible settings:
|
// possible settings:
|
||||||
// https://docs.oracle.com/en%2Fjava%2Fjavase%2F17%2Fdocs%2Fapi%2F%2F/java.desktop/javax/swing/plaf/nimbus/doc-files/properties.html
|
// https://docs.oracle.com/en/java/javase/23/docs/api/java.desktop/javax/swing/plaf/nimbus/doc-files/properties.html
|
||||||
|
|
||||||
// enable nimbus
|
// enable nimbus
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import mage.target.common.TargetOpponent;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
import mage.util.MultiAmountMessage;
|
import mage.util.MultiAmountMessage;
|
||||||
import mage.util.RandomUtil;
|
import mage.util.RandomUtil;
|
||||||
|
import mage.utils.testers.TestableDialogsRunner;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
|
|
@ -69,6 +70,7 @@ public final class SystemUtil {
|
||||||
private static final String COMMAND_LANDS_ADD_TO_BATTLEFIELD = "@lands add";
|
private static final String COMMAND_LANDS_ADD_TO_BATTLEFIELD = "@lands add";
|
||||||
private static final String COMMAND_UNDER_CONTROL_TAKE = "@under control take";
|
private static final String COMMAND_UNDER_CONTROL_TAKE = "@under control take";
|
||||||
private static final String COMMAND_UNDER_CONTROL_GIVE = "@under control give";
|
private static final String COMMAND_UNDER_CONTROL_GIVE = "@under control give";
|
||||||
|
private static final String COMMAND_SHOW_TEST_DIALOGS = "@show dialog";
|
||||||
private static final String COMMAND_MANA_ADD = "@mana add"; // TODO: not implemented
|
private static final String COMMAND_MANA_ADD = "@mana add"; // TODO: not implemented
|
||||||
private static final String COMMAND_RUN_CUSTOM_CODE = "@run custom code"; // TODO: not implemented
|
private static final String COMMAND_RUN_CUSTOM_CODE = "@run custom code"; // TODO: not implemented
|
||||||
private static final String COMMAND_SHOW_OPPONENT_HAND = "@show opponent hand";
|
private static final String COMMAND_SHOW_OPPONENT_HAND = "@show opponent hand";
|
||||||
|
|
@ -76,6 +78,7 @@ public final class SystemUtil {
|
||||||
private static final String COMMAND_SHOW_MY_HAND = "@show my hand";
|
private static final String COMMAND_SHOW_MY_HAND = "@show my hand";
|
||||||
private static final String COMMAND_SHOW_MY_LIBRARY = "@show my library";
|
private static final String COMMAND_SHOW_MY_LIBRARY = "@show my library";
|
||||||
private static final Map<String, String> supportedCommands = new HashMap<>();
|
private static final Map<String, String> supportedCommands = new HashMap<>();
|
||||||
|
private static final TestableDialogsRunner testableDialogsRunner = new TestableDialogsRunner(); // for tests
|
||||||
|
|
||||||
static {
|
static {
|
||||||
// special commands names in choose dialog
|
// special commands names in choose dialog
|
||||||
|
|
@ -89,6 +92,7 @@ public final class SystemUtil {
|
||||||
supportedCommands.put(COMMAND_SHOW_OPPONENT_LIBRARY, "SHOW OPPONENT LIBRARY");
|
supportedCommands.put(COMMAND_SHOW_OPPONENT_LIBRARY, "SHOW OPPONENT LIBRARY");
|
||||||
supportedCommands.put(COMMAND_SHOW_MY_HAND, "SHOW MY HAND");
|
supportedCommands.put(COMMAND_SHOW_MY_HAND, "SHOW MY HAND");
|
||||||
supportedCommands.put(COMMAND_SHOW_MY_LIBRARY, "SHOW MY LIBRARY");
|
supportedCommands.put(COMMAND_SHOW_MY_LIBRARY, "SHOW MY LIBRARY");
|
||||||
|
supportedCommands.put(COMMAND_SHOW_TEST_DIALOGS, "SHOW TEST DIALOGS");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final Pattern patternGroup = Pattern.compile("\\[(.+)\\]"); // [test new card]
|
private static final Pattern patternGroup = Pattern.compile("\\[(.+)\\]"); // [test new card]
|
||||||
|
|
@ -263,8 +267,13 @@ public final class SystemUtil {
|
||||||
public static void executeCheatCommands(Game game, String commandsFilePath, Player feedbackPlayer) {
|
public static void executeCheatCommands(Game game, String commandsFilePath, Player feedbackPlayer) {
|
||||||
|
|
||||||
// fake test ability for triggers and events
|
// fake test ability for triggers and events
|
||||||
Ability fakeSourceAbilityTemplate = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("adding testing cards"));
|
Ability fakeSourceAbilityTemplate = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("fake ability"));
|
||||||
fakeSourceAbilityTemplate.setControllerId(feedbackPlayer.getId());
|
fakeSourceAbilityTemplate.setControllerId(feedbackPlayer.getId());
|
||||||
|
Card fakeSourceCard = feedbackPlayer.getLibrary().getFromTop(game);
|
||||||
|
if (fakeSourceCard != null) {
|
||||||
|
// set any existing card as source, so dialogs will show all GUI elements, including source and workable popup info
|
||||||
|
fakeSourceAbilityTemplate.setSourceId(fakeSourceCard.getId());
|
||||||
|
}
|
||||||
|
|
||||||
List<String> errorsList = new ArrayList<>();
|
List<String> errorsList = new ArrayList<>();
|
||||||
try {
|
try {
|
||||||
|
|
@ -304,8 +313,9 @@ public final class SystemUtil {
|
||||||
// add default commands
|
// add default commands
|
||||||
initLines.add(0, String.format("[%s]", COMMAND_LANDS_ADD_TO_BATTLEFIELD));
|
initLines.add(0, String.format("[%s]", COMMAND_LANDS_ADD_TO_BATTLEFIELD));
|
||||||
initLines.add(1, String.format("[%s]", COMMAND_CARDS_ADD_TO_HAND));
|
initLines.add(1, String.format("[%s]", COMMAND_CARDS_ADD_TO_HAND));
|
||||||
initLines.add(2, String.format("[%s]", COMMAND_UNDER_CONTROL_TAKE));
|
initLines.add(2, String.format("[%s]", COMMAND_SHOW_TEST_DIALOGS));
|
||||||
initLines.add(3, String.format("[%s]", COMMAND_UNDER_CONTROL_GIVE));
|
initLines.add(3, String.format("[%s]", COMMAND_UNDER_CONTROL_TAKE));
|
||||||
|
initLines.add(4, String.format("[%s]", COMMAND_UNDER_CONTROL_GIVE));
|
||||||
|
|
||||||
// collect all commands
|
// collect all commands
|
||||||
CommandGroup currentGroup = null;
|
CommandGroup currentGroup = null;
|
||||||
|
|
@ -538,6 +548,11 @@ public final class SystemUtil {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case COMMAND_SHOW_TEST_DIALOGS: {
|
||||||
|
testableDialogsRunner.selectAndShowTestableDialog(feedbackPlayer, fakeSourceAbilityTemplate.copy(), game, opponent);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
String mes = String.format("Unknown system command: %s", runGroup.name);
|
String mes = String.format("Unknown system command: %s", runGroup.name);
|
||||||
errorsList.add(mes);
|
errorsList.add(mes);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.Target;
|
||||||
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
|
import mage.target.common.TargetPermanentOrPlayer;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of testable game dialogs
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
abstract class BaseTestableDialog implements TestableDialog {
|
||||||
|
|
||||||
|
private final String group;
|
||||||
|
private final String name;
|
||||||
|
private final String description;
|
||||||
|
|
||||||
|
public BaseTestableDialog(String group, String name, String description) {
|
||||||
|
this.group = group;
|
||||||
|
this.name = name;
|
||||||
|
this.description = description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final public String getGroup() {
|
||||||
|
return this.group;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final public String getDescription() {
|
||||||
|
return this.description;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
final public void showResult(Player player, Game game, String result) {
|
||||||
|
// show message with result
|
||||||
|
game.informPlayer(player, result);
|
||||||
|
// reset game and gui (in most use cases it must return to player's priority)
|
||||||
|
game.firePriorityEvent(player.getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
static Target createAnyTarget(int min, int max) {
|
||||||
|
return createAnyTarget(min, max, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Target createAnyTarget(int min, int max, boolean notTarget) {
|
||||||
|
return new TargetPermanentOrPlayer(min, max).withNotTarget(notTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Target createCreatureTarget(int min, int max) {
|
||||||
|
return createCreatureTarget(min, max, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Target createCreatureTarget(int min, int max, boolean notTarget) {
|
||||||
|
return new TargetCreaturePermanent(min, max).withNotTarget(notTarget);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Target createImpossibleTarget(int min, int max) {
|
||||||
|
return createImpossibleTarget(min, max, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Target createImpossibleTarget(int min, int max, boolean notTarget) {
|
||||||
|
return new TargetCreaturePermanent(min, max, new FilterCreaturePermanent(SubType.TROOPER, "rare type"), notTarget);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.TargetAmount;
|
||||||
|
import mage.target.Targets;
|
||||||
|
import mage.target.common.TargetAnyTargetAmount;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of testable game dialogs
|
||||||
|
* <p>
|
||||||
|
* Supported methods:
|
||||||
|
* - player.chooseTarget(amount)
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
class ChooseAmountTestableDialog extends BaseTestableDialog {
|
||||||
|
|
||||||
|
boolean isYou; // who choose - you or opponent
|
||||||
|
int distributeAmount;
|
||||||
|
int targetsMin;
|
||||||
|
int targetsMax;
|
||||||
|
|
||||||
|
public ChooseAmountTestableDialog(boolean isYou, String name, int distributeAmount, int targetsMin, int targetsMax) {
|
||||||
|
super(String.format("player.chooseTarget(%s, amount)", isYou ? "you" : "AI"),
|
||||||
|
name,
|
||||||
|
String.format("%d between %d-%d targets", distributeAmount, targetsMin, targetsMax));
|
||||||
|
this.isYou = isYou;
|
||||||
|
this.distributeAmount = distributeAmount;
|
||||||
|
this.targetsMin = targetsMin;
|
||||||
|
this.targetsMax = targetsMax;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||||
|
TargetAmount choosingTarget = new TargetAnyTargetAmount(this.distributeAmount, this.targetsMin, this.targetsMax);
|
||||||
|
Player choosingPlayer = this.isYou ? player : opponent;
|
||||||
|
|
||||||
|
// TODO: add "damage" word in ability text, so chooseTargetAmount an show diff dialog (due inner logic - distribute damage or 1/1)
|
||||||
|
boolean chooseRes = choosingPlayer.chooseTargetAmount(Outcome.Benefit, choosingTarget, source, game);
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
if (chooseRes) {
|
||||||
|
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "TRUE", new Targets(choosingTarget), source, game, result);
|
||||||
|
} else {
|
||||||
|
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "FALSE", new Targets(choosingTarget), source, game, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void register(TestableDialogsRunner runner) {
|
||||||
|
// test game started with 2 players and 1 land on battlefield
|
||||||
|
// so it's better to use target limits like 0, 1, 3, 5, max
|
||||||
|
|
||||||
|
List<Boolean> isYous = Arrays.asList(false, true);
|
||||||
|
|
||||||
|
for (boolean isYou : isYous) {
|
||||||
|
// up to
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 0));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 0, 0, 5));
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 1, 0, 0));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 1, 0, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 1, 0, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 1, 0, 5));
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 2, 0, 0));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 2, 0, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 2, 0, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 2, 0, 5));
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 3, 0, 0));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 3, 0, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 3, 0, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 3, 0, 5));
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to, invalid", 5, 0, 0));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 5, 0, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 5, 0, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "up to", 5, 0, 5));
|
||||||
|
|
||||||
|
// need target
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 0, 1, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 0, 1, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 0, 1, 5));
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 1, 1, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 1, 1, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 1, 1, 5));
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 2, 1, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 2, 1, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 2, 1, 5));
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 3, 1, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 3, 1, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 3, 1, 5));
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 5, 1, 1));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 5, 1, 3));
|
||||||
|
runner.registerDialog(new ChooseAmountTestableDialog(isYou, "need", 5, 1, 5));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,111 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.cards.Cards;
|
||||||
|
import mage.cards.CardsImpl;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.filter.FilterCard;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.TargetCard;
|
||||||
|
import mage.target.Targets;
|
||||||
|
import mage.target.common.TargetCardInHand;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of testable game dialogs
|
||||||
|
* <p>
|
||||||
|
* Supported methods:
|
||||||
|
* - player.choose(cards)
|
||||||
|
* - player.chooseTarget(cards)
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
class ChooseCardsTestableDialog extends BaseTestableDialog {
|
||||||
|
|
||||||
|
TargetCard target;
|
||||||
|
boolean isTargetChoice; // how to choose - by xxx.choose or xxx.chooseTarget
|
||||||
|
boolean isYou; // who choose - you or opponent
|
||||||
|
|
||||||
|
public ChooseCardsTestableDialog(boolean isTargetChoice, boolean notTarget, boolean isYou, String name, TargetCard target) {
|
||||||
|
super(String.format("%s(%s, %s, cards)",
|
||||||
|
isTargetChoice ? "player.chooseTarget" : "player.choose",
|
||||||
|
isYou ? "you" : "AI",
|
||||||
|
notTarget ? "not target" : "target"), name, target.toString());
|
||||||
|
this.isTargetChoice = isTargetChoice;
|
||||||
|
this.target = target.withNotTarget(notTarget);
|
||||||
|
this.isYou = isYou;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||||
|
TargetCard choosingTarget = this.target.copy();
|
||||||
|
Player choosingPlayer = this.isYou ? player : opponent;
|
||||||
|
|
||||||
|
// make sure hand go first, so user can test diff type of targets
|
||||||
|
List<Card> all = new ArrayList<>();
|
||||||
|
all.addAll(choosingPlayer.getHand().getCards(game));
|
||||||
|
//all.addAll(choosingPlayer.getLibrary().getCards(game));
|
||||||
|
Cards choosingCards = new CardsImpl(all.stream().limit(100).collect(Collectors.toList()));
|
||||||
|
|
||||||
|
boolean chooseRes;
|
||||||
|
if (this.isTargetChoice) {
|
||||||
|
chooseRes = choosingPlayer.chooseTarget(Outcome.Benefit, choosingCards, choosingTarget, source, game);
|
||||||
|
} else {
|
||||||
|
chooseRes = choosingPlayer.choose(Outcome.Benefit, choosingCards, choosingTarget, source, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
if (chooseRes) {
|
||||||
|
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "TRUE", new Targets(choosingTarget), source, game, result);
|
||||||
|
} else {
|
||||||
|
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "FALSE", new Targets(choosingTarget), source, game, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void register(TestableDialogsRunner runner) {
|
||||||
|
// test game started with 2 players and 7 cards in hand and 1 draw
|
||||||
|
// so it's better to use target limits like 0, 1, 3, 9, max
|
||||||
|
|
||||||
|
FilterCard anyCard = StaticFilters.FILTER_CARD;
|
||||||
|
FilterCard impossibleCard = new FilterCard();
|
||||||
|
impossibleCard.add(SubType.TROOPER.getPredicate());
|
||||||
|
|
||||||
|
List<Boolean> notTargets = Arrays.asList(false, true);
|
||||||
|
List<Boolean> isYous = Arrays.asList(false, true);
|
||||||
|
List<Boolean> isTargetChoices = Arrays.asList(false, true);
|
||||||
|
for (boolean notTarget : notTargets) {
|
||||||
|
for (boolean isYou : isYous) {
|
||||||
|
for (boolean isTargetChoice : isTargetChoices) {
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 0, X=0", new TargetCardInHand(0, 0, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 1", new TargetCardInHand(1, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 3", new TargetCardInHand(3, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 9", new TargetCardInHand(9, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 0-1", new TargetCardInHand(0, 1, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 0-3", new TargetCardInHand(0, 3, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 0-9", new TargetCardInHand(0, 9, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand any", new TargetCardInHand(0, Integer.MAX_VALUE, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 1-3", new TargetCardInHand(1, 3, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 2-3", new TargetCardInHand(2, 3, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 2-9", new TargetCardInHand(2, 9, anyCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "hand 8-9", new TargetCardInHand(8, 9, anyCard)));
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "impossible 0, X=0", new TargetCardInHand(0, impossibleCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "impossible 1", new TargetCardInHand(1, impossibleCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "impossible 3", new TargetCardInHand(3, impossibleCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "impossible 0-1", new TargetCardInHand(0, 1, impossibleCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "impossible 0-3", new TargetCardInHand(0, 3, impossibleCard)));
|
||||||
|
runner.registerDialog(new ChooseCardsTestableDialog(isTargetChoice, notTarget, isYou, "impossible any", new TargetCardInHand(0, Integer.MAX_VALUE, impossibleCard)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.choices.*;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of testable game dialogs
|
||||||
|
* <p>
|
||||||
|
* Supported methods:
|
||||||
|
* - player.choose(choice)
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
class ChooseChoiceTestableDialog extends BaseTestableDialog {
|
||||||
|
|
||||||
|
boolean isYou; // who choose - you or opponent
|
||||||
|
Choice choice;
|
||||||
|
|
||||||
|
public ChooseChoiceTestableDialog(boolean isYou, String name, Choice choice) {
|
||||||
|
super(String.format("player.choose(%s, choice)", isYou ? "you" : "AI"), name, choice.getClass().getSimpleName());
|
||||||
|
this.isYou = isYou;
|
||||||
|
this.choice = choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||||
|
Player choosingPlayer = this.isYou ? player : opponent;
|
||||||
|
Choice dialog = this.choice.copy();
|
||||||
|
boolean chooseRes = choosingPlayer.choose(Outcome.Benefit, dialog, game);
|
||||||
|
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
result.add(getGroup() + " - " + this.getName() + " - " + (chooseRes ? "TRUE" : "FALSE"));
|
||||||
|
result.add("");
|
||||||
|
if (dialog.isKeyChoice()) {
|
||||||
|
String key = dialog.getChoiceKey();
|
||||||
|
result.add(String.format("* selected key: %s (%s)", key, dialog.getKeyChoices().getOrDefault(key, null)));
|
||||||
|
} else {
|
||||||
|
result.add(String.format("* selected value: %s", dialog.getChoice()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void register(TestableDialogsRunner runner) {
|
||||||
|
// TODO: add require option
|
||||||
|
// TODO: add ChoiceImpl with diff popup hints
|
||||||
|
List<Boolean> isYous = Arrays.asList(false, true);
|
||||||
|
for (boolean isYou : isYous) {
|
||||||
|
runner.registerDialog(new ChooseChoiceTestableDialog(isYou, "", new ChoiceBasicLandType()));
|
||||||
|
runner.registerDialog(new ChooseChoiceTestableDialog(isYou, "", new ChoiceCardType()));
|
||||||
|
runner.registerDialog(new ChooseChoiceTestableDialog(isYou, "", new ChoiceColor()));
|
||||||
|
runner.registerDialog(new ChooseChoiceTestableDialog(isYou, "", new ChoiceColorOrArtifact()));
|
||||||
|
runner.registerDialog(new ChooseChoiceTestableDialog(isYou, "", new ChoiceCreatureType(null, null))); // TODO: must be dynamic to pass game/source
|
||||||
|
runner.registerDialog(new ChooseChoiceTestableDialog(isYou, "", new ChoiceLandType()));
|
||||||
|
runner.registerDialog(new ChooseChoiceTestableDialog(isYou, "", new ChoiceLeftOrRight()));
|
||||||
|
runner.registerDialog(new ChooseChoiceTestableDialog(isYou, "", new ChoicePlaneswalkerType())); // TODO: must be dynamic to pass game/source
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,69 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of testable game dialogs
|
||||||
|
* <p>
|
||||||
|
* Supported methods:
|
||||||
|
* - player.choosePile()
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
class ChoosePileTestableDialog extends BaseTestableDialog {
|
||||||
|
|
||||||
|
boolean isYou; // who choose - you or opponent
|
||||||
|
int pileSize1;
|
||||||
|
int pileSize2;
|
||||||
|
|
||||||
|
public ChoosePileTestableDialog(boolean isYou, int pileSize1, int pileSize2) {
|
||||||
|
super(String.format("player.choosePile(%s)", isYou ? "you" : "AI"), "pile sizes: " + pileSize1 + " and " + pileSize2, "");
|
||||||
|
this.isYou = isYou;
|
||||||
|
this.pileSize1 = pileSize1;
|
||||||
|
this.pileSize2 = pileSize2;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||||
|
// TODO: it's ok to show broken title - must add html support in windows's title someday
|
||||||
|
String mainMessage = "main <font color=green>message</font> with html" + CardUtil.getSourceLogName(game, source);
|
||||||
|
|
||||||
|
// random piles (make sure it contain good amount of cards)
|
||||||
|
List<Card> all = new ArrayList<>(game.getCards());
|
||||||
|
Collections.shuffle(all);
|
||||||
|
List<Card> pile1 = all.stream().limit(this.pileSize1).collect(Collectors.toList());
|
||||||
|
Collections.shuffle(all);
|
||||||
|
List<Card> pile2 = all.stream().limit(this.pileSize2).collect(Collectors.toList());
|
||||||
|
|
||||||
|
Player choosingPlayer = this.isYou ? player : opponent;
|
||||||
|
boolean chooseRes = choosingPlayer.choosePile(Outcome.Benefit, mainMessage, pile1, pile2, game);
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
result.add(getGroup() + " - " + this.getName() + " - " + (chooseRes ? "TRUE" : "FALSE"));
|
||||||
|
result.add(" * selected pile: " + (chooseRes ? "pile 1" : "pile 2"));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void register(TestableDialogsRunner runner) {
|
||||||
|
List<Boolean> isYous = Arrays.asList(false, true);
|
||||||
|
for (boolean isYou : isYous) {
|
||||||
|
runner.registerDialog(new ChoosePileTestableDialog(isYou, 3, 5));
|
||||||
|
runner.registerDialog(new ChoosePileTestableDialog(isYou, 10, 10));
|
||||||
|
runner.registerDialog(new ChoosePileTestableDialog(isYou, 30, 30));
|
||||||
|
runner.registerDialog(new ChoosePileTestableDialog(isYou, 90, 90));
|
||||||
|
runner.registerDialog(new ChoosePileTestableDialog(isYou, 0, 10));
|
||||||
|
runner.registerDialog(new ChoosePileTestableDialog(isYou, 10, 0));
|
||||||
|
runner.registerDialog(new ChoosePileTestableDialog(isYou, 0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.Target;
|
||||||
|
import mage.target.Targets;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of testable game dialogs
|
||||||
|
* <p>
|
||||||
|
* Supported methods:
|
||||||
|
* - target.choose()
|
||||||
|
* - target.chooseTarget()
|
||||||
|
* - player.choose(target)
|
||||||
|
* - player.chooseTarget(target)
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
class ChooseTargetTestableDialog extends BaseTestableDialog {
|
||||||
|
|
||||||
|
Target target;
|
||||||
|
boolean isPlayerChoice; // how to choose - by player.choose or by target.choose
|
||||||
|
boolean isTargetChoice; // how to choose - by xxx.choose or xxx.chooseTarget
|
||||||
|
boolean isYou; // who choose - you or opponent
|
||||||
|
|
||||||
|
public ChooseTargetTestableDialog(boolean isPlayerChoice, boolean isTargetChoice, boolean notTarget, boolean isYou, String name, Target target) {
|
||||||
|
super(String.format("%s%s(%s, %s)",
|
||||||
|
isPlayerChoice ? "player.choose" : "target.choose",
|
||||||
|
isTargetChoice ? "target" : "", // chooseTarget or choose
|
||||||
|
isYou ? "you" : "AI",
|
||||||
|
notTarget ? "not target" : "target"), name, target.toString());
|
||||||
|
this.isPlayerChoice = isPlayerChoice;
|
||||||
|
this.isTargetChoice = isTargetChoice;
|
||||||
|
this.target = target.withNotTarget(notTarget);
|
||||||
|
this.isYou = isYou;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||||
|
Target choosingTarget = this.target.copy();
|
||||||
|
Player choosingPlayer = this.isYou ? player : opponent;
|
||||||
|
|
||||||
|
boolean chooseRes;
|
||||||
|
if (this.isPlayerChoice) {
|
||||||
|
// player.chooseXXX
|
||||||
|
if (this.isTargetChoice) {
|
||||||
|
chooseRes = choosingPlayer.chooseTarget(Outcome.Benefit, choosingTarget, source, game);
|
||||||
|
} else {
|
||||||
|
chooseRes = choosingPlayer.choose(Outcome.Benefit, choosingTarget, source, game);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// target.chooseXXX
|
||||||
|
if (this.isTargetChoice) {
|
||||||
|
chooseRes = choosingTarget.chooseTarget(Outcome.Benefit, choosingPlayer.getId(), source, game);
|
||||||
|
} else {
|
||||||
|
chooseRes = choosingTarget.choose(Outcome.Benefit, choosingPlayer.getId(), source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
if (chooseRes) {
|
||||||
|
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "TRUE", new Targets(choosingTarget), source, game, result);
|
||||||
|
} else {
|
||||||
|
Targets.printDebugTargets(getGroup() + " - " + this.getName() + " - " + "FALSE", new Targets(choosingTarget), source, game, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void register(TestableDialogsRunner runner) {
|
||||||
|
// test game started with 2 players and 1 land on battlefield
|
||||||
|
// so it's better to use target limits like 0, 1, 3, 5, max
|
||||||
|
|
||||||
|
List<Boolean> notTargets = Arrays.asList(false, true);
|
||||||
|
List<Boolean> isYous = Arrays.asList(false, true);
|
||||||
|
List<Boolean> isPlayerChoices = Arrays.asList(false, true);
|
||||||
|
List<Boolean> isTargetChoices = Arrays.asList(false, true);
|
||||||
|
for (boolean notTarget : notTargets) {
|
||||||
|
for (boolean isYou : isYous) {
|
||||||
|
for (boolean isTargetChoice : isTargetChoices) {
|
||||||
|
for (boolean isPlayerChoice : isPlayerChoices) {
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0 e.g. X=0", createAnyTarget(0, 0))); // simulate X=0
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 1", createAnyTarget(1, 1)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 3", createAnyTarget(3, 3)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 5", createAnyTarget(5, 5)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any max", createAnyTarget(0, Integer.MAX_VALUE)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0-1", createAnyTarget(0, 1)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0-3", createAnyTarget(0, 3)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 0-5", createAnyTarget(0, 5)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 1-3", createAnyTarget(1, 3)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 2-3", createAnyTarget(2, 3)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 1-5", createAnyTarget(1, 5)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 2-5", createAnyTarget(2, 5)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 3-5", createAnyTarget(3, 5)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "any 4-5", createAnyTarget(4, 5))); // impossible on 3 targets
|
||||||
|
//
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 0, e.g. X=0", createImpossibleTarget(0, 0)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1", createImpossibleTarget(1, 1)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 3", createImpossibleTarget(3, 3)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 0-1", createImpossibleTarget(0, 1)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 0-3", createImpossibleTarget(0, 3)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 1-3", createImpossibleTarget(1, 3)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible 2-3", createImpossibleTarget(2, 3)));
|
||||||
|
runner.registerDialog(new ChooseTargetTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "impossible max", createImpossibleTarget(0, Integer.MAX_VALUE)));
|
||||||
|
//
|
||||||
|
/*
|
||||||
|
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures 0, e.g. X=0", createCreatureTarget(0, 0))); // simulate X=0
|
||||||
|
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures 1", createCreatureTarget(1, 1)));
|
||||||
|
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures 3", createCreatureTarget(3, 3)));
|
||||||
|
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures 5", createCreatureTarget(5, 5)));
|
||||||
|
runner.registerDialog(new PlayerChooseTestableDialog(isPlayerChoice, isTargetChoice, notTarget, isYou, "creatures max", createCreatureTarget(0, Integer.MAX_VALUE)));
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,77 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of testable game dialogs
|
||||||
|
* <p>
|
||||||
|
* Supported methods:
|
||||||
|
* - player.chooseUse()
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
class ChooseUseTestableDialog extends BaseTestableDialog {
|
||||||
|
|
||||||
|
boolean isYou; // who choose - you or opponent
|
||||||
|
String trueText;
|
||||||
|
String falseText;
|
||||||
|
String messageMain;
|
||||||
|
String messageAdditional;
|
||||||
|
|
||||||
|
public ChooseUseTestableDialog(boolean isYou, String name, String trueText, String falseText, String messageMain, String messageAdditional) {
|
||||||
|
super(String.format("player.chooseUse(%s)", isYou ? "you" : "AI"), name + buildName(trueText, falseText, messageMain, messageAdditional), "");
|
||||||
|
this.isYou = isYou;
|
||||||
|
this.trueText = trueText;
|
||||||
|
this.falseText = falseText;
|
||||||
|
this.messageMain = messageMain;
|
||||||
|
this.messageAdditional = messageAdditional;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String buildName(String trueText, String falseText, String messageMain, String messageAdditional) {
|
||||||
|
String buttonsInfo = (trueText == null ? "default" : "custom") + "/" + (falseText == null ? "default" : "custom");
|
||||||
|
String messagesInfo = (messageMain == null ? "-" : "main") + "/" + (messageAdditional == null ? "-" : "additional");
|
||||||
|
return String.format("buttons: %s, messages: %s", buttonsInfo, messagesInfo);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||||
|
Player choosingPlayer = this.isYou ? player : opponent;
|
||||||
|
boolean chooseRes = choosingPlayer.chooseUse(
|
||||||
|
Outcome.Benefit,
|
||||||
|
messageMain,
|
||||||
|
messageAdditional == null ? null : messageAdditional + CardUtil.getSourceLogName(game, source),
|
||||||
|
trueText,
|
||||||
|
falseText,
|
||||||
|
source,
|
||||||
|
game
|
||||||
|
);
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
result.add(chooseRes ? "TRUE" : "FALSE");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void register(TestableDialogsRunner runner) {
|
||||||
|
List<Boolean> isYous = Arrays.asList(false, true);
|
||||||
|
String trueButton = "true button";
|
||||||
|
String falseButton = "false button";
|
||||||
|
String mainMessage = "main <font color=green>message</font> with html";
|
||||||
|
String additionalMessage = "additional main <font color=red>message</font> with html";
|
||||||
|
for (boolean isYou : isYous) {
|
||||||
|
runner.registerDialog(new ChooseUseTestableDialog(isYou, "", null, null, mainMessage, additionalMessage));
|
||||||
|
runner.registerDialog(new ChooseUseTestableDialog(isYou, "", trueButton, falseButton, mainMessage, additionalMessage));
|
||||||
|
runner.registerDialog(new ChooseUseTestableDialog(isYou, "", null, falseButton, mainMessage, additionalMessage));
|
||||||
|
runner.registerDialog(new ChooseUseTestableDialog(isYou, "", trueButton, null, mainMessage, additionalMessage));
|
||||||
|
runner.registerDialog(new ChooseUseTestableDialog(isYou, "error ", trueButton, falseButton, null, additionalMessage));
|
||||||
|
runner.registerDialog(new ChooseUseTestableDialog(isYou, "", trueButton, falseButton, mainMessage, null));
|
||||||
|
runner.registerDialog(new ChooseUseTestableDialog(isYou, "error ", trueButton, falseButton, null, null));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of testable game dialogs
|
||||||
|
* <p>
|
||||||
|
* How to use:
|
||||||
|
* - extends BaseTestableDialog
|
||||||
|
* - implement showDialog
|
||||||
|
* - create register with all possible sample dialogs
|
||||||
|
* - call register in main runner's constructor
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
interface TestableDialog {
|
||||||
|
|
||||||
|
String getGroup();
|
||||||
|
|
||||||
|
String getName();
|
||||||
|
|
||||||
|
String getDescription();
|
||||||
|
|
||||||
|
List<String> showDialog(Player player, Ability source, Game game, Player opponent);
|
||||||
|
|
||||||
|
void showResult(Player player, Game game, String result);
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,201 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.choices.Choice;
|
||||||
|
import mage.choices.ChoiceHintType;
|
||||||
|
import mage.choices.ChoiceImpl;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Part of testable game dialogs
|
||||||
|
* <p>
|
||||||
|
* Helper class to create additional options in cheat menu - allow to test any game dialogs with any settings
|
||||||
|
* Allow to call game dialogs from you or from your opponent, e.g. from AI player
|
||||||
|
* <p>
|
||||||
|
* All existing human's choose dialogs (search by waitForResponse):
|
||||||
|
* <p>
|
||||||
|
* Support of single dialogs (can be called inside effects):
|
||||||
|
* [x] choose(target)
|
||||||
|
* [x] choose(cards)
|
||||||
|
* [x] choose(choice)
|
||||||
|
* [x] chooseTarget(target)
|
||||||
|
* [x] chooseTarget(cards)
|
||||||
|
* [x] chooseTargetAmount
|
||||||
|
* [x] chooseUse
|
||||||
|
* [x] choosePile
|
||||||
|
* [ ] announceXMana // TODO: implement
|
||||||
|
* [ ] announceXCost // TODO: implement
|
||||||
|
* [ ] getAmount // TODO: implement
|
||||||
|
* [ ] getMultiAmountWithIndividualConstraints // TODO: implement
|
||||||
|
* <p>
|
||||||
|
* Support of priority dialogs (can be called by game engine, some can be implemented in theory):
|
||||||
|
* --- priority
|
||||||
|
* --- playManaHandling
|
||||||
|
* --- activateSpecialAction
|
||||||
|
* --- activateAbility
|
||||||
|
* --- chooseAbilityForCast
|
||||||
|
* --- chooseLandOrSpellAbility
|
||||||
|
* --- chooseMode
|
||||||
|
* --- chooseMulligan
|
||||||
|
* --- chooseReplacementEffect
|
||||||
|
* --- chooseTriggeredAbility
|
||||||
|
* --- selectAttackers
|
||||||
|
* --- selectBlockers
|
||||||
|
* --- selectCombatGroup (part of selectBlockers)
|
||||||
|
* <p>
|
||||||
|
* Support of outdated dialogs (not used anymore)
|
||||||
|
* --- announceRepetitions (part of removed macro feature)
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class TestableDialogsRunner {
|
||||||
|
|
||||||
|
private final List<TestableDialog> dialogs = new ArrayList<>();
|
||||||
|
|
||||||
|
static final int LAST_SELECTED_GROUP_ID = 997;
|
||||||
|
static final int LAST_SELECTED_DIALOG_ID = 998;
|
||||||
|
|
||||||
|
// for better UX - save last selected options, so can return to it later
|
||||||
|
// it's ok to have it global, cause test mode for local and single user environment
|
||||||
|
static String lastSelectedGroup = null;
|
||||||
|
static TestableDialog lastSelectedDialog = null;
|
||||||
|
|
||||||
|
|
||||||
|
public TestableDialogsRunner() {
|
||||||
|
ChooseTargetTestableDialog.register(this);
|
||||||
|
ChooseCardsTestableDialog.register(this);
|
||||||
|
ChooseUseTestableDialog.register(this);
|
||||||
|
ChooseChoiceTestableDialog.register(this);
|
||||||
|
ChoosePileTestableDialog.register(this);
|
||||||
|
ChooseAmountTestableDialog.register(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void registerDialog(TestableDialog dialog) {
|
||||||
|
this.dialogs.add(dialog);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void selectAndShowTestableDialog(Player player, Ability source, Game game, Player opponent) {
|
||||||
|
// select group or fast links
|
||||||
|
List<String> groups = this.dialogs.stream()
|
||||||
|
.map(TestableDialog::getGroup)
|
||||||
|
.distinct()
|
||||||
|
.sorted()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
Choice choice = prepareSelectGroupChoice(groups);
|
||||||
|
player.choose(Outcome.Benefit, choice, game);
|
||||||
|
String needGroup = null;
|
||||||
|
TestableDialog needDialog = null;
|
||||||
|
if (choice.getChoiceKey() != null) {
|
||||||
|
int needIndex = Integer.parseInt(choice.getChoiceKey());
|
||||||
|
if (needIndex == LAST_SELECTED_GROUP_ID && lastSelectedGroup != null) {
|
||||||
|
// fast link to group
|
||||||
|
needGroup = lastSelectedGroup;
|
||||||
|
} else if (needIndex == LAST_SELECTED_DIALOG_ID && lastSelectedDialog != null) {
|
||||||
|
// fast link to dialog
|
||||||
|
needGroup = lastSelectedDialog.getGroup();
|
||||||
|
needDialog = lastSelectedDialog;
|
||||||
|
} else if (needIndex < groups.size()) {
|
||||||
|
// group
|
||||||
|
needGroup = groups.get(needIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (needGroup == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// select dialog
|
||||||
|
if (needDialog == null) {
|
||||||
|
choice = prepareSelectDialogChoice(needGroup);
|
||||||
|
player.choose(Outcome.Benefit, choice, game);
|
||||||
|
if (choice.getChoiceKey() != null) {
|
||||||
|
int needIndex = Integer.parseInt(choice.getChoiceKey());
|
||||||
|
if (needIndex < this.dialogs.size()) {
|
||||||
|
needDialog = this.dialogs.get(needIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (needDialog == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// all fine, can show it and finish
|
||||||
|
lastSelectedGroup = needGroup;
|
||||||
|
lastSelectedDialog = needDialog;
|
||||||
|
List<String> resInfo = needDialog.showDialog(player, source, game, opponent);
|
||||||
|
needDialog.showResult(player, game, String.join("<br>", resInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Choice prepareSelectGroupChoice(List<String> groups) {
|
||||||
|
// try to choose group or fast links
|
||||||
|
|
||||||
|
Choice choice = new ChoiceImpl(false);
|
||||||
|
choice.setMessage("Choose dialogs group to run");
|
||||||
|
|
||||||
|
// main groups
|
||||||
|
int recNumber = 0;
|
||||||
|
for (int i = 0; i < groups.size(); i++) {
|
||||||
|
recNumber++;
|
||||||
|
String group = groups.get(i);
|
||||||
|
choice.withItem(
|
||||||
|
String.valueOf(i),
|
||||||
|
String.format("%02d. %s", recNumber, group),
|
||||||
|
recNumber,
|
||||||
|
ChoiceHintType.TEXT,
|
||||||
|
String.join("<br>", group)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// fast link to last group
|
||||||
|
String lastGroupInfo = String.format(" -> last group: %s", lastSelectedGroup == null ? "not used" : lastSelectedGroup);
|
||||||
|
choice.withItem(
|
||||||
|
String.valueOf(LAST_SELECTED_GROUP_ID),
|
||||||
|
lastGroupInfo,
|
||||||
|
-2,
|
||||||
|
ChoiceHintType.TEXT,
|
||||||
|
lastGroupInfo
|
||||||
|
);
|
||||||
|
|
||||||
|
// fast link to last dialog
|
||||||
|
String lastDialogName = (lastSelectedDialog == null ? "not used" : String.format("%s - %s",
|
||||||
|
lastSelectedDialog.getName(), lastSelectedDialog.getDescription()));
|
||||||
|
String lastDialogInfo = String.format(" -> last dialog: %s", lastDialogName);
|
||||||
|
choice.withItem(
|
||||||
|
String.valueOf(LAST_SELECTED_DIALOG_ID),
|
||||||
|
lastDialogInfo,
|
||||||
|
-1,
|
||||||
|
ChoiceHintType.TEXT,
|
||||||
|
lastDialogInfo
|
||||||
|
);
|
||||||
|
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Choice prepareSelectDialogChoice(String needGroup) {
|
||||||
|
Choice choice = new ChoiceImpl(false);
|
||||||
|
choice.setMessage("Choose game dialog to run from " + needGroup);
|
||||||
|
int recNumber = 0;
|
||||||
|
for (int i = 0; i < this.dialogs.size(); i++) {
|
||||||
|
TestableDialog dialog = this.dialogs.get(i);
|
||||||
|
if (!dialog.getGroup().equals(needGroup)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
recNumber++;
|
||||||
|
String info = String.format("%s - %s - %s", dialog.getGroup(), dialog.getName(), dialog.getDescription());
|
||||||
|
choice.withItem(
|
||||||
|
String.valueOf(i),
|
||||||
|
String.format("%02d. %s", recNumber, info),
|
||||||
|
recNumber,
|
||||||
|
ChoiceHintType.TEXT,
|
||||||
|
String.join("<br>", info)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return choice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -399,7 +399,7 @@ 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.doneChoosing(game)) {
|
if (!target.isChoiceCompleted(game)) {
|
||||||
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();
|
||||||
|
|
@ -848,10 +848,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
||||||
if (targets.isEmpty()) {
|
if (targets.isEmpty()) {
|
||||||
return super.chooseTarget(outcome, cards, target, source, game);
|
return super.chooseTarget(outcome, cards, target, source, game);
|
||||||
}
|
}
|
||||||
if (!target.doneChoosing(game)) {
|
if (!target.isChoiceCompleted(game)) {
|
||||||
for (UUID targetId : targets) {
|
for (UUID targetId : targets) {
|
||||||
target.addTarget(targetId, source, game);
|
target.addTarget(targetId, source, game);
|
||||||
if (target.doneChoosing(game)) {
|
if (target.isChoiceCompleted(game)) {
|
||||||
targets.clear();
|
targets.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
@ -866,10 +866,10 @@ public class ComputerPlayer6 extends ComputerPlayer {
|
||||||
if (targets.isEmpty()) {
|
if (targets.isEmpty()) {
|
||||||
return super.choose(outcome, cards, target, source, game);
|
return super.choose(outcome, cards, target, source, game);
|
||||||
}
|
}
|
||||||
if (!target.doneChoosing(game)) {
|
if (!target.isChoiceCompleted(game)) {
|
||||||
for (UUID targetId : targets) {
|
for (UUID targetId : targets) {
|
||||||
target.add(targetId, game);
|
target.add(targetId, game);
|
||||||
if (target.doneChoosing(game)) {
|
if (target.isChoiceCompleted(game)) {
|
||||||
targets.clear();
|
targets.clear();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,7 @@ public final class SimulatedPlayer2 extends ComputerPlayer {
|
||||||
}
|
}
|
||||||
newAbility.adjustTargets(game);
|
newAbility.adjustTargets(game);
|
||||||
// add the different possible target option for the specific X value
|
// add the different possible target option for the specific X value
|
||||||
if (!newAbility.getTargets().getUnchosen(game).isEmpty()) {
|
if (newAbility.getTargets().getNextUnchosen(game) != null) {
|
||||||
addTargetOptions(options, newAbility, targetNum, game);
|
addTargetOptions(options, newAbility, targetNum, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -41,7 +41,6 @@ import mage.players.net.UserData;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
import mage.target.TargetAmount;
|
import mage.target.TargetAmount;
|
||||||
import mage.target.TargetCard;
|
import mage.target.TargetCard;
|
||||||
import mage.target.TargetPermanent;
|
|
||||||
import mage.target.common.TargetAttackingCreature;
|
import mage.target.common.TargetAttackingCreature;
|
||||||
import mage.target.common.TargetDefender;
|
import mage.target.common.TargetDefender;
|
||||||
import mage.target.targetpointer.TargetPointer;
|
import mage.target.targetpointer.TargetPointer;
|
||||||
|
|
@ -387,9 +386,10 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseMulligan(Game game) {
|
public boolean chooseMulligan(Game game) {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: rework to use existing chooseUse dialog, but do not remove chooseMulligan (AI must use special logic inside it)
|
||||||
while (canRespond()) {
|
while (canRespond()) {
|
||||||
int nextHandSize = game.mulliganDownTo(playerId);
|
int nextHandSize = game.mulliganDownTo(playerId);
|
||||||
String cardsCountInfo = nextHandSize + (nextHandSize == 1 ? " card" : " cards");
|
String cardsCountInfo = nextHandSize + (nextHandSize == 1 ? " card" : " cards");
|
||||||
|
|
@ -427,7 +427,11 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) {
|
public boolean chooseUse(Outcome outcome, String message, String secondMessage, String trueText, String falseText, Ability source, Game game) {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (message == null) {
|
||||||
|
throw new IllegalArgumentException("Wrong code usage: main message is null");
|
||||||
}
|
}
|
||||||
|
|
||||||
MessageToClient messageToClient = new MessageToClient(message, secondMessage);
|
MessageToClient messageToClient = new MessageToClient(message, secondMessage);
|
||||||
|
|
@ -620,7 +624,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Choice choice, Game game) {
|
public boolean choose(Outcome outcome, Choice choice, Game game) {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choice.isKeyChoice() && choice.getKeyChoices().isEmpty()) {
|
if (choice.isKeyChoice() && choice.getKeyChoices().isEmpty()) {
|
||||||
|
|
@ -683,7 +687,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
@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 (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// choose one or multiple permanents
|
// choose one or multiple permanents
|
||||||
|
|
@ -696,98 +700,85 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
options = new HashMap<>();
|
options = new HashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
while (canRespond()) {
|
// stop on completed, e.g. X=0
|
||||||
Set<UUID> possibleTargetIds = target.possibleTargets(abilityControllerId, source, game);
|
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
if (possibleTargetIds == null || possibleTargetIds.isEmpty()) {
|
return false;
|
||||||
return target.getTargets().size() >= target.getMinNumberOfTargets();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (canRespond()) {
|
||||||
|
|
||||||
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
||||||
|
|
||||||
|
// enable done button after min targets selected
|
||||||
if (target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
if (target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game);
|
// stop on impossible selection
|
||||||
|
if (required && !target.canChoose(abilityControllerId, source, game)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// responseId is null if a choice couldn't be automatically made
|
// stop on nothing to choose
|
||||||
if (responseId == null) {
|
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game);
|
||||||
List<UUID> chosenTargets = target.getTargets();
|
if (required && possibleTargets.isEmpty()) {
|
||||||
options.put("chosenTargets", (Serializable) chosenTargets);
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MAKE A CHOICE
|
||||||
|
UUID autoChosenId = target.tryToAutoChoose(abilityControllerId, source, game);
|
||||||
|
if (autoChosenId != null && !target.contains(autoChosenId)) {
|
||||||
|
// auto-choose
|
||||||
|
target.add(autoChosenId, game);
|
||||||
|
// continue to next target (example: auto-choose must fill min/max = 2 from 2 possible cards)
|
||||||
|
} else {
|
||||||
|
// manual choose
|
||||||
|
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||||
|
|
||||||
prepareForResponse(game);
|
prepareForResponse(game);
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(game), getRelatedObjectName(source, game)), possibleTargetIds, required, getOptions(target, options));
|
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(game), getRelatedObjectName(source, game)), possibleTargets, required, getOptions(target, options));
|
||||||
}
|
}
|
||||||
waitForResponse(game);
|
waitForResponse(game);
|
||||||
responseId = getFixedResponseUUID(game);
|
UUID responseId = getFixedResponseUUID(game);
|
||||||
}
|
|
||||||
|
|
||||||
if (responseId != null) {
|
if (responseId != null) {
|
||||||
// selected some target
|
// selected something
|
||||||
|
|
||||||
// remove selected
|
// remove selected
|
||||||
if (target.getTargets().contains(responseId)) {
|
if (target.contains(responseId)) {
|
||||||
target.remove(responseId);
|
target.remove(responseId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!possibleTargetIds.contains(responseId)) {
|
if (possibleTargets.contains(responseId) && target.canTarget(getId(), responseId, source, game)) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (target instanceof TargetPermanent) {
|
|
||||||
if (((TargetPermanent) target).canTarget(abilityControllerId, responseId, source, game, false)) {
|
|
||||||
target.add(responseId, game);
|
target.add(responseId, game);
|
||||||
if (target.doneChoosing(game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
return true;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
MageObject object = game.getObject(source);
|
// stop on done/cancel button press
|
||||||
if (object instanceof Ability) {
|
if (target.isChosen(game)) {
|
||||||
if (target.canTarget(responseId, (Ability) object, game)) {
|
break;
|
||||||
if (target.getTargets().contains(responseId)) { // if already included remove it with
|
|
||||||
target.remove(responseId);
|
|
||||||
} else {
|
} else {
|
||||||
target.addTarget(responseId, (Ability) object, game);
|
|
||||||
if (target.doneChoosing(game)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (target.canTarget(responseId, game)) {
|
|
||||||
if (target.getTargets().contains(responseId)) { // if already included remove it with
|
|
||||||
target.remove(responseId);
|
|
||||||
} else {
|
|
||||||
target.addTarget(responseId, null, game);
|
|
||||||
if (target.doneChoosing(game)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// send other command like cancel or done (??sends other commands like concede??)
|
|
||||||
|
|
||||||
// auto-complete on all selected
|
|
||||||
if (target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// cancel/done button
|
|
||||||
if (!required) {
|
if (!required) {
|
||||||
return false;
|
// can stop at any moment
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// continue to next target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return target.isChosen(game) && target.getTargets().size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
|
public boolean chooseTarget(Outcome outcome, Target target, Ability source, Game game) {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// choose one or multiple targets
|
// choose one or multiple targets
|
||||||
|
|
@ -799,57 +790,60 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
Map<String, Serializable> options = new HashMap<>();
|
Map<String, Serializable> options = new HashMap<>();
|
||||||
|
|
||||||
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 != null ? source.getSourceId() : null, game);
|
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
||||||
if (possibleTargetIds.isEmpty()
|
if (possibleTargets.isEmpty()
|
||||||
|| target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
|| target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// auto-choose
|
||||||
UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game);
|
UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game);
|
||||||
|
|
||||||
// responseId is null if a choice couldn't be automatically made
|
// manual choice
|
||||||
if (responseId == null) {
|
if (responseId == null) {
|
||||||
|
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||||
|
|
||||||
List<UUID> chosenTargets = target.getTargets();
|
|
||||||
options.put("chosenTargets", (Serializable) chosenTargets);
|
|
||||||
|
|
||||||
prepareForResponse(game);
|
prepareForResponse(game);
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(game), getRelatedObjectName(source, game)),
|
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(game), getRelatedObjectName(source, game)),
|
||||||
possibleTargetIds, required, getOptions(target, options));
|
possibleTargets, required, getOptions(target, options));
|
||||||
}
|
}
|
||||||
waitForResponse(game);
|
waitForResponse(game);
|
||||||
responseId = getFixedResponseUUID(game);
|
responseId = getFixedResponseUUID(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseId != null) {
|
if (responseId != null) {
|
||||||
// remove selected
|
// remove old target
|
||||||
if (target.getTargets().contains(responseId)) {
|
if (target.contains(responseId)) {
|
||||||
target.remove(responseId);
|
target.remove(responseId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (possibleTargetIds.contains(responseId)) {
|
// add new target
|
||||||
|
if (possibleTargets.contains(responseId)) {
|
||||||
if (target.canTarget(abilityControllerId, responseId, source, game)) {
|
if (target.canTarget(abilityControllerId, responseId, source, game)) {
|
||||||
target.addTarget(responseId, source, game);
|
target.addTarget(responseId, source, game);
|
||||||
if (target.doneChoosing(game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
// done or cancel button pressed
|
||||||
return true;
|
if (target.isChosen(game)) {
|
||||||
}
|
// try to finish
|
||||||
if (!required) {
|
|
||||||
return false;
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!required) {
|
||||||
|
// can stop at any moment
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return target.isChosen(game) && target.getTargets().size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Serializable> getOptions(Target target, Map<String, Serializable> options) {
|
private Map<String, Serializable> getOptions(Target target, Map<String, Serializable> options) {
|
||||||
|
|
@ -867,10 +861,10 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// choose one or multiple cards
|
// ignore bad state
|
||||||
if (cards == null || cards.isEmpty()) {
|
if (cards == null || cards.isEmpty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -884,6 +878,14 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (canRespond()) {
|
while (canRespond()) {
|
||||||
|
|
||||||
|
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);
|
||||||
if (count == 0
|
if (count == 0
|
||||||
|
|
@ -891,23 +893,22 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UUID> chosenTargets = target.getTargets();
|
|
||||||
List<UUID> possibleTargets = new ArrayList<>();
|
|
||||||
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)
|
||||||
|
// TODO: need research - is it used?
|
||||||
if (required && possibleTargets.isEmpty()) {
|
if (required && possibleTargets.isEmpty()) {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game, possibleTargets);
|
// MAKE A CHOICE
|
||||||
|
UUID autoChosenId = target.tryToAutoChoose(abilityControllerId, source, game, possibleTargets);
|
||||||
if (responseId == null) {
|
if (autoChosenId != null && !target.contains(autoChosenId)) {
|
||||||
|
// auto-choose
|
||||||
|
target.add(autoChosenId, game);
|
||||||
|
// continue to next target (example: auto-choose must fill min/max = 2 from 2 possible cards)
|
||||||
|
} else {
|
||||||
|
// manual choose
|
||||||
Map<String, Serializable> options = getOptions(target, null);
|
Map<String, Serializable> options = getOptions(target, null);
|
||||||
options.put("chosenTargets", (Serializable) chosenTargets);
|
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||||
if (!possibleTargets.isEmpty()) {
|
if (!possibleTargets.isEmpty()) {
|
||||||
options.put("possibleTargets", (Serializable) possibleTargets);
|
options.put("possibleTargets", (Serializable) possibleTargets);
|
||||||
}
|
}
|
||||||
|
|
@ -918,29 +919,38 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
waitForResponse(game);
|
waitForResponse(game);
|
||||||
|
|
||||||
responseId = getFixedResponseUUID(game);
|
UUID responseId = getFixedResponseUUID(game);
|
||||||
}
|
|
||||||
|
|
||||||
if (responseId != null) {
|
if (responseId != null) {
|
||||||
if (target.getTargets().contains(responseId)) { // if already included remove it with
|
// selected something
|
||||||
|
|
||||||
|
// remove selected
|
||||||
|
if (target.contains(responseId)) {
|
||||||
target.remove(responseId);
|
target.remove(responseId);
|
||||||
} else {
|
continue;
|
||||||
if (target.canTarget(abilityControllerId, responseId, source, cards, game)) {
|
}
|
||||||
|
|
||||||
|
if (possibleTargets.contains(responseId) && target.canTarget(getId(), responseId, source, cards, game)) {
|
||||||
target.add(responseId, game);
|
target.add(responseId, game);
|
||||||
if (target.doneChoosing(game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
if (target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
// done or cancel button pressed
|
||||||
return true;
|
if (target.isChosen(game)) {
|
||||||
}
|
// try to finish
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
if (!required) {
|
if (!required) {
|
||||||
|
// can stop at any moment
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// continue to next target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
@ -949,7 +959,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
public boolean chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cards == null || cards.isEmpty()) {
|
if (cards == null || cards.isEmpty()) {
|
||||||
|
|
@ -978,17 +988,16 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
possibleTargets.add(cardId);
|
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 (required && possibleTargets.isEmpty()) {
|
if (possibleTargets.isEmpty()) {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game, possibleTargets);
|
UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game, possibleTargets);
|
||||||
|
|
||||||
if (responseId == null) {
|
if (responseId == null) {
|
||||||
List<UUID> chosenTargets = target.getTargets();
|
|
||||||
Map<String, Serializable> options = getOptions(target, null);
|
Map<String, Serializable> options = getOptions(target, null);
|
||||||
options.put("chosenTargets", (Serializable) chosenTargets);
|
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||||
|
|
||||||
if (!possibleTargets.isEmpty()) {
|
if (!possibleTargets.isEmpty()) {
|
||||||
options.put("possibleTargets", (Serializable) possibleTargets);
|
options.put("possibleTargets", (Serializable) possibleTargets);
|
||||||
|
|
@ -1004,11 +1013,11 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (responseId != null) {
|
if (responseId != null) {
|
||||||
if (target.getTargets().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 (target.canTarget(abilityControllerId, responseId, source, cards, game)) {
|
||||||
target.addTarget(responseId, source, game);
|
target.addTarget(responseId, source, game);
|
||||||
if (target.doneChoosing(game)) {
|
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1030,7 +1039,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// choose amount
|
// choose amount
|
||||||
// human can choose or un-choose MULTIPLE targets at once
|
// human can choose or un-choose MULTIPLE targets at once
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (source == null) {
|
if (source == null) {
|
||||||
|
|
@ -1057,6 +1066,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// 2. Distribute amount between selected targets
|
// 2. Distribute amount between selected targets
|
||||||
|
|
||||||
// 1. Select targets
|
// 1. Select targets
|
||||||
|
// TODO: rework to use existing chooseTarget instead custom select?
|
||||||
while (canRespond()) {
|
while (canRespond()) {
|
||||||
Set<UUID> possibleTargetIds = target.possibleTargets(abilityControllerId, source, game);
|
Set<UUID> possibleTargetIds = target.possibleTargets(abilityControllerId, source, game);
|
||||||
boolean required = target.isRequired(source.getSourceId(), game);
|
boolean required = target.isRequired(source.getSourceId(), game);
|
||||||
|
|
@ -1069,7 +1079,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> chosenTargets = target.getTargets();
|
|
||||||
List<UUID> possibleTargets = new ArrayList<>();
|
List<UUID> possibleTargets = new ArrayList<>();
|
||||||
for (UUID targetId : possibleTargetIds) {
|
for (UUID targetId : possibleTargetIds) {
|
||||||
if (target.canTarget(abilityControllerId, targetId, source, game)) {
|
if (target.canTarget(abilityControllerId, targetId, source, game)) {
|
||||||
|
|
@ -1083,7 +1092,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) chosenTargets);
|
options.put("chosenTargets", (Serializable) target.getTargets());
|
||||||
if (!possibleTargets.isEmpty()) {
|
if (!possibleTargets.isEmpty()) {
|
||||||
options.put("possibleTargets", (Serializable) possibleTargets);
|
options.put("possibleTargets", (Serializable) possibleTargets);
|
||||||
}
|
}
|
||||||
|
|
@ -1185,17 +1194,8 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
// TODO: change pass and other states like passedUntilStackResolved for controlling player, not for "this"
|
// TODO: change pass and other states like passedUntilStackResolved for controlling player, not for "this"
|
||||||
// TODO: check and change all "this" to controling player calls, many bugs with hand, mana, skips - https://github.com/magefree/mage/issues/2088
|
// TODO: check and change all "this" to controling player calls, many bugs with hand, mana, skips - https://github.com/magefree/mage/issues/2088
|
||||||
// TODO: use controlling player in all choose dialogs (and canRespond too, what's with take control of player AI?!)
|
// TODO: use controlling player in all choose dialogs (and canRespond too, what's with take control of player AI?!)
|
||||||
UserData controllingUserData = this.userData;
|
UserData controllingUserData = this.getControllingPlayersUserData(game);
|
||||||
if (canRespond()) {
|
if (canRespond()) {
|
||||||
if (!isGameUnderControl()) {
|
|
||||||
Player player = game.getPlayer(getTurnControlledBy());
|
|
||||||
if (player instanceof HumanPlayer) {
|
|
||||||
controllingUserData = player.getUserData();
|
|
||||||
} else {
|
|
||||||
// TODO: add computer opponent here?!
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: check that all skips and stops used from real controlling player
|
// TODO: check that all skips and stops used from real controlling player
|
||||||
// like holdingPriority (is it a bug here?)
|
// like holdingPriority (is it a bug here?)
|
||||||
if (getJustActivatedType() != null && !holdingPriority) {
|
if (getJustActivatedType() != null && !holdingPriority) {
|
||||||
|
|
@ -1489,7 +1489,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TriggeredAbility chooseTriggeredAbility(java.util.List<TriggeredAbility> abilities, Game game) {
|
public TriggeredAbility chooseTriggeredAbility(java.util.List<TriggeredAbility> abilities, Game game) {
|
||||||
// choose triggered abilitity from list
|
// choose triggered ability from list
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return abilities.isEmpty() ? null : abilities.get(0);
|
return abilities.isEmpty() ? null : abilities.get(0);
|
||||||
}
|
}
|
||||||
|
|
@ -1620,7 +1620,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
protected boolean playManaHandling(Ability abilityToCast, ManaCost unpaid, String promptText, Game game) {
|
protected boolean playManaHandling(Ability abilityToCast, ManaCost unpaid, String promptText, Game game) {
|
||||||
// choose mana to pay (from permanents or from pool)
|
// choose mana to pay (from permanents or from pool)
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: make canRespond cycle?
|
// TODO: make canRespond cycle?
|
||||||
|
|
@ -2624,7 +2624,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
@Override
|
@Override
|
||||||
public boolean choosePile(Outcome outcome, String message, java.util.List<? extends Card> pile1, java.util.List<? extends Card> pile2, Game game) {
|
public boolean choosePile(Outcome outcome, String message, java.util.List<? extends Card> pile1, java.util.List<? extends Card> pile2, Game game) {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return true;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (canRespond()) {
|
while (canRespond()) {
|
||||||
|
|
|
||||||
|
|
@ -150,7 +150,6 @@ class AetherspoutsEffect extends OneShotEffect {
|
||||||
target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("order to put on bottom of library (last chosen will be bottommost card)"));
|
target = new TargetCard(Zone.BATTLEFIELD, new FilterCard("order to put on bottom of library (last chosen will be bottommost card)"));
|
||||||
while (player.canRespond() && cards.size() > 1) {
|
while (player.canRespond() && cards.size() > 1) {
|
||||||
player.choose(Outcome.Neutral, cards, target, source, game);
|
player.choose(Outcome.Neutral, cards, target, source, game);
|
||||||
|
|
||||||
Card card = cards.get(target.getFirstTarget(), game);
|
Card card = cards.get(target.getFirstTarget(), game);
|
||||||
if (card != null) {
|
if (card != null) {
|
||||||
cards.remove(card);
|
cards.remove(card);
|
||||||
|
|
@ -158,8 +157,10 @@ class AetherspoutsEffect extends OneShotEffect {
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
toLibrary.add(permanent);
|
toLibrary.add(permanent);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
target.clearChosen();
|
target.clearChosen();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (cards.size() == 1) {
|
if (cards.size() == 1) {
|
||||||
Card card = cards.get(cards.iterator().next(), game);
|
Card card = cards.get(cards.iterator().next(), game);
|
||||||
|
|
|
||||||
|
|
@ -114,11 +114,7 @@ class BalduvianWarlordUnblockEffect extends OneShotEffect {
|
||||||
filter.add(new PermanentReferenceInCollectionPredicate(list, game));
|
filter.add(new PermanentReferenceInCollectionPredicate(list, game));
|
||||||
TargetPermanent target = new TargetPermanent(filter);
|
TargetPermanent target = new TargetPermanent(filter);
|
||||||
target.withNotTarget(true);
|
target.withNotTarget(true);
|
||||||
if (target.canChoose(controller.getId(), source, game)) {
|
if (!controller.chooseTarget(outcome, target, source, game)) {
|
||||||
while (!target.isChosen(game) && target.canChoose(controller.getId(), source, game) && controller.canRespond()) {
|
|
||||||
controller.chooseTarget(outcome, target, source, game);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Permanent chosenPermanent = game.getPermanent(target.getFirstTarget());
|
Permanent chosenPermanent = game.getPermanent(target.getFirstTarget());
|
||||||
|
|
|
||||||
|
|
@ -135,17 +135,17 @@ class BrunaLightOfAlabasterEffect extends OneShotEffect {
|
||||||
&& controller.chooseUse(Outcome.Benefit, "Attach an Aura from your hand?", source, game)) {
|
&& controller.chooseUse(Outcome.Benefit, "Attach an Aura from your hand?", source, game)) {
|
||||||
TargetCard targetAura = new TargetCard(Zone.HAND, filterAuraCard);
|
TargetCard targetAura = new TargetCard(Zone.HAND, filterAuraCard);
|
||||||
if (!controller.choose(Outcome.Benefit, controller.getHand(), targetAura, source, game)) {
|
if (!controller.choose(Outcome.Benefit, controller.getHand(), targetAura, source, game)) {
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card aura = game.getCard(targetAura.getFirstTarget());
|
Card aura = game.getCard(targetAura.getFirstTarget());
|
||||||
if (aura == null) {
|
if (aura == null) {
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Target target = aura.getSpellAbility().getTargets().get(0);
|
Target target = aura.getSpellAbility().getTargets().get(0);
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
fromHandGraveyard.add(aura);
|
fromHandGraveyard.add(aura);
|
||||||
filterAuraCard.add(Predicates.not(new CardIdPredicate(aura.getId())));
|
filterAuraCard.add(Predicates.not(new CardIdPredicate(aura.getId())));
|
||||||
|
|
@ -159,17 +159,17 @@ class BrunaLightOfAlabasterEffect extends OneShotEffect {
|
||||||
&& controller.chooseUse(Outcome.Benefit, "Attach an Aura from your graveyard?", source, game)) {
|
&& controller.chooseUse(Outcome.Benefit, "Attach an Aura from your graveyard?", source, game)) {
|
||||||
TargetCard targetAura = new TargetCard(Zone.GRAVEYARD, filterAuraCard);
|
TargetCard targetAura = new TargetCard(Zone.GRAVEYARD, filterAuraCard);
|
||||||
if (!controller.choose(Outcome.Benefit, controller.getGraveyard(), targetAura, source, game)) {
|
if (!controller.choose(Outcome.Benefit, controller.getGraveyard(), targetAura, source, game)) {
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Card aura = game.getCard(targetAura.getFirstTarget());
|
Card aura = game.getCard(targetAura.getFirstTarget());
|
||||||
if (aura == null) {
|
if (aura == null) {
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Target target = aura.getSpellAbility().getTargets().get(0);
|
Target target = aura.getSpellAbility().getTargets().get(0);
|
||||||
if (target == null) {
|
if (target == null) {
|
||||||
continue;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
fromHandGraveyard.add(aura);
|
fromHandGraveyard.add(aura);
|
||||||
|
|
|
||||||
|
|
@ -82,18 +82,15 @@ class BurningOfXinyeEffect extends OneShotEffect {
|
||||||
|
|
||||||
Target target = new TargetControlledPermanent(amount, amount, filter, true);
|
Target target = new TargetControlledPermanent(amount, amount, filter, true);
|
||||||
if (amount > 0 && target.canChoose(player.getId(), source, game)) {
|
if (amount > 0 && target.canChoose(player.getId(), source, game)) {
|
||||||
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
|
if (player.choose(Outcome.DestroyPermanent, target, source, game)) {
|
||||||
player.choose(Outcome.DestroyPermanent, target, source, game);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (UUID targetId : target.getTargets()) {
|
for (UUID targetId : target.getTargets()) {
|
||||||
Permanent permanent = game.getPermanent(targetId);
|
Permanent permanent = game.getPermanent(targetId);
|
||||||
|
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
abilityApplied |= permanent.destroy(source, game, false);
|
abilityApplied |= permanent.destroy(source, game, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return abilityApplied;
|
return abilityApplied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,7 @@ class DevourFleshSacrificeEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
if (game.getBattlefield().count(TargetSacrifice.makeFilter(StaticFilters.FILTER_PERMANENT_CREATURE), player.getId(), source, game) > 0) {
|
if (game.getBattlefield().count(TargetSacrifice.makeFilter(StaticFilters.FILTER_PERMANENT_CREATURE), player.getId(), source, game) > 0) {
|
||||||
Target target = new TargetSacrifice(StaticFilters.FILTER_PERMANENT_CREATURE);
|
Target target = new TargetSacrifice(StaticFilters.FILTER_PERMANENT_CREATURE);
|
||||||
while (player.canRespond() && !target.isChosen(game) && target.canChoose(player.getId(), source, game)) {
|
|
||||||
player.choose(Outcome.Sacrifice, target, source, game);
|
player.choose(Outcome.Sacrifice, target, source, game);
|
||||||
}
|
|
||||||
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
int gainLife = permanent.getToughness().getValue();
|
int gainLife = permanent.getToughness().getValue();
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,7 @@ class DivineReckoningEffect extends OneShotEffect {
|
||||||
Player player = game.getPlayer(playerId);
|
Player player = game.getPlayer(playerId);
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
Target target = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true);
|
Target target = new TargetControlledPermanent(1, 1, new FilterControlledCreaturePermanent(), true);
|
||||||
if (target.canChoose(player.getId(), source, game)) {
|
if (player.chooseTarget(Outcome.Benefit, target, source, game)) {
|
||||||
while (player.canRespond() && !target.isChosen(game) && target.canChoose(player.getId(), source, game)) {
|
|
||||||
player.chooseTarget(Outcome.Benefit, target, source, game);
|
|
||||||
}
|
|
||||||
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
chosen.add(permanent);
|
chosen.add(permanent);
|
||||||
|
|
|
||||||
|
|
@ -66,9 +66,7 @@ class EntrapmentManeuverSacrificeEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
if (game.getBattlefield().count(TargetSacrifice.makeFilter(StaticFilters.FILTER_ATTACKING_CREATURE), player.getId(), source, game) > 0) {
|
if (game.getBattlefield().count(TargetSacrifice.makeFilter(StaticFilters.FILTER_ATTACKING_CREATURE), player.getId(), source, game) > 0) {
|
||||||
Target target = new TargetSacrifice(StaticFilters.FILTER_ATTACKING_CREATURE);
|
Target target = new TargetSacrifice(StaticFilters.FILTER_ATTACKING_CREATURE);
|
||||||
while (player.canRespond() && !target.isChosen(game) && target.canChoose(player.getId(), source, game)) {
|
|
||||||
player.choose(Outcome.Sacrifice, target, source, game);
|
player.choose(Outcome.Sacrifice, target, source, game);
|
||||||
}
|
|
||||||
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
int amount = permanent.getToughness().getValue();
|
int amount = permanent.getToughness().getValue();
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,7 @@ class EunuchsIntriguesEffect extends OneShotEffect {
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
|
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
|
||||||
filter.add(new ControllerIdPredicate(player.getId()));
|
filter.add(new ControllerIdPredicate(player.getId()));
|
||||||
Target target = new TargetPermanent(1, 1, filter, true);
|
Target target = new TargetPermanent(1, 1, filter, true);
|
||||||
if (target.canChoose(player.getId(), source, game)) {
|
if (player.chooseTarget(Outcome.DestroyPermanent, target, source, game)) {
|
||||||
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
|
|
||||||
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
|
|
||||||
}
|
|
||||||
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as their only creature able to block this turn");
|
game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as their only creature able to block this turn");
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,7 @@ class GoblinWarCryEffect extends OneShotEffect {
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
|
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
|
||||||
filter.add(new ControllerIdPredicate(player.getId()));
|
filter.add(new ControllerIdPredicate(player.getId()));
|
||||||
Target target = new TargetPermanent(1, 1, filter, true);
|
Target target = new TargetPermanent(1, 1, filter, true);
|
||||||
if (target.canChoose(player.getId(), source, game)) {
|
if (player.chooseTarget(Outcome.DestroyPermanent, target, source, game)) {
|
||||||
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
|
|
||||||
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
|
|
||||||
}
|
|
||||||
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as their only creature able to block this turn");
|
game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as their only creature able to block this turn");
|
||||||
|
|
|
||||||
|
|
@ -66,10 +66,7 @@ class ImperialEdictEffect extends OneShotEffect {
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
|
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
|
||||||
filter.add(new ControllerIdPredicate(player.getId()));
|
filter.add(new ControllerIdPredicate(player.getId()));
|
||||||
Target target = new TargetPermanent(1, 1, filter, true);
|
Target target = new TargetPermanent(1, 1, filter, true);
|
||||||
if (target.canChoose(player.getId(), source, game)) {
|
if (player.chooseTarget(Outcome.DestroyPermanent, target, source, game)) {
|
||||||
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
|
|
||||||
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
|
|
||||||
}
|
|
||||||
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
permanent.destroy(source, game, false);
|
permanent.destroy(source, game, false);
|
||||||
|
|
|
||||||
|
|
@ -65,10 +65,7 @@ class RuptureEffect extends OneShotEffect {
|
||||||
if (player != null) {
|
if (player != null) {
|
||||||
int power = 0;
|
int power = 0;
|
||||||
TargetSacrifice target = new TargetSacrifice(StaticFilters.FILTER_PERMANENT_CREATURE);
|
TargetSacrifice target = new TargetSacrifice(StaticFilters.FILTER_PERMANENT_CREATURE);
|
||||||
if (target.canChoose(player.getId(), source, game)) {
|
if (player.choose(Outcome.Sacrifice, target, source, game)){
|
||||||
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
|
|
||||||
player.choose(Outcome.Sacrifice, target, source, game);
|
|
||||||
}
|
|
||||||
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
power = permanent.getPower().getValue();
|
power = permanent.getPower().getValue();
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,8 @@ class ScapeshiftEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
int amount = 0;
|
int amount = 0;
|
||||||
TargetSacrifice sacrificeLand = new TargetSacrifice(0, Integer.MAX_VALUE, StaticFilters.FILTER_LANDS);
|
TargetSacrifice sacrificeLand = new TargetSacrifice(0, Integer.MAX_VALUE, StaticFilters.FILTER_LANDS);
|
||||||
|
// TODO: replace example for #8254:
|
||||||
|
// sacrificeLand.choose(Outcome.Sacrifice, controller.getId(), source.getSourceId(), source, game)
|
||||||
if (controller.choose(Outcome.Sacrifice, sacrificeLand, source, game)) {
|
if (controller.choose(Outcome.Sacrifice, sacrificeLand, source, game)) {
|
||||||
for (UUID uuid : sacrificeLand.getTargets()) {
|
for (UUID uuid : sacrificeLand.getTargets()) {
|
||||||
Permanent land = game.getPermanent(uuid);
|
Permanent land = game.getPermanent(uuid);
|
||||||
|
|
|
||||||
|
|
@ -76,10 +76,7 @@ class WeiAssassinsEffect extends OneShotEffect {
|
||||||
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
|
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
|
||||||
filter.add(new ControllerIdPredicate(player.getId()));
|
filter.add(new ControllerIdPredicate(player.getId()));
|
||||||
Target target = new TargetPermanent(1, 1, filter, true);
|
Target target = new TargetPermanent(1, 1, filter, true);
|
||||||
if (target.canChoose(player.getId(), source, game)) {
|
if (player.chooseTarget(Outcome.DestroyPermanent, target, source, game)) {
|
||||||
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
|
|
||||||
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
|
|
||||||
}
|
|
||||||
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
Permanent permanent = game.getPermanent(target.getFirstTarget());
|
||||||
if (permanent != null) {
|
if (permanent != null) {
|
||||||
permanent.destroy(source, game, false);
|
permanent.destroy(source, game, false);
|
||||||
|
|
|
||||||
|
|
@ -33,9 +33,10 @@ public class LightningStormTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
// B discard and re-target
|
// B discard and re-target
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Discard");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Discard");
|
||||||
|
setChoice(playerB, "Mountain"); // discard cost
|
||||||
setChoice(playerB, true); // change target
|
setChoice(playerB, true); // change target
|
||||||
addTarget(playerB, playerA); // new target
|
addTarget(playerB, playerA); // new target
|
||||||
setChoice(playerB, "Mountain"); // discard cost
|
|
||||||
|
|
||||||
// A discard and re-target
|
// A discard and re-target
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.abilities.enters;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -61,18 +62,19 @@ public class ValakutTheMoltenPinnacleTest extends CardTestPlayerBase {
|
||||||
@Test
|
@Test
|
||||||
public void sixEnterWithScapeshiftDamageToPlayerB() {
|
public void sixEnterWithScapeshiftDamageToPlayerB() {
|
||||||
|
|
||||||
addCard(Zone.LIBRARY, playerA, "Mountain", 6);
|
addCard(Zone.LIBRARY, playerA, "Mountain", 10);
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Valakut, the Molten Pinnacle");
|
addCard(Zone.BATTLEFIELD, playerA, "Valakut, the Molten Pinnacle");
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Forest", 6);
|
addCard(Zone.BATTLEFIELD, playerA, "Forest", 10);
|
||||||
addCard(Zone.HAND, playerA, "Scapeshift");
|
addCard(Zone.HAND, playerA, "Scapeshift");
|
||||||
|
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scapeshift");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Scapeshift");
|
||||||
setChoice(playerA, "Forest^Forest^Forest^Forest^Forest^Forest");
|
setChoice(playerA, "Forest^Forest^Forest^Forest^Forest^Forest"); // to sac
|
||||||
addTarget(playerA, "Mountain^Mountain^Mountain^Mountain^Mountain^Mountain");
|
addTarget(playerA, "Mountain^Mountain^Mountain^Mountain^Mountain^Mountain"); // to search
|
||||||
setChoice(playerA, "Whenever", 5); // order triggers
|
setChoice(playerA, "Whenever a Mountain", 6 - 1); // x6 triggers from valakut
|
||||||
setChoice(playerA, true, 6); // yes to deal damage
|
|
||||||
addTarget(playerA, playerB, 6); // to deal damage
|
addTarget(playerA, playerB, 6); // to deal damage
|
||||||
|
setChoice(playerA, true, 6); // yes to deal damage
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
@ -81,7 +83,7 @@ public class ValakutTheMoltenPinnacleTest extends CardTestPlayerBase {
|
||||||
assertPermanentCount(playerA, "Mountain", 6);
|
assertPermanentCount(playerA, "Mountain", 6);
|
||||||
|
|
||||||
assertLife(playerA, 20);
|
assertLife(playerA, 20);
|
||||||
assertLife(playerB, 2); // 6 * 3 damage = 18
|
assertLife(playerB, 20 - 18); // 6 * 3 damage = 18
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.abilities.keywords;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -26,9 +27,10 @@ public class ImproviseTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 4);
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}", 4);
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bastion Inventor");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Bastion Inventor");
|
||||||
setChoice(playerA, "Blue", 4); // pay 1-4
|
setChoice(playerA, "Blue", 4); // pay 1-4
|
||||||
// improvise pay by one card
|
// improvise pay by one card (after 2025 addTarget improve - Improvise can be pay by multiple addTarget)
|
||||||
setChoice(playerA, "Improvise");
|
setChoice(playerA, "Improvise");
|
||||||
addTarget(playerA, "Alpha Myr"); // pay 5 as improvise
|
addTarget(playerA, "Alpha Myr"); // pay 5 as improvise
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP); // choose only 1 of 2 possible permanent
|
||||||
setChoice(playerA, "Improvise");
|
setChoice(playerA, "Improvise");
|
||||||
addTarget(playerA, "Alpha Myr"); // pay 6 as improvise
|
addTarget(playerA, "Alpha Myr"); // pay 6 as improvise
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -33,6 +34,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 3);
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 3);
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
|
||||||
addTarget(playerA, "Balduvian Bears");
|
addTarget(playerA, "Balduvian Bears");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
|
||||||
// after
|
// after
|
||||||
|
|
@ -73,6 +75,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
|
||||||
setChoice(playerB, true); // continue
|
setChoice(playerB, true); // continue
|
||||||
addTarget(playerB, "Balduvian Bears"); // player B must take control for searching
|
addTarget(playerB, "Balduvian Bears"); // player B must take control for searching
|
||||||
|
addTarget(playerB, TestPlayer.TARGET_SKIP);
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
|
||||||
// after
|
// after
|
||||||
|
|
@ -116,6 +119,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
||||||
setChoice(playerA, true); // yes, try to cast a creature card from lib
|
setChoice(playerA, true); // yes, try to cast a creature card from lib
|
||||||
setChoice(playerA, "Panglacial Wurm"); // try to cast
|
setChoice(playerA, "Panglacial Wurm"); // try to cast
|
||||||
addTarget(playerA, "Balduvian Bears"); // choice for searching
|
addTarget(playerA, "Balduvian Bears"); // choice for searching
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
|
||||||
// after
|
// after
|
||||||
|
|
@ -170,6 +174,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
||||||
setChoice(playerB, true); // yes, try to cast a creature card from lib
|
setChoice(playerB, true); // yes, try to cast a creature card from lib
|
||||||
setChoice(playerB, "Panglacial Wurm"); // try to cast
|
setChoice(playerB, "Panglacial Wurm"); // try to cast
|
||||||
addTarget(playerB, "Balduvian Bears"); // choice for searching
|
addTarget(playerB, "Balduvian Bears"); // choice for searching
|
||||||
|
addTarget(playerB, TestPlayer.TARGET_SKIP);
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
|
||||||
// after
|
// after
|
||||||
|
|
@ -230,6 +235,7 @@ public class TakeControlWhileSearchingLibraryTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Buried Alive");
|
||||||
setChoice(playerB, true); // continue after new control
|
setChoice(playerB, true); // continue after new control
|
||||||
addTarget(playerB, "Balduvian Bears");
|
addTarget(playerB, "Balduvian Bears");
|
||||||
|
addTarget(playerB, TestPlayer.TARGET_SKIP);
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
checkGraveyardCount("after grave a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
|
checkGraveyardCount("after grave a", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Balduvian Bears", 0);
|
||||||
checkGraveyardCount("after grave b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears", 0);
|
checkGraveyardCount("after grave b", 1, PhaseStep.PRECOMBAT_MAIN, playerB, "Balduvian Bears", 0);
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ import mage.util.CardUtil;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
@ -509,6 +510,7 @@ public class AdjusterCostTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fireball");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fireball");
|
||||||
setChoice(playerA, "X=2");
|
setChoice(playerA, "X=2");
|
||||||
addTarget(playerA, "Arbor Elf^Arbor Elf");
|
addTarget(playerA, "Arbor Elf^Arbor Elf");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import mage.counters.CounterType;
|
||||||
import mage.game.stack.StackObject;
|
import mage.game.stack.StackObject;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -280,6 +281,7 @@ public class CollectEvidenceTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, monitor);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, monitor);
|
||||||
setChoice(playerA, true);
|
setChoice(playerA, true);
|
||||||
setChoice(playerA, giant);
|
setChoice(playerA, giant);
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.cost.additional;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -22,6 +23,7 @@ public class RemoveCounterCostTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, Remove two +1/+1 counters");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}, Remove two +1/+1 counters");
|
||||||
setChoice(playerA, "Novijen Sages"); // counters to remove
|
setChoice(playerA, "Novijen Sages"); // counters to remove
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
setChoice(playerA, "X=2"); // counters to remove
|
setChoice(playerA, "X=2"); // counters to remove
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}");
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}");
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vedalken Mastermind");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Vedalken Mastermind");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
@ -52,6 +53,7 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateManaAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}");
|
activateManaAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U}");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
@ -71,19 +73,26 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
||||||
// If Gemstone Caverns is in your opening hand and you're not playing first, you may begin the game with Gemstone Caverns on the battlefield with a luck counter on it. If you do, exile a card from your hand.
|
// If Gemstone Caverns is in your opening hand and you're not playing first, you may begin the game with Gemstone Caverns on the battlefield with a luck counter on it. If you do, exile a card from your hand.
|
||||||
// {T}: Add {C}. If Gemstone Caverns has a luck counter on it, instead add one mana of any color.
|
// {T}: Add {C}. If Gemstone Caverns has a luck counter on it, instead add one mana of any color.
|
||||||
addCard(Zone.HAND, playerB, "Gemstone Caverns", 1);
|
addCard(Zone.HAND, playerB, "Gemstone Caverns", 1);
|
||||||
|
addCard(Zone.HAND, playerB, "Swamp", 1);
|
||||||
|
|
||||||
addCard(Zone.HAND, playerB, "Silvercoat Lion", 2);
|
addCard(Zone.HAND, playerB, "Silvercoat Lion", 2);
|
||||||
|
|
||||||
|
// pay and put Gemstone to battlefield on starting
|
||||||
|
setChoice(playerB, true);
|
||||||
|
setChoice(playerB, "Swamp");
|
||||||
|
|
||||||
activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add");
|
activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add");
|
||||||
setChoice(playerB, "White");
|
setChoice(playerB, "White");
|
||||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion");
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Silvercoat Lion");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertPermanentCount(playerB, "Gemstone Caverns", 1);
|
assertPermanentCount(playerB, "Gemstone Caverns", 1);
|
||||||
assertCounterCount("Gemstone Caverns", CounterType.LUCK, 1);
|
assertCounterCount("Gemstone Caverns", CounterType.LUCK, 1);
|
||||||
assertPermanentCount(playerB, "Silvercoat Lion", 1);
|
assertPermanentCount(playerB, "Silvercoat Lion", 1);
|
||||||
assertExileCount("Silvercoat Lion", 1);
|
assertExileCount("Swamp", 1);
|
||||||
assertTapped("Gemstone Caverns", true);
|
assertTapped("Gemstone Caverns", true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -99,12 +108,19 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
||||||
// If Gemstone Caverns is in your opening hand and you're not playing first, you may begin the game with Gemstone Caverns on the battlefield with a luck counter on it. If you do, exile a card from your hand.
|
// If Gemstone Caverns is in your opening hand and you're not playing first, you may begin the game with Gemstone Caverns on the battlefield with a luck counter on it. If you do, exile a card from your hand.
|
||||||
// {T}: Add {C}. If Gemstone Caverns has a luck counter on it, instead add one mana of any color.
|
// {T}: Add {C}. If Gemstone Caverns has a luck counter on it, instead add one mana of any color.
|
||||||
addCard(Zone.HAND, playerB, "Gemstone Caverns", 1);
|
addCard(Zone.HAND, playerB, "Gemstone Caverns", 1);
|
||||||
|
addCard(Zone.HAND, playerB, "Swamp", 1);
|
||||||
|
|
||||||
addCard(Zone.HAND, playerB, "Silvercoat Lion", 2);
|
addCard(Zone.HAND, playerB, "Silvercoat Lion", 2);
|
||||||
|
|
||||||
activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add");
|
// pay and put Gemstone to battlefield on starting
|
||||||
|
setChoice(playerB, true);
|
||||||
|
setChoice(playerB, "Swamp");
|
||||||
|
|
||||||
|
activateManaAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}: Add {C}");
|
||||||
setChoice(playerB, "White");
|
setChoice(playerB, "White");
|
||||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Vorinclex, Voice of Hunger");
|
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Vorinclex, Voice of Hunger");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
|
|
@ -112,7 +128,6 @@ public class AddManaOfAnyTypeProducedTest extends CardTestPlayerBase {
|
||||||
assertCounterCount("Gemstone Caverns", CounterType.LUCK, 1);
|
assertCounterCount("Gemstone Caverns", CounterType.LUCK, 1);
|
||||||
assertPermanentCount(playerB, "Vorinclex, Voice of Hunger", 1);
|
assertPermanentCount(playerB, "Vorinclex, Voice of Hunger", 1);
|
||||||
assertTapped("Gemstone Caverns", true);
|
assertTapped("Gemstone Caverns", true);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String kinnan = "Kinnan, Bonder Prodigy";
|
private static final String kinnan = "Kinnan, Bonder Prodigy";
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.afc;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestCommander4Players;
|
import org.mage.test.serverside.base.CardTestCommander4Players;
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -313,6 +314,7 @@ public class ShareTheSpoilsTest extends CardTestCommander4Players {
|
||||||
// Cast an adventure card from hand
|
// Cast an adventure card from hand
|
||||||
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Dizzying Swoop");
|
castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, "Dizzying Swoop");
|
||||||
addTarget(playerA, "Prosper, Tome-Bound");
|
addTarget(playerA, "Prosper, Tome-Bound");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP); // tap 1 of 2 targets
|
||||||
waitStackResolved(5, PhaseStep.PRECOMBAT_MAIN);
|
waitStackResolved(5, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
|
||||||
// Make sure the creature card can't be played from exile since there isn't the {W}{W} for it
|
// Make sure the creature card can't be played from exile since there isn't the {W}{W} for it
|
||||||
|
|
|
||||||
|
|
@ -169,15 +169,25 @@ public class ApproachOfTheSecondSunTest extends CardTestPlayerBase {
|
||||||
@Test
|
@Test
|
||||||
public void testCastFromGraveyard() {
|
public void testCastFromGraveyard() {
|
||||||
removeAllCardsFromLibrary(playerA);
|
removeAllCardsFromLibrary(playerA);
|
||||||
|
|
||||||
addCard(Zone.LIBRARY, playerA, "Plains", 6);
|
addCard(Zone.LIBRARY, playerA, "Plains", 6);
|
||||||
addCard(Zone.HAND, playerA, "Approach of the Second Sun", 1);
|
//
|
||||||
addCard(Zone.GRAVEYARD, playerA, "Approach of the Second Sun", 2);
|
// If this spell was cast from your hand and you've cast another spell named Approach of the Second Sun this game,
|
||||||
addCard(Zone.HAND, playerA, "Finale of Promise", 2);
|
// you win the game. Otherwise, put Approach of the Second Sun into its owner's library seventh from the top
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Mystic Monastery", 25);
|
// and you gain 7 life.
|
||||||
|
addCard(Zone.HAND, playerA, "Approach of the Second Sun", 1); // {6}{W}
|
||||||
|
addCard(Zone.GRAVEYARD, playerA, "Approach of the Second Sun", 2); // {6}{W}
|
||||||
|
//
|
||||||
|
// You may cast up to one target instant card and/or up to one target sorcery card from your graveyard each
|
||||||
|
// with mana value X or less without paying their mana costs. If a spell cast this way would be put into
|
||||||
|
// your graveyard, exile it instead. If X is 10 or more, copy each of those spells twice. You may choose
|
||||||
|
// new targets for the copies.
|
||||||
|
addCard(Zone.HAND, playerA, "Finale of Promise", 2); // {X}{R}{R}
|
||||||
|
//
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mystic Monastery", 25); // for mana
|
||||||
|
|
||||||
// first may have been cast from anywhere.
|
// first may have been cast from anywhere.
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Finale of Promise", true);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Finale of Promise", true);
|
||||||
// You may cast up to one target instant card and/or up to one target sorcery card from your graveyard each with mana value X or less without paying their mana costs. If a spell cast this way would be put into your graveyard, exile it instead. If X is 10 or more, copy each of those spells twice. You may choose new targets for the copies.
|
|
||||||
setChoice(playerA, "X=7"); // each with mana value X or less
|
setChoice(playerA, "X=7"); // each with mana value X or less
|
||||||
setChoice(playerA, "Yes"); // You may cast
|
setChoice(playerA, "Yes"); // You may cast
|
||||||
addTarget(playerA, TARGET_SKIP); // up to one target instant card
|
addTarget(playerA, TARGET_SKIP); // up to one target instant card
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
||||||
addCard(Zone.GRAVEYARD, playerA, maaka, 2);
|
addCard(Zone.GRAVEYARD, playerA, maaka, 2);
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
||||||
|
setChoice(playerA, mountain); // discard cost
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, maaka);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, maaka);
|
||||||
|
|
||||||
|
|
@ -33,8 +34,8 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
attack(1, playerA, maaka);
|
attack(1, playerA, maaka);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertPermanentCount(playerA, maaka, 1);
|
assertPermanentCount(playerA, maaka, 1);
|
||||||
|
|
@ -52,7 +53,10 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
||||||
addCard(Zone.GRAVEYARD, playerA, khenra);
|
addCard(Zone.GRAVEYARD, playerA, khenra);
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Discard");
|
||||||
setChoice(playerA, true);
|
setChoice(playerA, mountain); // discard cost
|
||||||
|
setChoice(playerA, true); // use copy
|
||||||
|
setChoice(playerA, "0"); // for casting main spell - x2 permitting object
|
||||||
|
setChoice(playerA, "0"); // for casting copied spell - x2 permitting object
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, maaka, true);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, maaka, true);
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, khenra);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, khenra);
|
||||||
|
|
@ -60,8 +64,8 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
||||||
attack(1, playerA, maaka);
|
attack(1, playerA, maaka);
|
||||||
attack(1, playerA, khenra);
|
attack(1, playerA, khenra);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertPermanentCount(playerA, maaka, 1);
|
assertPermanentCount(playerA, maaka, 1);
|
||||||
|
|
@ -88,8 +92,8 @@ public class ChainerNightmareAdeptTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
attack(1, playerA, maaka);
|
attack(1, playerA, maaka);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertPermanentCount(playerA, maaka, 1);
|
assertPermanentCount(playerA, maaka, 1);
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.clb;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -29,6 +30,7 @@ public class BabaLysagaNightWitchTest extends CardTestPlayerBase {
|
||||||
activateAbility(1, PhaseStep.UPKEEP, playerA, "{1}: {this} becomes a 2/2 Assembly-Worker artifact creature until end of turn. It's still a land.");
|
activateAbility(1, PhaseStep.UPKEEP, playerA, "{1}: {this} becomes a 2/2 Assembly-Worker artifact creature until end of turn. It's still a land.");
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three permanents: If there ");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three permanents: If there ");
|
||||||
setChoice(playerA, "Mishra's Factory");
|
setChoice(playerA, "Mishra's Factory");
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
execute();
|
execute();
|
||||||
|
|
@ -48,6 +50,7 @@ public class BabaLysagaNightWitchTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three permanents: If there ");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three permanents: If there ");
|
||||||
setChoice(playerA, "Mishra's Factory");
|
setChoice(playerA, "Mishra's Factory");
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -45,6 +46,7 @@ public class MuYanlingWindRiderTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1");
|
activateAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Crew 1");
|
||||||
setChoice(playerA, "Memnite");
|
setChoice(playerA, "Memnite");
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
attack(3, playerA, "Vehicle Token", playerB);
|
attack(3, playerA, "Vehicle Token", playerB);
|
||||||
attack(3, playerA, muyanling, playerB);
|
attack(3, playerA, muyanling, playerB);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ package org.mage.test.cards.single.emn;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -77,6 +78,7 @@ public class TamiyoFieldResearcherTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
||||||
addTarget(playerA, "Bronze Sable");
|
addTarget(playerA, "Bronze Sable");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
|
||||||
attack(1, playerA, "Bronze Sable");
|
attack(1, playerA, "Bronze Sable");
|
||||||
|
|
||||||
|
|
@ -135,6 +137,7 @@ public class TamiyoFieldResearcherTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
||||||
addTarget(playerA, "Sylvan Advocate");
|
addTarget(playerA, "Sylvan Advocate");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
|
||||||
attack(1, playerA, "Sylvan Advocate");
|
attack(1, playerA, "Sylvan Advocate");
|
||||||
attack(2, playerB, "Memnite");
|
attack(2, playerB, "Memnite");
|
||||||
|
|
@ -167,6 +170,7 @@ public class TamiyoFieldResearcherTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo, Field Researcher", true);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo, Field Researcher", true);
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
||||||
addTarget(playerA, "Sylvan Advocate");
|
addTarget(playerA, "Sylvan Advocate");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
|
||||||
attack(1, playerA, "Sylvan Advocate");
|
attack(1, playerA, "Sylvan Advocate");
|
||||||
|
|
||||||
|
|
@ -236,6 +240,7 @@ public class TamiyoFieldResearcherTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo, Field Researcher", true);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tamiyo, Field Researcher", true);
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+1: Choose up to two");
|
||||||
addTarget(playerA, "Bronze Sable");
|
addTarget(playerA, "Bronze Sable");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
|
||||||
attack(2, playerB, "Bronze Sable");
|
attack(2, playerB, "Bronze Sable");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.grn;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -31,6 +32,7 @@ public class WandOfVertebraeTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}, {T}");
|
||||||
addTarget(playerA, lavaCoil);
|
addTarget(playerA, lavaCoil);
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP); // must choose 1 of 5
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.j22;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -80,13 +81,17 @@ public class AgrusKosEternalSoldierTest extends CardTestPlayerBase {
|
||||||
addCard(Zone.HAND, playerA, "Smoldering Werewolf");
|
addCard(Zone.HAND, playerA, "Smoldering Werewolf");
|
||||||
|
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Smoldering Werewolf");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Smoldering Werewolf");
|
||||||
setChoice(playerA, "When {this} enters, it deals");
|
setChoice(playerB, true); // gain life on cast
|
||||||
addTarget(playerA, agrus);
|
//
|
||||||
addTarget(playerA, agrus);
|
setChoice(playerA, "When {this} enters, it deals"); // x2 triggers from Panharmonicon
|
||||||
setChoice(playerB, true); // gain life
|
addTarget(playerA, agrus); // x1 trigger
|
||||||
setChoice(playerB, "Whenever {this} becomes");
|
addTarget(playerA, TestPlayer.TARGET_SKIP); // x1 trigger
|
||||||
setChoice(playerB, true); // pay to copy
|
addTarget(playerA, agrus); // x2 trigger
|
||||||
setChoice(playerB, true); // pay to copy
|
addTarget(playerA, TestPlayer.TARGET_SKIP); // x2 trigger
|
||||||
|
//
|
||||||
|
setChoice(playerB, "Whenever {this} becomes"); // x2 triggers from Panharmonicon
|
||||||
|
setChoice(playerB, true); // x1 pay to copy
|
||||||
|
setChoice(playerB, true); // x2 pay to copy
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.m21;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
||||||
|
|
@ -22,6 +23,7 @@ public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster");
|
||||||
setChoice(playerA, true);
|
setChoice(playerA, true);
|
||||||
addTarget(playerA, "Alpine Watchdog");
|
addTarget(playerA, "Alpine Watchdog");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP); // only single card
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
|
@ -44,6 +46,7 @@ public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster");
|
||||||
setChoice(playerA, true);
|
setChoice(playerA, true);
|
||||||
addTarget(playerA, "Igneous Cur");
|
addTarget(playerA, "Igneous Cur");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP); // only single card
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
|
@ -54,7 +57,7 @@ public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void searchBoth() {
|
public void searchBoth_TestFramework_AddTargetsAsSingle() {
|
||||||
// When Alpine Houndmaster enters the battlefield, you may search your library for a card named
|
// When Alpine Houndmaster enters the battlefield, you may search your library for a card named
|
||||||
// Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.
|
// Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.
|
||||||
addCard(Zone.HAND, playerA, "Alpine Houndmaster", 1);
|
addCard(Zone.HAND, playerA, "Alpine Houndmaster", 1);
|
||||||
|
|
@ -72,7 +75,30 @@ public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertHandCount(playerA, 2);
|
assertHandCount(playerA, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void searchBoth_TestFramework_AddTargetsAsMultiple() {
|
||||||
|
// test framework must support both
|
||||||
|
|
||||||
|
// When Alpine Houndmaster enters the battlefield, you may search your library for a card named
|
||||||
|
// Alpine Watchdog and/or a card named Igneous Cur, reveal them, put them into your hand, then shuffle your library.
|
||||||
|
addCard(Zone.HAND, playerA, "Alpine Houndmaster", 1);
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Mountain");
|
||||||
|
addCard(Zone.BATTLEFIELD, playerA, "Plains");
|
||||||
|
addCard(Zone.LIBRARY, playerA, "Alpine Watchdog");
|
||||||
|
addCard(Zone.LIBRARY, playerA, "Igneous Cur");
|
||||||
|
|
||||||
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster");
|
||||||
|
setChoice(playerA, true);
|
||||||
|
addTarget(playerA, "Igneous Cur");
|
||||||
|
addTarget(playerA, "Alpine Watchdog");
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertHandCount(playerA, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.single.mh3;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -72,6 +73,7 @@ public class NethergoyfTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
checkPlayableAbility("can escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", true);
|
checkPlayableAbility("can escape", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cast " + nethergoyf + " with Escape", true);
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nethergoyf + " with Escape");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, nethergoyf + " with Escape");
|
||||||
setChoice(playerA, "Memnite^Memnite^Memnite^Memnite^Bitterblossom"); // cards exiled for escape cost: Exile all the Memnite but one.
|
setChoice(playerA, "Memnite^Memnite^Memnite^Memnite^Bitterblossom"); // cards exiled for escape cost: Exile all the Memnite but one.
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -39,6 +40,7 @@ public class SuppressionRayTest extends CardTestPlayerBase {
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Suppression Ray", playerB);
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Suppression Ray", playerB);
|
||||||
setChoiceAmount(playerA, 3); // decide to pay 3 energy
|
setChoiceAmount(playerA, 3); // decide to pay 3 energy
|
||||||
setChoice(playerA, "Zodiac Pig^Zodiac Rabbit"); // put stun on those 2 creatures
|
setChoice(playerA, "Zodiac Pig^Zodiac Rabbit"); // put stun on those 2 creatures
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ package org.mage.test.cards.single.mkm;
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
import mage.target.TargetPlayer;
|
||||||
import org.junit.Ignore;
|
import org.junit.Ignore;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
public class CovetedFalconTest extends CardTestPlayerBase {
|
public class CovetedFalconTest extends CardTestPlayerBase {
|
||||||
|
|
@ -31,6 +33,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
addTarget(playerA, playerB);
|
addTarget(playerA, playerB);
|
||||||
addTarget(playerA, "Grizzly Bears");
|
addTarget(playerA, "Grizzly Bears");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
|
@ -57,6 +60,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
addTarget(playerA, playerB);
|
addTarget(playerA, playerB);
|
||||||
addTarget(playerA, "Grizzly Bears");
|
addTarget(playerA, "Grizzly Bears");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||||
|
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Turn Against", "Grizzly Bears");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Turn Against", "Grizzly Bears");
|
||||||
|
|
@ -82,6 +86,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
addTarget(playerA, playerB);
|
addTarget(playerA, playerB);
|
||||||
addTarget(playerA, "Putrid Goblin");
|
addTarget(playerA, "Putrid Goblin");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||||
|
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Murder", "Putrid Goblin");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Murder", "Putrid Goblin");
|
||||||
|
|
@ -111,6 +116,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
addTarget(playerA, playerB);
|
addTarget(playerA, playerB);
|
||||||
addTarget(playerA, "Treacherous Pit-Dweller");
|
addTarget(playerA, "Treacherous Pit-Dweller");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, true);
|
||||||
|
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Murder", "Treacherous Pit-Dweller");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Murder", "Treacherous Pit-Dweller");
|
||||||
|
|
@ -151,6 +157,7 @@ public class CovetedFalconTest extends CardTestPlayerBase {
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}: Turn this face-down permanent face up.");
|
||||||
addTarget(playerA, playerB);
|
addTarget(playerA, playerB);
|
||||||
addTarget(playerA, "Darksteel Relic^Grizzly Bears");
|
addTarget(playerA, "Darksteel Relic^Grizzly Bears");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
|
||||||
castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Murder", "Guardian Beast");
|
castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Murder", "Guardian Beast");
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import mage.constants.PhaseStep;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -65,6 +66,7 @@ public class TenthDistrictHeroTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}");
|
||||||
setChoice(playerA, giant);
|
setChoice(playerA, giant);
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
|
@ -92,6 +94,7 @@ public class TenthDistrictHeroTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}");
|
||||||
setChoice(playerA, giant);
|
setChoice(playerA, giant);
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -37,6 +38,7 @@ public class TheWiseMothmanTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{3}");
|
||||||
addTarget(playerA, mothman + "^Grizzly Bears"); // up to three targets => choosing 2
|
addTarget(playerA, mothman + "^Grizzly Bears"); // up to three targets => choosing 2
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
|
package org.mage.test.cards.targets;
|
||||||
package org.mage.test.cards.asthough;
|
|
||||||
|
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
|
@ -7,13 +6,11 @@ import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
* @author LevelX2, JayDi85
|
||||||
* @author LevelX2
|
|
||||||
*/
|
*/
|
||||||
public class CastAsInstantTest extends CardTestPlayerBase {
|
public class AutoChooseTargetsAndCastAsInstantTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
@Test
|
private void run_WithAutoSelection(int selectedTargets, int possibleTargets) {
|
||||||
public void testEffectOnlyForOneTurn() {
|
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Island");
|
addCard(Zone.BATTLEFIELD, playerB, "Island");
|
||||||
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4);
|
addCard(Zone.BATTLEFIELD, playerB, "Swamp", 4);
|
||||||
// The next sorcery card you cast this turn can be cast as though it had flash.
|
// The next sorcery card you cast this turn can be cast as though it had flash.
|
||||||
|
|
@ -23,22 +20,45 @@ public class CastAsInstantTest extends CardTestPlayerBase {
|
||||||
// Target opponent exiles two cards from their hand and loses 2 life.
|
// Target opponent exiles two cards from their hand and loses 2 life.
|
||||||
addCard(Zone.HAND, playerB, "Witness the End"); // {3}{B}
|
addCard(Zone.HAND, playerB, "Witness the End"); // {3}{B}
|
||||||
|
|
||||||
addCard(Zone.HAND, playerA, "Silvercoat Lion", 2);
|
addCard(Zone.HAND, playerA, "Silvercoat Lion", possibleTargets);
|
||||||
|
|
||||||
castSpell(1, PhaseStep.UPKEEP, playerB, "Quicken", true);
|
castSpell(1, PhaseStep.UPKEEP, playerB, "Quicken", true);
|
||||||
castSpell(1, PhaseStep.UPKEEP, playerB, "Witness the End", playerA);
|
castSpell(1, PhaseStep.UPKEEP, playerB, "Witness the End", playerA);
|
||||||
|
|
||||||
|
// it uses auto-choose logic inside, so disable strict mode
|
||||||
|
// logic for possible targets with min/max = 2:
|
||||||
|
// 0, 1, 2 - auto-choose all possible targets
|
||||||
|
// 3+ - AI choose best targets
|
||||||
|
setStrictChooseMode(false);
|
||||||
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertGraveyardCount(playerB, "Quicken", 1);
|
assertGraveyardCount(playerB, "Quicken", 1);
|
||||||
assertGraveyardCount(playerB, "Witness the End", 1);
|
assertGraveyardCount(playerB, "Witness the End", 1);
|
||||||
|
|
||||||
assertExileCount("Silvercoat Lion", 2);
|
assertExileCount("Silvercoat Lion", selectedTargets);
|
||||||
|
|
||||||
assertLife(playerA, 18);
|
assertLife(playerA, 18);
|
||||||
assertLife(playerB, 20);
|
assertLife(playerB, 20);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AutoChoose_0_of_0() {
|
||||||
|
run_WithAutoSelection(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AutoChoose_1_of_1() {
|
||||||
|
run_WithAutoSelection(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AutoChoose_2_of_2() {
|
||||||
|
run_WithAutoSelection(2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_AutoChoose_2_of_5() {
|
||||||
|
run_WithAutoSelection(2, 5);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,258 @@
|
||||||
|
package org.mage.test.cards.targets;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
|
import mage.abilities.costs.common.TapAttachedCost;
|
||||||
|
import mage.abilities.costs.common.TapSourceCost;
|
||||||
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.ExileTargetEffect;
|
||||||
|
import mage.abilities.effects.common.GainLifeEffect;
|
||||||
|
import mage.cards.CardsImpl;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.PhaseStep;
|
||||||
|
import mage.constants.Zone;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.Target;
|
||||||
|
import mage.target.common.TargetCardInHand;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
import org.mage.test.serverside.base.CardTestPlayerBaseWithAIHelps;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.stream.IntStream;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper class for target selection tests in diff use cases like selection on targets declare or on resolve
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class TargetsSelectionBaseTest extends CardTestPlayerBaseWithAIHelps {
|
||||||
|
|
||||||
|
static final boolean DEBUG_ENABLE_DETAIL_LOGS = true;
|
||||||
|
|
||||||
|
protected void run_PlayerChooseTarget_OnActivate(int chooseCardsCount, int availableCardsCount) {
|
||||||
|
// custom effect - exile and gain life due selected targets
|
||||||
|
int minTarget = 0;
|
||||||
|
int maxTarget = 3;
|
||||||
|
String startingText = "exile and gain";
|
||||||
|
Ability ability = new SimpleActivatedAbility(
|
||||||
|
new ExileTargetEffect().setText("exile"),
|
||||||
|
new ManaCostsImpl<>("")
|
||||||
|
);
|
||||||
|
ability.addEffect(new GainLifeEffect(TotalTargetsValue.instance).concatBy("and").setText("gain life"));
|
||||||
|
ability.addTarget(new TargetCardInHand(minTarget, maxTarget, StaticFilters.FILTER_CARD).withNotTarget(false));
|
||||||
|
addCustomCardWithAbility("test choice", playerA, ability);
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, "Swamp", availableCardsCount);
|
||||||
|
|
||||||
|
checkHandCardCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp", availableCardsCount);
|
||||||
|
checkExileCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp", 0);
|
||||||
|
|
||||||
|
if (availableCardsCount > 0) {
|
||||||
|
checkPlayableAbility("can activate on non-zero targets", 1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText, true);
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText);
|
||||||
|
} else {
|
||||||
|
checkPlayableAbility("can't activate on zero targets", 1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chooseCardsCount > 0) {
|
||||||
|
// need selection
|
||||||
|
List<String> targetCards = new ArrayList<>();
|
||||||
|
IntStream.rangeClosed(1, chooseCardsCount).forEach(x -> {
|
||||||
|
targetCards.add("Swamp");
|
||||||
|
});
|
||||||
|
addTarget(playerA, String.join("^", targetCards));
|
||||||
|
// end selection:
|
||||||
|
// - x of 0 - no
|
||||||
|
// - 1 of 3 - yes
|
||||||
|
// - 2 of 3 - yes
|
||||||
|
// - 3 of 3 - no, it's auto-finish on last select
|
||||||
|
// - 3 of 5 - no, it's auto-finish on last select
|
||||||
|
if (chooseCardsCount < maxTarget) {
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// need skip
|
||||||
|
// on 0 cards there are not valid targets, so no any dialogs
|
||||||
|
if (availableCardsCount > 0) {
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG_ENABLE_DETAIL_LOGS) {
|
||||||
|
System.out.println("planning actions:");
|
||||||
|
playerA.getActions().forEach(System.out::println);
|
||||||
|
System.out.println("planning targets:");
|
||||||
|
playerA.getTargets().forEach(System.out::println);
|
||||||
|
System.out.println("planning choices:");
|
||||||
|
playerA.getChoices().forEach(System.out::println);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertExileCount(playerA, "Swamp", chooseCardsCount);
|
||||||
|
assertLife(playerA, 20 + chooseCardsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void run_PlayerChoose_OnResolve(int chooseCardsCount, int availableCardsCount) {
|
||||||
|
// custom effect - select, exile and gain life
|
||||||
|
int minTarget = 0;
|
||||||
|
int maxTarget = 3;
|
||||||
|
String startingText = "select, exile and gain life";
|
||||||
|
Ability ability = new SimpleActivatedAbility(
|
||||||
|
new SelectExileAndGainLifeCustomEffect(minTarget, maxTarget, Outcome.Benefit),
|
||||||
|
new ManaCostsImpl<>("")
|
||||||
|
);
|
||||||
|
addCustomCardWithAbility("test choice", playerA, ability);
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, "Swamp", availableCardsCount);
|
||||||
|
|
||||||
|
checkHandCardCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp", availableCardsCount);
|
||||||
|
checkExileCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Swamp", 0);
|
||||||
|
|
||||||
|
checkPlayableAbility("can activate any time (even with zero cards)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText, true);
|
||||||
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText);
|
||||||
|
|
||||||
|
if (chooseCardsCount > 0) {
|
||||||
|
// need selection
|
||||||
|
List<String> targetCards = new ArrayList<>();
|
||||||
|
IntStream.rangeClosed(1, chooseCardsCount).forEach(x -> {
|
||||||
|
targetCards.add("Swamp");
|
||||||
|
});
|
||||||
|
setChoice(playerA, String.join("^", targetCards));
|
||||||
|
} else {
|
||||||
|
// need skip
|
||||||
|
// on 0 cards there are must be dialog with done button anyway
|
||||||
|
|
||||||
|
// end selection:
|
||||||
|
// - x of 0 - yes
|
||||||
|
// - 1 of 3 - yes
|
||||||
|
// - 2 of 3 - yes
|
||||||
|
// - 3 of 3 - no, it's auto-finish on last select
|
||||||
|
// - 3 of 5 - no, it's auto-finish on last select
|
||||||
|
if (chooseCardsCount < maxTarget) {
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DEBUG_ENABLE_DETAIL_LOGS) {
|
||||||
|
System.out.println("planning actions:");
|
||||||
|
playerA.getActions().forEach(System.out::println);
|
||||||
|
System.out.println("planning targets:");
|
||||||
|
playerA.getTargets().forEach(System.out::println);
|
||||||
|
System.out.println("planning choices:");
|
||||||
|
playerA.getChoices().forEach(System.out::println);
|
||||||
|
}
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertExileCount(playerA, "Swamp", chooseCardsCount);
|
||||||
|
assertLife(playerA, 20 + chooseCardsCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void run_PlayerChoose_OnResolve_AI(Outcome outcome, int minTargets, int maxTargets, int aiMustChooseCardsCount, int availableCardsCount) {
|
||||||
|
// custom effect - select, exile and gain life
|
||||||
|
String startingText = "{T}: Select, exile and gain life";
|
||||||
|
Ability ability = new SimpleActivatedAbility(
|
||||||
|
new SelectExileAndGainLifeCustomEffect(minTargets, maxTargets, outcome),
|
||||||
|
new ManaCostsImpl<>("")
|
||||||
|
);
|
||||||
|
ability.addCost(new TapSourceCost());
|
||||||
|
addCustomCardWithAbility("test choice", playerA, ability);
|
||||||
|
|
||||||
|
addCard(Zone.HAND, playerA, "Swamp", availableCardsCount);
|
||||||
|
addCard(Zone.HAND, playerA, "Forest", 1);
|
||||||
|
|
||||||
|
// Forest play is workaround to disable lands play in ai priority
|
||||||
|
// {T} cost is workaround to disable multiple calls of the ability
|
||||||
|
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Forest");
|
||||||
|
|
||||||
|
checkPlayableAbility("can activate any time (even with zero cards)", 1, PhaseStep.PRECOMBAT_MAIN, playerA, startingText, true);
|
||||||
|
|
||||||
|
// AI must see bad effect and select only halves of the max targets, e.g. 1 of 3
|
||||||
|
aiPlayPriority(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||||
|
|
||||||
|
setStrictChooseMode(true);
|
||||||
|
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
|
||||||
|
execute();
|
||||||
|
|
||||||
|
assertExileCount(playerA, "Swamp", aiMustChooseCardsCount);
|
||||||
|
assertLife(playerA, 20 + aiMustChooseCardsCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum TotalTargetsValue implements DynamicValue {
|
||||||
|
instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TotalTargetsValue copy() {
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||||
|
return effect.getTargetPointer().getTargets(game, sourceAbility).size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "total targets";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "X";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SelectExileAndGainLifeCustomEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
private final int minTargets;
|
||||||
|
private final int maxTargets;
|
||||||
|
|
||||||
|
SelectExileAndGainLifeCustomEffect(int minTargets, int maxTargets, Outcome outcome) {
|
||||||
|
super(outcome);
|
||||||
|
this.minTargets = minTargets;
|
||||||
|
this.maxTargets = maxTargets;
|
||||||
|
staticText = "select, exile and gain life";
|
||||||
|
}
|
||||||
|
|
||||||
|
private SelectExileAndGainLifeCustomEffect(final SelectExileAndGainLifeCustomEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
this.minTargets = effect.minTargets;
|
||||||
|
this.maxTargets = effect.maxTargets;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public SelectExileAndGainLifeCustomEffect copy() {
|
||||||
|
return new SelectExileAndGainLifeCustomEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
|
if (player == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Target target = new TargetCardInHand(this.minTargets, this.maxTargets, StaticFilters.FILTER_CARD).withNotTarget(true);
|
||||||
|
if (!player.choose(outcome, target, source, game)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
player.moveCardsToExile(new CardsImpl(target.getTargets()).getCards(game), source, game, false, source.getSourceId(), player.getLogName());
|
||||||
|
player.gainLife(target.getTargets().size(), game, source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,89 @@
|
||||||
|
package org.mage.test.cards.targets;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testing targets selection on activate/cast (player.chooseTarget)
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class TargetsSelectionOnActivateTest extends TargetsSelectionBaseTest {
|
||||||
|
|
||||||
|
// no selects
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_0() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_1() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_2() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_3() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_10() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 select
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_1_of_1() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_1_of_2() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_1_of_3() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_1_of_10() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(1, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 selects
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_2_of_2() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_2_of_3() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(2, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_2_of_10() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(2, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 selects
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_3_of_3() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_3_of_10() {
|
||||||
|
run_PlayerChooseTarget_OnActivate(3, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,66 @@
|
||||||
|
package org.mage.test.cards.targets;
|
||||||
|
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testing targets selection on resolve (player.choose) for AI
|
||||||
|
* <p>
|
||||||
|
* AI must use logic like:
|
||||||
|
* - for good effects - choose as much as possible targets
|
||||||
|
* - for bad effects - choose as much as lower targets
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class TargetsSelectionOnResolveAITest extends TargetsSelectionBaseTest {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Good_0_of_0() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Good_1_of_1() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Good_2_of_2() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Good_3_of_3() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Good_3_of_10() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Benefit, 0, 3, 3, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Bad_0_of_0() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Bad_0_of_1() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Bad_0_of_2() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Bad_0_of_3() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnResolve_Bad_0_of_10() {
|
||||||
|
run_PlayerChoose_OnResolve_AI(Outcome.Detriment, 0, 3, 0, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
package org.mage.test.cards.targets;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testing targets selection on resolve (player.choose)
|
||||||
|
* <p>
|
||||||
|
* Player can use any logic and choose any number of targets
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
public class TargetsSelectionOnResolveTest extends TargetsSelectionBaseTest {
|
||||||
|
|
||||||
|
// no selects
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_0() {
|
||||||
|
run_PlayerChoose_OnResolve(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_1() {
|
||||||
|
run_PlayerChoose_OnResolve(0, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_2() {
|
||||||
|
run_PlayerChoose_OnResolve(0, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_3() {
|
||||||
|
run_PlayerChoose_OnResolve(0, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_0_of_10() {
|
||||||
|
run_PlayerChoose_OnResolve(0, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 select
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_1_of_1() {
|
||||||
|
run_PlayerChoose_OnResolve(1, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_1_of_2() {
|
||||||
|
run_PlayerChoose_OnResolve(1, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_1_of_3() {
|
||||||
|
run_PlayerChoose_OnResolve(1, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_1_of_10() {
|
||||||
|
run_PlayerChoose_OnResolve(1, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 selects
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_2_of_2() {
|
||||||
|
run_PlayerChoose_OnResolve(2, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_2_of_3() {
|
||||||
|
run_PlayerChoose_OnResolve(2, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_2_of_10() {
|
||||||
|
run_PlayerChoose_OnResolve(2, 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 selects
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_3_of_3() {
|
||||||
|
run_PlayerChoose_OnResolve(3, 3);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void test_OnActivate_3_of_10() {
|
||||||
|
run_PlayerChoose_OnResolve(3, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.triggers;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -23,6 +24,7 @@ public class EnterLeaveBattlefieldExileTargetTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Serenity");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Angel of Serenity");
|
||||||
addTarget(playerA, "Silvercoat Lion^Pillarfield Ox");
|
addTarget(playerA, "Silvercoat Lion^Pillarfield Ox");
|
||||||
|
addTarget(playerA, TestPlayer.TARGET_SKIP);
|
||||||
setChoice(playerA, true);
|
setChoice(playerA, true);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import org.junit.Test;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author LevelX2
|
* @author LevelX2
|
||||||
*/
|
*/
|
||||||
public class WorldgorgerDragonTest extends CardTestPlayerBase {
|
public class WorldgorgerDragonTest extends CardTestPlayerBase {
|
||||||
|
|
@ -83,62 +82,48 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase {
|
||||||
// When Staunch Defenders enters the battlefield, you gain 4 life.
|
// When Staunch Defenders enters the battlefield, you gain 4 life.
|
||||||
addCard(Zone.BATTLEFIELD, playerA, "Staunch Defenders", 1);
|
addCard(Zone.BATTLEFIELD, playerA, "Staunch Defenders", 1);
|
||||||
|
|
||||||
|
// 1 cast and resolve
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Worldgorger Dragon");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Animate Dead", "Worldgorger Dragon");
|
||||||
|
setChoice(playerA, "Worldgorger Dragon"); // attach
|
||||||
|
setChoice(playerA, "When {this} enters, if it's"); // x2 triggers (gain life from Staunch Defenders and etb from Animate Dead)
|
||||||
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||||
|
|
||||||
|
// 2 etb and attach
|
||||||
setChoice(playerA, "Worldgorger Dragon");
|
setChoice(playerA, "Worldgorger Dragon");
|
||||||
setChoice(playerA, "When {this} enters, if it's");
|
setChoice(playerA, "When {this} enters, if it's");
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
|
|
||||||
|
// 3 etb and attach
|
||||||
setChoice(playerA, "Worldgorger Dragon");
|
setChoice(playerA, "Worldgorger Dragon");
|
||||||
setChoice(playerA, "When {this} enters, if it's");
|
setChoice(playerA, "When {this} enters, if it's");
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
|
|
||||||
|
// 4 etb and attach
|
||||||
setChoice(playerA, "Worldgorger Dragon");
|
setChoice(playerA, "Worldgorger Dragon");
|
||||||
setChoice(playerA, "When {this} enters, if it's");
|
setChoice(playerA, "When {this} enters, if it's");
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
|
|
||||||
|
// 5 etb and attach
|
||||||
|
setChoice(playerA, "Worldgorger Dragon");
|
||||||
|
setChoice(playerA, false); // no draws on infinite loop
|
||||||
|
setChoice(playerA, "When {this} enters, if it's");
|
||||||
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||||
|
|
||||||
|
// 6 etb and attach
|
||||||
setChoice(playerA, "Worldgorger Dragon");
|
setChoice(playerA, "Worldgorger Dragon");
|
||||||
setChoice(playerA, "When {this} enters, if it's");
|
setChoice(playerA, "When {this} enters, if it's");
|
||||||
setChoice(playerA, false);
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3);
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
|
|
||||||
setChoice(playerA, "Worldgorger Dragon");
|
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}", 3 - 1);
|
||||||
setChoice(playerA, "When {this} enters, if it's");
|
|
||||||
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
// cast spell and stop infinite loop after 20+ mana in pool
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB, 20);
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
|
|
||||||
setChoice(playerA, "Worldgorger Dragon");
|
|
||||||
setChoice(playerA, "When {this} enters, if it's");
|
|
||||||
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
|
|
||||||
setChoice(playerA, "Worldgorger Dragon");
|
|
||||||
setChoice(playerA, "When {this} enters, if it's");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {R}");
|
|
||||||
|
|
||||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Volcanic Geyser", playerB, 22);
|
|
||||||
setChoice(playerA, "X=20");
|
setChoice(playerA, "X=20");
|
||||||
|
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
assertLife(playerA, 44);
|
assertLife(playerA, 20 + 5 * 4);
|
||||||
assertLife(playerB, 0);
|
assertLife(playerB, 0);
|
||||||
|
|
||||||
assertGraveyardCount(playerA, "Volcanic Geyser", 1);
|
assertGraveyardCount(playerA, "Volcanic Geyser", 1);
|
||||||
|
|
@ -154,12 +139,11 @@ public class WorldgorgerDragonTest extends CardTestPlayerBase {
|
||||||
* you choose to skip or pick a different creature, it always returns the
|
* you choose to skip or pick a different creature, it always returns the
|
||||||
* first creature you picked. Kind of hard to explain, but here's how to
|
* first creature you picked. Kind of hard to explain, but here's how to
|
||||||
* reproduce:
|
* reproduce:
|
||||||
*
|
* <p>
|
||||||
* 1) Cast Animate Dead, targeting Worldgorger Dragon 2) Worldgorger Dragon
|
* 1) Cast Animate Dead, targeting Worldgorger Dragon 2) Worldgorger Dragon
|
||||||
* will exile Animate Dead, killing the dragon and returning the permanents
|
* will exile Animate Dead, killing the dragon and returning the permanents
|
||||||
* 3) Select Worldgorger again 4) Step 2 repeats 5) Attempt to select a
|
* 3) Select Worldgorger again 4) Step 2 repeats 5) Attempt to select a
|
||||||
* different creature. Worldgorger Dragon is returned instead.
|
* different creature. Worldgorger Dragon is returned instead.
|
||||||
*
|
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void testWithAnimateDeadDifferentTargets() {
|
public void testWithAnimateDeadDifferentTargets() {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.triggers.dies;
|
||||||
import mage.constants.PhaseStep;
|
import mage.constants.PhaseStep;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mage.test.player.TestPlayer;
|
||||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -159,6 +160,7 @@ public class VengefulTownsfolkTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three");
|
||||||
setChoice(playerA, "Angel of the God-Pharaoh"); // sac cost
|
setChoice(playerA, "Angel of the God-Pharaoh"); // sac cost
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
|
@ -183,6 +185,7 @@ public class VengefulTownsfolkTest extends CardTestPlayerBase {
|
||||||
|
|
||||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three");
|
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}, Sacrifice up to three");
|
||||||
setChoice(playerA, "Grizzly Bears^Angel of the God-Pharaoh"); // sac cost
|
setChoice(playerA, "Grizzly Bears^Angel of the God-Pharaoh"); // sac cost
|
||||||
|
setChoice(playerA, TestPlayer.CHOICE_SKIP);
|
||||||
|
|
||||||
setStrictChooseMode(true);
|
setStrictChooseMode(true);
|
||||||
setStopAt(1, PhaseStep.END_TURN);
|
setStopAt(1, PhaseStep.END_TURN);
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ public class TestPlayer implements Player {
|
||||||
|
|
||||||
private static final Logger LOGGER = Logger.getLogger(TestPlayer.class);
|
private static final Logger LOGGER = Logger.getLogger(TestPlayer.class);
|
||||||
|
|
||||||
private static final int takeMaxTargetsPerChoose = Integer.MAX_VALUE; // TODO: set 1, fix broken tests and replace all "for (String targetDefinition" by targets.get(0)
|
private static final int takeMaxTargetsPerChoose = Integer.MAX_VALUE; // TODO: set 1 here, fix broken tests and replace all "for (String targetDefinition" by targets.get(0)
|
||||||
|
|
||||||
public static final String TARGET_SKIP = "[target_skip]"; // stop/skip targeting
|
public static final String TARGET_SKIP = "[target_skip]"; // stop/skip targeting
|
||||||
public static final String CHOICE_SKIP = "[choice_skip]"; // stop/skip choice
|
public static final String CHOICE_SKIP = "[choice_skip]"; // stop/skip choice
|
||||||
|
|
@ -113,7 +113,7 @@ public class TestPlayer implements Player {
|
||||||
// (example: card call TestPlayer's choice, but it uses another choices, see docs in TestComputerPlayer)
|
// (example: card call TestPlayer's choice, but it uses another choices, see docs in TestComputerPlayer)
|
||||||
private boolean strictChooseMode = false;
|
private boolean strictChooseMode = false;
|
||||||
|
|
||||||
private String[] groupsForTargetHandling = null;
|
private String[] groupsForTargetHandling = null; // predefined targets list from cast/activate command
|
||||||
|
|
||||||
// Tracks the initial turns (turn 0s) both players are given at the start of the game.
|
// Tracks the initial turns (turn 0s) both players are given at the start of the game.
|
||||||
// Before actual turns start. Needed for checking attacker/blocker legality in the tests
|
// Before actual turns start. Needed for checking attacker/blocker legality in the tests
|
||||||
|
|
@ -516,7 +516,7 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (UUID id : currentTarget.possibleTargets(ability.getControllerId(), ability, game)) {
|
for (UUID id : currentTarget.possibleTargets(ability.getControllerId(), ability, game)) {
|
||||||
if (!currentTarget.getTargets().contains(id)) {
|
if (!currentTarget.contains(id)) {
|
||||||
MageObject object = game.getObject(id);
|
MageObject object = game.getObject(id);
|
||||||
|
|
||||||
if (object == null) {
|
if (object == null) {
|
||||||
|
|
@ -594,7 +594,7 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
// fake test ability for triggers and events
|
// fake test ability for triggers and events
|
||||||
Ability source = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("adding testing cards"));
|
Ability source = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("fake ability"));
|
||||||
source.setControllerId(this.getId());
|
source.setControllerId(this.getId());
|
||||||
|
|
||||||
int numberOfActions = actions.size();
|
int numberOfActions = actions.size();
|
||||||
|
|
@ -2099,8 +2099,18 @@ public class TestPlayer implements Player {
|
||||||
return "Ability: null";
|
return "Ability: null";
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getInfo(Target o, Game game) {
|
private String getInfo(Target target, Ability source, Game game) {
|
||||||
return "Target: " + (o != null ? o.getClass().getSimpleName() + ": " + o.getMessage(game) : "null");
|
if (target == null) {
|
||||||
|
return "Target: null";
|
||||||
|
}
|
||||||
|
UUID abilityControllerId = getId();
|
||||||
|
if (target.getTargetController() != null && target.getAbilityController() != null) {
|
||||||
|
abilityControllerId = target.getAbilityController();
|
||||||
|
}
|
||||||
|
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game);
|
||||||
|
|
||||||
|
return "Target: selected " + target.getSize() + ", possible " + possibleTargets.size()
|
||||||
|
+ ", " + target.getClass().getSimpleName() + ": " + target.getMessage(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertAliasSupportInChoices(boolean methodSupportAliases) {
|
private void assertAliasSupportInChoices(boolean methodSupportAliases) {
|
||||||
|
|
@ -2211,7 +2221,7 @@ public class TestPlayer implements Player {
|
||||||
// skip choices
|
// skip choices
|
||||||
if (possibleChoice.equals(CHOICE_SKIP)) {
|
if (possibleChoice.equals(CHOICE_SKIP)) {
|
||||||
choices.remove(0);
|
choices.remove(0);
|
||||||
return true;
|
return false; // false - stop to choose
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choice.setChoiceByAnswers(choices, true)) {
|
if (choice.setChoiceByAnswers(choices, true)) {
|
||||||
|
|
@ -2264,27 +2274,28 @@ public class TestPlayer implements Player {
|
||||||
abilityControllerId = target.getAbilityController();
|
abilityControllerId = target.getAbilityController();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: warning, some cards call player.choose methods instead target.choose, see #8254
|
||||||
|
// most use cases - discard and other cost with choice like that method
|
||||||
|
// must migrate all choices.remove(xxx) to choices.remove(0), takeMaxTargetsPerChoose can help to find it
|
||||||
|
|
||||||
// ignore player select
|
// ignore player select
|
||||||
if (target.getMessage(game).equals("Select a starting player")) {
|
if (target.getMessage(game).equals("Select a starting player")) {
|
||||||
return computerPlayer.choose(outcome, target, source, game, options);
|
return computerPlayer.choose(outcome, target, source, game, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isAddedSomething = false; // must return true on any changes in targets, so game can ask next choose dialog until finish
|
||||||
|
|
||||||
assertAliasSupportInChoices(true);
|
assertAliasSupportInChoices(true);
|
||||||
if (!choices.isEmpty()) {
|
if (!choices.isEmpty()) {
|
||||||
|
|
||||||
// skip choices
|
// skip choices
|
||||||
if (choices.get(0).equals(CHOICE_SKIP)) {
|
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||||
Assert.assertTrue("found skip choice, but it require more choices, needs "
|
Assert.assertTrue("found skip choice, but it require more choices, needs "
|
||||||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||||
choices.remove(0);
|
return false; // false - stop to choose
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Integer> usedChoices = new ArrayList<>();
|
|
||||||
List<UUID> usedTargets = new ArrayList<>();
|
|
||||||
|
|
||||||
|
|
||||||
// TODO: Allow to choose a player with TargetPermanentOrPlayer
|
// TODO: Allow to choose a player with TargetPermanentOrPlayer
|
||||||
if ((target.getOriginalTarget() instanceof TargetPermanent)
|
if ((target.getOriginalTarget() instanceof TargetPermanent)
|
||||||
|| (target.getOriginalTarget() instanceof TargetPermanentOrPlayer)) { // player target not implemented yet
|
|| (target.getOriginalTarget() instanceof TargetPermanentOrPlayer)) { // player target not implemented yet
|
||||||
|
|
@ -2294,10 +2305,13 @@ public class TestPlayer implements Player {
|
||||||
} else {
|
} else {
|
||||||
filterPermanent = ((TargetPermanent) target.getOriginalTarget()).getFilter();
|
filterPermanent = ((TargetPermanent) target.getOriginalTarget()).getFilter();
|
||||||
}
|
}
|
||||||
while (!choices.isEmpty()) {
|
while (!choices.isEmpty()) { // TODO: remove cycle after main commits
|
||||||
|
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||||
|
return false; // stop dialog
|
||||||
|
}
|
||||||
String choiceRecord = choices.get(0);
|
String choiceRecord = choices.get(0);
|
||||||
String[] targetList = choiceRecord.split("\\^");
|
String[] targetList = choiceRecord.split("\\^");
|
||||||
boolean targetFound = false;
|
isAddedSomething = false;
|
||||||
for (String targetName : targetList) {
|
for (String targetName : targetList) {
|
||||||
boolean originOnly = false;
|
boolean originOnly = false;
|
||||||
boolean copyOnly = false;
|
boolean copyOnly = false;
|
||||||
|
|
@ -2312,14 +2326,14 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterPermanent, abilityControllerId, source, game)) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filterPermanent, abilityControllerId, source, game)) {
|
||||||
if (target.getTargets().contains(permanent.getId())) {
|
if (target.contains(permanent.getId())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (hasObjectTargetNameOrAlias(permanent, targetName)) {
|
if (hasObjectTargetNameOrAlias(permanent, targetName)) {
|
||||||
if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
|
if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
|
||||||
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
||||||
target.add(permanent.getId(), game);
|
target.add(permanent.getId(), game);
|
||||||
targetFound = true;
|
isAddedSomething = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2327,7 +2341,7 @@ public class TestPlayer implements Player {
|
||||||
if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
|
if (target.isNotTarget() || target.canTarget(abilityControllerId, permanent.getId(), source, game)) {
|
||||||
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
||||||
target.add(permanent.getId(), game);
|
target.add(permanent.getId(), game);
|
||||||
targetFound = true;
|
isAddedSomething = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2335,51 +2349,50 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!targetFound) {
|
|
||||||
//failOnLastBadChoice(game, source, target, choiceRecord, "unknown or can't target");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (target.isChosen(game)) {
|
if (isAddedSomething) {
|
||||||
|
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
return true;
|
return true;
|
||||||
} else {
|
|
||||||
// TODO: move check above and fix all fail tests (not after target.isChosen)
|
|
||||||
if (!targetFound) {
|
|
||||||
failOnLastBadChoice(game, source, target, choiceRecord, "selected, but not all required targets");
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
failOnLastBadChoice(game, source, target, choiceRecord, "invalid target or miss skip command");
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
choices.remove(0);
|
choices.remove(0);
|
||||||
}
|
}
|
||||||
}
|
return isAddedSomething;
|
||||||
|
} // choices
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target instanceof TargetPlayer) {
|
if (target instanceof TargetPlayer) {
|
||||||
while (!choices.isEmpty()) {
|
while (!choices.isEmpty()) { // TODO: remove cycle after main commits
|
||||||
|
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||||
|
return false; // stop dialog
|
||||||
|
}
|
||||||
String choiceRecord = choices.get(0);
|
String choiceRecord = choices.get(0);
|
||||||
boolean targetFound = false;
|
isAddedSomething = false;
|
||||||
for (Player player : game.getPlayers().values()) {
|
for (Player player : game.getPlayers().values()) {
|
||||||
if (player.getName().equals(choiceRecord)) {
|
if (player.getName().equals(choiceRecord)) {
|
||||||
if (target.canTarget(abilityControllerId, player.getId(), null, game) && !target.getTargets().contains(player.getId())) {
|
if (target.canTarget(abilityControllerId, player.getId(), null, game) && !target.contains(player.getId())) {
|
||||||
target.add(player.getId(), game);
|
target.add(player.getId(), game);
|
||||||
targetFound = true;
|
isAddedSomething = true;
|
||||||
} else {
|
|
||||||
failOnLastBadChoice(game, source, target, choiceRecord, "can't target");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (target.isChosen(game)) {
|
if (isAddedSomething) {
|
||||||
|
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!targetFound) {
|
} else {
|
||||||
failOnLastBadChoice(game, source, target, choiceRecord, "unknown target");
|
failOnLastBadChoice(game, source, target, choiceRecord, "invalid target or miss skip command");
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
choices.remove(0);
|
choices.remove(0);
|
||||||
}
|
}
|
||||||
}
|
return isAddedSomething;
|
||||||
|
} // while choices
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: add same choices fixes for other target types (one choice must uses only one time for one target)
|
// TODO: add same choices fixes for other target types (one choice must uses only one time for one target)
|
||||||
|
|
@ -2388,102 +2401,78 @@ public class TestPlayer implements Player {
|
||||||
// only unique targets
|
// only unique targets
|
||||||
//TargetCard targetFull = ((TargetCard) target);
|
//TargetCard targetFull = ((TargetCard) target);
|
||||||
|
|
||||||
usedChoices.clear();
|
for (String choiceRecord : new ArrayList<>(choices)) { // TODO: remove cycle after main commits
|
||||||
usedTargets.clear();
|
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||||
boolean targetCompleted = false;
|
return false; // stop dialog
|
||||||
|
|
||||||
CheckAllChoices:
|
|
||||||
for (int choiceIndex = 0; choiceIndex < choices.size(); choiceIndex++) {
|
|
||||||
String choiceRecord = choices.get(choiceIndex);
|
|
||||||
if (targetCompleted) {
|
|
||||||
break CheckAllChoices;
|
|
||||||
}
|
}
|
||||||
|
isAddedSomething = false;
|
||||||
boolean targetFound = false;
|
|
||||||
String[] possibleChoices = choiceRecord.split("\\^");
|
String[] possibleChoices = choiceRecord.split("\\^");
|
||||||
|
|
||||||
CheckOneChoice:
|
|
||||||
for (String possibleChoice : possibleChoices) {
|
for (String possibleChoice : possibleChoices) {
|
||||||
Set<UUID> possibleCards = target.possibleTargets(abilityControllerId, source, game);
|
Set<UUID> possibleCards = target.possibleTargets(abilityControllerId, source, game);
|
||||||
CheckTargetsList:
|
|
||||||
for (UUID targetId : possibleCards) {
|
for (UUID targetId : possibleCards) {
|
||||||
MageObject targetObject = game.getCard(targetId);
|
MageObject targetObject = game.getCard(targetId);
|
||||||
if (hasObjectTargetNameOrAlias(targetObject, possibleChoice)) {
|
if (hasObjectTargetNameOrAlias(targetObject, possibleChoice)) {
|
||||||
if (target.canTarget(targetObject.getId(), game)) {
|
if (target.canTarget(targetObject.getId(), game) && !target.contains(targetObject.getId())) {
|
||||||
// only unique targets
|
|
||||||
if (usedTargets.contains(targetObject.getId())) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// OK, can use it
|
|
||||||
target.add(targetObject.getId(), game);
|
target.add(targetObject.getId(), game);
|
||||||
targetFound = true;
|
isAddedSomething = true;
|
||||||
usedTargets.add(targetObject.getId());
|
break;
|
||||||
|
|
||||||
// break on full targets list
|
|
||||||
if (target.getTargets().size() >= target.getMaxNumberOfTargets()) {
|
|
||||||
targetCompleted = true;
|
|
||||||
break CheckOneChoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
// restart search
|
|
||||||
break CheckTargetsList;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
if (targetFound) {
|
if (isAddedSomething) {
|
||||||
usedChoices.add(choiceIndex);
|
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// apply only on ALL targets or revert
|
|
||||||
if (usedChoices.size() > 0) {
|
|
||||||
if (target.isChosen(game)) {
|
|
||||||
// remove all used choices
|
|
||||||
for (int i = choices.size(); i >= 0; i--) {
|
|
||||||
if (usedChoices.contains(i)) {
|
|
||||||
choices.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
Assert.fail("Not full targets list.");
|
failOnLastBadChoice(game, source, target, choiceRecord, "invalid target or miss skip command");
|
||||||
target.clearChosen();
|
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
choices.remove(0);
|
||||||
}
|
}
|
||||||
|
return isAddedSomething;
|
||||||
|
} // for choices
|
||||||
}
|
}
|
||||||
|
|
||||||
if (target.getOriginalTarget() instanceof TargetSource) {
|
if (target.getOriginalTarget() instanceof TargetSource) {
|
||||||
Set<UUID> possibleTargets;
|
|
||||||
TargetSource t = ((TargetSource) target.getOriginalTarget());
|
TargetSource t = ((TargetSource) target.getOriginalTarget());
|
||||||
possibleTargets = t.possibleTargets(abilityControllerId, source, game);
|
Set<UUID> possibleTargets = t.possibleTargets(abilityControllerId, source, game);
|
||||||
for (String choiceRecord : choices) {
|
// TODO: enable choices.get first instead all
|
||||||
|
for (String choiceRecord : new ArrayList<>(choices)) { // TODO: remove cycle after main commits
|
||||||
|
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||||
|
return false; // stop dialog
|
||||||
|
}
|
||||||
String[] targetList = choiceRecord.split("\\^");
|
String[] targetList = choiceRecord.split("\\^");
|
||||||
boolean targetFound = false;
|
isAddedSomething = false;
|
||||||
for (String targetName : targetList) {
|
for (String targetName : targetList) {
|
||||||
for (UUID targetId : possibleTargets) {
|
for (UUID targetId : possibleTargets) {
|
||||||
MageObject targetObject = game.getObject(targetId);
|
MageObject targetObject = game.getObject(targetId);
|
||||||
if (targetObject != null) {
|
if (targetObject != null) {
|
||||||
if (hasObjectTargetNameOrAlias(targetObject, targetName)) {
|
if (hasObjectTargetNameOrAlias(targetObject, targetName)) {
|
||||||
List<UUID> alreadyTargetted = target.getTargets();
|
if (t.canTarget(targetObject.getId(), game) && !target.contains(targetObject.getId())) {
|
||||||
if (t.canTarget(targetObject.getId(), game)) {
|
|
||||||
if (alreadyTargetted != null && !alreadyTargetted.contains(targetObject.getId())) {
|
|
||||||
target.add(targetObject.getId(), game);
|
target.add(targetObject.getId(), game);
|
||||||
choices.remove(choiceRecord);
|
isAddedSomething = true;
|
||||||
targetFound = true;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (targetFound) {
|
}
|
||||||
choices.remove(choiceRecord);
|
try {
|
||||||
|
if (isAddedSomething) {
|
||||||
|
if (target.isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
failOnLastBadChoice(game, source, target, choiceRecord, "invalid target or miss skip command");
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
choices.remove(choiceRecord);
|
||||||
}
|
}
|
||||||
}
|
return isAddedSomething;
|
||||||
|
} // for choices
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: enable fail checks and fix tests
|
// TODO: enable fail checks and fix tests
|
||||||
|
|
@ -2492,7 +2481,7 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||||
return computerPlayer.choose(outcome, target, source, game, options);
|
return computerPlayer.choose(outcome, target, source, game, options);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2535,7 +2524,7 @@ public class TestPlayer implements Player {
|
||||||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||||
targets.remove(0);
|
targets.remove(0);
|
||||||
return true;
|
return false; // false - stop to choose
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<Zone> targetCardZonesChecked = new HashSet<>(); // control miss implementation
|
Set<Zone> targetCardZonesChecked = new HashSet<>(); // control miss implementation
|
||||||
|
|
@ -2594,7 +2583,7 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents((FilterPermanent) filter, abilityControllerId, source, game)) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents((FilterPermanent) filter, abilityControllerId, source, game)) {
|
||||||
if (hasObjectTargetNameOrAlias(permanent, targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove exp code search?
|
if (hasObjectTargetNameOrAlias(permanent, targetName) || (permanent.getName() + '-' + permanent.getExpansionSetCode()).equals(targetName)) { // TODO: remove exp code search?
|
||||||
if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.getTargets().contains(permanent.getId())) {
|
if (target.canTarget(abilityControllerId, permanent.getId(), source, game) && !target.contains(permanent.getId())) {
|
||||||
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
if ((permanent.isCopy() && !originOnly) || (!permanent.isCopy() && !copyOnly)) {
|
||||||
target.addTarget(permanent.getId(), source, game);
|
target.addTarget(permanent.getId(), source, game);
|
||||||
targetFound = true;
|
targetFound = true;
|
||||||
|
|
@ -2624,7 +2613,7 @@ public class TestPlayer implements Player {
|
||||||
for (String targetName : targetList) {
|
for (String targetName : targetList) {
|
||||||
for (Card card : computerPlayer.getHand().getCards(((TargetCard) target.getOriginalTarget()).getFilter(), game)) {
|
for (Card card : computerPlayer.getHand().getCards(((TargetCard) target.getOriginalTarget()).getFilter(), game)) {
|
||||||
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
||||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
|
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) {
|
||||||
target.addTarget(card.getId(), source, game);
|
target.addTarget(card.getId(), source, game);
|
||||||
targetFound = true;
|
targetFound = true;
|
||||||
break; // return to next targetName
|
break; // return to next targetName
|
||||||
|
|
@ -2662,7 +2651,7 @@ public class TestPlayer implements Player {
|
||||||
for (String targetName : targetList) {
|
for (String targetName : targetList) {
|
||||||
for (Card card : game.getExile().getCards(filter, game)) {
|
for (Card card : game.getExile().getCards(filter, game)) {
|
||||||
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
||||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
|
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) {
|
||||||
target.addTarget(card.getId(), source, game);
|
target.addTarget(card.getId(), source, game);
|
||||||
targetFound = true;
|
targetFound = true;
|
||||||
break; // return to next targetName
|
break; // return to next targetName
|
||||||
|
|
@ -2687,7 +2676,7 @@ public class TestPlayer implements Player {
|
||||||
for (String targetName : targetList) {
|
for (String targetName : targetList) {
|
||||||
for (Card card : game.getBattlefield().getAllActivePermanents()) {
|
for (Card card : game.getBattlefield().getAllActivePermanents()) {
|
||||||
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
||||||
if (targetFull.canTarget(abilityControllerId, card.getId(), source, game) && !targetFull.getTargets().contains(card.getId())) {
|
if (targetFull.canTarget(abilityControllerId, card.getId(), source, game) && !targetFull.contains(card.getId())) {
|
||||||
targetFull.add(card.getId(), game);
|
targetFull.add(card.getId(), game);
|
||||||
targetFound = true;
|
targetFound = true;
|
||||||
break; // return to next targetName
|
break; // return to next targetName
|
||||||
|
|
@ -2739,7 +2728,7 @@ public class TestPlayer implements Player {
|
||||||
Player player = game.getPlayer(playerId);
|
Player player = game.getPlayer(playerId);
|
||||||
for (Card card : player.getGraveyard().getCards(targetFull.getFilter(), game)) {
|
for (Card card : player.getGraveyard().getCards(targetFull.getFilter(), game)) {
|
||||||
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
if (hasObjectTargetNameOrAlias(card, targetName) || (card.getName() + '-' + card.getExpansionSetCode()).equals(targetName)) { // TODO: remove set code search?
|
||||||
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.getTargets().contains(card.getId())) {
|
if (target.canTarget(abilityControllerId, card.getId(), source, game) && !target.contains(card.getId())) {
|
||||||
target.addTarget(card.getId(), source, game);
|
target.addTarget(card.getId(), source, game);
|
||||||
targetFound = true;
|
targetFound = true;
|
||||||
break IterateGraveyards; // return to next targetName
|
break IterateGraveyards; // return to next targetName
|
||||||
|
|
@ -2754,7 +2743,6 @@ public class TestPlayer implements Player {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stack
|
// stack
|
||||||
|
|
@ -2769,7 +2757,7 @@ public class TestPlayer implements Player {
|
||||||
for (String targetName : targetList) {
|
for (String targetName : targetList) {
|
||||||
for (StackObject stackObject : game.getStack()) {
|
for (StackObject stackObject : game.getStack()) {
|
||||||
if (hasObjectTargetNameOrAlias(stackObject, targetName)) {
|
if (hasObjectTargetNameOrAlias(stackObject, targetName)) {
|
||||||
if (target.canTarget(abilityControllerId, stackObject.getId(), source, game) && !target.getTargets().contains(stackObject.getId())) {
|
if (target.canTarget(abilityControllerId, stackObject.getId(), source, game) && !target.contains(stackObject.getId())) {
|
||||||
target.addTarget(stackObject.getId(), source, game);
|
target.addTarget(stackObject.getId(), source, game);
|
||||||
targetFound = true;
|
targetFound = true;
|
||||||
break; // return to next targetName
|
break; // return to next targetName
|
||||||
|
|
@ -2804,24 +2792,25 @@ public class TestPlayer implements Player {
|
||||||
// how to fix: implement target class processing above (if it a permanent target then check "filter instanceof" code too)
|
// how to fix: implement target class processing above (if it a permanent target then check "filter instanceof" code too)
|
||||||
if (!targets.isEmpty()) {
|
if (!targets.isEmpty()) {
|
||||||
String message;
|
String message;
|
||||||
|
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game);
|
||||||
|
|
||||||
if (source != null) {
|
if (source != null) {
|
||||||
message = this.getName() + " - Targets list was setup by addTarget with " + targets + ", but not used"
|
message = this.getName() + " - Targets list was setup by addTarget with " + targets + ", but not used"
|
||||||
+ "\nCard: " + source.getSourceObject(game)
|
+ "\nCard: " + source.getSourceObject(game)
|
||||||
+ "\nAbility: " + source.getClass().getSimpleName() + " (" + source.getRule() + ")"
|
+ "\nAbility: " + source.getClass().getSimpleName() + " (" + source.getRule() + ")"
|
||||||
+ "\nTarget: " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
|
+ "\nTarget: selected " + target.getSize() + ", possible " + possibleTargets.size() + ", " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
|
||||||
+ "\nYou must implement target class support in TestPlayer, \"filter instanceof\", or setup good targets";
|
+ "\nYou must implement target class support in TestPlayer, \"filter instanceof\", or setup good targets";
|
||||||
} else {
|
} else {
|
||||||
message = this.getName() + " - Targets list was setup by addTarget with " + targets + ", but not used"
|
message = this.getName() + " - Targets list was setup by addTarget with " + targets + ", but not used"
|
||||||
+ "\nCard: unknown source"
|
+ "\nCard: unknown source"
|
||||||
+ "\nAbility: unknown source"
|
+ "\nAbility: unknown source"
|
||||||
+ "\nTarget: " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
|
+ "\nTarget: selected " + target.getSize() + ", possible " + possibleTargets.size() + ", " + target.getClass().getSimpleName() + " (" + target.getMessage(game) + ")"
|
||||||
+ "\nYou must implement target class support in TestPlayer, \"filter instanceof\", or setup good targets";
|
+ "\nYou must implement target class support in TestPlayer, \"filter instanceof\", or setup good targets";
|
||||||
}
|
}
|
||||||
Assert.fail(message);
|
Assert.fail(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||||
return computerPlayer.chooseTarget(outcome, target, source, game);
|
return computerPlayer.chooseTarget(outcome, target, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2840,7 +2829,7 @@ public class TestPlayer implements Player {
|
||||||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||||
targets.remove(0);
|
targets.remove(0);
|
||||||
return true;
|
return false; // false - stop to choose
|
||||||
}
|
}
|
||||||
for (String targetDefinition : targets.stream().limit(takeMaxTargetsPerChoose).collect(Collectors.toList())) {
|
for (String targetDefinition : targets.stream().limit(takeMaxTargetsPerChoose).collect(Collectors.toList())) {
|
||||||
String[] targetList = targetDefinition.split("\\^");
|
String[] targetList = targetDefinition.split("\\^");
|
||||||
|
|
@ -2848,7 +2837,7 @@ public class TestPlayer implements Player {
|
||||||
for (String targetName : targetList) {
|
for (String targetName : targetList) {
|
||||||
for (Card card : cards.getCards(game)) {
|
for (Card card : cards.getCards(game)) {
|
||||||
if (hasObjectTargetNameOrAlias(card, targetName)
|
if (hasObjectTargetNameOrAlias(card, targetName)
|
||||||
&& !target.getTargets().contains(card.getId())
|
&& !target.contains(card.getId())
|
||||||
&& target.canTarget(abilityControllerId, card.getId(), source, cards, game)) {
|
&& target.canTarget(abilityControllerId, card.getId(), source, cards, game)) {
|
||||||
target.addTarget(card.getId(), source, game);
|
target.addTarget(card.getId(), source, game);
|
||||||
targetFound = true;
|
targetFound = true;
|
||||||
|
|
@ -2867,7 +2856,7 @@ public class TestPlayer implements Player {
|
||||||
LOGGER.warn("Wrong target");
|
LOGGER.warn("Wrong target");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||||
return computerPlayer.chooseTarget(outcome, cards, target, source, game);
|
return computerPlayer.chooseTarget(outcome, cards, target, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2930,7 +2919,7 @@ public class TestPlayer implements Player {
|
||||||
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
|
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
|
||||||
assertAliasSupportInChoices(false);
|
assertAliasSupportInChoices(false);
|
||||||
if (!choices.isEmpty()) {
|
if (!choices.isEmpty()) {
|
||||||
for (String choice : choices) {
|
for (String choice : new ArrayList<>(choices)) {
|
||||||
if (choice.startsWith("X=")) {
|
if (choice.startsWith("X=")) {
|
||||||
int xValue = Integer.parseInt(choice.substring(2));
|
int xValue = Integer.parseInt(choice.substring(2));
|
||||||
assertXMinMaxValue(game, ability, xValue, min, max);
|
assertXMinMaxValue(game, ability, xValue, min, max);
|
||||||
|
|
@ -4276,32 +4265,40 @@ public class TestPlayer implements Player {
|
||||||
return choose(outcome, target, source, game, null);
|
return choose(outcome, target, source, game, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean tryToSkipSelection(List<String> selections, String selectionMark) {
|
||||||
|
if (!selections.isEmpty() && selections.get(0).equals(selectionMark)) {
|
||||||
|
selections.remove(0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
public boolean choose(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game) {
|
||||||
assertAliasSupportInChoices(false);
|
assertAliasSupportInChoices(false);
|
||||||
if (!choices.isEmpty()) {
|
if (!choices.isEmpty()) {
|
||||||
|
|
||||||
// skip choices
|
// skip choices
|
||||||
if (choices.get(0).equals(CHOICE_SKIP)) {
|
if (tryToSkipSelection(choices, CHOICE_SKIP)) {
|
||||||
choices.remove(0);
|
|
||||||
if (cards.isEmpty()) {
|
if (cards.isEmpty()) {
|
||||||
// cancel button forced in GUI on no possible choices
|
// cancel button forced in GUI on no possible choices
|
||||||
|
// TODO: need research
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
Assert.assertTrue("found skip choice, but it require more choices, needs "
|
Assert.assertTrue("found skip choice, but it require more choices, needs "
|
||||||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||||
return true;
|
return false; // stop dialog
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (String choose2 : choices) {
|
for (String choose2 : new ArrayList<>(choices)) {
|
||||||
// TODO: More targetting to fix
|
// TODO: More targetting to fix
|
||||||
String[] targetList = choose2.split("\\^");
|
String[] targetList = choose2.split("\\^");
|
||||||
boolean targetFound = false;
|
boolean targetFound = false;
|
||||||
for (String targetName : targetList) {
|
for (String targetName : targetList) {
|
||||||
for (Card card : cards.getCards(game)) {
|
for (Card card : cards.getCards(game)) {
|
||||||
if (target.getTargets().contains(card.getId())) {
|
if (target.contains(card.getId())) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (hasObjectTargetNameOrAlias(card, targetName)) {
|
if (hasObjectTargetNameOrAlias(card, targetName)) {
|
||||||
|
|
@ -4322,7 +4319,7 @@ public class TestPlayer implements Player {
|
||||||
assertWrongChoiceUsage(choices.size() > 0 ? choices.get(0) : "empty list");
|
assertWrongChoiceUsage(choices.size() > 0 ? choices.get(0) : "empty list");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
this.chooseStrictModeFailed("choice", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||||
return computerPlayer.choose(outcome, cards, target, source, game);
|
return computerPlayer.choose(outcome, cards, target, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4344,7 +4341,7 @@ public class TestPlayer implements Player {
|
||||||
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
+ (target.getMinNumberOfTargets() - target.getTargets().size()) + " more",
|
||||||
target.getTargets().size() >= target.getMinNumberOfTargets());
|
target.getTargets().size() >= target.getMinNumberOfTargets());
|
||||||
targets.remove(0);
|
targets.remove(0);
|
||||||
return false; // false in chooseTargetAmount = stop to choose
|
return false; // false - stop to choose
|
||||||
}
|
}
|
||||||
|
|
||||||
// only target amount needs
|
// only target amount needs
|
||||||
|
|
@ -4384,7 +4381,7 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (foundTarget) {
|
if (foundTarget) {
|
||||||
if (!target.getTargets().contains(possibleTarget) && target.canTarget(possibleTarget, source, game)) {
|
if (!target.contains(possibleTarget) && target.canTarget(possibleTarget, source, game)) {
|
||||||
// can select
|
// can select
|
||||||
target.addTarget(possibleTarget, targetAmount, source, game);
|
target.addTarget(possibleTarget, targetAmount, source, game);
|
||||||
targets.remove(0);
|
targets.remove(0);
|
||||||
|
|
@ -4395,7 +4392,7 @@ public class TestPlayer implements Player {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, game));
|
this.chooseStrictModeFailed("target", game, getInfo(source, game) + "\n" + getInfo(target, source, game));
|
||||||
return computerPlayer.chooseTargetAmount(outcome, target, source, game);
|
return computerPlayer.chooseTargetAmount(outcome, target, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4744,7 +4741,7 @@ public class TestPlayer implements Player {
|
||||||
Assert.fail(String.format("Found wrong choice command (%s):\n%s\n%s\n%s",
|
Assert.fail(String.format("Found wrong choice command (%s):\n%s\n%s\n%s",
|
||||||
reason,
|
reason,
|
||||||
lastChoice,
|
lastChoice,
|
||||||
getInfo(target, game),
|
getInfo(target, source, game),
|
||||||
getInfo(source, game)
|
getInfo(source, game)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,6 @@ import mage.ObjectColor;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.effects.ContinuousEffect;
|
import mage.abilities.effects.ContinuousEffect;
|
||||||
import mage.abilities.effects.ContinuousEffectsList;
|
import mage.abilities.effects.ContinuousEffectsList;
|
||||||
import mage.abilities.effects.Effect;
|
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.decks.Deck;
|
import mage.cards.decks.Deck;
|
||||||
import mage.cards.decks.DeckCardLists;
|
import mage.cards.decks.DeckCardLists;
|
||||||
|
|
@ -32,7 +31,6 @@ import mage.players.Player;
|
||||||
import mage.server.game.GameSessionPlayer;
|
import mage.server.game.GameSessionPlayer;
|
||||||
import mage.util.CardUtil;
|
import mage.util.CardUtil;
|
||||||
import mage.util.ThreadUtils;
|
import mage.util.ThreadUtils;
|
||||||
import mage.utils.StreamUtils;
|
|
||||||
import mage.utils.SystemUtil;
|
import mage.utils.SystemUtil;
|
||||||
import mage.view.GameView;
|
import mage.view.GameView;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
|
|
@ -1677,7 +1675,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
|
|
||||||
public void assertChoicesCount(TestPlayer player, int count) throws AssertionError {
|
public void assertChoicesCount(TestPlayer player, int count) throws AssertionError {
|
||||||
String mes = String.format(
|
String mes = String.format(
|
||||||
"(Choices of %s) Count are not equal (found %s). Some inner choose dialogs can be set up only in strict mode.",
|
"(Choices of %s) Count are not equal (found %s). Make sure you use target.chooseXXX instead player.choose. Also some inner choose dialogs can be set up only in strict mode.",
|
||||||
player.getName(),
|
player.getName(),
|
||||||
player.getChoices()
|
player.getChoices()
|
||||||
);
|
);
|
||||||
|
|
@ -1686,7 +1684,7 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
|
|
||||||
public void assertTargetsCount(TestPlayer player, int count) throws AssertionError {
|
public void assertTargetsCount(TestPlayer player, int count) throws AssertionError {
|
||||||
String mes = String.format(
|
String mes = String.format(
|
||||||
"(Targets of %s) Count are not equal (found %s). Some inner choose dialogs can be set up only in strict mode.",
|
"(Targets of %s) Count are not equal (found %s). Make sure you use target.chooseXXX instead player.choose. Also some inner choose dialogs can be set up only in strict mode.",
|
||||||
player.getName(),
|
player.getName(),
|
||||||
player.getTargets()
|
player.getTargets()
|
||||||
);
|
);
|
||||||
|
|
@ -2271,11 +2269,18 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
setChoice(player, choice ? "Yes" : "No", timesToChoose);
|
setChoice(player, choice ? "Yes" : "No", timesToChoose);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Declare non target choice. You can use multiple choices in one line like setChoice(name1^name2)
|
||||||
|
* Also support "up to" choices, e.g. choose 2 of 3 cards by setChoice(card1^card2) + setChoice(TestPlayer.CHOICE_SKIP)
|
||||||
|
*/
|
||||||
public void setChoice(TestPlayer player, String choice) {
|
public void setChoice(TestPlayer player, String choice) {
|
||||||
setChoice(player, choice, 1);
|
setChoice(player, choice, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setChoice(TestPlayer player, String choice, int timesToChoose) {
|
public void setChoice(TestPlayer player, String choice, int timesToChoose) {
|
||||||
|
if (choice.equals(TestPlayer.TARGET_SKIP)) {
|
||||||
|
Assert.fail("setChoice allow only TestPlayer.CHOICE_SKIP, but found " + choice);
|
||||||
|
}
|
||||||
for (int i = 0; i < timesToChoose; i++) {
|
for (int i = 0; i < timesToChoose; i++) {
|
||||||
player.addChoice(choice);
|
player.addChoice(choice);
|
||||||
}
|
}
|
||||||
|
|
@ -2340,13 +2345,18 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
*
|
*
|
||||||
* @param player
|
* @param player
|
||||||
* @param target you can add multiple targets by separating them by the "^"
|
* @param target you can add multiple targets by separating them by the "^"
|
||||||
* character e.g. "creatureName1^creatureName2" you can
|
* character e.g. "creatureName1^creatureName2"
|
||||||
* qualify the target additional by setcode e.g.
|
* -
|
||||||
|
* you can qualify the target additional by setcode e.g.
|
||||||
* "creatureName-M15" you can add [no copy] to the end of the
|
* "creatureName-M15" you can add [no copy] to the end of the
|
||||||
* target name to prohibit targets that are copied you can add
|
* target name to prohibit targets that are copied you can add
|
||||||
* [only copy] to the end of the target name to allow only
|
* [only copy] to the end of the target name to allow only
|
||||||
* targets that are copies. For modal spells use a prefix with
|
* targets that are copies. For modal spells use a prefix with
|
||||||
* the mode number: mode=1Lightning Bolt^mode=2Silvercoat Lion
|
* the mode number: mode=1Lightning Bolt^mode=2Silvercoat Lion
|
||||||
|
* -
|
||||||
|
* it's also support multiple addTarget commands instead single line,
|
||||||
|
* so you can declare not full "up to" targets list by addTarget(name)
|
||||||
|
* and addTarget(TestPlayer.TARGET_SKIP)
|
||||||
*/
|
*/
|
||||||
// TODO: mode options doesn't work here (see BrutalExpulsionTest)
|
// TODO: mode options doesn't work here (see BrutalExpulsionTest)
|
||||||
public void addTarget(TestPlayer player, String target) {
|
public void addTarget(TestPlayer player, String target) {
|
||||||
|
|
@ -2354,6 +2364,10 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addTarget(TestPlayer player, String target, int timesToChoose) {
|
public void addTarget(TestPlayer player, String target, int timesToChoose) {
|
||||||
|
if (target.equals(TestPlayer.CHOICE_SKIP)) {
|
||||||
|
Assert.fail("addTarget allow only TestPlayer.TARGET_SKIP, but found " + target);
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < timesToChoose; i++) {
|
for (int i = 0; i < timesToChoose; i++) {
|
||||||
assertAliaseSupportInActivateCommand(target, true);
|
assertAliaseSupportInActivateCommand(target, true);
|
||||||
player.addTarget(target);
|
player.addTarget(target);
|
||||||
|
|
|
||||||
|
|
@ -55,7 +55,7 @@ public class LoadCheatsTest extends CardTestPlayerBase {
|
||||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||||
execute();
|
execute();
|
||||||
|
|
||||||
setChoice(playerA, "7"); // choose [group 3]: 7 = 4 default menus + 3 group
|
setChoice(playerA, "8"); // choose [group 3]: 8 = 5 default menus + 3 group
|
||||||
SystemUtil.executeCheatCommands(currentGame, commandsFile, playerA);
|
SystemUtil.executeCheatCommands(currentGame, commandsFile, playerA);
|
||||||
|
|
||||||
assertHandCount(playerA, "Razorclaw Bear", 1);
|
assertHandCount(playerA, "Razorclaw Bear", 1);
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ public class CollectEvidenceCost extends CostImpl {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}.withNotTarget(true);
|
}.withNotTarget(true);
|
||||||
player.choose(Outcome.Exile, target, source, game);
|
target.choose(Outcome.Exile, player.getId(), source.getSourceId(), source, game);
|
||||||
Cards cards = new CardsImpl(target.getTargets());
|
Cards cards = new CardsImpl(target.getTargets());
|
||||||
paid = cards
|
paid = cards
|
||||||
.getCards(game)
|
.getCards(game)
|
||||||
|
|
|
||||||
|
|
@ -91,9 +91,7 @@ public class SacrificeAllEffect extends OneShotEffect {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TargetSacrifice target = new TargetSacrifice(numTargets, filter);
|
TargetSacrifice target = new TargetSacrifice(numTargets, filter);
|
||||||
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
|
target.choose(Outcome.Sacrifice, player.getId(), source, game);
|
||||||
player.choose(Outcome.Sacrifice, target, source, game);
|
|
||||||
}
|
|
||||||
perms.addAll(target.getTargets());
|
perms.addAll(target.getTargets());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -64,9 +64,7 @@ public class SacrificeEffect extends OneShotEffect {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
TargetSacrifice target = new TargetSacrifice(amount, filter);
|
TargetSacrifice target = new TargetSacrifice(amount, filter);
|
||||||
while (!target.isChosen(game) && target.canChoose(player.getId(), source, game) && player.canRespond()) {
|
if (target.choose(Outcome.Sacrifice, player.getId(), source.getSourceId(), source, game)) {
|
||||||
player.choose(Outcome.Sacrifice, target, source, game);
|
|
||||||
}
|
|
||||||
for (UUID targetId : target.getTargets()) {
|
for (UUID targetId : target.getTargets()) {
|
||||||
Permanent permanent = game.getPermanent(targetId);
|
Permanent permanent = game.getPermanent(targetId);
|
||||||
if (permanent != null && permanent.sacrifice(source, game)) {
|
if (permanent != null && permanent.sacrifice(source, game)) {
|
||||||
|
|
@ -74,6 +72,7 @@ public class SacrificeEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return applied;
|
return applied;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1522,7 +1522,7 @@ public abstract class GameImpl implements Game {
|
||||||
UUID[] players = getPlayers().keySet().toArray(new UUID[0]);
|
UUID[] players = getPlayers().keySet().toArray(new UUID[0]);
|
||||||
UUID playerId;
|
UUID playerId;
|
||||||
while (!hasEnded()) {
|
while (!hasEnded()) {
|
||||||
playerId = players[RandomUtil.nextInt(players.length)];
|
playerId = players[RandomUtil.nextInt(players.length)]; // test game
|
||||||
Player player = getPlayer(playerId);
|
Player player = getPlayer(playerId);
|
||||||
if (player != null && player.canRespond()) {
|
if (player != null && player.canRespond()) {
|
||||||
fireInformEvent(state.getPlayer(playerId).getLogName() + " won the toss");
|
fireInformEvent(state.getPlayer(playerId).getLogName() + " won the toss");
|
||||||
|
|
@ -1810,14 +1810,21 @@ public abstract class GameImpl implements Game {
|
||||||
|
|
||||||
protected void resolve() {
|
protected void resolve() {
|
||||||
StackObject top = null;
|
StackObject top = null;
|
||||||
|
boolean wasError = false;
|
||||||
try {
|
try {
|
||||||
top = state.getStack().peek();
|
top = state.getStack().peek();
|
||||||
top.resolve(this);
|
top.resolve(this);
|
||||||
resetControlAfterSpellResolve(top.getId());
|
resetControlAfterSpellResolve(top.getId());
|
||||||
|
} catch (Throwable e) {
|
||||||
|
// workaround to show real error in tests instead checkInfiniteLoop
|
||||||
|
wasError = true;
|
||||||
|
throw e;
|
||||||
} finally {
|
} finally {
|
||||||
if (top != null) {
|
if (top != null) {
|
||||||
state.getStack().remove(top, this); // seems partly redundant because move card from stack to grave is already done and the stack removed
|
state.getStack().remove(top, this); // seems partly redundant because move card from stack to grave is already done and the stack removed
|
||||||
|
if (!wasError) {
|
||||||
checkInfiniteLoop(top.getSourceId());
|
checkInfiniteLoop(top.getSourceId());
|
||||||
|
}
|
||||||
if (!getTurn().isEndTurnRequested()) {
|
if (!getTurn().isEndTurnRequested()) {
|
||||||
while (state.hasSimultaneousEvents()) {
|
while (state.hasSimultaneousEvents()) {
|
||||||
state.handleSimultaneousEvent(this);
|
state.handleSimultaneousEvent(this);
|
||||||
|
|
@ -3746,7 +3753,7 @@ public abstract class GameImpl implements Game {
|
||||||
@Override
|
@Override
|
||||||
public void cheat(UUID ownerId, List<Card> library, List<Card> hand, List<PutToBattlefieldInfo> battlefield, List<Card> graveyard, List<Card> command, List<Card> exiled) {
|
public void cheat(UUID ownerId, List<Card> library, List<Card> hand, List<PutToBattlefieldInfo> battlefield, List<Card> graveyard, List<Card> command, List<Card> exiled) {
|
||||||
// fake test ability for triggers and events
|
// fake test ability for triggers and events
|
||||||
Ability fakeSourceAbilityTemplate = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("adding testing cards"));
|
Ability fakeSourceAbilityTemplate = new SimpleStaticAbility(Zone.OUTSIDE, new InfoEffect("fake ability"));
|
||||||
fakeSourceAbilityTemplate.setControllerId(ownerId);
|
fakeSourceAbilityTemplate.setControllerId(ownerId);
|
||||||
|
|
||||||
Player player = getPlayer(ownerId);
|
Player player = getPlayer(ownerId);
|
||||||
|
|
|
||||||
|
|
@ -458,10 +458,14 @@ public final class ZonesHandler {
|
||||||
target.setRequired(true);
|
target.setRequired(true);
|
||||||
while (player.canRespond() && cards.size() > 1) {
|
while (player.canRespond() && cards.size() > 1) {
|
||||||
player.choose(Outcome.Neutral, cards, target, source, game);
|
player.choose(Outcome.Neutral, cards, target, source, game);
|
||||||
UUID targetObjectId = target.getFirstTarget();
|
Card card = cards.get(target.getFirstTarget(), game);
|
||||||
order.add(cards.get(targetObjectId, game));
|
if (card != null) {
|
||||||
cards.remove(targetObjectId);
|
order.add(card);
|
||||||
|
cards.remove(target.getFirstTarget());
|
||||||
target.clearChosen();
|
target.clearChosen();
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
order.addAll(cards.getCards(game));
|
order.addAll(cards.getCards(game));
|
||||||
return order;
|
return order;
|
||||||
|
|
|
||||||
|
|
@ -168,6 +168,8 @@ public class Library implements Serializable {
|
||||||
}
|
}
|
||||||
|
|
||||||
public Collection<Card> getUniqueCards(Game game) {
|
public Collection<Card> getUniqueCards(Game game) {
|
||||||
|
// TODO: on no performance issues - remove unique code after few releases, 2025-05-13
|
||||||
|
if (true) return getCards(game);
|
||||||
Map<String, Card> cards = new HashMap<>();
|
Map<String, Card> cards = new HashMap<>();
|
||||||
for (UUID cardId : library) {
|
for (UUID cardId : library) {
|
||||||
Card card = game.getCard(cardId);
|
Card card = game.getCard(cardId);
|
||||||
|
|
|
||||||
|
|
@ -672,6 +672,10 @@ public interface Player extends MageItem, Copyable<Player> {
|
||||||
|
|
||||||
boolean priority(Game game);
|
boolean priority(Game game);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Warning, any choose and chooseTarget dialogs must return false to stop choosing, e.g. no more possible targets
|
||||||
|
* Same logic as "something changes" in "apply"
|
||||||
|
*/
|
||||||
boolean choose(Outcome outcome, Target target, Ability source, Game game);
|
boolean choose(Outcome outcome, Target target, Ability source, Game game);
|
||||||
|
|
||||||
boolean choose(Outcome outcome, Target target, Ability source, Game game, Map<String, Serializable> options);
|
boolean choose(Outcome outcome, Target target, Ability source, Game game, Map<String, Serializable> options);
|
||||||
|
|
|
||||||
|
|
@ -886,7 +886,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
return toDiscard;
|
return toDiscard;
|
||||||
}
|
}
|
||||||
TargetDiscard target = new TargetDiscard(minAmount, maxAmount, StaticFilters.FILTER_CARD, getId());
|
TargetDiscard target = new TargetDiscard(minAmount, maxAmount, StaticFilters.FILTER_CARD, getId());
|
||||||
choose(Outcome.Discard, target, source, game);
|
target.choose(Outcome.Discard, getId(), source, game);
|
||||||
toDiscard.addAll(target.getTargets());
|
toDiscard.addAll(target.getTargets());
|
||||||
return toDiscard;
|
return toDiscard;
|
||||||
}
|
}
|
||||||
|
|
@ -4472,14 +4472,14 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
List<Ability> options = new ArrayList<>();
|
List<Ability> options = new ArrayList<>();
|
||||||
if (ability.isModal()) {
|
if (ability.isModal()) {
|
||||||
addModeOptions(options, ability, game);
|
addModeOptions(options, ability, game);
|
||||||
} else if (!ability.getTargets().getUnchosen(game).isEmpty()) {
|
} else if (ability.getTargets().getNextUnchosen(game) != null) {
|
||||||
// TODO: Handle other variable costs than mana costs
|
// TODO: Handle other variable costs than mana costs
|
||||||
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
|
if (!ability.getManaCosts().getVariableCosts().isEmpty()) {
|
||||||
addVariableXOptions(options, ability, 0, game);
|
addVariableXOptions(options, ability, 0, game);
|
||||||
} else {
|
} else {
|
||||||
addTargetOptions(options, ability, 0, game);
|
addTargetOptions(options, ability, 0, game);
|
||||||
}
|
}
|
||||||
} else if (!ability.getCosts().getTargets().getUnchosen(game).isEmpty()) {
|
} else if (ability.getCosts().getTargets().getNextUnchosen(game) != null) {
|
||||||
addCostTargetOptions(options, ability, 0, game);
|
addCostTargetOptions(options, ability, 0, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -4497,13 +4497,13 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
newOption.getModes().clearSelectedModes();
|
newOption.getModes().clearSelectedModes();
|
||||||
newOption.getModes().addSelectedMode(mode.getId());
|
newOption.getModes().addSelectedMode(mode.getId());
|
||||||
newOption.getModes().setActiveMode(mode);
|
newOption.getModes().setActiveMode(mode);
|
||||||
if (!newOption.getTargets().getUnchosen(game).isEmpty()) {
|
if (newOption.getTargets().getNextUnchosen(game) != null) {
|
||||||
if (!newOption.getManaCosts().getVariableCosts().isEmpty()) {
|
if (!newOption.getManaCosts().getVariableCosts().isEmpty()) {
|
||||||
addVariableXOptions(options, newOption, 0, game);
|
addVariableXOptions(options, newOption, 0, game);
|
||||||
} else {
|
} else {
|
||||||
addTargetOptions(options, newOption, 0, game);
|
addTargetOptions(options, newOption, 0, game);
|
||||||
}
|
}
|
||||||
} else if (!newOption.getCosts().getTargets().getUnchosen(game).isEmpty()) {
|
} else if (newOption.getCosts().getTargets().getNextUnchosen(game) != null) {
|
||||||
addCostTargetOptions(options, newOption, 0, game);
|
addCostTargetOptions(options, newOption, 0, game);
|
||||||
} else {
|
} else {
|
||||||
options.add(newOption);
|
options.add(newOption);
|
||||||
|
|
@ -4523,7 +4523,9 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
*/
|
*/
|
||||||
protected void addTargetOptions(List<Ability> options, Ability option, int targetNum, Game game) {
|
protected void addTargetOptions(List<Ability> options, Ability option, int targetNum, Game game) {
|
||||||
// TODO: target options calculated for triggered ability too, but do not used in real game
|
// TODO: target options calculated for triggered ability too, but do not used in real game
|
||||||
for (Target target : option.getTargets().getUnchosen(game).get(targetNum).getTargetOptions(option, game)) {
|
// TODO: there are rare errors with wrong targetNum - maybe multiple game sims can change same target object somehow?
|
||||||
|
// do not hide NullPointError here, research instead
|
||||||
|
for (Target target : option.getTargets().getNextUnchosen(game, targetNum).getTargetOptions(option, game)) {
|
||||||
Ability newOption = option.copy();
|
Ability newOption = option.copy();
|
||||||
if (target instanceof TargetAmount) {
|
if (target instanceof TargetAmount) {
|
||||||
for (UUID targetId : target.getTargets()) {
|
for (UUID targetId : target.getTargets()) {
|
||||||
|
|
@ -5562,8 +5564,7 @@ public abstract class PlayerImpl implements Player, Serializable {
|
||||||
TargetPermanent target = new TargetControlledCreaturePermanent();
|
TargetPermanent target = new TargetControlledCreaturePermanent();
|
||||||
target.withNotTarget(true);
|
target.withNotTarget(true);
|
||||||
target.withChooseHint("to be your Ring-bearer");
|
target.withChooseHint("to be your Ring-bearer");
|
||||||
choose(Outcome.Neutral, target, null, game);
|
target.choose(Outcome.Neutral, getId(), null, null, game);
|
||||||
|
|
||||||
newBearerId = target.getFirstTarget();
|
newBearerId = target.getFirstTarget();
|
||||||
} else {
|
} else {
|
||||||
newBearerId = currentBearerId;
|
newBearerId = currentBearerId;
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,9 @@ public class StubPlayer extends PlayerImpl {
|
||||||
public boolean choose(Outcome outcome, Target target, Ability source, Game game) {
|
public boolean choose(Outcome outcome, Target target, Ability source, Game game) {
|
||||||
if (target instanceof TargetPlayer) {
|
if (target instanceof TargetPlayer) {
|
||||||
for (Player player : game.getPlayers().values()) {
|
for (Player player : game.getPlayers().values()) {
|
||||||
if (player.getId().equals(getId()) && target.canTarget(getId(), game)) {
|
if (player.getId().equals(getId())
|
||||||
|
&& target.canTarget(getId(), game)
|
||||||
|
&& !target.contains(getId())) {
|
||||||
target.add(player.getId(), game);
|
target.add(player.getId(), game);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,12 +24,13 @@ public interface Target extends Copyable<Target>, Serializable {
|
||||||
* All targets selected by a player
|
* All targets selected by a player
|
||||||
* <p>
|
* <p>
|
||||||
* Warning, for "up to" targets it will return true all the time, so make sure your dialog
|
* Warning, for "up to" targets it will return true all the time, so make sure your dialog
|
||||||
* use do-while logic and call "choose" one time min or use doneChoosing
|
* use do-while logic and call "choose" one time min or use isChoiceCompleted
|
||||||
*/
|
*/
|
||||||
boolean isChosen(Game game);
|
boolean isChosen(Game game);
|
||||||
|
|
||||||
// TODO: combine code or research usages (doneChoosing must be in while cycles, isChosen in other places, see #13606)
|
boolean isChoiceCompleted(Game game);
|
||||||
boolean doneChoosing(Game game);
|
|
||||||
|
boolean isChoiceCompleted(UUID abilityControllerId, Ability source, Game game);
|
||||||
|
|
||||||
void clearChosen();
|
void clearChosen();
|
||||||
|
|
||||||
|
|
@ -63,6 +64,9 @@ public interface Target extends Copyable<Target>, Serializable {
|
||||||
*/
|
*/
|
||||||
Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game);
|
Set<UUID> possibleTargets(UUID sourceControllerId, Ability source, Game game);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority method to make a choice from cards and other places, not a player.chooseXXX
|
||||||
|
*/
|
||||||
boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game);
|
boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -101,10 +105,19 @@ public interface Target extends Copyable<Target>, Serializable {
|
||||||
|
|
||||||
Set<UUID> possibleTargets(UUID sourceControllerId, Game game);
|
Set<UUID> possibleTargets(UUID sourceControllerId, Game game);
|
||||||
|
|
||||||
|
@Deprecated // TODO: need replace to source only version?
|
||||||
boolean choose(Outcome outcome, UUID playerId, UUID sourceId, Ability source, Game game);
|
boolean choose(Outcome outcome, UUID playerId, UUID sourceId, Ability source, Game game);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Priority method to make a choice from cards and other places, not a player.chooseXXX
|
||||||
|
*/
|
||||||
|
default boolean choose(Outcome outcome, UUID playerId, Ability source, Game game) {
|
||||||
|
return choose(outcome, playerId, source == null ? null : source.getSourceId(), source, game);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add target from non targeting methods like choose
|
* Add target from non targeting methods like choose
|
||||||
|
* TODO: need usage research, looks like there are wrong usage of addTarget, e.g. in choose method (must be add)
|
||||||
*/
|
*/
|
||||||
void add(UUID id, Game game);
|
void add(UUID id, Game game);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -46,11 +46,11 @@ public abstract class TargetAmount extends TargetImpl {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isChosen(Game game) {
|
public boolean isChosen(Game game) {
|
||||||
return doneChoosing(game);
|
return isChoiceCompleted(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doneChoosing(Game game) {
|
public boolean isChoiceCompleted(Game game) {
|
||||||
return amountWasSet
|
return amountWasSet
|
||||||
&& (remainingAmount == 0
|
&& (remainingAmount == 0
|
||||||
|| (getMinNumberOfTargets() < getMaxNumberOfTargets()
|
|| (getMinNumberOfTargets() < getMaxNumberOfTargets()
|
||||||
|
|
@ -109,15 +109,16 @@ public abstract class TargetAmount extends TargetImpl {
|
||||||
if (!amountWasSet) {
|
if (!amountWasSet) {
|
||||||
setAmount(source, game);
|
setAmount(source, game);
|
||||||
}
|
}
|
||||||
chosen = false;
|
|
||||||
while (remainingAmount > 0) {
|
while (remainingAmount > 0) {
|
||||||
|
chosen = false;
|
||||||
if (!player.canRespond()) {
|
if (!player.canRespond()) {
|
||||||
chosen = isChosen(game);
|
chosen = isChosen(game);
|
||||||
return chosen;
|
break;
|
||||||
}
|
}
|
||||||
if (!getTargetController(game, playerId).chooseTargetAmount(outcome, this, source, game)) {
|
if (!getTargetController(game, playerId).chooseTargetAmount(outcome, this, source, game)) {
|
||||||
chosen = isChosen(game);
|
chosen = isChosen(game);
|
||||||
return chosen;
|
break;
|
||||||
}
|
}
|
||||||
chosen = isChosen(game);
|
chosen = isChosen(game);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,12 @@ public class TargetCard extends TargetObject {
|
||||||
return this.filter;
|
return this.filter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TargetCard withNotTarget(boolean notTarget) {
|
||||||
|
super.withNotTarget(notTarget);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if there are enough {@link Card cards} in the appropriate zone that the player can choose from among them
|
* Checks if there are enough {@link Card cards} in the appropriate zone that the player can choose from among them
|
||||||
* or if they are autochosen since there are fewer than the minimum number.
|
* or if they are autochosen since there are fewer than the minimum number.
|
||||||
|
|
|
||||||
|
|
@ -173,11 +173,14 @@ public abstract class TargetImpl implements Target {
|
||||||
if (getMaxNumberOfTargets() != 1) {
|
if (getMaxNumberOfTargets() != 1) {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Select ").append(targetName);
|
sb.append("Select ").append(targetName);
|
||||||
|
sb.append(" (selected ").append(targets.size());
|
||||||
if (getMaxNumberOfTargets() > 0 && getMaxNumberOfTargets() != Integer.MAX_VALUE) {
|
if (getMaxNumberOfTargets() > 0 && getMaxNumberOfTargets() != Integer.MAX_VALUE) {
|
||||||
sb.append(" (selected ").append(targets.size()).append(" of ").append(getMaxNumberOfTargets()).append(')');
|
sb.append(" of ").append(getMaxNumberOfTargets());
|
||||||
} else {
|
|
||||||
sb.append(" (selected ").append(targets.size()).append(')');
|
|
||||||
}
|
}
|
||||||
|
if (getMinNumberOfTargets() > 0) {
|
||||||
|
sb.append(", min ").append(getMinNumberOfTargets());
|
||||||
|
}
|
||||||
|
sb.append(')');
|
||||||
sb.append(suffix);
|
sb.append(suffix);
|
||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
@ -245,12 +248,12 @@ public abstract class TargetImpl implements Target {
|
||||||
|
|
||||||
// limit by max amount
|
// limit by max amount
|
||||||
if (getMaxNumberOfTargets() > 0 && targets.size() > getMaxNumberOfTargets()) {
|
if (getMaxNumberOfTargets() > 0 && targets.size() > getMaxNumberOfTargets()) {
|
||||||
return chosen;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// limit by min amount
|
// limit by min amount
|
||||||
if (getMinNumberOfTargets() > 0 && targets.size() < getMinNumberOfTargets()) {
|
if (getMinNumberOfTargets() > 0 && targets.size() < getMinNumberOfTargets()) {
|
||||||
return chosen;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// all fine
|
// all fine
|
||||||
|
|
@ -258,8 +261,44 @@ public abstract class TargetImpl implements Target {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean doneChoosing(Game game) {
|
@Deprecated // TODO: replace usage in cards by full version from choose methods
|
||||||
return isChoiceSelected() && isChosen(game);
|
public boolean isChoiceCompleted(Game game) {
|
||||||
|
return isChoiceCompleted(null, null, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isChoiceCompleted(UUID abilityControllerId, Ability source, Game game) {
|
||||||
|
// make sure target request called one time minimum (for "up to" targets)
|
||||||
|
// choice is selected after any addTarget call (by test, AI or human players)
|
||||||
|
if (!isChoiceSelected()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure selected targets are valid
|
||||||
|
if (!isChosen(game)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure to auto-finish on all targets selection
|
||||||
|
// - human player can select and deselect targets until fill all targets amount or press done button
|
||||||
|
// - AI player can select all new targets as much as possible
|
||||||
|
if (getMaxNumberOfTargets() > 0) {
|
||||||
|
if (getMaxNumberOfTargets() == Integer.MAX_VALUE) {
|
||||||
|
if (abilityControllerId != null && source != null) {
|
||||||
|
// any amount - nothing to choose
|
||||||
|
return this.getSize() >= this.possibleTargets(abilityControllerId, source, game).size();
|
||||||
|
} else {
|
||||||
|
// any amount - any selected
|
||||||
|
return this.getSize() > 0;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// check selected limit
|
||||||
|
return this.getSize() >= getMaxNumberOfTargets();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all other use cases are fine
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -289,7 +328,7 @@ public abstract class TargetImpl implements Target {
|
||||||
@Override
|
@Override
|
||||||
public void remove(UUID id) {
|
public void remove(UUID id) {
|
||||||
if (targets.containsKey(id)) {
|
if (targets.containsKey(id)) {
|
||||||
targets.remove(id);
|
targets.remove(id); // TODO: miss chosen update here?
|
||||||
zoneChangeCounters.remove(id);
|
zoneChangeCounters.remove(id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -315,6 +354,8 @@ public abstract class TargetImpl implements Target {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
targets.put(id, 0);
|
targets.put(id, 0);
|
||||||
|
rememberZoneChangeCounter(id, game);
|
||||||
|
chosen = isChosen(game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -366,20 +407,45 @@ public abstract class TargetImpl implements Target {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UUID abilityControllerId = playerId;
|
||||||
|
if (this.getTargetController() != null && this.getAbilityController() != null) {
|
||||||
|
abilityControllerId = this.getAbilityController();
|
||||||
|
}
|
||||||
|
|
||||||
chosen = false;
|
chosen = false;
|
||||||
do {
|
do {
|
||||||
if (!targetController.canRespond()) {
|
int prevTargetsCount = this.getTargets().size();
|
||||||
chosen = isChosen(game);
|
|
||||||
return chosen;
|
|
||||||
}
|
|
||||||
if (!targetController.choose(outcome, this, source, game)) {
|
|
||||||
chosen = isChosen(game);
|
|
||||||
return chosen;
|
|
||||||
}
|
|
||||||
chosen = isChosen(game);
|
|
||||||
} while (!doneChoosing(game));
|
|
||||||
|
|
||||||
return isChosen(game);
|
// stop by disconnect
|
||||||
|
if (!targetController.canRespond()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop by cancel/done
|
||||||
|
if (!targetController.choose(outcome, this, source, game)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: miss auto-choose code? see chooseTarget below
|
||||||
|
// TODO: miss random code? see chooseTarget below
|
||||||
|
|
||||||
|
chosen = isChosen(game);
|
||||||
|
|
||||||
|
// stop by full complete
|
||||||
|
if (isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop by nothing to use (actual for human and done button)
|
||||||
|
if (prevTargetsCount == this.getTargets().size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can select next target
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
chosen = isChosen(game);
|
||||||
|
return this.getTargets().size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -389,44 +455,80 @@ public abstract class TargetImpl implements Target {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
List<UUID> possibleTargets = new ArrayList<>(possibleTargets(playerId, source, game));
|
UUID abilityControllerId = playerId;
|
||||||
|
if (this.getTargetController() != null && this.getAbilityController() != null) {
|
||||||
|
abilityControllerId = this.getAbilityController();
|
||||||
|
}
|
||||||
|
|
||||||
|
List<UUID> randomPossibleTargets = new ArrayList<>(possibleTargets(playerId, source, game));
|
||||||
|
|
||||||
chosen = false;
|
chosen = false;
|
||||||
do {
|
do {
|
||||||
|
int prevTargetsCount = this.getTargets().size();
|
||||||
|
|
||||||
|
// stop by disconnect
|
||||||
if (!targetController.canRespond()) {
|
if (!targetController.canRespond()) {
|
||||||
chosen = isChosen(game);
|
break;
|
||||||
return chosen;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MAKE A CHOICE
|
||||||
if (isRandom()) {
|
if (isRandom()) {
|
||||||
if (possibleTargets.isEmpty()) {
|
// random choice
|
||||||
chosen = isChosen(game);
|
|
||||||
return chosen;
|
// stop on nothing to choose
|
||||||
|
if (randomPossibleTargets.isEmpty()) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// find valid target
|
|
||||||
while (!possibleTargets.isEmpty()) {
|
// add valid random target one by one
|
||||||
int index = RandomUtil.nextInt(possibleTargets.size());
|
while (!randomPossibleTargets.isEmpty()) {
|
||||||
if (this.canTarget(playerId, possibleTargets.get(index), source, game)) {
|
UUID possibleTarget = RandomUtil.randomFromCollection(randomPossibleTargets);
|
||||||
this.addTarget(possibleTargets.get(index), source, game);
|
if (this.canTarget(playerId, possibleTarget, source, game) && !this.contains(possibleTarget)) {
|
||||||
possibleTargets.remove(index);
|
this.addTarget(possibleTarget, source, game);
|
||||||
|
randomPossibleTargets.remove(possibleTarget);
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
possibleTargets.remove(index);
|
randomPossibleTargets.remove(possibleTarget);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// continue to next target
|
||||||
} else {
|
} else {
|
||||||
// Try to autochoosen
|
// player's choice
|
||||||
UUID autoChosenId = tryToAutoChoose(playerId, source, game);
|
|
||||||
if (autoChosenId != null) {
|
|
||||||
addTarget(autoChosenId, source, game);
|
|
||||||
} else if (!targetController.chooseTarget(outcome, this, source, game)) { // If couldn't autochoose ask player
|
|
||||||
chosen = isChosen(game);
|
|
||||||
return chosen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
chosen = isChosen(game);
|
|
||||||
} while (!doneChoosing(game));
|
|
||||||
|
|
||||||
return isChosen(game);
|
UUID autoChosenId = tryToAutoChoose(playerId, source, game);
|
||||||
|
if (autoChosenId != null && !this.contains(autoChosenId)) {
|
||||||
|
// auto-choose
|
||||||
|
addTarget(autoChosenId, source, game);
|
||||||
|
// continue to next target (example: auto-choose must fill min/max = 2 from 2 possible cards)
|
||||||
|
} else {
|
||||||
|
// manual
|
||||||
|
|
||||||
|
// stop by cancel/done
|
||||||
|
if (!targetController.chooseTarget(outcome, this, source, game)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue to next target
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
chosen = isChosen(game);
|
||||||
|
|
||||||
|
// stop by full complete
|
||||||
|
if (isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop by nothing to choose (actual for human and done button?)
|
||||||
|
if (prevTargetsCount == this.getTargets().size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can select next target
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
chosen = isChosen(game);
|
||||||
|
return this.getTargets().size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -665,6 +767,7 @@ public abstract class TargetImpl implements Target {
|
||||||
public void setTargetAmount(UUID targetId, int amount, Game game) {
|
public void setTargetAmount(UUID targetId, int amount, Game game) {
|
||||||
targets.put(targetId, amount);
|
targets.put(targetId, amount);
|
||||||
rememberZoneChangeCounter(targetId, game);
|
rememberZoneChangeCounter(targetId, game);
|
||||||
|
chosen = isChosen(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -716,9 +819,20 @@ public abstract class TargetImpl implements Target {
|
||||||
} else {
|
} else {
|
||||||
playerAutoTargetLevel = 2;
|
playerAutoTargetLevel = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// freeze protection on disconnect - auto-choice works for online players only
|
||||||
|
boolean isOnline = player.canRespond();
|
||||||
|
if (!player.isGameUnderControl()) {
|
||||||
|
Player controllingPlayer = game.getPlayer(player.getTurnControlledBy());
|
||||||
|
if (player.isHuman()) {
|
||||||
|
isOnline = controllingPlayer.canRespond();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String abilityText = source.getRule(true).toLowerCase();
|
String abilityText = source.getRule(true).toLowerCase();
|
||||||
boolean strictModeEnabled = player.getStrictChooseMode();
|
boolean strictModeEnabled = player.getStrictChooseMode();
|
||||||
boolean canAutoChoose = this.getMinNumberOfTargets() == this.getMaxNumberOfTargets() // Targets must be picked
|
boolean canAutoChoose = this.getMinNumberOfTargets() == this.getMaxNumberOfTargets() // Targets must be picked
|
||||||
|
&& isOnline
|
||||||
&& possibleTargets.size() == this.getMinNumberOfTargets() - this.getSize() // Available targets are equal to the number that must be picked
|
&& possibleTargets.size() == this.getMinNumberOfTargets() - this.getSize() // Available targets are equal to the number that must be picked
|
||||||
&& !strictModeEnabled // Test AI is not set to strictChooseMode(true)
|
&& !strictModeEnabled // Test AI is not set to strictChooseMode(true)
|
||||||
&& playerAutoTargetLevel > 0 // Human player has enabled auto-choose in settings
|
&& playerAutoTargetLevel > 0 // Human player has enabled auto-choose in settings
|
||||||
|
|
@ -775,4 +889,13 @@ public abstract class TargetImpl implements Target {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return this.getClass().getSimpleName()
|
||||||
|
+ ", from " + this.getMinNumberOfTargets()
|
||||||
|
+ " to " + this.getMaxNumberOfTargets()
|
||||||
|
+ ", " + this.getDescription()
|
||||||
|
+ ", selected " + this.getTargets().size();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
package mage.target;
|
package mage.target;
|
||||||
|
|
||||||
|
import mage.MageObject;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.events.GameEvent;
|
import mage.game.events.GameEvent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.util.Copyable;
|
import mage.util.Copyable;
|
||||||
|
import mage.util.DebugUtil;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
@ -38,12 +40,19 @@ public class Targets extends ArrayList<Target> implements Copyable<Targets> {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Target> getUnchosen(Game game) {
|
public Target getNextUnchosen(Game game) {
|
||||||
return stream().filter(target -> !target.isChoiceSelected()).collect(Collectors.toList());
|
return getNextUnchosen(game, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean doneChoosing(Game game) {
|
public Target getNextUnchosen(Game game, int unchosenIndex) {
|
||||||
return stream().allMatch(t -> t.doneChoosing(game));
|
List<Target> res = stream()
|
||||||
|
.filter(target -> !target.isChoiceSelected())
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return unchosenIndex < res.size() ? res.get(unchosenIndex) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isChoiceCompleted(Game game) {
|
||||||
|
return stream().allMatch(t -> t.isChoiceCompleted(game));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearChosen() {
|
public void clearChosen() {
|
||||||
|
|
@ -62,19 +71,38 @@ public class Targets extends ArrayList<Target> implements Copyable<Targets> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.size() > 0 && !this.doneChoosing(game)) {
|
// in test mode some targets can be predefined already, e.g. by cast/activate command
|
||||||
|
// so do not clear chosen status here
|
||||||
|
|
||||||
|
if (this.size() > 0) {
|
||||||
do {
|
do {
|
||||||
|
// stop on disconnect or nothing to choose
|
||||||
if (!player.canRespond() || !canChoose(playerId, source, game)) {
|
if (!player.canRespond() || !canChoose(playerId, source, game)) {
|
||||||
return false;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Target target = this.getUnchosen(game).get(0);
|
// stop on complete
|
||||||
|
Target target = this.getNextUnchosen(game);
|
||||||
|
if (target == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop on cancel/done
|
||||||
if (!target.choose(outcome, playerId, sourceId, source, game)) {
|
if (!target.choose(outcome, playerId, sourceId, source, game)) {
|
||||||
return false;
|
if (!target.isChosen(game)) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
} while (!doneChoosing(game));
|
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
|
// target done, can take next one
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DebugUtil.GAME_SHOW_CHOOSE_TARGET_LOGS && !game.isSimulation()) {
|
||||||
|
printDebugTargets("choose finish", this, source, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isChosen(game);
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean chooseTargets(Outcome outcome, UUID playerId, Ability source, boolean noMana, Game game, boolean canCancel) {
|
public boolean chooseTargets(Outcome outcome, UUID playerId, Ability source, boolean noMana, Game game, boolean canCancel) {
|
||||||
|
|
@ -83,43 +111,100 @@ public class Targets extends ArrayList<Target> implements Copyable<Targets> {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.size() > 0 && !this.doneChoosing(game)) {
|
// in test mode some targets can be predefined already, e.g. by cast/activate command
|
||||||
|
// so do not clear chosen status here
|
||||||
|
|
||||||
|
if (this.size() > 0) {
|
||||||
do {
|
do {
|
||||||
|
// stop on disconnect or nothing to choose
|
||||||
if (!player.canRespond() || !canChoose(playerId, source, game)) {
|
if (!player.canRespond() || !canChoose(playerId, source, game)) {
|
||||||
return false;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Target target = this.getUnchosen(game).get(0);
|
// stop on complete
|
||||||
UUID targetController = playerId;
|
Target target = this.getNextUnchosen(game);
|
||||||
|
if (target == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
// some targets can have controller different than ability controller
|
// some targets can have controller different than ability controller
|
||||||
|
UUID targetController = playerId;
|
||||||
if (target.getTargetController() != null) {
|
if (target.getTargetController() != null) {
|
||||||
targetController = target.getTargetController();
|
targetController = target.getTargetController();
|
||||||
}
|
}
|
||||||
|
|
||||||
// if cast without mana (e.g. by suspend you may not be able to cancel the casting if you are able to cast it
|
// disable cancel button - if cast without mana (e.g. by suspend you may not be able to cancel the casting if you are able to cast it
|
||||||
if (noMana) {
|
if (noMana) {
|
||||||
target.setRequired(true);
|
target.setRequired(true);
|
||||||
}
|
}
|
||||||
|
// enable cancel button
|
||||||
// can be cancel by user
|
|
||||||
if (canCancel) {
|
if (canCancel) {
|
||||||
target.setRequired(false);
|
target.setRequired(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// make response checks
|
// stop on cancel/done
|
||||||
if (!target.chooseTarget(outcome, targetController, source, game)) {
|
if (!target.chooseTarget(outcome, targetController, source, game)) {
|
||||||
return false;
|
if (!target.isChosen(game)) {
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
// Check if there are some rules for targets are violated, if so reset the targets and start again
|
}
|
||||||
if (this.getUnchosen(game).isEmpty()
|
|
||||||
|
// reset on wrong restrictions and start from scratch
|
||||||
|
if (this.getNextUnchosen(game) == null
|
||||||
&& game.replaceEvent(new GameEvent(GameEvent.EventType.TARGETS_VALID, source.getSourceId(), source, source.getControllerId()), source)) {
|
&& game.replaceEvent(new GameEvent(GameEvent.EventType.TARGETS_VALID, source.getSourceId(), source, source.getControllerId()), source)) {
|
||||||
//game.restoreState(state, "Targets");
|
|
||||||
clearChosen();
|
clearChosen();
|
||||||
}
|
}
|
||||||
} while (!doneChoosing(game));
|
|
||||||
|
// target done, can take next one
|
||||||
|
} while (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (DebugUtil.GAME_SHOW_CHOOSE_TARGET_LOGS && !game.isSimulation()) {
|
||||||
|
printDebugTargets("chooseTargets finish", this, source, game);
|
||||||
|
}
|
||||||
|
|
||||||
|
return isChosen(game);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printDebugTargets(String name, Targets targets, Ability source, Game game) {
|
||||||
|
List<String> output = new ArrayList<>();
|
||||||
|
printDebugTargets(name, targets, source, game, output);
|
||||||
|
output.forEach(System.out::println);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printDebugTargets(String name, Targets targets, Ability source, Game game, List<String> output) {
|
||||||
|
output.add("");
|
||||||
|
output.add(name + ":");
|
||||||
|
output.add(String.format("* chosen: %s", targets.isChosen(game) ? "yes" : "no"));
|
||||||
|
output.add(String.format("* ability: %s", source));
|
||||||
|
for (int i = 0; i < targets.size(); i++) {
|
||||||
|
Target target = targets.get(i);
|
||||||
|
output.add(String.format("* target %d: %s", i + 1, target));
|
||||||
|
if (target.getTargets().isEmpty()) {
|
||||||
|
output.add(" - no choices");
|
||||||
|
} else {
|
||||||
|
for (int j = 0; j < target.getTargets().size(); j++) {
|
||||||
|
UUID targetId = target.getTargets().get(j);
|
||||||
|
String targetInfo;
|
||||||
|
Player targetPlayer = game.getPlayer(targetId);
|
||||||
|
if (targetPlayer != null) {
|
||||||
|
targetInfo = targetPlayer.toString();
|
||||||
|
} else {
|
||||||
|
MageObject targetObject = game.getObject(targetId);
|
||||||
|
if (targetObject != null) {
|
||||||
|
targetInfo = targetObject.toString();
|
||||||
|
} else {
|
||||||
|
targetInfo = "unknown " + targetId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (target instanceof TargetAmount) {
|
||||||
|
output.add(String.format(" - choice %d.%d: amount %d, %s", i + 1, j + 1, target.getTargetAmount(targetId), targetInfo));
|
||||||
|
} else {
|
||||||
|
output.add(String.format(" - choice %d.%d: %s", i + 1, j + 1, targetInfo));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean stillLegal(Ability source, Game game) {
|
public boolean stillLegal(Ability source, Game game) {
|
||||||
|
|
|
||||||
|
|
@ -76,20 +76,44 @@ public class TargetCardInLibrary extends TargetCard {
|
||||||
Cards cardsId = new CardsImpl();
|
Cards cardsId = new CardsImpl();
|
||||||
cards.forEach(cardsId::add);
|
cards.forEach(cardsId::add);
|
||||||
|
|
||||||
|
UUID abilityControllerId = playerId;
|
||||||
|
if (this.getTargetController() != null && this.getAbilityController() != null) {
|
||||||
|
abilityControllerId = this.getAbilityController();
|
||||||
|
}
|
||||||
|
|
||||||
chosen = false;
|
chosen = false;
|
||||||
do {
|
do {
|
||||||
if (!player.canRespond()) {
|
int prevTargetsCount = this.getTargets().size();
|
||||||
chosen = isChosen(game);
|
|
||||||
return chosen;
|
|
||||||
}
|
|
||||||
if (!player.chooseTarget(outcome, cardsId, this, source, game)) {
|
|
||||||
chosen = isChosen(game);
|
|
||||||
return chosen;
|
|
||||||
}
|
|
||||||
chosen = isChosen(game);
|
|
||||||
} while (!doneChoosing(game));
|
|
||||||
|
|
||||||
return isChosen(game);
|
// stop by disconnect
|
||||||
|
if (!player.canRespond()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop by cancel/done
|
||||||
|
// TODO: need research - why it used chooseTarget instead choose? Need random and other options?
|
||||||
|
// someday must be replaced by player.choose (require whole tests fix from addTarget to setChoice)
|
||||||
|
if (!player.chooseTarget(outcome, cardsId, this, source, game)) {
|
||||||
|
return chosen;
|
||||||
|
}
|
||||||
|
|
||||||
|
chosen = isChosen(game);
|
||||||
|
|
||||||
|
// stop by full complete
|
||||||
|
if (isChoiceCompleted(abilityControllerId, source, game)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop by nothing to use (actual for human and done button)
|
||||||
|
if (prevTargetsCount == this.getTargets().size()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// can select next target
|
||||||
|
} while (true);
|
||||||
|
|
||||||
|
chosen = isChosen(game);
|
||||||
|
return this.getTargets().size() > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -133,7 +133,7 @@ public class TargetPermanentOrPlayer extends TargetImpl {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) {
|
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter.getPermanentFilter(), sourceControllerId, game)) { // TODO: miss source?
|
||||||
if (permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game) && filter.match(permanent, sourceControllerId, source, game)) {
|
if (permanent.canBeTargetedBy(targetSource, sourceControllerId, source, game) && filter.match(permanent, sourceControllerId, source, game)) {
|
||||||
count++;
|
count++;
|
||||||
if (count >= this.minNumberOfTargets) {
|
if (count >= this.minNumberOfTargets) {
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,11 @@ public class DebugUtil {
|
||||||
public static boolean AI_ENABLE_DEBUG_MODE = false;
|
public static boolean AI_ENABLE_DEBUG_MODE = false;
|
||||||
public static boolean AI_SHOW_TARGET_OPTIMIZATION_LOGS = false; // works with target amount
|
public static boolean AI_SHOW_TARGET_OPTIMIZATION_LOGS = false; // works with target amount
|
||||||
|
|
||||||
|
// GAME
|
||||||
|
// print detail target info for activate/cast/trigger only, not a single choose dialog
|
||||||
|
// can be useful to debug unit tests, auto-choose or AI
|
||||||
|
public static boolean GAME_SHOW_CHOOSE_TARGET_LOGS = false;
|
||||||
|
|
||||||
// cards basic (card panels)
|
// cards basic (card panels)
|
||||||
public static boolean GUI_CARD_DRAW_OUTER_BORDER = false;
|
public static boolean GUI_CARD_DRAW_OUTER_BORDER = false;
|
||||||
public static boolean GUI_CARD_DRAW_INNER_BORDER = false;
|
public static boolean GUI_CARD_DRAW_INNER_BORDER = false;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue