forked from External/mage
Merge pull request 'master' (#26) from External/mage:master into master
All checks were successful
/ build_release (push) Successful in 14m41s
All checks were successful
/ build_release (push) Successful in 14m41s
Reviewed-on: #26
This commit is contained in:
commit
9a2382cd2c
788 changed files with 29882 additions and 3734 deletions
|
|
@ -83,7 +83,7 @@ public enum CombatManager {
|
||||||
UUID defenderId = group.getDefenderId();
|
UUID defenderId = group.getDefenderId();
|
||||||
if (defenderId != null) {
|
if (defenderId != null) {
|
||||||
// if attacker was blocked then use another arrow color
|
// if attacker was blocked then use another arrow color
|
||||||
Color attackColor = group.getBlockers().isEmpty() ? ARROW_COLOR_ATTACKER : ARROW_COLOR_BLOCKED_ATTACKER;
|
Color attackColor = group.isBlocked() ? ARROW_COLOR_BLOCKED_ATTACKER : ARROW_COLOR_ATTACKER;
|
||||||
parentPoint = getParentPoint(attackerCard);
|
parentPoint = getParentPoint(attackerCard);
|
||||||
PlayAreaPanel p = MageFrame.getGamePlayers(gameId).get(defenderId);
|
PlayAreaPanel p = MageFrame.getGamePlayers(gameId).get(defenderId);
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,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);
|
||||||
}
|
}
|
||||||
|
|
@ -658,7 +674,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);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
@ -1186,7 +1202,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);
|
||||||
}
|
}
|
||||||
|
|
@ -1802,12 +1818,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) {
|
||||||
|
|
@ -2037,7 +2048,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 {
|
||||||
|
|
|
||||||
|
|
@ -105,21 +105,25 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
// = cardWidth - 2 x totalContentInset
|
// = cardWidth - 2 x totalContentInset
|
||||||
protected int contentWidth;
|
protected int contentWidth;
|
||||||
|
|
||||||
// Width of art / text box
|
// dimensions of art / text box
|
||||||
protected int innerContentWidth;
|
protected int innerContentWidth;
|
||||||
// X position of inside content
|
protected int artHeight;
|
||||||
private int innerContentStart;
|
|
||||||
|
|
||||||
|
|
||||||
// How tall the name / type lines and P/T box are
|
// How tall the name / type lines and P/T box are
|
||||||
protected static final float BOX_HEIGHT_FRAC = 0.065f; // x cardHeight
|
protected static final float BOX_HEIGHT_FRAC = 0.051f; // x cardHeight
|
||||||
protected static final int BOX_HEIGHT_MIN = 16;
|
protected static final float PT_BOX_HEIGHT_FRAC = 0.065f; // x cardHeight
|
||||||
|
protected static final int BOX_HEIGHT_MIN = 8;
|
||||||
protected int boxHeight;
|
protected int boxHeight;
|
||||||
|
protected int ptBoxHeight;
|
||||||
|
|
||||||
// How far down the card is the type line placed?
|
// How far down the card is the type line placed?
|
||||||
protected static final float TYPE_LINE_Y_FRAC = 0.52f; // x cardHeight
|
protected static final float TYPE_LINE_Y_FRAC = 0.525f; // x cardHeight
|
||||||
protected int typeLineY;
|
protected int typeLineY;
|
||||||
|
|
||||||
|
// The left and right frame inset
|
||||||
|
protected static final float INSET_WIDTH_FRAC = .055f;
|
||||||
|
protected int insetWidth;
|
||||||
|
|
||||||
// Possible sizes of rules text font
|
// Possible sizes of rules text font
|
||||||
protected static final int[] RULES_TEXT_FONT_SIZES = {24, 18, 15, 12, 9};
|
protected static final int[] RULES_TEXT_FONT_SIZES = {24, 18, 15, 12, 9};
|
||||||
|
|
||||||
|
|
@ -156,14 +160,18 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
|
|
||||||
borderWidth = (int) Math.max(
|
borderWidth = (int) Math.max(
|
||||||
BORDER_WIDTH_MIN,
|
BORDER_WIDTH_MIN,
|
||||||
0.042 * cardWidth);
|
0.048 * cardWidth);
|
||||||
|
|
||||||
frameInset = (int) Math.max(
|
frameInset = (int) Math.max(
|
||||||
BORDER_WIDTH_MIN,
|
BORDER_WIDTH_MIN,
|
||||||
0.012 * cardWidth);
|
0.012 * cardWidth);
|
||||||
|
|
||||||
|
insetWidth = (int) Math.max(
|
||||||
|
BORDER_WIDTH_MIN,
|
||||||
|
INSET_WIDTH_FRAC * cardWidth);
|
||||||
|
|
||||||
// Content inset, just equal to border width
|
// Content inset, just equal to border width
|
||||||
contentInset = borderWidth - frameInset;
|
contentInset = borderWidth + insetWidth;
|
||||||
|
|
||||||
// Total content inset helper
|
// Total content inset helper
|
||||||
totalContentInset = borderWidth + contentInset;
|
totalContentInset = borderWidth + contentInset;
|
||||||
|
|
@ -175,14 +183,20 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
boxHeight = (int) Math.max(
|
boxHeight = (int) Math.max(
|
||||||
BOX_HEIGHT_MIN,
|
BOX_HEIGHT_MIN,
|
||||||
BOX_HEIGHT_FRAC * cardHeight);
|
BOX_HEIGHT_FRAC * cardHeight);
|
||||||
|
ptBoxHeight = (int) Math.max(
|
||||||
// Art / text box size
|
BOX_HEIGHT_MIN * 2,
|
||||||
innerContentWidth = (int) (cardWidth * 0.81f);
|
PT_BOX_HEIGHT_FRAC * cardHeight);
|
||||||
innerContentStart = (int) (cardWidth * 0.095f);
|
|
||||||
|
|
||||||
// Type line at
|
// Type line at
|
||||||
typeLineY = (int) (TYPE_LINE_Y_FRAC * cardHeight);
|
typeLineY = (int) (TYPE_LINE_Y_FRAC * cardHeight);
|
||||||
|
|
||||||
|
// Art / text box size
|
||||||
|
innerContentWidth = (int) (cardWidth * .8f);
|
||||||
|
if (innerContentWidth < 160) {
|
||||||
|
innerContentWidth += 2;
|
||||||
|
}
|
||||||
|
artHeight = typeLineY - (borderWidth + boxHeight);
|
||||||
|
|
||||||
// Box text height
|
// Box text height
|
||||||
boxTextHeight = getTextHeightForBoxHeight(boxHeight);
|
boxTextHeight = getTextHeightForBoxHeight(boxHeight);
|
||||||
boxTextOffset = (boxHeight - boxTextHeight) / 2;
|
boxTextOffset = (boxHeight - boxTextHeight) / 2;
|
||||||
|
|
@ -190,8 +204,8 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
boxTextFontNarrow = new Font("Arial Narrow", Font.PLAIN, boxTextHeight);
|
boxTextFontNarrow = new Font("Arial Narrow", Font.PLAIN, boxTextHeight);
|
||||||
|
|
||||||
// Box text height
|
// Box text height
|
||||||
ptTextHeight = getPTTextHeightForLineHeight(boxHeight);
|
ptTextHeight = getPTTextHeightForLineHeight(ptBoxHeight);
|
||||||
ptTextOffset = (boxHeight - ptTextHeight) / 2;
|
ptTextOffset = (ptBoxHeight - ptTextHeight) / 2;
|
||||||
ptTextFont = new Font("Arial", Font.BOLD, ptTextHeight);
|
ptTextFont = new Font("Arial", Font.BOLD, ptTextHeight);
|
||||||
|
|
||||||
// Inset Frame Colors
|
// Inset Frame Colors
|
||||||
|
|
@ -251,7 +265,7 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
protected void drawArt(Graphics2D g) {
|
protected void drawArt(Graphics2D g) {
|
||||||
if (artImage != null) {
|
if (artImage != null) {
|
||||||
|
|
||||||
boolean shouldPreserveAspect = false;
|
boolean shouldPreserveAspect = true;
|
||||||
Rectangle2D sourceRect = ArtRect.RETRO.rect;
|
Rectangle2D sourceRect = ArtRect.RETRO.rect;
|
||||||
if (cardView.getFrameStyle() != FrameStyle.RETRO) {
|
if (cardView.getFrameStyle() != FrameStyle.RETRO) {
|
||||||
sourceRect = new Rectangle2D.Double(sourceRect.getX(), sourceRect.getY() + .01, sourceRect.getWidth(), sourceRect.getHeight());
|
sourceRect = new Rectangle2D.Double(sourceRect.getX(), sourceRect.getY() + .01, sourceRect.getWidth(), sourceRect.getHeight());
|
||||||
|
|
@ -266,8 +280,8 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
|
|
||||||
// Normal drawing of art from a source part of the card frame into the rect
|
// Normal drawing of art from a source part of the card frame into the rect
|
||||||
drawArtIntoRect(g,
|
drawArtIntoRect(g,
|
||||||
innerContentStart + frameInset, innerContentStart + frameInset * 2,
|
contentInset + frameInset, borderWidth + boxHeight + frameInset,
|
||||||
innerContentWidth - frameInset * 2, typeLineY - borderWidth * 2 - frameInset,
|
innerContentWidth - frameInset * 2, artHeight - frameInset * 2,
|
||||||
sourceRect, shouldPreserveAspect);
|
sourceRect, shouldPreserveAspect);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
@ -291,28 +305,28 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
// Draw the textbox fill
|
// Draw the textbox fill
|
||||||
drawTextboxBackground(g, textboxPaint, frameColors, borderPaint, isOriginalDualLand());
|
drawTextboxBackground(g, textboxPaint, frameColors, borderPaint, isOriginalDualLand());
|
||||||
|
|
||||||
drawInsetFrame(g, innerContentStart, innerContentStart + frameInset,
|
drawInsetFrame(g, contentInset, borderWidth + boxHeight,
|
||||||
innerContentWidth, typeLineY - borderWidth * 2 + frameInset, borderPaint, cardView.getCardTypes().contains(CardType.LAND));
|
innerContentWidth, artHeight, borderPaint, cardView.getCardTypes().contains(CardType.LAND));
|
||||||
|
|
||||||
drawTypeLine(g, attribs, getCardTypeLine(),
|
drawTypeLine(g, attribs, getCardTypeLine(),
|
||||||
innerContentStart, typeLineY + frameInset,
|
contentInset, typeLineY,
|
||||||
innerContentWidth, boxHeight + frameInset);
|
innerContentWidth, boxHeight);
|
||||||
|
|
||||||
// Draw the transform circle
|
// Draw the transform circle
|
||||||
int nameOffset = drawTransformationCircle(g, attribs, borderPaint);
|
int nameOffset = drawTransformationCircle(g, attribs, borderPaint);
|
||||||
|
|
||||||
// Draw the name line
|
// Draw the name line
|
||||||
drawNameLine(g, attribs, cardView.getDisplayName(), manaCostString,
|
drawNameLine(g, attribs, cardView.getDisplayName(), manaCostString,
|
||||||
innerContentStart + nameOffset, totalContentInset / 2 - frameInset,
|
contentInset + nameOffset, borderWidth,
|
||||||
contentWidth - nameOffset - borderWidth);
|
innerContentWidth);
|
||||||
|
|
||||||
// Draw the textbox rules
|
// Draw the textbox rules
|
||||||
drawRulesText(g, textboxKeywords, textboxRules,
|
drawRulesText(g, textboxKeywords, textboxRules,
|
||||||
innerContentStart + 2, typeLineY + boxHeight + 2,
|
contentInset + 2, typeLineY + boxHeight + 2,
|
||||||
innerContentWidth - 4, (int) ((cardHeight - borderWidth * 2) * 0.32f));
|
innerContentWidth - 4, (int) ((cardHeight - borderWidth * 2) * 0.32f));
|
||||||
|
|
||||||
// Draw the bottom right stuff
|
// Draw the bottom right stuff
|
||||||
drawBottomRight(g, borderPaint, boxColor);
|
drawBottomRight(g, attribs, borderPaint, boxColor);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void drawInsetFrame(Graphics2D g2, int x, int y, int width, int height, Paint borderPaint, boolean isLand) {
|
private void drawInsetFrame(Graphics2D g2, int x, int y, int width, int height, Paint borderPaint, boolean isLand) {
|
||||||
|
|
@ -390,7 +404,7 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
|
|
||||||
private void drawTextboxBackground(Graphics2D g, Paint textboxPaint, ObjectColor frameColors, Paint borderPaint, boolean isOriginalDual) {
|
private void drawTextboxBackground(Graphics2D g, Paint textboxPaint, ObjectColor frameColors, Paint borderPaint, boolean isOriginalDual) {
|
||||||
g.setPaint(textboxPaint);
|
g.setPaint(textboxPaint);
|
||||||
int x = innerContentStart;
|
int x = contentInset;
|
||||||
int backgroundHeight = (int) ((cardHeight - borderWidth * 2) * 0.33f);
|
int backgroundHeight = (int) ((cardHeight - borderWidth * 2) * 0.33f);
|
||||||
if (cardView.getCardTypes().contains(CardType.LAND)) {
|
if (cardView.getCardTypes().contains(CardType.LAND)) {
|
||||||
|
|
||||||
|
|
@ -526,12 +540,14 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the P/T and/or Loyalty boxes
|
// Draw the P/T and/or Loyalty boxes
|
||||||
protected void drawBottomRight(Graphics2D g, Paint borderPaint, Color fill) {
|
protected void drawBottomRight(Graphics2D g, CardPanelAttributes attribs, Paint borderPaint, Color fill) {
|
||||||
// No bottom right for abilities
|
// No bottom right for abilities
|
||||||
if (cardView.isAbility()) {
|
if (cardView.isAbility()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int contentInset = borderWidth - frameInset;
|
||||||
|
|
||||||
// Where to start drawing the things
|
// Where to start drawing the things
|
||||||
int curY = cardHeight - (int) (0.03f * cardHeight);
|
int curY = cardHeight - (int) (0.03f * cardHeight);
|
||||||
|
|
||||||
|
|
@ -559,15 +575,16 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
|
|
||||||
// Draw PT box
|
// Draw PT box
|
||||||
CardRendererUtils.drawRoundedBox(g,
|
CardRendererUtils.drawRoundedBox(g,
|
||||||
x, curY - boxHeight,
|
x, curY - ptBoxHeight,
|
||||||
partBoxWidth, boxHeight,
|
partBoxWidth, ptBoxHeight,
|
||||||
contentInset,
|
contentInset,
|
||||||
borderPaint,
|
borderPaint,
|
||||||
isVehicle ? BOX_VEHICLE : fill);
|
isVehicle ? BOX_VEHICLE : fill);
|
||||||
|
|
||||||
// Draw text
|
// Draw text
|
||||||
Color defaultTextColor = Color.black;
|
Color defaultTextColor = Color.black;
|
||||||
boolean defaultTextLight = true;
|
boolean defaultTextLight = cardView.getColor().isMulticolored() || cardView.getColor().equals(ObjectColor.RED)
|
||||||
|
|| cardView.getColor().equals(ObjectColor.COLORLESS);
|
||||||
g.setFont(ptTextFont);
|
g.setFont(ptTextFont);
|
||||||
|
|
||||||
// real PT info
|
// real PT info
|
||||||
|
|
@ -592,7 +609,7 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
g.setColor(defaultTextColor);
|
g.setColor(defaultTextColor);
|
||||||
|
|
||||||
// Advance
|
// Advance
|
||||||
curY -= boxHeight;
|
curY -= ptBoxHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Is it a walker? (But don't draw the box if it's a non-permanent view
|
// Is it a walker? (But don't draw the box if it's a non-permanent view
|
||||||
|
|
@ -696,14 +713,14 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
// does it have damage on it?
|
// does it have damage on it?
|
||||||
if ((cardView instanceof PermanentView) && ((PermanentView) cardView).getDamage() > 0) {
|
if ((cardView instanceof PermanentView) && ((PermanentView) cardView).getDamage() > 0) {
|
||||||
int x = cardWidth - partBoxWidth - borderWidth;
|
int x = cardWidth - partBoxWidth - borderWidth;
|
||||||
int y = curY - boxHeight;
|
int y = curY - ptBoxHeight;
|
||||||
String damage = String.valueOf(((PermanentView) cardView).getDamage());
|
String damage = String.valueOf(((PermanentView) cardView).getDamage());
|
||||||
g.setFont(ptTextFont);
|
g.setFont(ptTextFont);
|
||||||
int txWidth = g.getFontMetrics().stringWidth(damage);
|
int txWidth = g.getFontMetrics().stringWidth(damage);
|
||||||
g.setColor(Color.red);
|
g.setColor(Color.red);
|
||||||
g.fillRect(x, y, partBoxWidth, boxHeight);
|
g.fillRect(x, y, partBoxWidth, ptBoxHeight);
|
||||||
g.setColor(Color.white);
|
g.setColor(Color.white);
|
||||||
g.drawRect(x, y, partBoxWidth, boxHeight);
|
g.drawRect(x, y, partBoxWidth, ptBoxHeight);
|
||||||
g.drawString(damage, x + (partBoxWidth - txWidth) / 2, curY - 1);
|
g.drawString(damage, x + (partBoxWidth - txWidth) / 2, curY - 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -998,11 +1015,7 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
|
|
||||||
// Get the text height for a given box height
|
// Get the text height for a given box height
|
||||||
protected static int getTextHeightForBoxHeight(int h) {
|
protected static int getTextHeightForBoxHeight(int h) {
|
||||||
if (h < 15) {
|
return Math.max(10, (int) Math.ceil(.95 * h));
|
||||||
return h - 3;
|
|
||||||
} else {
|
|
||||||
return (int) Math.ceil(.6 * h);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected static int getPTTextHeightForLineHeight(int h) {
|
protected static int getPTTextHeightForLineHeight(int h) {
|
||||||
|
|
@ -1056,8 +1069,6 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
protected static BufferedImage getBackgroundTexture(ObjectColor colors, Collection<CardType> types) {
|
protected static BufferedImage getBackgroundTexture(ObjectColor colors, Collection<CardType> types) {
|
||||||
if (types.contains(CardType.LAND)) {
|
if (types.contains(CardType.LAND)) {
|
||||||
return BG_IMG_LAND;
|
return BG_IMG_LAND;
|
||||||
} else if (types.contains(CardType.ARTIFACT)) {
|
|
||||||
return BG_IMG_ARTIFACT;
|
|
||||||
} else if (colors.isMulticolored()) {
|
} else if (colors.isMulticolored()) {
|
||||||
return BG_IMG_GOLD;
|
return BG_IMG_GOLD;
|
||||||
} else if (colors.isWhite()) {
|
} else if (colors.isWhite()) {
|
||||||
|
|
@ -1070,6 +1081,8 @@ public class RetroCardRenderer extends CardRenderer {
|
||||||
return BG_IMG_RED;
|
return BG_IMG_RED;
|
||||||
} else if (colors.isGreen()) {
|
} else if (colors.isGreen()) {
|
||||||
return BG_IMG_GREEN;
|
return BG_IMG_GREEN;
|
||||||
|
} else if (types.contains(CardType.ARTIFACT)) {
|
||||||
|
return BG_IMG_ARTIFACT;
|
||||||
} else {
|
} else {
|
||||||
// Colorless
|
// Colorless
|
||||||
return BG_IMG_COLORLESS;
|
return BG_IMG_COLORLESS;
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
|
|
@ -438,7 +448,7 @@ public final class SystemUtil {
|
||||||
cardName = cardChoice.getChoice();
|
cardName = cardChoice.getChoice();
|
||||||
|
|
||||||
// amount
|
// amount
|
||||||
int cardAmount = feedbackPlayer.getAmount(1, 100, "How many [" + cardName + "] to add?", game);
|
int cardAmount = feedbackPlayer.getAmount(1, 100, "How many [" + cardName + "] to add?", null, game);
|
||||||
if (cardAmount == 0) {
|
if (cardAmount == 0) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -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,65 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
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.announceX()
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
class AnnounceXTestableDialog extends BaseTestableDialog {
|
||||||
|
|
||||||
|
boolean isYou; // who choose - you or opponent
|
||||||
|
boolean isMana; // reason - for mana payment or another value
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
|
||||||
|
public AnnounceXTestableDialog(boolean isYou, boolean isMana, int min, int max) {
|
||||||
|
super(String.format("player.announceX(%s)", isYou ? "you" : "AI"),
|
||||||
|
String.format("%s from %d to %d", isMana ? "mana" : "cost", min, max), "");
|
||||||
|
this.isYou = isYou;
|
||||||
|
this.isMana = isMana;
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||||
|
Player choosingPlayer = this.isYou ? player : opponent;
|
||||||
|
String message = "<font color=green>message</font> with html";
|
||||||
|
int chooseRes;
|
||||||
|
chooseRes = choosingPlayer.announceX(this.min, this.max, message, game, source, this.isMana);
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
result.add(getGroup() + " - " + this.getName() + " selected " + chooseRes);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void register(TestableDialogsRunner runner) {
|
||||||
|
List<Boolean> isYous = Arrays.asList(false, true);
|
||||||
|
List<Boolean> isManas = Arrays.asList(false, true);
|
||||||
|
for (boolean isYou : isYous) {
|
||||||
|
for (boolean isMana : isManas) {
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 0, 0));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 0, 1));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 0, 3));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 0, 50));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 0, 500));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 1, 1));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 1, 3));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 1, 50));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 3, 3));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 3, 10));
|
||||||
|
runner.registerDialog(new AnnounceXTestableDialog(isYou, isMana, 10, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,60 @@
|
||||||
|
package mage.utils.testers;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
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.getAmount()
|
||||||
|
*
|
||||||
|
* @author JayDi85
|
||||||
|
*/
|
||||||
|
class GetAmountTestableDialog extends BaseTestableDialog {
|
||||||
|
|
||||||
|
boolean isYou; // who choose - you or opponent
|
||||||
|
int min;
|
||||||
|
int max;
|
||||||
|
|
||||||
|
public GetAmountTestableDialog(boolean isYou, int min, int max) {
|
||||||
|
super(String.format("player.getAmount(%s)", isYou ? "you" : "AI"),
|
||||||
|
String.format("from %d to %d", min, max), "");
|
||||||
|
this.isYou = isYou;
|
||||||
|
this.min = min;
|
||||||
|
this.max = max;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<String> showDialog(Player player, Ability source, Game game, Player opponent) {
|
||||||
|
Player choosingPlayer = this.isYou ? player : opponent;
|
||||||
|
String message = "<font color=green>message</font> with html";
|
||||||
|
int chooseRes;
|
||||||
|
chooseRes = choosingPlayer.getAmount(this.min, this.max, message, source, game);
|
||||||
|
List<String> result = new ArrayList<>();
|
||||||
|
result.add(getGroup() + " - " + this.getName() + " selected " + chooseRes);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static public void register(TestableDialogsRunner runner) {
|
||||||
|
List<Boolean> isYous = Arrays.asList(false, true);
|
||||||
|
for (boolean isYou : isYous) {
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 0, 0));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 0, 1));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 0, 3));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 0, 50));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 0, 500));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 1, 1));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 1, 3));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 1, 50));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 3, 3));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 3, 10));
|
||||||
|
runner.registerDialog(new GetAmountTestableDialog(isYou, 10, 10));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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,202 @@
|
||||||
|
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
|
||||||
|
* [x] announceX
|
||||||
|
* [x] getAmount
|
||||||
|
* [ ] 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);
|
||||||
|
AnnounceXTestableDialog.register(this);
|
||||||
|
GetAmountTestableDialog.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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -19,6 +19,7 @@ public class CombatGroupView implements Serializable {
|
||||||
|
|
||||||
private final CardsView attackers = new CardsView();
|
private final CardsView attackers = new CardsView();
|
||||||
private final CardsView blockers = new CardsView();
|
private final CardsView blockers = new CardsView();
|
||||||
|
private final boolean isBlocked;
|
||||||
private String defenderName = "";
|
private String defenderName = "";
|
||||||
private final UUID defenderId;
|
private final UUID defenderId;
|
||||||
|
|
||||||
|
|
@ -46,6 +47,7 @@ public class CombatGroupView implements Serializable {
|
||||||
blockers.put(id, new PermanentView(blocker, game.getCard(blocker.getId()), null, game));
|
blockers.put(id, new PermanentView(blocker, game.getCard(blocker.getId()), null, game));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
isBlocked = combatGroup.getBlocked();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDefenderName() {
|
public String getDefenderName() {
|
||||||
|
|
@ -63,4 +65,8 @@ public class CombatGroupView implements Serializable {
|
||||||
public UUID getDefenderId() {
|
public UUID getDefenderId() {
|
||||||
return defenderId;
|
return defenderId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isBlocked() {
|
||||||
|
return isBlocked;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package mage.player.ai;
|
||||||
|
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.*;
|
import mage.abilities.*;
|
||||||
import mage.abilities.costs.VariableCost;
|
|
||||||
import mage.abilities.costs.mana.ManaCost;
|
import mage.abilities.costs.mana.ManaCost;
|
||||||
import mage.cards.Card;
|
import mage.cards.Card;
|
||||||
import mage.cards.Cards;
|
import mage.cards.Cards;
|
||||||
|
|
@ -250,20 +249,11 @@ public class ComputerPlayerControllableProxy extends ComputerPlayer7 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
|
public int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay) {
|
||||||
if (isUnderMe(game)) {
|
if (isUnderMe(game)) {
|
||||||
return super.announceXMana(min, max, message, game, ability);
|
return super.announceX(min, max, message, game, source, isManaPay);
|
||||||
} else {
|
} else {
|
||||||
return getControllingPlayer(game).announceXMana(min, max, message, game, ability);
|
return getControllingPlayer(game).announceX(min, max, message, game, source, isManaPay);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) {
|
|
||||||
if (isUnderMe(game)) {
|
|
||||||
return super.announceXCost(min, max, message, game, ability, variableCost);
|
|
||||||
} else {
|
|
||||||
return getControllingPlayer(game).announceXCost(min, max, message, game, ability, variableCost);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -286,11 +276,11 @@ public class ComputerPlayerControllableProxy extends ComputerPlayer7 {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAmount(int min, int max, String message, Game game) {
|
public int getAmount(int min, int max, String message, Ability source, Game game) {
|
||||||
if (isUnderMe(game)) {
|
if (isUnderMe(game)) {
|
||||||
return super.getAmount(min, max, message, game);
|
return super.getAmount(min, max, message, source, game);
|
||||||
} else {
|
} else {
|
||||||
return getControllingPlayer(game).getAmount(min, max, message, game);
|
return getControllingPlayer(game).getAmount(min, max, message, source, game);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
@ -375,11 +375,11 @@ public final class SimulatedPlayerMCTS extends MCTSPlayer {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAmount(int min, int max, String message, Game game) {
|
public int getAmount(int min, int max, String message, Ability source, Game game) {
|
||||||
if (this.isHuman()) {
|
if (this.isHuman()) {
|
||||||
return RandomUtil.nextInt(max - min) + min;
|
return RandomUtil.nextInt(max - min + 1) + min;
|
||||||
}
|
}
|
||||||
return super.getAmount(min, max, message, game);
|
return super.getAmount(min, max, message, source, game);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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.getNumberOfTargets();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
while (canRespond()) {
|
||||||
|
|
||||||
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
||||||
if (target.getTargets().size() >= target.getNumberOfTargets()) {
|
|
||||||
|
// enable done button after min targets selected
|
||||||
|
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.getNumberOfTargets()) {
|
|
||||||
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,64 +790,67 @@ 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.getNumberOfTargets()) {
|
|| 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.getNumberOfTargets()) {
|
// 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) {
|
||||||
if (options == null) {
|
if (options == null) {
|
||||||
options = new HashMap<>();
|
options = new HashMap<>();
|
||||||
}
|
}
|
||||||
if (target.getTargets().size() >= target.getNumberOfTargets()
|
if (target.getTargets().size() >= target.getMinNumberOfTargets()
|
||||||
&& !options.containsKey("UI.right.btn.text")) {
|
&& !options.containsKey("UI.right.btn.text")) {
|
||||||
options.put("UI.right.btn.text", "Done");
|
options.put("UI.right.btn.text", "Done");
|
||||||
}
|
}
|
||||||
|
|
@ -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,30 +878,37 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
while (canRespond()) {
|
while (canRespond()) {
|
||||||
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
|
||||||
int count = cards.count(target.getFilter(), abilityControllerId, source, game);
|
|
||||||
if (count == 0
|
|
||||||
|| target.getTargets().size() >= target.getNumberOfTargets()) {
|
|
||||||
required = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
List<UUID> chosenTargets = target.getTargets();
|
|
||||||
List<UUID> possibleTargets = new ArrayList<>();
|
List<UUID> possibleTargets = new ArrayList<>();
|
||||||
for (UUID cardId : cards) {
|
for (UUID cardId : cards) {
|
||||||
if (target.canTarget(abilityControllerId, cardId, source, cards, game)) {
|
if (target.canTarget(abilityControllerId, cardId, source, cards, game)) {
|
||||||
possibleTargets.add(cardId);
|
possibleTargets.add(cardId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
|
||||||
|
int count = cards.count(target.getFilter(), abilityControllerId, source, game);
|
||||||
|
if (count == 0
|
||||||
|
|| target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
||||||
|
required = false;
|
||||||
|
}
|
||||||
|
|
||||||
// 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.getNumberOfTargets()) {
|
// 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()) {
|
||||||
|
|
@ -968,7 +978,7 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
boolean required = target.isRequiredExplicitlySet() ? target.isRequired() : target.isRequired(source);
|
boolean required = target.isRequiredExplicitlySet() ? target.isRequired() : target.isRequired(source);
|
||||||
int count = cards.count(target.getFilter(), abilityControllerId, source, game);
|
int count = cards.count(target.getFilter(), abilityControllerId, source, game);
|
||||||
if (count == 0
|
if (count == 0
|
||||||
|| target.getTargets().size() >= target.getNumberOfTargets()) {
|
|| target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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,16 +1013,16 @@ 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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (target.getTargets().size() >= target.getNumberOfTargets()) {
|
if (target.getTargets().size() >= target.getMinNumberOfTargets()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!required) {
|
if (!required) {
|
||||||
|
|
@ -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,11 +1066,12 @@ 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);
|
||||||
if (possibleTargetIds.isEmpty()
|
if (possibleTargetIds.isEmpty()
|
||||||
|| target.getSize() >= target.getNumberOfTargets()) {
|
|| target.getSize() >= target.getMinNumberOfTargets()) {
|
||||||
required = false;
|
required = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -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?
|
||||||
|
|
@ -1687,64 +1687,39 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the amount of mana the player want to spent for a x spell
|
* Gets the amount of mana the player want to spend for an x spell
|
||||||
*
|
|
||||||
* @param min
|
|
||||||
* @param max
|
|
||||||
* @param message
|
|
||||||
* @param ability
|
|
||||||
* @param game
|
|
||||||
* @return
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int announceXMana(int min, int max, String message, Game game, Ability ability) {
|
public int announceX(int min, int max, String message, Game game, Ability source, boolean isManaPay) {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
int xValue = 0;
|
// fast calc on nothing to choose
|
||||||
while (canRespond()) {
|
if (min >= max) {
|
||||||
prepareForResponse(game);
|
|
||||||
if (!isExecutingMacro()) {
|
|
||||||
game.fireGetAmountEvent(playerId, message + CardUtil.getSourceLogName(game, ability), min, max);
|
|
||||||
}
|
|
||||||
waitForResponse(game);
|
|
||||||
|
|
||||||
if (response.getInteger() != null) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: add response verify here
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response.getInteger() != null) {
|
|
||||||
xValue = response.getInteger();
|
|
||||||
}
|
|
||||||
return xValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int announceXCost(int min, int max, String message, Game game, Ability ability, VariableCost variableCost) {
|
|
||||||
if (!canCallFeedback(game)) {
|
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
int xValue = 0;
|
int xValue = min;
|
||||||
while (canRespond()) {
|
while (canRespond()) {
|
||||||
prepareForResponse(game);
|
prepareForResponse(game);
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
game.fireGetAmountEvent(playerId, message, min, max);
|
game.fireGetAmountEvent(playerId, message + CardUtil.getSourceLogName(game, source), min, max);
|
||||||
}
|
}
|
||||||
waitForResponse(game);
|
waitForResponse(game);
|
||||||
|
|
||||||
if (response.getInteger() != null) {
|
if (response.getInteger() == null) {
|
||||||
break;
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.getInteger() != null) {
|
|
||||||
xValue = response.getInteger();
|
xValue = response.getInteger();
|
||||||
|
if (xValue < min || xValue > max) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return xValue;
|
return xValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2183,28 +2158,37 @@ public class HumanPlayer extends PlayerImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getAmount(int min, int max, String message, Game game) {
|
public int getAmount(int min, int max, String message, Ability source, Game game) {
|
||||||
if (!canCallFeedback(game)) {
|
if (!canCallFeedback(game)) {
|
||||||
return min;
|
return min;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// fast calc on nothing to choose
|
||||||
|
if (min >= max) {
|
||||||
|
return min;
|
||||||
|
}
|
||||||
|
|
||||||
|
int xValue = min;
|
||||||
while (canRespond()) {
|
while (canRespond()) {
|
||||||
prepareForResponse(game);
|
prepareForResponse(game);
|
||||||
if (!isExecutingMacro()) {
|
if (!isExecutingMacro()) {
|
||||||
game.fireGetAmountEvent(playerId, message, min, max);
|
game.fireGetAmountEvent(playerId, message + CardUtil.getSourceLogName(game, source), min, max);
|
||||||
}
|
}
|
||||||
waitForResponse(game);
|
waitForResponse(game);
|
||||||
|
|
||||||
if (response.getInteger() != null) {
|
if (response.getInteger() == null) {
|
||||||
break;
|
continue;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.getInteger() != null) {
|
xValue = response.getInteger();
|
||||||
return response.getInteger();
|
if (xValue < min || xValue > max) {
|
||||||
} else {
|
continue;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return xValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -2624,7 +2608,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()) {
|
||||||
|
|
|
||||||
37
Mage.Sets/src/mage/cards/a/ARealmReborn.java
Normal file
37
Mage.Sets/src/mage/cards/a/ARealmReborn.java
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||||
|
import mage.abilities.mana.AnyColorManaAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Duration;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class ARealmReborn extends CardImpl {
|
||||||
|
|
||||||
|
public ARealmReborn(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}{G}");
|
||||||
|
|
||||||
|
// Other permanents you control have "{T}: Add one mana of any color."
|
||||||
|
this.addAbility(new SimpleStaticAbility(new GainAbilityControlledEffect(
|
||||||
|
new AnyColorManaAbility(), Duration.WhileOnBattlefield,
|
||||||
|
StaticFilters.FILTER_PERMANENTS, true
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ARealmReborn(final ARealmReborn card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ARealmReborn copy() {
|
||||||
|
return new ARealmReborn(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
85
Mage.Sets/src/mage/cards/a/AbsoluteVirtue.java
Normal file
85
Mage.Sets/src/mage/cards/a/AbsoluteVirtue.java
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.MageItem;
|
||||||
|
import mage.MageObject;
|
||||||
|
import mage.abilities.common.CantBeCounteredSourceAbility;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.effects.common.continuous.GainAbilityControllerEffect;
|
||||||
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
|
import mage.abilities.keyword.ProtectionAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.game.Game;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AbsoluteVirtue extends CardImpl {
|
||||||
|
|
||||||
|
public AbsoluteVirtue(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{W}{U}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.AVATAR);
|
||||||
|
this.subtype.add(SubType.WARRIOR);
|
||||||
|
this.power = new MageInt(8);
|
||||||
|
this.toughness = new MageInt(8);
|
||||||
|
|
||||||
|
// This spell can't be countered.
|
||||||
|
this.addAbility(new CantBeCounteredSourceAbility());
|
||||||
|
|
||||||
|
// Flying
|
||||||
|
this.addAbility(FlyingAbility.getInstance());
|
||||||
|
|
||||||
|
// You have protection from each of your opponents.
|
||||||
|
this.addAbility(new SimpleStaticAbility(new GainAbilityControllerEffect(new AbsoluteVirtueAbility())));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbsoluteVirtue(final AbsoluteVirtue card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbsoluteVirtue copy() {
|
||||||
|
return new AbsoluteVirtue(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AbsoluteVirtueAbility extends ProtectionAbility {
|
||||||
|
|
||||||
|
public AbsoluteVirtueAbility() {
|
||||||
|
super(StaticFilters.FILTER_CARD);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AbsoluteVirtueAbility(final AbsoluteVirtueAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AbsoluteVirtueAbility copy() {
|
||||||
|
return new AbsoluteVirtueAbility(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getRule() {
|
||||||
|
return "protection from each of your opponents";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canTarget(MageObject source, Game game) {
|
||||||
|
return Optional
|
||||||
|
.ofNullable(source)
|
||||||
|
.map(MageItem::getId)
|
||||||
|
.map(game::getControllerId)
|
||||||
|
.map(uuid -> !game.getOpponents(this.getControllerId()).contains(uuid))
|
||||||
|
.orElse(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Mage.Sets/src/mage/cards/a/AdventurersInn.java
Normal file
37
Mage.Sets/src/mage/cards/a/AdventurersInn.java
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
|
import mage.abilities.effects.common.GainLifeEffect;
|
||||||
|
import mage.abilities.mana.ColorlessManaAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author balazskristof
|
||||||
|
*/
|
||||||
|
public final class AdventurersInn extends CardImpl {
|
||||||
|
|
||||||
|
public AdventurersInn(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.TOWN);
|
||||||
|
|
||||||
|
// When this land enters, you gain 2 life.
|
||||||
|
this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(2)));
|
||||||
|
|
||||||
|
// {T}: Add {C}.
|
||||||
|
this.addAbility(new ColorlessManaAbility());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AdventurersInn(final AdventurersInn card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AdventurersInn copy() {
|
||||||
|
return new AdventurersInn(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
58
Mage.Sets/src/mage/cards/a/AerithGainsborough.java
Normal file
58
Mage.Sets/src/mage/cards/a/AerithGainsborough.java
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.common.DiesSourceTriggeredAbility;
|
||||||
|
import mage.abilities.common.GainLifeControllerTriggeredAbility;
|
||||||
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
|
import mage.abilities.dynamicvalue.common.CountersSourceCount;
|
||||||
|
import mage.abilities.effects.common.counter.AddCountersAllEffect;
|
||||||
|
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||||
|
import mage.abilities.keyword.LifelinkAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.counters.CounterType;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AerithGainsborough extends CardImpl {
|
||||||
|
|
||||||
|
private static final DynamicValue xValue = new CountersSourceCount(CounterType.P1P1);
|
||||||
|
|
||||||
|
public AerithGainsborough(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.CLERIC);
|
||||||
|
this.power = new MageInt(2);
|
||||||
|
this.toughness = new MageInt(2);
|
||||||
|
|
||||||
|
// Lifelink
|
||||||
|
this.addAbility(LifelinkAbility.getInstance());
|
||||||
|
|
||||||
|
// Whenever you gain life, put a +1/+1 counter on Aerith Gainsborough.
|
||||||
|
this.addAbility(new GainLifeControllerTriggeredAbility(new AddCountersSourceEffect(CounterType.P1P1.createInstance())));
|
||||||
|
|
||||||
|
// When Aerith Gainsborough dies, put X +1/+1 counters on each legendary creature you control, where X is the number of +1/+1 counters on Aerith Gainsborough.
|
||||||
|
this.addAbility(new DiesSourceTriggeredAbility(new AddCountersAllEffect(
|
||||||
|
CounterType.P1P1.createInstance(), xValue, StaticFilters.FILTER_CONTROLLED_CREATURE_LEGENDARY
|
||||||
|
).setText("put X +1/+1 counters on each legendary creature you control, " +
|
||||||
|
"where X is the number of +1/+1 counters on {this}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AerithGainsborough(final AerithGainsborough card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AerithGainsborough copy() {
|
||||||
|
return new AerithGainsborough(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
64
Mage.Sets/src/mage/cards/a/AerithLastAncient.java
Normal file
64
Mage.Sets/src/mage/cards/a/AerithLastAncient.java
Normal file
|
|
@ -0,0 +1,64 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.condition.Condition;
|
||||||
|
import mage.abilities.condition.common.YouGainedLifeCondition;
|
||||||
|
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||||
|
import mage.abilities.dynamicvalue.common.ControllerGainedLifeCount;
|
||||||
|
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
|
||||||
|
import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
|
||||||
|
import mage.abilities.keyword.LifelinkAbility;
|
||||||
|
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.ComparisonType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.target.common.TargetCardInYourGraveyard;
|
||||||
|
import mage.watchers.common.PlayerGainedLifeWatcher;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AerithLastAncient extends CardImpl {
|
||||||
|
|
||||||
|
private static final Condition condition = new YouGainedLifeCondition();
|
||||||
|
private static final Condition condition2 = new YouGainedLifeCondition(ComparisonType.MORE_THAN, 6);
|
||||||
|
|
||||||
|
public AerithLastAncient(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{W}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.CLERIC);
|
||||||
|
this.subtype.add(SubType.DRUID);
|
||||||
|
this.power = new MageInt(3);
|
||||||
|
this.toughness = new MageInt(5);
|
||||||
|
|
||||||
|
// Lifelink
|
||||||
|
this.addAbility(LifelinkAbility.getInstance());
|
||||||
|
|
||||||
|
// Raise -- At the beginning of your end step, if you gained life this turn, return target creature card from your graveyard to your hand. If you gained 7 or more life this turn, return that card to the battlefield instead.
|
||||||
|
Ability ability = new BeginningOfEndStepTriggeredAbility(new ConditionalOneShotEffect(
|
||||||
|
new ReturnFromGraveyardToBattlefieldTargetEffect(), new ReturnFromGraveyardToHandTargetEffect(),
|
||||||
|
condition2, "return target creature card from your graveyard to your hand. " +
|
||||||
|
"If you gained 7 or more life this turn, return that card to the battlefield instead"
|
||||||
|
)).withInterveningIf(condition);
|
||||||
|
ability.addTarget(new TargetCardInYourGraveyard(StaticFilters.FILTER_CARD_CREATURE_YOUR_GRAVEYARD));
|
||||||
|
this.addAbility(ability.withFlavorWord("Raise").addHint(ControllerGainedLifeCount.getHint()), new PlayerGainedLifeWatcher());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AerithLastAncient(final AerithLastAncient card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AerithLastAncient copy() {
|
||||||
|
return new AerithLastAncient(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,5 @@
|
||||||
|
|
||||||
package mage.cards.a;
|
package mage.cards.a;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.EntersBattlefieldAbility;
|
import mage.abilities.common.EntersBattlefieldAbility;
|
||||||
|
|
@ -15,6 +13,8 @@ import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author nantuko, BetaSteward_at_googlemail.com
|
* @author nantuko, BetaSteward_at_googlemail.com
|
||||||
*/
|
*/
|
||||||
|
|
@ -27,9 +27,6 @@ public final class AetherFigment extends CardImpl {
|
||||||
this.power = new MageInt(1);
|
this.power = new MageInt(1);
|
||||||
this.toughness = new MageInt(1);
|
this.toughness = new MageInt(1);
|
||||||
|
|
||||||
// Aether Figment can't be blocked.
|
|
||||||
this.addAbility(new CantBeBlockedSourceAbility());
|
|
||||||
|
|
||||||
// Kicker {3}
|
// Kicker {3}
|
||||||
this.addAbility(new KickerAbility("{3}"));
|
this.addAbility(new KickerAbility("{3}"));
|
||||||
|
|
||||||
|
|
@ -40,6 +37,9 @@ public final class AetherFigment extends CardImpl {
|
||||||
"If {this} was kicked, it enters with two +1/+1 counters on it.",
|
"If {this} was kicked, it enters with two +1/+1 counters on it.",
|
||||||
"");
|
"");
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
// Aether Figment can't be blocked.
|
||||||
|
this.addAbility(new CantBeBlockedSourceAbility());
|
||||||
}
|
}
|
||||||
|
|
||||||
private AetherFigment(final AetherFigment card) {
|
private AetherFigment(final AetherFigment card) {
|
||||||
|
|
|
||||||
|
|
@ -119,7 +119,7 @@ class AetherRefineryTokenEffect extends OneShotEffect {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int numberToPay = controller.getAmount(1, totalEnergy,
|
int numberToPay = controller.getAmount(1, totalEnergy,
|
||||||
"Pay one or more {E}", game);
|
"Pay one or more {E}", source, game);
|
||||||
|
|
||||||
Cost cost = new PayEnergyCost(numberToPay);
|
Cost cost = new PayEnergyCost(numberToPay);
|
||||||
if (cost.pay(source, game, source, source.getControllerId(), true)) {
|
if (cost.pay(source, game, source, source.getControllerId(), true)) {
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public final class AetherRevolt extends CardImpl {
|
||||||
// Revolt -- As long as a permanent you controlled left the battlefield this turn, if a source you control would deal noncombat damage to an opponent or a permanent an opponent controls, it deals that much damage plus 2 instead.
|
// Revolt -- As long as a permanent you controlled left the battlefield this turn, if a source you control would deal noncombat damage to an opponent or a permanent an opponent controls, it deals that much damage plus 2 instead.
|
||||||
this.addAbility(new SimpleStaticAbility(new ConditionalReplacementEffect(
|
this.addAbility(new SimpleStaticAbility(new ConditionalReplacementEffect(
|
||||||
new AetherRevoltEffect(), RevoltCondition.instance
|
new AetherRevoltEffect(), RevoltCondition.instance
|
||||||
).setText("As long as a permanent you controlled left the battlefield this turn, "
|
).setText("as long as a permanent left the battlefield under your control this turn, "
|
||||||
+ "if a source you control would deal noncombat damage to an opponent or a permanent an opponent controls, "
|
+ "if a source you control would deal noncombat damage to an opponent or a permanent an opponent controls, "
|
||||||
+ "it deals that much damage plus 2 instead")
|
+ "it deals that much damage plus 2 instead")
|
||||||
).setAbilityWord(AbilityWord.REVOLT).addHint(RevoltCondition.getHint()), new RevoltWatcher());
|
).setAbilityWord(AbilityWord.REVOLT).addHint(RevoltCondition.getHint()), new RevoltWatcher());
|
||||||
|
|
|
||||||
|
|
@ -68,7 +68,7 @@ class AetherSpikeEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
int numberToPay = controller.getAmount(
|
int numberToPay = controller.getAmount(
|
||||||
0, controller.getCountersCount(CounterType.ENERGY),
|
0, controller.getCountersCount(CounterType.ENERGY),
|
||||||
"How many {E} do you want to pay?", game
|
"How many {E} do you want to pay?", source, game
|
||||||
);
|
);
|
||||||
Cost cost = new PayEnergyCost(numberToPay);
|
Cost cost = new PayEnergyCost(numberToPay);
|
||||||
int numberPaid = 0;
|
int numberPaid = 0;
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ class AetherbornMarauderEffect extends OneShotEffect {
|
||||||
int numberOfCounters = fromPermanent.getCounters(game).getCount(CounterType.P1P1);
|
int numberOfCounters = fromPermanent.getCounters(game).getCount(CounterType.P1P1);
|
||||||
int numberToMove = 1;
|
int numberToMove = 1;
|
||||||
if (numberOfCounters > 1) {
|
if (numberOfCounters > 1) {
|
||||||
numberToMove = controller.getAmount(0, numberOfCounters, "Choose how many +1/+1 counters to move", game);
|
numberToMove = controller.getAmount(0, numberOfCounters, "Choose how many +1/+1 counters to move", source, game);
|
||||||
}
|
}
|
||||||
if (numberToMove > 0) {
|
if (numberToMove > 0) {
|
||||||
fromPermanent.removeCounters(CounterType.P1P1.createInstance(numberToMove), source, game);
|
fromPermanent.removeCounters(CounterType.P1P1.createInstance(numberToMove), source, game);
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
|
|
@ -1,38 +1,38 @@
|
||||||
package mage.cards.a;
|
package mage.cards.a;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageObject;
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.TriggeredAbility;
|
|
||||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
|
||||||
import mage.abilities.condition.common.RevoltCondition;
|
import mage.abilities.condition.common.RevoltCondition;
|
||||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.cards.*;
|
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.cards.CardsImpl;
|
||||||
|
import mage.constants.AbilityWord;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.common.FilterPermanentCard;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
import mage.util.CardUtil;
|
||||||
import mage.watchers.common.RevoltWatcher;
|
import mage.watchers.common.RevoltWatcher;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author fireshoes
|
* @author fireshoes
|
||||||
*/
|
*/
|
||||||
public final class AidFromTheCowl extends CardImpl {
|
public final class AidFromTheCowl extends CardImpl {
|
||||||
|
|
||||||
private static final String ruleText = "<i>Revolt</i> — At the beginning of your end step, if a permanent you controlled left the battlefield this turn, "
|
|
||||||
+ "reveal the top card of your library. If it's a permanent card, you may put it onto the battlefield. Otherwise, you may put it on the bottom of your library.";
|
|
||||||
|
|
||||||
public AidFromTheCowl(UUID ownerId, CardSetInfo setInfo) {
|
public AidFromTheCowl(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{G}{G}");
|
||||||
|
|
||||||
// <i>Revolt</i> — At the beginning of your end step, if a permanent you controlled left the battlefield this turn,
|
// <i>Revolt</i> — At the beginning of your end step, if a permanent you controlled left the battlefield this turn,
|
||||||
// reveal the top card of your library. If it is a permanent card, you may put it onto the battlefield. Otherwise, put it on the bottom of your library.
|
// reveal the top card of your library. If it is a permanent card, you may put it onto the battlefield. Otherwise, put it on the bottom of your library.
|
||||||
TriggeredAbility ability = new BeginningOfEndStepTriggeredAbility(new AidFromTheCowlEffect());
|
this.addAbility(new BeginningOfEndStepTriggeredAbility(new AidFromTheCowlEffect())
|
||||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, RevoltCondition.instance, ruleText).addHint(RevoltCondition.getHint()), new RevoltWatcher());
|
.withInterveningIf(RevoltCondition.instance)
|
||||||
|
.setAbilityWord(AbilityWord.REVOLT)
|
||||||
|
.addHint(RevoltCondition.getHint()), new RevoltWatcher());
|
||||||
}
|
}
|
||||||
|
|
||||||
private AidFromTheCowl(final AidFromTheCowl card) {
|
private AidFromTheCowl(final AidFromTheCowl card) {
|
||||||
|
|
@ -49,7 +49,8 @@ class AidFromTheCowlEffect extends OneShotEffect {
|
||||||
|
|
||||||
AidFromTheCowlEffect() {
|
AidFromTheCowlEffect() {
|
||||||
super(Outcome.PutCreatureInPlay);
|
super(Outcome.PutCreatureInPlay);
|
||||||
this.staticText = "reveal the top card of your library. If it's a permanent card, you may put it onto the battlefield. Otherwise, you may put that card on the bottom of your library";
|
this.staticText = "reveal the top card of your library. If it's a permanent card, " +
|
||||||
|
"you may put it onto the battlefield. Otherwise, you may put that card on the bottom of your library";
|
||||||
}
|
}
|
||||||
|
|
||||||
private AidFromTheCowlEffect(final AidFromTheCowlEffect effect) {
|
private AidFromTheCowlEffect(final AidFromTheCowlEffect effect) {
|
||||||
|
|
@ -64,26 +65,21 @@ class AidFromTheCowlEffect extends OneShotEffect {
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
MageObject sourceObject = game.getObject(source);
|
if (controller == null || !controller.getLibrary().hasCards()) {
|
||||||
if (controller == null || sourceObject == null) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (controller.getLibrary().hasCards()) {
|
|
||||||
Card card = controller.getLibrary().getFromTop(game);
|
Card card = controller.getLibrary().getFromTop(game);
|
||||||
Cards cards = new CardsImpl(card);
|
if (card == null) {
|
||||||
controller.revealCards(sourceObject.getIdName(), cards, game);
|
return false;
|
||||||
|
}
|
||||||
if (card != null) {
|
controller.revealCards(CardUtil.getSourceIdName(game, source), new CardsImpl(card), game);
|
||||||
if (new FilterPermanentCard().match(card, game) && controller.chooseUse(Outcome.Neutral, "Put " + card.getIdName() + " onto the battlefield?", source, game)) {
|
if (card.isPermanent(game) && controller.chooseUse(Outcome.Neutral, "Put " + card.getIdName() + " onto the battlefield?", source, game)) {
|
||||||
controller.moveCards(card, Zone.BATTLEFIELD, source, game);
|
controller.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||||
} else if (controller.chooseUse(Outcome.Neutral, "Put " + card.getIdName() + " on the bottom of your library?", source, game)) {
|
} else if (controller.chooseUse(Outcome.Neutral, "Put " + card.getIdName() + " on the bottom of your library?", source, game)) {
|
||||||
controller.putCardsOnBottomOfLibrary(cards, game, source, false);
|
controller.putCardsOnBottomOfLibrary(card, game, source, false);
|
||||||
} else {
|
} else {
|
||||||
game.informPlayers(controller.getLogName() + " puts the revealed card back to the top of the library.");
|
game.informPlayers(controller.getLogName() + " puts the revealed card back to the top of the library.");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,8 @@
|
||||||
package mage.cards.a;
|
package mage.cards.a;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.condition.common.RevoltCondition;
|
import mage.abilities.condition.common.RevoltCondition;
|
||||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
|
||||||
import mage.abilities.effects.common.GainLifeEffect;
|
import mage.abilities.effects.common.GainLifeEffect;
|
||||||
import mage.abilities.keyword.FlyingAbility;
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
|
@ -33,13 +31,10 @@ public final class AirdropAeronauts extends CardImpl {
|
||||||
this.addAbility(FlyingAbility.getInstance());
|
this.addAbility(FlyingAbility.getInstance());
|
||||||
|
|
||||||
// <i>Revolt</i> — When Airdrop Aeronauts enters the battlefield, if a permanent you controlled left the battlefield this turn, you gain 5 life.
|
// <i>Revolt</i> — When Airdrop Aeronauts enters the battlefield, if a permanent you controlled left the battlefield this turn, you gain 5 life.
|
||||||
Ability ability = new ConditionalInterveningIfTriggeredAbility(
|
this.addAbility(new EntersBattlefieldTriggeredAbility(new GainLifeEffect(5))
|
||||||
new EntersBattlefieldTriggeredAbility(new GainLifeEffect(5), false),
|
.withInterveningIf(RevoltCondition.instance)
|
||||||
RevoltCondition.instance, "When {this} enters, " +
|
.setAbilityWord(AbilityWord.REVOLT)
|
||||||
"if a permanent you controlled left the battlefield this turn, you gain 5 life."
|
.addHint(RevoltCondition.getHint()), new RevoltWatcher());
|
||||||
);
|
|
||||||
ability.setAbilityWord(AbilityWord.REVOLT);
|
|
||||||
this.addAbility(ability.addHint(RevoltCondition.getHint()), new RevoltWatcher());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AirdropAeronauts(final AirdropAeronauts card) {
|
private AirdropAeronauts(final AirdropAeronauts card) {
|
||||||
|
|
|
||||||
49
Mage.Sets/src/mage/cards/a/AlBhedSalvagers.java
Normal file
49
Mage.Sets/src/mage/cards/a/AlBhedSalvagers.java
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.DiesThisOrAnotherTriggeredAbility;
|
||||||
|
import mage.abilities.effects.common.GainLifeEffect;
|
||||||
|
import mage.abilities.effects.common.LoseLifeTargetEffect;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.target.common.TargetOpponent;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AlBhedSalvagers extends CardImpl {
|
||||||
|
|
||||||
|
public AlBhedSalvagers(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.ARTIFICER);
|
||||||
|
this.subtype.add(SubType.WARRIOR);
|
||||||
|
this.power = new MageInt(2);
|
||||||
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
|
// Whenever this creature or another creature or artifact you control dies, target opponent loses 1 life and you gain 1 life.
|
||||||
|
Ability ability = new DiesThisOrAnotherTriggeredAbility(
|
||||||
|
new LoseLifeTargetEffect(1), false,
|
||||||
|
StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE_OR_ARTIFACT
|
||||||
|
);
|
||||||
|
ability.addEffect(new GainLifeEffect(1).concatBy("and"));
|
||||||
|
ability.addTarget(new TargetOpponent());
|
||||||
|
this.addAbility(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AlBhedSalvagers(final AlBhedSalvagers card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlBhedSalvagers copy() {
|
||||||
|
return new AlBhedSalvagers(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,7 +27,7 @@ public final class AlignedHeart extends CardImpl {
|
||||||
// Flurry -- Whenever you cast your second spell each turn, put a rally counter on this enchantment. Then create a 1/1 white Monk creature token with prowess for each rally counter on it.
|
// Flurry -- Whenever you cast your second spell each turn, put a rally counter on this enchantment. Then create a 1/1 white Monk creature token with prowess for each rally counter on it.
|
||||||
Ability ability = new FlurryAbility(new AddCountersSourceEffect(CounterType.RALLY.createInstance()));
|
Ability ability = new FlurryAbility(new AddCountersSourceEffect(CounterType.RALLY.createInstance()));
|
||||||
ability.addEffect(new CreateTokenEffect(new MonasteryMentorToken(), xValue)
|
ability.addEffect(new CreateTokenEffect(new MonasteryMentorToken(), xValue)
|
||||||
.setText("then create a 1/1 white Monk creature token with prowess for each rally counter on it"));
|
.setText("Then create a 1/1 white Monk creature token with prowess for each rally counter on it"));
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
54
Mage.Sets/src/mage/cards/a/AlisaieLeveilleur.java
Normal file
54
Mage.Sets/src/mage/cards/a/AlisaieLeveilleur.java
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.condition.common.YouCastExactOneSpellThisTurnCondition;
|
||||||
|
import mage.abilities.decorator.ConditionalCostModificationEffect;
|
||||||
|
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
|
||||||
|
import mage.abilities.keyword.FirstStrikeAbility;
|
||||||
|
import mage.abilities.keyword.PartnerWithAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AlisaieLeveilleur extends CardImpl {
|
||||||
|
|
||||||
|
public AlisaieLeveilleur(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.ELF);
|
||||||
|
this.subtype.add(SubType.WIZARD);
|
||||||
|
this.power = new MageInt(3);
|
||||||
|
this.toughness = new MageInt(2);
|
||||||
|
|
||||||
|
// Partner with Alphinaud Leveilleur
|
||||||
|
this.addAbility(new PartnerWithAbility("Alphinaud Leveilleur"));
|
||||||
|
|
||||||
|
// First strike
|
||||||
|
this.addAbility(FirstStrikeAbility.getInstance());
|
||||||
|
|
||||||
|
// Dualcast -- The second spell you cast each turn costs {2} less to cast.
|
||||||
|
this.addAbility(new SimpleStaticAbility(new ConditionalCostModificationEffect(
|
||||||
|
new SpellsCostReductionControllerEffect(StaticFilters.FILTER_CARD, 2),
|
||||||
|
YouCastExactOneSpellThisTurnCondition.instance, "the second spell you cast each turn costs {2} less to cast"
|
||||||
|
)).withFlavorWord("Dualcast"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AlisaieLeveilleur(final AlisaieLeveilleur card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlisaieLeveilleur copy() {
|
||||||
|
return new AlisaieLeveilleur(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -4,7 +4,7 @@ import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.effects.common.AttachEffect;
|
import mage.abilities.effects.common.AttachEffect;
|
||||||
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||||
import mage.abilities.effects.common.continuous.SetBasePowerToughnessEnchantedEffect;
|
import mage.abilities.effects.common.continuous.SetBasePowerToughnessAttachedEffect;
|
||||||
import mage.abilities.keyword.EnchantAbility;
|
import mage.abilities.keyword.EnchantAbility;
|
||||||
import mage.abilities.keyword.IndestructibleAbility;
|
import mage.abilities.keyword.IndestructibleAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
|
@ -35,7 +35,7 @@ public final class AlmostPerfect extends CardImpl {
|
||||||
this.addAbility(new EnchantAbility(auraTarget));
|
this.addAbility(new EnchantAbility(auraTarget));
|
||||||
|
|
||||||
// Enchanted creature has base power and toughness 9/10 and has indestructible.
|
// Enchanted creature has base power and toughness 9/10 and has indestructible.
|
||||||
Ability ability = new SimpleStaticAbility(new SetBasePowerToughnessEnchantedEffect(9, 10));
|
Ability ability = new SimpleStaticAbility(new SetBasePowerToughnessAttachedEffect(9, 10, AttachmentType.AURA));
|
||||||
ability.addEffect(new GainAbilityAttachedEffect(
|
ability.addEffect(new GainAbilityAttachedEffect(
|
||||||
IndestructibleAbility.getInstance(), AttachmentType.AURA
|
IndestructibleAbility.getInstance(), AttachmentType.AURA
|
||||||
).setText("and has indestructible"));
|
).setText("and has indestructible"));
|
||||||
|
|
|
||||||
48
Mage.Sets/src/mage/cards/a/AlphinaudLeveilleur.java
Normal file
48
Mage.Sets/src/mage/cards/a/AlphinaudLeveilleur.java
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.common.CastSecondSpellTriggeredAbility;
|
||||||
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
|
import mage.abilities.keyword.PartnerWithAbility;
|
||||||
|
import mage.abilities.keyword.VigilanceAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AlphinaudLeveilleur extends CardImpl {
|
||||||
|
|
||||||
|
public AlphinaudLeveilleur(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.ELF);
|
||||||
|
this.subtype.add(SubType.WIZARD);
|
||||||
|
this.power = new MageInt(2);
|
||||||
|
this.toughness = new MageInt(4);
|
||||||
|
|
||||||
|
// Partner with Alisaie Leveilleur
|
||||||
|
this.addAbility(new PartnerWithAbility("Alisaie Leveilleur"));
|
||||||
|
|
||||||
|
// Vigilance
|
||||||
|
this.addAbility(VigilanceAbility.getInstance());
|
||||||
|
|
||||||
|
// Eukrasia -- Whenever you cast your second spell each turn, draw a card.
|
||||||
|
this.addAbility(new CastSecondSpellTriggeredAbility(new DrawCardSourceControllerEffect(1)).withFlavorWord("Eukrasia"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AlphinaudLeveilleur(final AlphinaudLeveilleur card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AlphinaudLeveilleur copy() {
|
||||||
|
return new AlphinaudLeveilleur(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
144
Mage.Sets/src/mage/cards/a/AltairIbnLaAhad.java
Normal file
144
Mage.Sets/src/mage/cards/a/AltairIbnLaAhad.java
Normal file
|
|
@ -0,0 +1,144 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.AttacksTriggeredAbility;
|
||||||
|
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
|
||||||
|
import mage.abilities.effects.common.ExileTargetEffect;
|
||||||
|
import mage.abilities.keyword.FirstStrikeAbility;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.counters.CounterType;
|
||||||
|
import mage.filter.FilterCard;
|
||||||
|
import mage.filter.common.FilterCreatureCard;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.game.permanent.PermanentCard;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.common.TargetCardInYourGraveyard;
|
||||||
|
import mage.target.targetpointer.FixedTargets;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AltairIbnLaAhad extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterCard filter = new FilterCreatureCard("Assassin creature card from your graveyard");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(SubType.ASSASSIN.getPredicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AltairIbnLaAhad(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}{W}{B}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.ASSASSIN);
|
||||||
|
this.power = new MageInt(3);
|
||||||
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
|
// First strike
|
||||||
|
this.addAbility(FirstStrikeAbility.getInstance());
|
||||||
|
|
||||||
|
// Whenever Altair Ibn-La'Ahad attacks, exile up to one target Assassin creature card from your graveyard with a memory counter on it. Then for each creature card you own in exile with a memory counter on it, create a tapped and attacking token that's a copy of it. Exile those tokens at end of combat.
|
||||||
|
Ability ability = new AttacksTriggeredAbility(new AltairIbnLaAhadExileEffect());
|
||||||
|
ability.addEffect(new AltairIbnLaAhadTokenEffect());
|
||||||
|
ability.addTarget(new TargetCardInYourGraveyard(0, 1, filter));
|
||||||
|
this.addAbility(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AltairIbnLaAhad(final AltairIbnLaAhad card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AltairIbnLaAhad copy() {
|
||||||
|
return new AltairIbnLaAhad(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AltairIbnLaAhadExileEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
AltairIbnLaAhadExileEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "exile up to one target Assassin creature card from your graveyard with a memory counter on it";
|
||||||
|
}
|
||||||
|
|
||||||
|
private AltairIbnLaAhadExileEffect(final AltairIbnLaAhadExileEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AltairIbnLaAhadExileEffect copy() {
|
||||||
|
return new AltairIbnLaAhadExileEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
|
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||||
|
if (player == null || card == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
player.moveCards(card, Zone.EXILED, source, game);
|
||||||
|
card.addCounters(CounterType.MEMORY.createInstance(), source, game);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AltairIbnLaAhadTokenEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
AltairIbnLaAhadTokenEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "Then for each creature card you own in exile with a memory counter on it, " +
|
||||||
|
"create a tapped and attacking token that's a copy of it. Exile those tokens at end of combat";
|
||||||
|
}
|
||||||
|
|
||||||
|
private AltairIbnLaAhadTokenEffect(final AltairIbnLaAhadTokenEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AltairIbnLaAhadTokenEffect copy() {
|
||||||
|
return new AltairIbnLaAhadTokenEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Set<Card> cards = game
|
||||||
|
.getExile()
|
||||||
|
.getAllCards(game, source.getControllerId())
|
||||||
|
.stream()
|
||||||
|
.filter(card -> card.getCounters(game).containsKey(CounterType.MEMORY))
|
||||||
|
.filter(card -> card.isCreature(game))
|
||||||
|
.collect(Collectors.toSet());
|
||||||
|
if (cards.isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Set<Permanent> permanents = new HashSet<>();
|
||||||
|
for (Card card : cards) {
|
||||||
|
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(
|
||||||
|
source.getControllerId(), null,
|
||||||
|
false, 1, true, true
|
||||||
|
);
|
||||||
|
effect.setSavedPermanent(new PermanentCard(card, source.getControllerId(), game));
|
||||||
|
effect.apply(game, source);
|
||||||
|
permanents.addAll(effect.getAddedPermanents());
|
||||||
|
}
|
||||||
|
game.addDelayedTriggeredAbility(new AtTheEndOfCombatDelayedTriggeredAbility(
|
||||||
|
new ExileTargetEffect("exile those tokens")
|
||||||
|
.setTargetPointer(new FixedTargets(permanents, game))
|
||||||
|
), source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -47,7 +47,7 @@ public final class AnafenzaUnyieldingLineage extends CardImpl {
|
||||||
|
|
||||||
// Whenever another nontoken creature you control dies, Anafenza endures 2.
|
// Whenever another nontoken creature you control dies, Anafenza endures 2.
|
||||||
this.addAbility(new DiesCreatureTriggeredAbility(
|
this.addAbility(new DiesCreatureTriggeredAbility(
|
||||||
new EndureSourceEffect(2, "{this}"), true, filter
|
new EndureSourceEffect(2, "{this}"), false, filter
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
113
Mage.Sets/src/mage/cards/a/AncientCellarspawn.java
Normal file
113
Mage.Sets/src/mage/cards/a/AncientCellarspawn.java
Normal file
|
|
@ -0,0 +1,113 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||||
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
|
import mage.abilities.effects.Effect;
|
||||||
|
import mage.abilities.effects.common.LoseLifeTargetEffect;
|
||||||
|
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.filter.FilterCard;
|
||||||
|
import mage.filter.FilterSpell;
|
||||||
|
import mage.filter.predicate.Predicate;
|
||||||
|
import mage.filter.predicate.Predicates;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.stack.Spell;
|
||||||
|
import mage.game.stack.StackObject;
|
||||||
|
import mage.target.common.TargetOpponent;
|
||||||
|
import mage.watchers.common.ManaPaidSourceWatcher;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AncientCellarspawn extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterCard filter
|
||||||
|
= new FilterCard("each spell you cast that's a Demon, Horror, or Nightmare");
|
||||||
|
private static final FilterSpell filter2
|
||||||
|
= new FilterSpell("a spell, if the amount of mana spent to cast it was less than its mana value");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(Predicates.or(
|
||||||
|
SubType.DEMON.getPredicate(),
|
||||||
|
SubType.HORROR.getPredicate(),
|
||||||
|
SubType.NIGHTMARE.getPredicate()
|
||||||
|
));
|
||||||
|
filter2.add(AncientCellarspawnPredicate.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AncientCellarspawn(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "{1}{B}{B}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.HORROR);
|
||||||
|
this.power = new MageInt(3);
|
||||||
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
|
// Each spell you cast that's a Demon, Horror, or Nightmare costs {1} less to cast.
|
||||||
|
this.addAbility(new SimpleStaticAbility(new SpellsCostReductionControllerEffect(filter, 1)));
|
||||||
|
|
||||||
|
// Whenever you cast a spell, if the amount of mana spent to cast it was less than its mana value, target opponent loses life equal to the difference.
|
||||||
|
Ability ability = new SpellCastControllerTriggeredAbility(
|
||||||
|
new LoseLifeTargetEffect(AncientCellarspawnValue.instance)
|
||||||
|
.setText("target opponent loses life equal to the difference"),
|
||||||
|
filter2, false
|
||||||
|
);
|
||||||
|
ability.addTarget(new TargetOpponent());
|
||||||
|
this.addAbility(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
private AncientCellarspawn(final AncientCellarspawn card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AncientCellarspawn copy() {
|
||||||
|
return new AncientCellarspawn(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AncientCellarspawnPredicate implements Predicate<StackObject> {
|
||||||
|
instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(StackObject input, Game game) {
|
||||||
|
return ManaPaidSourceWatcher.getTotalPaid(input.getSourceId(), game) < input.getManaValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AncientCellarspawnValue implements DynamicValue {
|
||||||
|
instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||||
|
return Optional
|
||||||
|
.ofNullable(effect.getValue("spellCast"))
|
||||||
|
.filter(Spell.class::isInstance)
|
||||||
|
.map(Spell.class::cast)
|
||||||
|
.map(spell -> Math.abs(spell.getManaValue() - ManaPaidSourceWatcher.getTotalPaid(spell.getId(), game)))
|
||||||
|
.orElse(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AncientCellarspawnValue copy() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getMessage() {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "1";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,9 +2,8 @@ package mage.cards.a;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.dynamicvalue.DynamicValue;
|
|
||||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||||
import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
|
import mage.abilities.effects.common.AffinityEffect;
|
||||||
import mage.abilities.hint.Hint;
|
import mage.abilities.hint.Hint;
|
||||||
import mage.abilities.hint.ValueHint;
|
import mage.abilities.hint.ValueHint;
|
||||||
import mage.abilities.keyword.FlyingAbility;
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
|
|
@ -13,7 +12,6 @@ import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.filter.FilterPermanent;
|
|
||||||
import mage.filter.common.FilterControlledPermanent;
|
import mage.filter.common.FilterControlledPermanent;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -23,10 +21,8 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
public final class AngelicObserver extends CardImpl {
|
public final class AngelicObserver extends CardImpl {
|
||||||
|
|
||||||
private static final FilterPermanent filter
|
private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.CITIZEN, "Citizens");
|
||||||
= new FilterControlledPermanent(SubType.CITIZEN, "Citizen you control");
|
private static final Hint hint = new ValueHint("Citizens you control", new PermanentsOnBattlefieldCount(filter));
|
||||||
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
|
|
||||||
private static final Hint hint = new ValueHint("Citizens you control", xValue);
|
|
||||||
|
|
||||||
public AngelicObserver(UUID ownerId, CardSetInfo setInfo) {
|
public AngelicObserver(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}");
|
||||||
|
|
@ -37,10 +33,7 @@ public final class AngelicObserver extends CardImpl {
|
||||||
this.toughness = new MageInt(3);
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
// This spell costs {1} less to cast for each Citizen you control.
|
// This spell costs {1} less to cast for each Citizen you control.
|
||||||
this.addAbility(new SimpleStaticAbility(
|
this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).setRuleAtTheTop(true).addHint(hint));
|
||||||
Zone.ALL,
|
|
||||||
new SpellCostReductionForEachSourceEffect(1, xValue).setCanWorksOnStackOnly(true)
|
|
||||||
).setRuleAtTheTop(true).addHint(hint));
|
|
||||||
|
|
||||||
// Flying
|
// Flying
|
||||||
this.addAbility(FlyingAbility.getInstance());
|
this.addAbility(FlyingAbility.getInstance());
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public final class ArashinSunshield extends CardImpl {
|
||||||
|
|
||||||
// When this creature enters, exile up to two target cards from a single graveyard.
|
// When this creature enters, exile up to two target cards from a single graveyard.
|
||||||
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect());
|
Ability ability = new EntersBattlefieldTriggeredAbility(new ExileTargetEffect());
|
||||||
ability.addTarget(new TargetCardInASingleGraveyard(0, 2, StaticFilters.FILTER_CARDS_NON_LAND));
|
ability.addTarget(new TargetCardInASingleGraveyard(0, 2, StaticFilters.FILTER_CARD_CARDS));
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
// {W}, {T}: Tap target creature.
|
// {W}, {T}: Tap target creature.
|
||||||
|
|
|
||||||
|
|
@ -137,7 +137,7 @@ class ArchdruidsCharmMode1Effect extends SearchEffect {
|
||||||
private void setText() {
|
private void setText() {
|
||||||
StringBuilder sb = new StringBuilder();
|
StringBuilder sb = new StringBuilder();
|
||||||
sb.append("Search your library for ");
|
sb.append("Search your library for ");
|
||||||
if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) {
|
if (target.getMinNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) {
|
||||||
sb.append("up to ").append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(' ');
|
sb.append("up to ").append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(' ');
|
||||||
sb.append(target.getTargetName()).append(revealCards ? " and reveal them." : ".");
|
sb.append(target.getTargetName()).append(revealCards ? " and reveal them." : ".");
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
75
Mage.Sets/src/mage/cards/a/ArdbertWarriorOfDarkness.java
Normal file
75
Mage.Sets/src/mage/cards/a/ArdbertWarriorOfDarkness.java
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.ObjectColor;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||||
|
import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
|
||||||
|
import mage.abilities.effects.common.counter.AddCountersAllEffect;
|
||||||
|
import mage.abilities.keyword.MenaceAbility;
|
||||||
|
import mage.abilities.keyword.VigilanceAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Duration;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.counters.CounterType;
|
||||||
|
import mage.filter.FilterSpell;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.filter.predicate.mageobject.ColorPredicate;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class ArdbertWarriorOfDarkness extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterSpell filter = new FilterSpell("a white spell");
|
||||||
|
private static final FilterSpell filter2 = new FilterSpell("a black spell");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(new ColorPredicate(ObjectColor.WHITE));
|
||||||
|
filter2.add(new ColorPredicate(ObjectColor.BLACK));
|
||||||
|
}
|
||||||
|
|
||||||
|
public ArdbertWarriorOfDarkness(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{B}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.SPIRIT);
|
||||||
|
this.subtype.add(SubType.WARRIOR);
|
||||||
|
this.power = new MageInt(2);
|
||||||
|
this.toughness = new MageInt(2);
|
||||||
|
|
||||||
|
// Whenever you cast a white spell, put a +1/+1 counter on each legendary creature you control. They gain vigilance until end of turn.
|
||||||
|
Ability ability = new SpellCastControllerTriggeredAbility(new AddCountersAllEffect(
|
||||||
|
CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE_LEGENDARY
|
||||||
|
), filter, false);
|
||||||
|
ability.addEffect(new GainAbilityAllEffect(
|
||||||
|
VigilanceAbility.getInstance(), Duration.EndOfTurn,
|
||||||
|
StaticFilters.FILTER_CONTROLLED_CREATURE_LEGENDARY
|
||||||
|
).setText("They gain vigilance until end of turn"));
|
||||||
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
// Whenever you cast a black spell, put a +1/+1 counter on each legendary creature you control. They gain menace until end of turn.
|
||||||
|
ability = new SpellCastControllerTriggeredAbility(new AddCountersAllEffect(
|
||||||
|
CounterType.P1P1.createInstance(), StaticFilters.FILTER_CONTROLLED_CREATURE_LEGENDARY
|
||||||
|
), filter2, false);
|
||||||
|
ability.addEffect(new GainAbilityAllEffect(
|
||||||
|
new MenaceAbility(false), Duration.EndOfTurn,
|
||||||
|
StaticFilters.FILTER_CONTROLLED_CREATURE_LEGENDARY
|
||||||
|
).setText("They gain menace until end of turn"));
|
||||||
|
this.addAbility(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArdbertWarriorOfDarkness(final ArdbertWarriorOfDarkness card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArdbertWarriorOfDarkness copy() {
|
||||||
|
return new ArdbertWarriorOfDarkness(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
107
Mage.Sets/src/mage/cards/a/ArdynTheUsurper.java
Normal file
107
Mage.Sets/src/mage/cards/a/ArdynTheUsurper.java
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.ObjectColor;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||||
|
import mage.abilities.keyword.HasteAbility;
|
||||||
|
import mage.abilities.keyword.LifelinkAbility;
|
||||||
|
import mage.abilities.keyword.MenaceAbility;
|
||||||
|
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.common.TargetCardInGraveyard;
|
||||||
|
import mage.target.targetpointer.FixedTarget;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class ArdynTheUsurper extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterPermanent(SubType.DEMON, "Demons");
|
||||||
|
|
||||||
|
public ArdynTheUsurper(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{B}{B}{B}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.ELDER);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.NOBLE);
|
||||||
|
this.power = new MageInt(4);
|
||||||
|
this.toughness = new MageInt(4);
|
||||||
|
|
||||||
|
// Demons you control have menace, lifelink, and haste.
|
||||||
|
Ability ability = new SimpleStaticAbility(new GainAbilityControlledEffect(
|
||||||
|
new MenaceAbility(false), Duration.WhileOnBattlefield, filter
|
||||||
|
));
|
||||||
|
ability.addEffect(new GainAbilityControlledEffect(
|
||||||
|
LifelinkAbility.getInstance(), Duration.WhileOnBattlefield, filter
|
||||||
|
).setText(", lifelink"));
|
||||||
|
ability.addEffect(new GainAbilityControlledEffect(
|
||||||
|
HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter
|
||||||
|
).setText(", and haste"));
|
||||||
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
// Starscourge -- At the beginning of combat on your turn, exile up to one target creature card from a graveyard. If you exiled a card this way, create a token that's a copy of that card, except it's a 5/5 black Demon.
|
||||||
|
ability = new BeginningOfCombatTriggeredAbility(new ArdynTheUsurperEffect());
|
||||||
|
ability.addTarget(new TargetCardInGraveyard(
|
||||||
|
0, 1, StaticFilters.FILTER_CARD_CREATURE_A_GRAVEYARD
|
||||||
|
));
|
||||||
|
this.addAbility(ability.withFlavorWord("Starscourge"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArdynTheUsurper(final ArdynTheUsurper card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArdynTheUsurper copy() {
|
||||||
|
return new ArdynTheUsurper(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ArdynTheUsurperEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
ArdynTheUsurperEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "at the beginning of combat on your turn, exile up to one target creature card from a graveyard. " +
|
||||||
|
"If you exiled a card this way, create a token that's a copy of that card, except it's a 5/5 black Demon";
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArdynTheUsurperEffect(final ArdynTheUsurperEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ArdynTheUsurperEffect copy() {
|
||||||
|
return new ArdynTheUsurperEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
|
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||||
|
if (player == null || card == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
player.moveCards(card, Zone.EXILED, source, game);
|
||||||
|
return new CreateTokenCopyTargetEffect(
|
||||||
|
null, null, false, 1, false,
|
||||||
|
false, null, 5, 5, false
|
||||||
|
).setOnlyColor(ObjectColor.BLACK)
|
||||||
|
.setOnlySubType(SubType.DEMON)
|
||||||
|
.setTargetPointer(new FixedTarget(card, game))
|
||||||
|
.apply(game, source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -2,8 +2,7 @@ package mage.cards.a;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.dynamicvalue.common.CreaturesYouControlCount;
|
import mage.abilities.effects.common.AffinityEffect;
|
||||||
import mage.abilities.effects.common.cost.SpellCostReductionSourceEffect;
|
|
||||||
import mage.abilities.hint.common.CreaturesYouControlHint;
|
import mage.abilities.hint.common.CreaturesYouControlHint;
|
||||||
import mage.abilities.keyword.VigilanceAbility;
|
import mage.abilities.keyword.VigilanceAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
|
@ -11,6 +10,8 @@ import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||||
|
import mage.filter.common.FilterControlledPermanent;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -19,6 +20,8 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
public final class ArgivianPhalanx extends CardImpl {
|
public final class ArgivianPhalanx extends CardImpl {
|
||||||
|
|
||||||
|
static final FilterControlledPermanent filter = new FilterControlledCreaturePermanent("creatures");
|
||||||
|
|
||||||
public ArgivianPhalanx(UUID ownerId, CardSetInfo setInfo) {
|
public ArgivianPhalanx(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}");
|
||||||
|
|
||||||
|
|
@ -29,11 +32,7 @@ public final class ArgivianPhalanx extends CardImpl {
|
||||||
this.toughness = new MageInt(4);
|
this.toughness = new MageInt(4);
|
||||||
|
|
||||||
// This spell costs {1} less to cast for each creature you control.
|
// This spell costs {1} less to cast for each creature you control.
|
||||||
this.addAbility(new SimpleStaticAbility(
|
this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(CreaturesYouControlHint.instance));
|
||||||
Zone.ALL,
|
|
||||||
new SpellCostReductionSourceEffect(CreaturesYouControlCount.instance)
|
|
||||||
.setText("This spell costs {1} less to cast for each creature you control.")
|
|
||||||
).addHint(CreaturesYouControlHint.instance));
|
|
||||||
|
|
||||||
// Vigilance
|
// Vigilance
|
||||||
this.addAbility(VigilanceAbility.getInstance());
|
this.addAbility(VigilanceAbility.getInstance());
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,8 @@ import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import mage.target.common.TargetCreaturePermanentAmount;
|
import mage.filter.StaticFilters;
|
||||||
|
import mage.target.common.TargetPermanentAmount;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -31,7 +32,7 @@ public final class ArmamentDragon extends CardImpl {
|
||||||
|
|
||||||
// When this creature enters, distribute three +1/+1 counters among one, two, or three target creatures you control.
|
// When this creature enters, distribute three +1/+1 counters among one, two, or three target creatures you control.
|
||||||
Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect(CounterType.P1P1));
|
Ability ability = new EntersBattlefieldTriggeredAbility(new DistributeCountersEffect(CounterType.P1P1));
|
||||||
ability.addTarget(new TargetCreaturePermanentAmount(3));
|
ability.addTarget(new TargetPermanentAmount(3, 1, StaticFilters.FILTER_CONTROLLED_CREATURES));
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
86
Mage.Sets/src/mage/cards/a/AuronVeneratedGuardian.java
Normal file
86
Mage.Sets/src/mage/cards/a/AuronVeneratedGuardian.java
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.MageObject;
|
||||||
|
import mage.abilities.common.AttacksTriggeredAbility;
|
||||||
|
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
|
||||||
|
import mage.abilities.costs.common.PutCountersSourceCost;
|
||||||
|
import mage.abilities.effects.common.DoWhenCostPaid;
|
||||||
|
import mage.abilities.effects.common.ExileUntilSourceLeavesEffect;
|
||||||
|
import mage.abilities.keyword.VigilanceAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.counters.CounterType;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
|
import mage.filter.predicate.ObjectSourcePlayer;
|
||||||
|
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||||
|
import mage.filter.predicate.permanent.DefendingPlayerControlsSourceAttackingPredicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.target.TargetPermanent;
|
||||||
|
|
||||||
|
import java.util.Optional;
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AuronVeneratedGuardian extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter
|
||||||
|
= new FilterCreaturePermanent("creature defending player controls with power less than {this}'s power");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(DefendingPlayerControlsSourceAttackingPredicate.instance);
|
||||||
|
filter.add(AuronVeneratedGuardianPredicate.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public AuronVeneratedGuardian(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.SPIRIT);
|
||||||
|
this.subtype.add(SubType.SAMURAI);
|
||||||
|
this.power = new MageInt(2);
|
||||||
|
this.toughness = new MageInt(5);
|
||||||
|
|
||||||
|
// Vigilance
|
||||||
|
this.addAbility(VigilanceAbility.getInstance());
|
||||||
|
|
||||||
|
// Shooting Star -- Whenever Auron attacks, put a +1/+1 counter on it. When you do, exile target creature defending player controls with power less than Auron's power until Auron leaves the battlefield.
|
||||||
|
ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(new ExileUntilSourceLeavesEffect(), false);
|
||||||
|
ability.addTarget(new TargetPermanent(filter));
|
||||||
|
this.addAbility(new AttacksTriggeredAbility(new DoWhenCostPaid(
|
||||||
|
ability, new PutCountersSourceCost(CounterType.P1P1.createInstance())
|
||||||
|
.setText("put a +1/+1 counter on it"), "", false
|
||||||
|
)).withFlavorWord("Shooting Star"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuronVeneratedGuardian(final AuronVeneratedGuardian card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuronVeneratedGuardian copy() {
|
||||||
|
return new AuronVeneratedGuardian(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum AuronVeneratedGuardianPredicate implements ObjectSourcePlayerPredicate<Permanent> {
|
||||||
|
instance;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
|
||||||
|
return Optional
|
||||||
|
.ofNullable(input.getSource().getSourcePermanentOrLKI(game))
|
||||||
|
.map(MageObject::getPower)
|
||||||
|
.map(MageInt::getValue)
|
||||||
|
.map(x -> input.getObject().getPower().getValue() < x)
|
||||||
|
.orElse(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
40
Mage.Sets/src/mage/cards/a/AuronsInspiration.java
Normal file
40
Mage.Sets/src/mage/cards/a/AuronsInspiration.java
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
import mage.abilities.effects.common.continuous.BoostAllEffect;
|
||||||
|
import mage.abilities.keyword.FlashbackAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Duration;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AuronsInspiration extends CardImpl {
|
||||||
|
|
||||||
|
public AuronsInspiration(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}");
|
||||||
|
|
||||||
|
// Attacking creatures get +2/+0 until end of turn.
|
||||||
|
this.getSpellAbility().addEffect(new BoostAllEffect(
|
||||||
|
2, 0, Duration.EndOfTurn,
|
||||||
|
StaticFilters.FILTER_ATTACKING_CREATURES, false
|
||||||
|
));
|
||||||
|
|
||||||
|
// Flashback {2}{W}{W}
|
||||||
|
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{2}{W}{W}")));
|
||||||
|
}
|
||||||
|
|
||||||
|
private AuronsInspiration(final AuronsInspiration card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AuronsInspiration copy() {
|
||||||
|
return new AuronsInspiration(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -47,7 +47,7 @@ public final class AutonSoldier extends CardImpl {
|
||||||
Zone.ALL,
|
Zone.ALL,
|
||||||
new EntersBattlefieldEffect(new CopyPermanentEffect(
|
new EntersBattlefieldEffect(new CopyPermanentEffect(
|
||||||
StaticFilters.FILTER_PERMANENT_CREATURE, applier
|
StaticFilters.FILTER_PERMANENT_CREATURE, applier
|
||||||
).setText("You may have {this} enter the battlefield as a copy of any creature on the battlefield, " +
|
).setText("You may have {this} enter as a copy of any creature on the battlefield, " +
|
||||||
"except it isn't legendary, is an artifact in addition to its other types, and has myriad"), "", true))
|
"except it isn't legendary, is an artifact in addition to its other types, and has myriad"), "", true))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
98
Mage.Sets/src/mage/cards/a/AvalancheOfSector7.java
Normal file
98
Mage.Sets/src/mage/cards/a/AvalancheOfSector7.java
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
package mage.cards.a;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.TriggeredAbilityImpl;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
|
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||||
|
import mage.abilities.effects.common.DamageTargetEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect;
|
||||||
|
import mage.abilities.keyword.MenaceAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterArtifactPermanent;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.events.GameEvent;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.target.targetpointer.FixedTarget;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class AvalancheOfSector7 extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterArtifactPermanent("artifacts your opponents control");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(TargetController.OPPONENT.getControllerPredicate());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
|
||||||
|
|
||||||
|
public AvalancheOfSector7(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.REBEL);
|
||||||
|
this.power = new MageInt(0);
|
||||||
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
|
// Menace
|
||||||
|
this.addAbility(new MenaceAbility());
|
||||||
|
|
||||||
|
// Avalanche of Sector 7's power is equal to the number of artifacts your opponents control.
|
||||||
|
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetBasePowerSourceEffect(xValue)));
|
||||||
|
|
||||||
|
// Whenever an opponent activates an ability of an artifact they control, Avalanche of Sector 7 deals 1 damage to that player.
|
||||||
|
this.addAbility(new AvalancheOfSector7TriggeredAbility());
|
||||||
|
}
|
||||||
|
|
||||||
|
private AvalancheOfSector7(final AvalancheOfSector7 card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AvalancheOfSector7 copy() {
|
||||||
|
return new AvalancheOfSector7(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AvalancheOfSector7TriggeredAbility extends TriggeredAbilityImpl {
|
||||||
|
|
||||||
|
AvalancheOfSector7TriggeredAbility() {
|
||||||
|
super(Zone.BATTLEFIELD, new DamageTargetEffect(1, true, "that player", true));
|
||||||
|
setTriggerPhrase("Whenever an opponent activates an ability of an artifact they control, ");
|
||||||
|
}
|
||||||
|
|
||||||
|
private AvalancheOfSector7TriggeredAbility(final AvalancheOfSector7TriggeredAbility ability) {
|
||||||
|
super(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public AvalancheOfSector7TriggeredAbility copy() {
|
||||||
|
return new AvalancheOfSector7TriggeredAbility(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkEventType(GameEvent event, Game game) {
|
||||||
|
return event.getType() == GameEvent.EventType.ACTIVATED_ABILITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean checkTrigger(GameEvent event, Game game) {
|
||||||
|
if (!game.getOpponents(getControllerId()).contains(event.getPlayerId())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||||
|
if (permanent == null || !permanent.isArtifact(game) || !permanent.isControlledBy(event.getPlayerId())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
this.getEffects().setTargetPointer(new FixedTarget(event.getPlayerId()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,11 @@
|
||||||
|
|
||||||
package mage.cards.a;
|
package mage.cards.a;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.Mana;
|
import mage.Mana;
|
||||||
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||||
|
import mage.abilities.hint.Hint;
|
||||||
|
import mage.abilities.hint.ValueHint;
|
||||||
import mage.abilities.keyword.DefenderAbility;
|
import mage.abilities.keyword.DefenderAbility;
|
||||||
import mage.abilities.mana.DynamicManaAbility;
|
import mage.abilities.mana.DynamicManaAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
|
|
@ -15,18 +16,22 @@ import mage.filter.FilterPermanent;
|
||||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Plopman
|
* @author Plopman
|
||||||
*/
|
*/
|
||||||
public final class AxebaneGuardian extends CardImpl {
|
public final class AxebaneGuardian extends CardImpl {
|
||||||
|
|
||||||
private static final FilterPermanent filter = new FilterControlledCreaturePermanent("creatures with defender you control");
|
private static final FilterPermanent filter = new FilterControlledCreaturePermanent();
|
||||||
|
|
||||||
static {
|
static {
|
||||||
filter.add(new AbilityPredicate(DefenderAbility.class));
|
filter.add(new AbilityPredicate(DefenderAbility.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
|
||||||
|
private static final Hint hint = new ValueHint("Creatures you control with defender", xValue);
|
||||||
|
|
||||||
public AxebaneGuardian(UUID ownerId, CardSetInfo setInfo) {
|
public AxebaneGuardian(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}");
|
||||||
this.subtype.add(SubType.HUMAN);
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
|
@ -39,8 +44,10 @@ public final class AxebaneGuardian extends CardImpl {
|
||||||
this.addAbility(DefenderAbility.getInstance());
|
this.addAbility(DefenderAbility.getInstance());
|
||||||
|
|
||||||
// {tap}: Add X mana in any combination of colors, where X is the number of creatures with defender you control.
|
// {tap}: Add X mana in any combination of colors, where X is the number of creatures with defender you control.
|
||||||
this.addAbility(new DynamicManaAbility(new Mana(0, 0, 0, 0,0, 0,1, 0), new PermanentsOnBattlefieldCount(filter),
|
this.addAbility(new DynamicManaAbility(
|
||||||
"Add X mana in any combination of colors, where X is the number of creatures with defender you control."));
|
Mana.AnyMana(1), xValue, "Add X mana in any combination of colors, " +
|
||||||
|
"where X is the number of creatures you control with defender."
|
||||||
|
).addHint(hint));
|
||||||
}
|
}
|
||||||
|
|
||||||
private AxebaneGuardian(final AxebaneGuardian card) {
|
private AxebaneGuardian(final AxebaneGuardian card) {
|
||||||
|
|
|
||||||
|
|
@ -163,7 +163,7 @@ class AzorTheLawbringerAttacksEffect extends OneShotEffect {
|
||||||
if (controller != null) {
|
if (controller != null) {
|
||||||
ManaCosts cost = new ManaCostsImpl<>("{X}{W}{U}{U}");
|
ManaCosts cost = new ManaCostsImpl<>("{X}{W}{U}{U}");
|
||||||
if (controller.chooseUse(Outcome.Damage, "Pay " + cost.getText() + "? If you do, you gain X life and draw X cards.", source, game)) {
|
if (controller.chooseUse(Outcome.Damage, "Pay " + cost.getText() + "? If you do, you gain X life and draw X cards.", source, game)) {
|
||||||
int costX = controller.announceXMana(0, Integer.MAX_VALUE, "Announce the value for {X}", game, source);
|
int costX = controller.announceX(0, Integer.MAX_VALUE, "Announce the value for {X} (gain life and draw cards)", game, source,true);
|
||||||
cost.add(new GenericManaCost(costX));
|
cost.add(new GenericManaCost(costX));
|
||||||
if (cost.pay(source, game, source, source.getControllerId(), false, null)) {
|
if (cost.pay(source, game, source, source.getControllerId(), false, null)) {
|
||||||
controller.resetStoredBookmark(game); // otherwise you can undo the payment
|
controller.resetStoredBookmark(game); // otherwise you can undo the payment
|
||||||
|
|
|
||||||
|
|
@ -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());
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -30,6 +31,7 @@ public final class BanesInvoker extends CardImpl {
|
||||||
// Wind Walk — {8}: Up to two target creatures each get +2/+2 and gain flying until end of turn.
|
// Wind Walk — {8}: Up to two target creatures each get +2/+2 and gain flying until end of turn.
|
||||||
Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(2, 2).setText("up to two target creatures each get +2/+2"), new GenericManaCost(8));
|
Ability ability = new SimpleActivatedAbility(new BoostTargetEffect(2, 2).setText("up to two target creatures each get +2/+2"), new GenericManaCost(8));
|
||||||
ability.addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance()).setText("and gain flying until end of turn"));
|
ability.addEffect(new GainAbilityTargetEffect(FlyingAbility.getInstance()).setText("and gain flying until end of turn"));
|
||||||
|
ability.addTarget(new TargetCreaturePermanent(0, 2));
|
||||||
this.addAbility(ability.withFlavorWord("Wind Walk"));
|
this.addAbility(ability.withFlavorWord("Wind Walk"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
43
Mage.Sets/src/mage/cards/b/BarbaraWright.java
Normal file
43
Mage.Sets/src/mage/cards/b/BarbaraWright.java
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.common.SagaAbility;
|
||||||
|
import mage.abilities.keyword.DoctorsCompanionAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class BarbaraWright extends CardImpl {
|
||||||
|
|
||||||
|
public BarbaraWright(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.ADVISOR);
|
||||||
|
this.power = new MageInt(1);
|
||||||
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
|
// History Teacher -- Sagas you control have read ahead.
|
||||||
|
this.addAbility(SagaAbility.makeGainReadAheadAbility().withFlavorWord("History Teacher"));
|
||||||
|
|
||||||
|
// Doctor's companion
|
||||||
|
this.addAbility(DoctorsCompanionAbility.getInstance());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BarbaraWright(final BarbaraWright card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BarbaraWright copy() {
|
||||||
|
return new BarbaraWright(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
88
Mage.Sets/src/mage/cards/b/BarbflareGremlin.java
Normal file
88
Mage.Sets/src/mage/cards/b/BarbflareGremlin.java
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.TapForManaAllTriggeredManaAbility;
|
||||||
|
import mage.abilities.condition.common.SourceTappedCondition;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.mana.AddManaOfAnyTypeProducedEffect;
|
||||||
|
import mage.abilities.keyword.FirstStrikeAbility;
|
||||||
|
import mage.abilities.keyword.HasteAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.SetTargetPointer;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterLandPermanent;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.players.Player;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class BarbflareGremlin extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterLandPermanent("a player taps a land");
|
||||||
|
|
||||||
|
public BarbflareGremlin(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.GREMLIN);
|
||||||
|
this.power = new MageInt(3);
|
||||||
|
this.toughness = new MageInt(2);
|
||||||
|
|
||||||
|
// First strike
|
||||||
|
this.addAbility(FirstStrikeAbility.getInstance());
|
||||||
|
|
||||||
|
// Haste
|
||||||
|
this.addAbility(HasteAbility.getInstance());
|
||||||
|
|
||||||
|
// Whenever a player taps a land for mana, if Barbflare Gremlin is tapped, that player adds one mana of any type that land produced. Then that land deals 1 damage to that player.
|
||||||
|
Ability ability = new TapForManaAllTriggeredManaAbility(
|
||||||
|
new AddManaOfAnyTypeProducedEffect(), filter, SetTargetPointer.PERMANENT
|
||||||
|
).withInterveningIf(SourceTappedCondition.TAPPED);
|
||||||
|
ability.addEffect(new BarbflareGremlinEffect());
|
||||||
|
this.addAbility(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BarbflareGremlin(final BarbflareGremlin card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BarbflareGremlin copy() {
|
||||||
|
return new BarbflareGremlin(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BarbflareGremlinEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
BarbflareGremlinEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "Then that land deals 1 damage to that player";
|
||||||
|
}
|
||||||
|
|
||||||
|
private BarbflareGremlinEffect(final BarbflareGremlinEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BarbflareGremlinEffect copy() {
|
||||||
|
return new BarbflareGremlinEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Permanent permanent = (Permanent) getValue("tappedPermanent");
|
||||||
|
if (permanent == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Player player = game.getPlayer(permanent.getControllerId());
|
||||||
|
return player != null && player.damage(1, permanent.getId(), source, game) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
99
Mage.Sets/src/mage/cards/b/BarretAvalancheLeader.java
Normal file
99
Mage.Sets/src/mage/cards/b/BarretAvalancheLeader.java
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
|
import mage.abilities.keyword.ReachAbility;
|
||||||
|
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterControlledPermanent;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.game.permanent.token.RebelRedToken;
|
||||||
|
import mage.target.TargetPermanent;
|
||||||
|
import mage.target.targetpointer.EachTargetPointer;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class BarretAvalancheLeader extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.EQUIPMENT);
|
||||||
|
private static final FilterPermanent filter2 = new FilterControlledPermanent(SubType.REBEL);
|
||||||
|
|
||||||
|
public BarretAvalancheLeader(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{G}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.REBEL);
|
||||||
|
this.power = new MageInt(4);
|
||||||
|
this.toughness = new MageInt(4);
|
||||||
|
|
||||||
|
// Reach
|
||||||
|
this.addAbility(ReachAbility.getInstance());
|
||||||
|
|
||||||
|
// Avalanche! -- Whenever an Equipment you control enters, create a 2/2 red Rebel creature token.
|
||||||
|
this.addAbility(new EntersBattlefieldAllTriggeredAbility(
|
||||||
|
new CreateTokenEffect(new RebelRedToken()), filter
|
||||||
|
).withFlavorWord("Avalanche!"));
|
||||||
|
|
||||||
|
// At the beginning of combat on your turn, attach up to one target Equipment you control to target Rebel you control.
|
||||||
|
Ability ability = new BeginningOfCombatTriggeredAbility(new BarretAvalancheLeaderEffect());
|
||||||
|
ability.addTarget(new TargetPermanent(0, 1, filter));
|
||||||
|
ability.addTarget(new TargetPermanent(filter2));
|
||||||
|
this.addAbility(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BarretAvalancheLeader(final BarretAvalancheLeader card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BarretAvalancheLeader copy() {
|
||||||
|
return new BarretAvalancheLeader(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BarretAvalancheLeaderEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
BarretAvalancheLeaderEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "attach up to one target Equipment you control to target Rebel you control";
|
||||||
|
this.setTargetPointer(new EachTargetPointer());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BarretAvalancheLeaderEffect(final BarretAvalancheLeaderEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BarretAvalancheLeaderEffect copy() {
|
||||||
|
return new BarretAvalancheLeaderEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
List<Permanent> permanents = this
|
||||||
|
.getTargetPointer()
|
||||||
|
.getTargets(game, source)
|
||||||
|
.stream()
|
||||||
|
.map(game::getPermanent)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
return permanents.size() >= 2 && permanents.get(1).addAttachment(permanents.get(0).getId(), source, game);
|
||||||
|
}
|
||||||
|
}
|
||||||
65
Mage.Sets/src/mage/cards/b/BarretWallace.java
Normal file
65
Mage.Sets/src/mage/cards/b/BarretWallace.java
Normal file
|
|
@ -0,0 +1,65 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.common.AttacksTriggeredAbility;
|
||||||
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
|
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||||
|
import mage.abilities.effects.common.DamageTargetEffect;
|
||||||
|
import mage.abilities.hint.Hint;
|
||||||
|
import mage.abilities.hint.ValueHint;
|
||||||
|
import mage.abilities.keyword.ReachAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.SetTargetPointer;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||||
|
import mage.filter.predicate.permanent.EquippedPredicate;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class BarretWallace extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter
|
||||||
|
= new FilterControlledCreaturePermanent("equipped creatures you control");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(EquippedPredicate.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
|
||||||
|
private static final Hint hint = new ValueHint("Equipped creatures you control", xValue);
|
||||||
|
|
||||||
|
public BarretWallace(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.REBEL);
|
||||||
|
this.power = new MageInt(4);
|
||||||
|
this.toughness = new MageInt(4);
|
||||||
|
|
||||||
|
// Reach
|
||||||
|
this.addAbility(ReachAbility.getInstance());
|
||||||
|
|
||||||
|
// Whenever Barret Wallace attacks, it deals damage equal to the number of equipped creatures you control to defending player.
|
||||||
|
this.addAbility(new AttacksTriggeredAbility(
|
||||||
|
new DamageTargetEffect(xValue, true, "it", true),
|
||||||
|
false, null, SetTargetPointer.PLAYER
|
||||||
|
).withRuleTextReplacement(true).addHint(hint));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BarretWallace(final BarretWallace card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BarretWallace copy() {
|
||||||
|
return new BarretWallace(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -7,11 +7,12 @@ import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
import mage.target.common.TargetCreaturePermanentSameController;
|
import mage.target.common.TargetPermanentSameController;
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
@ -29,7 +30,7 @@ public final class BarrinsSpite extends CardImpl {
|
||||||
|
|
||||||
// Choose two target creatures controlled by the same player. Their controller chooses and sacrifices one of them. Return the other to its owner's hand.
|
// Choose two target creatures controlled by the same player. Their controller chooses and sacrifices one of them. Return the other to its owner's hand.
|
||||||
this.getSpellAbility().addEffect(new BarrinsSpiteEffect());
|
this.getSpellAbility().addEffect(new BarrinsSpiteEffect());
|
||||||
this.getSpellAbility().addTarget(new TargetCreaturePermanentSameController(2));
|
this.getSpellAbility().addTarget(new TargetPermanentSameController(StaticFilters.FILTER_PERMANENT_CREATURES));
|
||||||
}
|
}
|
||||||
|
|
||||||
private BarrinsSpite(final BarrinsSpite card) {
|
private BarrinsSpite(final BarrinsSpite card) {
|
||||||
|
|
|
||||||
93
Mage.Sets/src/mage/cards/b/BartzAndBoko.java
Normal file
93
Mage.Sets/src/mage/cards/b/BartzAndBoko.java
Normal file
|
|
@ -0,0 +1,93 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.AffinityEffect;
|
||||||
|
import mage.abilities.hint.Hint;
|
||||||
|
import mage.abilities.hint.ValueHint;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterControlledPermanent;
|
||||||
|
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.target.common.TargetOpponentsCreaturePermanent;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class BartzAndBoko extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterControlledPermanent filter = new FilterControlledPermanent(SubType.BIRD, "Birds");
|
||||||
|
private static final Hint hint = new ValueHint("Birds you control", new PermanentsOnBattlefieldCount(filter));
|
||||||
|
|
||||||
|
public BartzAndBoko(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.BIRD);
|
||||||
|
this.power = new MageInt(4);
|
||||||
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
|
// Affinity for Birds
|
||||||
|
this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).setRuleAtTheTop(true).addHint(hint));
|
||||||
|
|
||||||
|
// When Bartz and Boko enters, each other Bird you control deals damage equal to its power to target creature an opponent controls.
|
||||||
|
Ability ability = new EntersBattlefieldTriggeredAbility(new BartzAndBokoEffect());
|
||||||
|
ability.addTarget(new TargetOpponentsCreaturePermanent());
|
||||||
|
this.addAbility(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BartzAndBoko(final BartzAndBoko card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BartzAndBoko copy() {
|
||||||
|
return new BartzAndBoko(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BartzAndBokoEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.BIRD);
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(AnotherPredicate.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
BartzAndBokoEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "each other Bird you control deals damage equal to its power to target creature an opponent controls";
|
||||||
|
}
|
||||||
|
|
||||||
|
private BartzAndBokoEffect(final BartzAndBokoEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BartzAndBokoEffect copy() {
|
||||||
|
return new BartzAndBokoEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||||
|
if (permanent == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
for (Permanent bird : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source, game)) {
|
||||||
|
permanent.damage(bird.getPower().getValue(), bird.getId(), source, game);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
89
Mage.Sets/src/mage/cards/b/BeatrixLoyalGeneral.java
Normal file
89
Mage.Sets/src/mage/cards/b/BeatrixLoyalGeneral.java
Normal file
|
|
@ -0,0 +1,89 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.keyword.VigilanceAbility;
|
||||||
|
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterControlledPermanent;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.TargetPermanent;
|
||||||
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class BeatrixLoyalGeneral extends CardImpl {
|
||||||
|
|
||||||
|
public BeatrixLoyalGeneral(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}{W}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.SOLDIER);
|
||||||
|
this.power = new MageInt(4);
|
||||||
|
this.toughness = new MageInt(4);
|
||||||
|
|
||||||
|
// Vigilance
|
||||||
|
this.addAbility(VigilanceAbility.getInstance());
|
||||||
|
|
||||||
|
// At the beginning of combat on your turn, you may attach any number of Equipment you control to target creature you control.
|
||||||
|
Ability ability = new BeginningOfCombatTriggeredAbility(new BeatrixLoyalGeneralEffect());
|
||||||
|
ability.addTarget(new TargetControlledCreaturePermanent());
|
||||||
|
this.addAbility(ability);
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeatrixLoyalGeneral(final BeatrixLoyalGeneral card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BeatrixLoyalGeneral copy() {
|
||||||
|
return new BeatrixLoyalGeneral(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BeatrixLoyalGeneralEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.EQUIPMENT);
|
||||||
|
|
||||||
|
BeatrixLoyalGeneralEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "you may attach any number of Equipment you control to target creature you control";
|
||||||
|
}
|
||||||
|
|
||||||
|
private BeatrixLoyalGeneralEffect(final BeatrixLoyalGeneralEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BeatrixLoyalGeneralEffect copy() {
|
||||||
|
return new BeatrixLoyalGeneralEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
|
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||||
|
if (player == null || permanent == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
TargetPermanent target = new TargetPermanent(0, Integer.MAX_VALUE, filter, true);
|
||||||
|
player.choose(outcome, target, source, game);
|
||||||
|
for (UUID targetId : target.getTargets()) {
|
||||||
|
permanent.addAttachment(targetId, source, game);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -40,7 +40,7 @@ public final class BecomeTheAvalanche extends CardImpl {
|
||||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue));
|
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(xValue));
|
||||||
this.getSpellAbility().addEffect(new BoostControlledEffect(
|
this.getSpellAbility().addEffect(new BoostControlledEffect(
|
||||||
CardsInControllerHandCount.ANY, CardsInControllerHandCount.ANY, Duration.EndOfTurn
|
CardsInControllerHandCount.ANY, CardsInControllerHandCount.ANY, Duration.EndOfTurn
|
||||||
));
|
).setText("Then creatures you control get +X/+X until end of turn, where X is the number of cards in your hand"));
|
||||||
this.getSpellAbility().addHint(hint);
|
this.getSpellAbility().addHint(hint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.costs.CostAdjuster;
|
import mage.abilities.costs.CostAdjuster;
|
||||||
import mage.abilities.costs.mana.GenericManaCost;
|
import mage.abilities.costs.mana.GenericManaCost;
|
||||||
import mage.abilities.effects.common.continuous.SetBasePowerToughnessEnchantedEffect;
|
import mage.abilities.effects.common.continuous.SetBasePowerToughnessAttachedEffect;
|
||||||
import mage.abilities.keyword.EquipAbility;
|
import mage.abilities.keyword.EquipAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.AttachmentType;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
|
|
@ -30,8 +31,9 @@ public final class BeltOfGiantStrength extends CardImpl {
|
||||||
this.subtype.add(SubType.EQUIPMENT);
|
this.subtype.add(SubType.EQUIPMENT);
|
||||||
|
|
||||||
// Equipped creature has base power and toughness 10/10.
|
// Equipped creature has base power and toughness 10/10.
|
||||||
this.addAbility(new SimpleStaticAbility(new SetBasePowerToughnessEnchantedEffect(10, 10)
|
this.addAbility(new SimpleStaticAbility(
|
||||||
.setText("equipped creature has base power and toughness 10/10")));
|
new SetBasePowerToughnessAttachedEffect(10, 10, AttachmentType.EQUIPMENT)
|
||||||
|
));
|
||||||
|
|
||||||
// Equip {10}. This ability costs {X} less to activate where X is the power of the creature it targets.
|
// Equip {10}. This ability costs {X} less to activate where X is the power of the creature it targets.
|
||||||
EquipAbility ability = new EquipAbility(Outcome.BoostCreature, new GenericManaCost(10), new TargetControlledCreaturePermanent(), false);
|
EquipAbility ability = new EquipAbility(Outcome.BoostCreature, new GenericManaCost(10), new TargetControlledCreaturePermanent(), false);
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
package mage.cards.b;
|
package mage.cards.b;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageObject;
|
import mage.MageObject;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.condition.Condition;
|
import mage.abilities.condition.Condition;
|
||||||
|
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||||
import mage.abilities.dynamicvalue.DynamicValue;
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
import mage.abilities.effects.Effect;
|
import mage.abilities.effects.Effect;
|
||||||
import mage.abilities.effects.OneShotEffect;
|
import mage.abilities.effects.OneShotEffect;
|
||||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
|
import mage.abilities.effects.common.UntapAllEffect;
|
||||||
import mage.abilities.hint.Hint;
|
import mage.abilities.hint.Hint;
|
||||||
import mage.abilities.hint.ValueHint;
|
import mage.abilities.hint.ValueHint;
|
||||||
import mage.abilities.keyword.FlyingAbility;
|
import mage.abilities.keyword.FlyingAbility;
|
||||||
|
|
@ -21,19 +21,19 @@ import mage.constants.Outcome;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.SuperType;
|
import mage.constants.SuperType;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.common.FilterCreaturePermanent;
|
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author androosss
|
* @author androosss
|
||||||
*/
|
*/
|
||||||
public final class BetorKinToAll extends CardImpl {
|
public final class BetorKinToAll extends CardImpl {
|
||||||
|
|
||||||
private static final Hint hint = new ValueHint(
|
private static final Hint hint = new ValueHint(
|
||||||
"Total toughness of creatures you control", ControlledCreaturesToughnessValue.instance);
|
"Total toughness of creatures you control", ControlledCreaturesToughnessValue.instance
|
||||||
|
);
|
||||||
|
|
||||||
public BetorKinToAll(UUID ownerId, CardSetInfo setInfo) {
|
public BetorKinToAll(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}{G}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{B}{G}");
|
||||||
|
|
@ -47,15 +47,18 @@ public final class BetorKinToAll extends CardImpl {
|
||||||
// Flying
|
// Flying
|
||||||
this.addAbility(FlyingAbility.getInstance());
|
this.addAbility(FlyingAbility.getInstance());
|
||||||
|
|
||||||
// At the beginning of your end step, if creatures you control have total
|
// At the beginning of your end step, if creatures you control have total toughness 10 or greater, draw a card. Then if creatures you control have total toughness 20 or greater, untap each creature you control. Then if creatures you control have total toughness 40 or greater, each opponent loses half their life, rounded up.
|
||||||
// toughness 10 or greater, draw a card. Then if creatures you control have
|
Ability ability = new BeginningOfEndStepTriggeredAbility(new DrawCardSourceControllerEffect(1))
|
||||||
// total toughness 20 or greater, untap each creature you control. Then if
|
.withInterveningIf(BetorKinToAllCondition.TEN);
|
||||||
// creatures you control have total toughness 40 or greater, each opponent loses
|
ability.addEffect(new ConditionalOneShotEffect(
|
||||||
// half their life, rounded up.
|
new UntapAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE), BetorKinToAllCondition.TWENTY,
|
||||||
Ability betorAbility = new BeginningOfEndStepTriggeredAbility(new DrawCardSourceControllerEffect(1))
|
"Then if creatures you control have total toughness 20 or greater, untap each creature you control"
|
||||||
.withInterveningIf(BetorKinToAllCondition.instance).addHint(hint);
|
));
|
||||||
betorAbility.addEffect(new BetorKinToAllEffect());
|
ability.addEffect(new ConditionalOneShotEffect(
|
||||||
this.addAbility(betorAbility);
|
new BetorKinToAllEffect(), BetorKinToAllCondition.FORTY, "Then if creatures you control " +
|
||||||
|
"have total toughness 40 or greater, each opponent loses half their life, rounded up"
|
||||||
|
));
|
||||||
|
this.addAbility(ability.addHint(hint));
|
||||||
}
|
}
|
||||||
|
|
||||||
private BetorKinToAll(final BetorKinToAll card) {
|
private BetorKinToAll(final BetorKinToAll card) {
|
||||||
|
|
@ -69,22 +72,30 @@ public final class BetorKinToAll extends CardImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
enum BetorKinToAllCondition implements Condition {
|
enum BetorKinToAllCondition implements Condition {
|
||||||
instance;
|
TEN(10),
|
||||||
|
TWENTY(20),
|
||||||
|
FORTY(40);
|
||||||
|
private final int amount;
|
||||||
|
|
||||||
|
private BetorKinToAllCondition(int amount) {
|
||||||
|
this.amount = amount;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
return ControlledCreaturesToughnessValue.instance.calculate(game, source, null) >= 10;
|
return ControlledCreaturesToughnessValue.instance.calculate(game, source, null) >= amount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "creatures you control have total toughness " + amount + " or greater";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class BetorKinToAllEffect extends OneShotEffect {
|
class BetorKinToAllEffect extends OneShotEffect {
|
||||||
|
|
||||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
|
|
||||||
|
|
||||||
BetorKinToAllEffect() {
|
BetorKinToAllEffect() {
|
||||||
super(Outcome.Benefit);
|
super(Outcome.Benefit);
|
||||||
this.staticText = "Then if creatures you control have total toughness 20 or greater, untap each creature you control. Then if creatures you control have total toughness 40 or greater, each opponent loses half their life, rounded up.";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BetorKinToAllEffect(final BetorKinToAllEffect effect) {
|
private BetorKinToAllEffect(final BetorKinToAllEffect effect) {
|
||||||
|
|
@ -98,36 +109,12 @@ class BetorKinToAllEffect extends OneShotEffect {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean apply(Game game, Ability source) {
|
public boolean apply(Game game, Ability source) {
|
||||||
Player controller = game.getPlayer(source.getControllerId());
|
for (UUID playerId : game.getOpponents(source.getControllerId())) {
|
||||||
if (controller == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
int sumToughness = ControlledCreaturesToughnessValue.instance.calculate(game, source, null);
|
|
||||||
|
|
||||||
if (sumToughness < 20) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(),
|
|
||||||
game)) {
|
|
||||||
permanent.untap(game);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sumToughness < 40) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (UUID playerId : game.getOpponents(controller.getId())) {
|
|
||||||
Player opponent = game.getPlayer(playerId);
|
Player opponent = game.getPlayer(playerId);
|
||||||
if (opponent == null) {
|
if (opponent != null) {
|
||||||
continue;
|
opponent.loseLife(opponent.getLife() / 2 + opponent.getLife() % 2, game, source, false);
|
||||||
}
|
|
||||||
int amount = (int) Math.ceil(opponent.getLife() / 2f);
|
|
||||||
if (amount > 0) {
|
|
||||||
opponent.loseLife(amount, game, source, false);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,48 +1,38 @@
|
||||||
package mage.cards.b;
|
package mage.cards.b;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.MageItem;
|
|
||||||
import mage.abilities.Ability;
|
|
||||||
import mage.abilities.Abilities;
|
import mage.abilities.Abilities;
|
||||||
import mage.abilities.AbilitiesImpl;
|
import mage.abilities.AbilitiesImpl;
|
||||||
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.Mode;
|
import mage.abilities.Mode;
|
||||||
import mage.abilities.common.ActivateAbilityTriggeredAbility;
|
import mage.abilities.common.ActivateAbilityTriggeredAbility;
|
||||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||||
import mage.abilities.effects.common.CopyStackObjectEffect;
|
import mage.abilities.effects.common.CopyStackObjectEffect;
|
||||||
import mage.abilities.meta.OrTriggeredAbility;
|
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.constants.SuperType;
|
|
||||||
import mage.abilities.keyword.DoctorsCompanionAbility;
|
import mage.abilities.keyword.DoctorsCompanionAbility;
|
||||||
|
import mage.abilities.meta.OrTriggeredAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.*;
|
||||||
import mage.constants.SetTargetPointer;
|
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.filter.common.FilterInstantOrSorcerySpell;
|
|
||||||
import mage.filter.FilterSpell;
|
import mage.filter.FilterSpell;
|
||||||
import mage.filter.FilterStackObject;
|
import mage.filter.FilterStackObject;
|
||||||
|
import mage.filter.common.FilterInstantOrSorcerySpell;
|
||||||
import mage.filter.predicate.ObjectSourcePlayer;
|
import mage.filter.predicate.ObjectSourcePlayer;
|
||||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.stack.StackObject;
|
|
||||||
import mage.game.stack.Spell;
|
import mage.game.stack.Spell;
|
||||||
|
import mage.game.stack.StackObject;
|
||||||
import mage.target.Target;
|
import mage.target.Target;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author padfoot
|
* @author padfoot
|
||||||
*/
|
*/
|
||||||
public final class BillPotts extends CardImpl {
|
public final class BillPotts extends CardImpl {
|
||||||
|
|
||||||
private static final FilterSpell filterInstantOrSorcery = new FilterInstantOrSorcerySpell("an instant or sorcery that targets only {this}");
|
private static final FilterSpell filterInstantOrSorcery = new FilterInstantOrSorcerySpell("an instant or sorcery spell that targets only {this}");
|
||||||
private static final FilterStackObject filterAbility = new FilterStackObject("an ability that targets only {this}");
|
private static final FilterStackObject filterAbility = new FilterStackObject("an ability that targets only {this}");
|
||||||
|
|
||||||
static {
|
static {
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ class MoveCounterFromTargetToTargetEffect extends OneShotEffect {
|
||||||
}
|
}
|
||||||
int amountCounters = fromPermanent.getCounters(game).getCount(CounterType.P1P1);
|
int amountCounters = fromPermanent.getCounters(game).getCount(CounterType.P1P1);
|
||||||
if (amountCounters > 0) {
|
if (amountCounters > 0) {
|
||||||
int amountToMove = controller.getAmount(0, amountCounters, "Choose how many counters to move", game);
|
int amountToMove = controller.getAmount(0, amountCounters, "Choose how many counters to move", source, game);
|
||||||
if (amountToMove > 0) {
|
if (amountToMove > 0) {
|
||||||
fromPermanent.removeCounters(CounterType.P1P1.createInstance(amountToMove), source, game);
|
fromPermanent.removeCounters(CounterType.P1P1.createInstance(amountToMove), source, game);
|
||||||
toPermanent.addCounters(CounterType.P1P1.createInstance(amountToMove), source.getControllerId(), source, game);
|
toPermanent.addCounters(CounterType.P1P1.createInstance(amountToMove), source.getControllerId(), source, game);
|
||||||
|
|
|
||||||
62
Mage.Sets/src/mage/cards/b/BlackMagesRod.java
Normal file
62
Mage.Sets/src/mage/cards/b/BlackMagesRod.java
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||||
|
import mage.abilities.effects.common.DamagePlayersEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||||
|
import mage.constants.AttachmentType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.abilities.keyword.EquipAbility;
|
||||||
|
import mage.abilities.keyword.JobSelectAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.TargetController;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author balazskristof
|
||||||
|
*/
|
||||||
|
public final class BlackMagesRod extends CardImpl {
|
||||||
|
|
||||||
|
public BlackMagesRod(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}{B}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.EQUIPMENT);
|
||||||
|
|
||||||
|
// Job select
|
||||||
|
this.addAbility(new JobSelectAbility());
|
||||||
|
|
||||||
|
// Equipped creature gets +1/+0, has "Whenever you cast a noncreature spell, this creature deals 1 damage to each opponent," and is a Wizard in addition to its other types.
|
||||||
|
Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(1, 0));
|
||||||
|
ability.addEffect(new GainAbilityAttachedEffect(
|
||||||
|
new SpellCastControllerTriggeredAbility(
|
||||||
|
new DamagePlayersEffect(1, TargetController.OPPONENT, "this creature"),
|
||||||
|
StaticFilters.FILTER_SPELL_A_NON_CREATURE, false
|
||||||
|
),
|
||||||
|
AttachmentType.EQUIPMENT
|
||||||
|
).setText(", has \"Whenever you cast a noncreature spell, this creature deals 1 damage to each opponent,\""));
|
||||||
|
ability.addEffect(new AddCardSubtypeAttachedEffect(
|
||||||
|
SubType.WIZARD,
|
||||||
|
AttachmentType.EQUIPMENT
|
||||||
|
).setText("is a Wizard in addition to its other types").concatBy("and"));
|
||||||
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
// Equip {3}
|
||||||
|
this.addAbility(new EquipAbility(3));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlackMagesRod(final BlackMagesRod card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlackMagesRod copy() {
|
||||||
|
return new BlackMagesRod(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
package mage.cards.b;
|
package mage.cards.b;
|
||||||
|
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
|
||||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.condition.common.MyTurnCondition;
|
import mage.abilities.condition.common.MyTurnCondition;
|
||||||
|
|
@ -14,6 +13,7 @@ import mage.abilities.keyword.ClassLevelAbility;
|
||||||
import mage.abilities.keyword.ClassReminderAbility;
|
import mage.abilities.keyword.ClassReminderAbility;
|
||||||
import mage.abilities.keyword.DoubleStrikeAbility;
|
import mage.abilities.keyword.DoubleStrikeAbility;
|
||||||
import mage.abilities.keyword.HasteAbility;
|
import mage.abilities.keyword.HasteAbility;
|
||||||
|
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
|
|
@ -74,7 +74,7 @@ public final class BlacksmithsTalent extends CardImpl {
|
||||||
DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter2
|
DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter2
|
||||||
), MyTurnCondition.instance, "during your turn, equipped creatures you control have double strike"));
|
), MyTurnCondition.instance, "during your turn, equipped creatures you control have double strike"));
|
||||||
ability.addEffect(new ConditionalContinuousEffect(new GainAbilityControlledEffect(
|
ability.addEffect(new ConditionalContinuousEffect(new GainAbilityControlledEffect(
|
||||||
HasteAbility.getInstance(), Duration.WhileOnBattlefield
|
HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter2
|
||||||
), MyTurnCondition.instance, "and haste"));
|
), MyTurnCondition.instance, "and haste"));
|
||||||
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 3)));
|
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 3)));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public final class BlightPile extends CardImpl {
|
||||||
filter.add(new AbilityPredicate(DefenderAbility.class));
|
filter.add(new AbilityPredicate(DefenderAbility.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
|
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null);
|
||||||
private static final Hint hint = new ValueHint("Creatures with defender you control", xValue);
|
private static final Hint hint = new ValueHint("Creatures with defender you control", xValue);
|
||||||
|
|
||||||
public BlightPile(UUID ownerId, CardSetInfo setInfo) {
|
public BlightPile(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
|
@ -47,7 +47,11 @@ public final class BlightPile extends CardImpl {
|
||||||
this.addAbility(DefenderAbility.getInstance());
|
this.addAbility(DefenderAbility.getInstance());
|
||||||
|
|
||||||
// {2}{B}, {T}: Each opponent loses X life, where X is the number of creatures with defender you control.
|
// {2}{B}, {T}: Each opponent loses X life, where X is the number of creatures with defender you control.
|
||||||
Ability ability = new SimpleActivatedAbility(new LoseLifeOpponentsEffect(xValue), new ManaCostsImpl<>("{2}{B}"));
|
Ability ability = new SimpleActivatedAbility(
|
||||||
|
new LoseLifeOpponentsEffect(xValue)
|
||||||
|
.setText("each opponent loses X life, where X is the number of creatures with defender you control"),
|
||||||
|
new ManaCostsImpl<>("{2}{B}")
|
||||||
|
);
|
||||||
ability.addCost(new TapSourceCost());
|
ability.addCost(new TapSourceCost());
|
||||||
this.addAbility(ability.addHint(hint));
|
this.addAbility(ability.addHint(hint));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
97
Mage.Sets/src/mage/cards/b/Blink.java
Normal file
97
Mage.Sets/src/mage/cards/b/Blink.java
Normal file
|
|
@ -0,0 +1,97 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.SagaAbility;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.CreateTokenEffect;
|
||||||
|
import mage.abilities.effects.keyword.InvestigateEffect;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.SagaChapter;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.game.permanent.Permanent;
|
||||||
|
import mage.game.permanent.token.AlienAngelToken;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class Blink extends CardImpl {
|
||||||
|
|
||||||
|
public Blink(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}{B}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.SAGA);
|
||||||
|
|
||||||
|
// (As this Saga enters and after your draw step, add a lore counter. Sacrifice after IV.)
|
||||||
|
SagaAbility sagaAbility = new SagaAbility(this, SagaChapter.CHAPTER_IV);
|
||||||
|
|
||||||
|
// I, III -- Choose target creature. Its owner shuffles it into their library, then investigates.
|
||||||
|
sagaAbility.addChapterEffect(
|
||||||
|
this, SagaChapter.CHAPTER_I,
|
||||||
|
new BlinkEffect(), new TargetCreaturePermanent()
|
||||||
|
);
|
||||||
|
sagaAbility.addChapterEffect(
|
||||||
|
this, SagaChapter.CHAPTER_III,
|
||||||
|
new BlinkEffect(), new TargetCreaturePermanent()
|
||||||
|
);
|
||||||
|
|
||||||
|
// II, IV -- Create a 2/2 black Alien Angel artifact creature token with first strike, vigilance, and "Whenever an opponent casts a creature spell, this permanent isn't a creature until end of turn."
|
||||||
|
sagaAbility.addChapterEffect(
|
||||||
|
this, SagaChapter.CHAPTER_II,
|
||||||
|
new CreateTokenEffect(new AlienAngelToken())
|
||||||
|
);
|
||||||
|
sagaAbility.addChapterEffect(
|
||||||
|
this, SagaChapter.CHAPTER_IV,
|
||||||
|
new CreateTokenEffect(new AlienAngelToken())
|
||||||
|
);
|
||||||
|
this.addAbility(sagaAbility);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Blink(final Blink card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Blink copy() {
|
||||||
|
return new Blink(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlinkEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
BlinkEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "choose target creature. Its owner shuffles it into their library, then investigates";
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlinkEffect(final BlinkEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlinkEffect copy() {
|
||||||
|
return new BlinkEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||||
|
if (permanent == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Player player = game.getPlayer(permanent.getOwnerId());
|
||||||
|
if (player == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
player.shuffleCardsToLibrary(permanent, game, source);
|
||||||
|
InvestigateEffect.doInvestigate(player.getId(), 1, game, source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
108
Mage.Sets/src/mage/cards/b/BlueMagesCane.java
Normal file
108
Mage.Sets/src/mage/cards/b/BlueMagesCane.java
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
import mage.ApprovingObject;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.SpellAbility;
|
||||||
|
import mage.abilities.common.AttacksTriggeredAbility;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.AddCardSubtypeAttachedEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||||
|
import mage.abilities.keyword.EquipAbility;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.abilities.keyword.JobSelectAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.filter.common.FilterInstantOrSorceryCard;
|
||||||
|
import mage.filter.predicate.card.DefendingPlayerOwnsCardPredicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.common.TargetCardInGraveyard;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author balazskristof
|
||||||
|
*/
|
||||||
|
public final class BlueMagesCane extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterInstantOrSorceryCard filter = new FilterInstantOrSorceryCard(
|
||||||
|
"instant or sorcery card from defending player's graveyard"
|
||||||
|
);
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(DefendingPlayerOwnsCardPredicate.instance);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BlueMagesCane(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{U}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.EQUIPMENT);
|
||||||
|
|
||||||
|
// Job select
|
||||||
|
this.addAbility(new JobSelectAbility());
|
||||||
|
|
||||||
|
// Equipped creature gets +0/+2, is a Wizard in addition to its other types, and has "Whenever this creature attacks, exile up to one target instant or sorcery card from defending player's graveyard. If you do, copy it. You may cast the copy by paying {3} rather than paying its mana cost."
|
||||||
|
Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(0, 2));
|
||||||
|
ability.addEffect(new AddCardSubtypeAttachedEffect(SubType.WIZARD, AttachmentType.EQUIPMENT)
|
||||||
|
.setText(", is a Wizard in addition to its other types")
|
||||||
|
);
|
||||||
|
Ability attackAbility = new AttacksTriggeredAbility(new BlueMagesCaneEffect());
|
||||||
|
attackAbility.addTarget(new TargetCardInGraveyard(0, 1, filter));
|
||||||
|
ability.addEffect(new GainAbilityAttachedEffect(attackAbility, AttachmentType.EQUIPMENT)
|
||||||
|
.setText("and has \"Whenever this creature attacks, exile up to one target instant or sorcery card from defending player's graveyard. "
|
||||||
|
+ "If you do, copy it. You may cast the copy by paying {3} rather than paying its mana cost.\"")
|
||||||
|
);
|
||||||
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
// Spirit of the Whalaqee -- Equip {2}
|
||||||
|
this.addAbility(new EquipAbility(2).withFlavorWord("Spirit of the Whalaqee"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlueMagesCane(final BlueMagesCane card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlueMagesCane copy() {
|
||||||
|
return new BlueMagesCane(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BlueMagesCaneEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
BlueMagesCaneEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "exile up to one target instant or sorcery card from defending player's graveyard. "
|
||||||
|
+ "If you do, copy it. You may cast the copy by paying {3} rather than paying its mana cost.";
|
||||||
|
}
|
||||||
|
|
||||||
|
private BlueMagesCaneEffect(final BlueMagesCaneEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BlueMagesCaneEffect copy() {
|
||||||
|
return new BlueMagesCaneEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player controller = game.getPlayer(source.getControllerId());
|
||||||
|
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||||
|
if (controller == null || card == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
controller.moveCards(card, Zone.EXILED, source, game);
|
||||||
|
Card copiedCard = game.copyCard(card, source, source.getControllerId());
|
||||||
|
if (!controller.chooseUse(Outcome.Benefit, "Cast " + copiedCard.getName() + " by paying {3}?", source, game)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
CardUtil.castSingle(controller, source, game, copiedCard, new ManaCostsImpl<>("{3}"));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -48,7 +48,7 @@ public final class BoneDevourer extends CardImpl {
|
||||||
|
|
||||||
// When this creature dies, you draw X cards and you lose X life, where X is the number of +1/+1 counters on it.
|
// When this creature dies, you draw X cards and you lose X life, where X is the number of +1/+1 counters on it.
|
||||||
Ability ability = new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(xValue).setText("you draw X cards"));
|
Ability ability = new DiesSourceTriggeredAbility(new DrawCardSourceControllerEffect(xValue).setText("you draw X cards"));
|
||||||
ability.addEffect(new LoseLifeSourceControllerEffect(xValue));
|
ability.addEffect(new LoseLifeSourceControllerEffect(xValue).setText("and you lose X life, where X is the number of +1/+1 counters on it"));
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ package mage.cards.b;
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.Ability;
|
import mage.abilities.Ability;
|
||||||
import mage.abilities.common.SimpleActivatedAbility;
|
import mage.abilities.common.SimpleActivatedAbility;
|
||||||
import mage.abilities.common.SpellCastOpponentNoManaSpentTriggeredAbility;
|
import mage.abilities.common.SpellCastOpponentTriggeredAbility;
|
||||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||||
import mage.abilities.effects.common.CounterTargetEffect;
|
import mage.abilities.effects.common.CounterTargetEffect;
|
||||||
import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
|
import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
|
||||||
|
|
@ -12,16 +12,12 @@ import mage.abilities.keyword.IndestructibleAbility;
|
||||||
import mage.abilities.keyword.VigilanceAbility;
|
import mage.abilities.keyword.VigilanceAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.*;
|
||||||
import mage.constants.Duration;
|
|
||||||
import mage.constants.SubType;
|
|
||||||
import mage.constants.SuperType;
|
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author Susucr
|
* @author Susucr
|
||||||
*/
|
*/
|
||||||
public final class BoromirWardenOfTheTower extends CardImpl {
|
public final class BoromirWardenOfTheTower extends CardImpl {
|
||||||
|
|
@ -39,7 +35,10 @@ public final class BoromirWardenOfTheTower extends CardImpl {
|
||||||
this.addAbility(VigilanceAbility.getInstance());
|
this.addAbility(VigilanceAbility.getInstance());
|
||||||
|
|
||||||
// Whenever an opponent casts a spell, if no mana was spent to cast it, counter that spell.
|
// Whenever an opponent casts a spell, if no mana was spent to cast it, counter that spell.
|
||||||
this.addAbility(new SpellCastOpponentNoManaSpentTriggeredAbility(new CounterTargetEffect().setText("counter that spell")));
|
this.addAbility(new SpellCastOpponentTriggeredAbility(
|
||||||
|
Zone.BATTLEFIELD, new CounterTargetEffect(),
|
||||||
|
StaticFilters.FILTER_SPELL_NO_MANA_SPENT, false, true
|
||||||
|
));
|
||||||
|
|
||||||
// Sacrifice Boromir, Warden of the Tower: Creatures you control gain indestructible until end of turn. The Ring tempts you.
|
// Sacrifice Boromir, Warden of the Tower: Creatures you control gain indestructible until end of turn. The Ring tempts you.
|
||||||
Ability ability = new SimpleActivatedAbility(new GainAbilityAllEffect(
|
Ability ability = new SimpleActivatedAbility(new GainAbilityAllEffect(
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package mage.cards.b;
|
||||||
|
|
||||||
import mage.abilities.Mode;
|
import mage.abilities.Mode;
|
||||||
import mage.abilities.effects.common.DamageTargetEffect;
|
import mage.abilities.effects.common.DamageTargetEffect;
|
||||||
import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
|
|
||||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||||
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
import mage.abilities.effects.common.continuous.GainAbilityTargetEffect;
|
||||||
import mage.abilities.keyword.DoubleStrikeAbility;
|
import mage.abilities.keyword.DoubleStrikeAbility;
|
||||||
|
|
@ -11,6 +10,7 @@ import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
import mage.target.common.TargetPlayerOrPlaneswalker;
|
import mage.target.common.TargetPlayerOrPlaneswalker;
|
||||||
|
|
||||||
|
|
@ -27,10 +27,12 @@ public final class BorosCharm extends CardImpl {
|
||||||
//Choose one - Boros Charm deals 4 damage to target player
|
//Choose one - Boros Charm deals 4 damage to target player
|
||||||
this.getSpellAbility().addEffect(new DamageTargetEffect(4));
|
this.getSpellAbility().addEffect(new DamageTargetEffect(4));
|
||||||
this.getSpellAbility().addTarget(new TargetPlayerOrPlaneswalker());
|
this.getSpellAbility().addTarget(new TargetPlayerOrPlaneswalker());
|
||||||
|
|
||||||
//or permanents you control are indestructible this turn
|
//or permanents you control are indestructible this turn
|
||||||
this.getSpellAbility().addMode(new Mode(new GainAbilityControlledEffect(
|
this.getSpellAbility().addMode(new Mode(new GainAbilityControlledEffect(
|
||||||
IndestructibleAbility.getInstance(), Duration.EndOfTurn
|
IndestructibleAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENTS
|
||||||
)));
|
)));
|
||||||
|
|
||||||
//or target creature gains double strike until end of turn.
|
//or target creature gains double strike until end of turn.
|
||||||
Mode mode2 = new Mode(new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn));
|
Mode mode2 = new Mode(new GainAbilityTargetEffect(DoubleStrikeAbility.getInstance(), Duration.EndOfTurn));
|
||||||
mode2.addTarget(new TargetCreaturePermanent());
|
mode2.addTarget(new TargetCreaturePermanent());
|
||||||
|
|
|
||||||
61
Mage.Sets/src/mage/cards/b/BraskasFinalAeon.java
Normal file
61
Mage.Sets/src/mage/cards/b/BraskasFinalAeon.java
Normal file
|
|
@ -0,0 +1,61 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.common.SagaAbility;
|
||||||
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
|
import mage.abilities.effects.common.SacrificeOpponentsEffect;
|
||||||
|
import mage.abilities.effects.common.discard.DiscardEachPlayerEffect;
|
||||||
|
import mage.abilities.keyword.MenaceAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class BraskasFinalAeon extends CardImpl {
|
||||||
|
|
||||||
|
public BraskasFinalAeon(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT, CardType.CREATURE}, "");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.SAGA);
|
||||||
|
this.subtype.add(SubType.NIGHTMARE);
|
||||||
|
this.power = new MageInt(7);
|
||||||
|
this.toughness = new MageInt(7);
|
||||||
|
this.nightCard = true;
|
||||||
|
this.color.setBlack(true);
|
||||||
|
|
||||||
|
// (As this Saga enters and after your draw step, add a lore counter. Sacrifice after III.)
|
||||||
|
SagaAbility sagaAbility = new SagaAbility(this);
|
||||||
|
|
||||||
|
// I, II -- Jecht Beam -- Each opponent discards a card and you draw a card.
|
||||||
|
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, ability -> {
|
||||||
|
ability.addEffect(new DiscardEachPlayerEffect(TargetController.OPPONENT));
|
||||||
|
ability.addEffect(new DrawCardSourceControllerEffect(1).concatBy("and you"));
|
||||||
|
ability.withFlavorWord("Jecht Beam");
|
||||||
|
});
|
||||||
|
|
||||||
|
// III -- Ultimate Jecht Shot -- Each opponent sacrifices two creatures of their choice.
|
||||||
|
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, ability -> {
|
||||||
|
ability.addEffect(new SacrificeOpponentsEffect(2, StaticFilters.FILTER_PERMANENT_CREATURES));
|
||||||
|
ability.withFlavorWord("Ultimate Jecht Shot");
|
||||||
|
});
|
||||||
|
this.addAbility(sagaAbility);
|
||||||
|
|
||||||
|
// Menace
|
||||||
|
this.addAbility(new MenaceAbility());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BraskasFinalAeon(final BraskasFinalAeon card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BraskasFinalAeon copy() {
|
||||||
|
return new BraskasFinalAeon(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -108,7 +108,7 @@ class BrenardGingerSculptorEffect extends OneShotEffect {
|
||||||
effect.setBecomesArtifact(true);
|
effect.setBecomesArtifact(true);
|
||||||
effect.withAdditionalSubType(SubType.FOOD);
|
effect.withAdditionalSubType(SubType.FOOD);
|
||||||
effect.withAdditionalSubType(SubType.GOLEM);
|
effect.withAdditionalSubType(SubType.GOLEM);
|
||||||
effect.addAdditionalAbilities(new FoodAbility(false));
|
effect.addAdditionalAbilities(new FoodAbility());
|
||||||
|
|
||||||
player.moveCards(card, Zone.EXILED, source, game);
|
player.moveCards(card, Zone.EXILED, source, game);
|
||||||
effect.apply(game, source);
|
effect.apply(game, source);
|
||||||
|
|
|
||||||
|
|
@ -2,15 +2,16 @@ package mage.cards.b;
|
||||||
|
|
||||||
import mage.MageInt;
|
import mage.MageInt;
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.dynamicvalue.DynamicValue;
|
|
||||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||||
import mage.abilities.effects.common.cost.SpellCostReductionForEachSourceEffect;
|
import mage.abilities.effects.common.AffinityEffect;
|
||||||
|
import mage.abilities.hint.Hint;
|
||||||
import mage.abilities.hint.ValueHint;
|
import mage.abilities.hint.ValueHint;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
|
import mage.filter.common.FilterControlledEnchantmentPermanent;
|
||||||
import mage.filter.common.FilterControlledPermanent;
|
import mage.filter.common.FilterControlledPermanent;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -20,13 +21,8 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
public final class BrineGiant extends CardImpl {
|
public final class BrineGiant extends CardImpl {
|
||||||
|
|
||||||
static final FilterControlledPermanent filter = new FilterControlledPermanent("enchantment you control");
|
static final FilterControlledPermanent filter = new FilterControlledEnchantmentPermanent("enchantments");
|
||||||
|
private static final Hint hint = new ValueHint("Enchantments you control", new PermanentsOnBattlefieldCount(filter));
|
||||||
static {
|
|
||||||
filter.add(CardType.ENCHANTMENT.getPredicate());
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
|
|
||||||
|
|
||||||
public BrineGiant(UUID ownerId, CardSetInfo setInfo) {
|
public BrineGiant(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{U}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{6}{U}");
|
||||||
|
|
@ -36,9 +32,7 @@ public final class BrineGiant extends CardImpl {
|
||||||
this.toughness = new MageInt(6);
|
this.toughness = new MageInt(6);
|
||||||
|
|
||||||
// This spell costs {1} less to cast for each enchantment you control.
|
// This spell costs {1} less to cast for each enchantment you control.
|
||||||
this.addAbility(new SimpleStaticAbility(
|
this.addAbility(new SimpleStaticAbility(Zone.ALL, new AffinityEffect(filter)).addHint(hint));
|
||||||
Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)
|
|
||||||
).addHint(new ValueHint("Enchantments you control", xValue)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private BrineGiant(final BrineGiant card) {
|
private BrineGiant(final BrineGiant card) {
|
||||||
|
|
|
||||||
|
|
@ -69,8 +69,7 @@ enum BroodcallerScourgePredicate implements ObjectSourcePlayerPredicate<Card> {
|
||||||
.getManaValue()
|
.getManaValue()
|
||||||
<= CardUtil
|
<= CardUtil
|
||||||
.getEffectValueFromAbility(
|
.getEffectValueFromAbility(
|
||||||
input.getSource(), "damage",
|
input.getSource(), "damage", Integer.class
|
||||||
Integer.class, 0
|
).orElse(0);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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);
|
||||||
|
|
|
||||||
66
Mage.Sets/src/mage/cards/b/BugenhagenWiseElder.java
Normal file
66
Mage.Sets/src/mage/cards/b/BugenhagenWiseElder.java
Normal file
|
|
@ -0,0 +1,66 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.condition.Condition;
|
||||||
|
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
|
||||||
|
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||||
|
import mage.abilities.hint.ConditionHint;
|
||||||
|
import mage.abilities.hint.Hint;
|
||||||
|
import mage.abilities.keyword.ReachAbility;
|
||||||
|
import mage.abilities.mana.AnyColorManaAbility;
|
||||||
|
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.ComparisonType;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.constants.SuperType;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||||
|
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class BugenhagenWiseElder extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter
|
||||||
|
= new FilterControlledCreaturePermanent("you control a creature with power 7 or greater");
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 7));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Condition condition = new PermanentsOnTheBattlefieldCondition(filter);
|
||||||
|
private static final Hint hint = new ConditionHint(condition, "You control a creature with power 7 or greater");
|
||||||
|
|
||||||
|
public BugenhagenWiseElder(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{G}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.HUMAN);
|
||||||
|
this.subtype.add(SubType.SHAMAN);
|
||||||
|
this.power = new MageInt(1);
|
||||||
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
|
// Reach
|
||||||
|
this.addAbility(ReachAbility.getInstance());
|
||||||
|
|
||||||
|
// At the beginning of your upkeep, if you control a creature with power 7 or greater, draw a card.
|
||||||
|
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new DrawCardSourceControllerEffect(1)).withInterveningIf(condition).addHint(hint));
|
||||||
|
|
||||||
|
// {T}: Add one mana of any color.
|
||||||
|
this.addAbility(new AnyColorManaAbility());
|
||||||
|
}
|
||||||
|
|
||||||
|
private BugenhagenWiseElder(final BugenhagenWiseElder card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BugenhagenWiseElder copy() {
|
||||||
|
return new BugenhagenWiseElder(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
|
||||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||||
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
||||||
import mage.abilities.keyword.FirstStrikeAbility;
|
import mage.abilities.keyword.FirstStrikeAbility;
|
||||||
|
import mage.constants.Duration;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
|
|
@ -37,7 +38,7 @@ public final class BullRushBruiser extends CardImpl {
|
||||||
|
|
||||||
// Whenever Bull-Rush Bruiser attacks, if your team controls another Warrior, Bull-Rush Bruiser gains first strike until end of turn.
|
// Whenever Bull-Rush Bruiser attacks, if your team controls another Warrior, Bull-Rush Bruiser gains first strike until end of turn.
|
||||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
||||||
new AttacksTriggeredAbility(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance()), false),
|
new AttacksTriggeredAbility(new GainAbilitySourceEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn), false),
|
||||||
new PermanentsOnTheBattlefieldCondition(filter),
|
new PermanentsOnTheBattlefieldCondition(filter),
|
||||||
"Whenever {this} attacks, if your team controls another Warrior, "
|
"Whenever {this} attacks, if your team controls another Warrior, "
|
||||||
+ "{this} gains first strike until end of turn."
|
+ "{this} gains first strike until end of turn."
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,9 @@ import mage.constants.CardType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
import mage.constants.SubType;
|
import mage.constants.SubType;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.common.FilterCreaturePermanent;
|
||||||
|
import mage.filter.predicate.permanent.CounterAnyPredicate;
|
||||||
import mage.target.common.TargetCreaturePermanent;
|
import mage.target.common.TargetCreaturePermanent;
|
||||||
|
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
@ -25,6 +28,12 @@ import java.util.UUID;
|
||||||
*/
|
*/
|
||||||
public final class BulwarkOx extends CardImpl {
|
public final class BulwarkOx extends CardImpl {
|
||||||
|
|
||||||
|
private static final FilterPermanent filter = new FilterCreaturePermanent();
|
||||||
|
|
||||||
|
static {
|
||||||
|
filter.add(CounterAnyPredicate.instance);
|
||||||
|
}
|
||||||
|
|
||||||
public BulwarkOx(UUID ownerId, CardSetInfo setInfo) {
|
public BulwarkOx(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
|
||||||
|
|
||||||
|
|
@ -40,10 +49,10 @@ public final class BulwarkOx extends CardImpl {
|
||||||
|
|
||||||
// Sacrifice this creature: Creatures you control with counters on them gain hexproof and indestructible until end of turn.
|
// Sacrifice this creature: Creatures you control with counters on them gain hexproof and indestructible until end of turn.
|
||||||
ability = new SimpleActivatedAbility(new GainAbilityControlledEffect(
|
ability = new SimpleActivatedAbility(new GainAbilityControlledEffect(
|
||||||
HexproofAbility.getInstance(), Duration.EndOfTurn
|
HexproofAbility.getInstance(), Duration.EndOfTurn, filter
|
||||||
).setText("creatures you control with counters on them gain hexproof"), new SacrificeSourceCost());
|
).setText("creatures you control with counters on them gain hexproof"), new SacrificeSourceCost());
|
||||||
ability.addEffect(new GainAbilityControlledEffect(
|
ability.addEffect(new GainAbilityControlledEffect(
|
||||||
IndestructibleAbility.getInstance(), Duration.EndOfTurn
|
IndestructibleAbility.getInstance(), Duration.EndOfTurn, filter
|
||||||
).setText("and indestructible until end of turn"));
|
).setText("and indestructible until end of turn"));
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
85
Mage.Sets/src/mage/cards/b/BusterSword.java
Normal file
85
Mage.Sets/src/mage/cards/b/BusterSword.java
Normal file
|
|
@ -0,0 +1,85 @@
|
||||||
|
package mage.cards.b;
|
||||||
|
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.DealsCombatDamageEquippedTriggeredAbility;
|
||||||
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.DrawCardTargetEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
|
||||||
|
import mage.abilities.keyword.EquipAbility;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.cards.CardsImpl;
|
||||||
|
import mage.constants.CardType;
|
||||||
|
import mage.constants.ComparisonType;
|
||||||
|
import mage.constants.Outcome;
|
||||||
|
import mage.constants.SubType;
|
||||||
|
import mage.filter.FilterCard;
|
||||||
|
import mage.filter.predicate.mageobject.ManaValuePredicate;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class BusterSword extends CardImpl {
|
||||||
|
|
||||||
|
public BusterSword(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
|
||||||
|
|
||||||
|
this.subtype.add(SubType.EQUIPMENT);
|
||||||
|
|
||||||
|
// Equipped creature gets +3/+2.
|
||||||
|
this.addAbility(new SimpleStaticAbility(new BoostEquippedEffect(3, 2)));
|
||||||
|
|
||||||
|
// Whenever equipped creature deals combat damage to a player, draw a card, then you may cast a spell from your hand with mana value less than or equal to that damage without paying its mana cost.
|
||||||
|
Ability ability = new DealsCombatDamageEquippedTriggeredAbility(new DrawCardTargetEffect(1));
|
||||||
|
ability.addEffect(new BusterSwordEffect());
|
||||||
|
this.addAbility(ability);
|
||||||
|
|
||||||
|
// Equip {2}
|
||||||
|
this.addAbility(new EquipAbility(2));
|
||||||
|
}
|
||||||
|
|
||||||
|
private BusterSword(final BusterSword card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BusterSword copy() {
|
||||||
|
return new BusterSword(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class BusterSwordEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
BusterSwordEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = ", then you may cast a spell from your hand with mana value " +
|
||||||
|
"less than or equal to that damage without paying its mana cost";
|
||||||
|
}
|
||||||
|
|
||||||
|
private BusterSwordEffect(final BusterSwordEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BusterSwordEffect copy() {
|
||||||
|
return new BusterSwordEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
|
if (player == null || player.getHand().isEmpty()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
int damage = (Integer) getValue("damage");
|
||||||
|
FilterCard filter = new FilterCard();
|
||||||
|
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, damage + 1));
|
||||||
|
return CardUtil.castSpellWithAttributesForFree(player, source, game, new CardsImpl(player.getHand()), filter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -58,7 +58,7 @@ class ByInvitationOnlyEffect extends OneShotEffect {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
int number = player.getAmount(
|
int number = player.getAmount(
|
||||||
0, 13, "Choose a number between 0 and 13", game
|
0, 13, "Choose a number between 0 and 13", source, game
|
||||||
);
|
);
|
||||||
return new SacrificeAllEffect(
|
return new SacrificeAllEffect(
|
||||||
number, StaticFilters.FILTER_PERMANENT_CREATURE
|
number, StaticFilters.FILTER_PERMANENT_CREATURE
|
||||||
|
|
|
||||||
86
Mage.Sets/src/mage/cards/c/CaitSithFortuneTeller.java
Normal file
86
Mage.Sets/src/mage/cards/c/CaitSithFortuneTeller.java
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
package mage.cards.c;
|
||||||
|
|
||||||
|
import mage.MageInt;
|
||||||
|
import mage.abilities.Ability;
|
||||||
|
import mage.abilities.common.delayed.ReflexiveTriggeredAbility;
|
||||||
|
import mage.abilities.effects.OneShotEffect;
|
||||||
|
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||||
|
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||||
|
import mage.cards.Card;
|
||||||
|
import mage.cards.CardImpl;
|
||||||
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.*;
|
||||||
|
import mage.game.Game;
|
||||||
|
import mage.players.Player;
|
||||||
|
import mage.target.common.TargetControlledCreaturePermanent;
|
||||||
|
import mage.util.CardUtil;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author TheElk801
|
||||||
|
*/
|
||||||
|
public final class CaitSithFortuneTeller extends CardImpl {
|
||||||
|
|
||||||
|
public CaitSithFortuneTeller(UUID ownerId, CardSetInfo setInfo) {
|
||||||
|
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{R}");
|
||||||
|
|
||||||
|
this.supertype.add(SuperType.LEGENDARY);
|
||||||
|
this.subtype.add(SubType.CAT);
|
||||||
|
this.subtype.add(SubType.MOOGLE);
|
||||||
|
this.power = new MageInt(3);
|
||||||
|
this.toughness = new MageInt(3);
|
||||||
|
|
||||||
|
// Lucky Slots -- At the beginning of combat on your turn, scry 1, then exile the top card of your library. You may play that card this turn. When you exile a card this way, target creature you control gets +X/+0 until end of turn, where X is that card's mana value.
|
||||||
|
this.addAbility(new BeginningOfCombatTriggeredAbility(new CaitSithFortuneTellerEffect()).withFlavorWord("Lucky Slots"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private CaitSithFortuneTeller(final CaitSithFortuneTeller card) {
|
||||||
|
super(card);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CaitSithFortuneTeller copy() {
|
||||||
|
return new CaitSithFortuneTeller(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CaitSithFortuneTellerEffect extends OneShotEffect {
|
||||||
|
|
||||||
|
CaitSithFortuneTellerEffect() {
|
||||||
|
super(Outcome.Benefit);
|
||||||
|
staticText = "scry 1, then exile the top card of your library. You may play that card this turn. " +
|
||||||
|
"When you exile a card this way, target creature you control gets +X/+0 until end of turn, " +
|
||||||
|
"where X is that card's mana value";
|
||||||
|
}
|
||||||
|
|
||||||
|
private CaitSithFortuneTellerEffect(final CaitSithFortuneTellerEffect effect) {
|
||||||
|
super(effect);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CaitSithFortuneTellerEffect copy() {
|
||||||
|
return new CaitSithFortuneTellerEffect(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean apply(Game game, Ability source) {
|
||||||
|
Player player = game.getPlayer(source.getControllerId());
|
||||||
|
if (player == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
player.scry(1, source, game);
|
||||||
|
Card card = player.getLibrary().getFromTop(game);
|
||||||
|
if (card == null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
player.moveCards(card, Zone.EXILED, source, game);
|
||||||
|
CardUtil.makeCardPlayable(game, source, card, false, Duration.EndOfTurn, false);
|
||||||
|
ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
|
||||||
|
new BoostTargetEffect(card.getManaValue(), 0), false
|
||||||
|
);
|
||||||
|
ability.addTarget(new TargetControlledCreaturePermanent());
|
||||||
|
game.fireReflexiveTriggeredAbility(ability, source);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ import mage.cards.CardSetInfo;
|
||||||
import mage.constants.*;
|
import mage.constants.*;
|
||||||
import mage.filter.FilterCard;
|
import mage.filter.FilterCard;
|
||||||
import mage.filter.FilterPermanent;
|
import mage.filter.FilterPermanent;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
import mage.filter.common.FilterControlledEnchantmentPermanent;
|
import mage.filter.common.FilterControlledEnchantmentPermanent;
|
||||||
import mage.filter.common.FilterEnchantmentCard;
|
import mage.filter.common.FilterEnchantmentCard;
|
||||||
import mage.filter.predicate.Predicates;
|
import mage.filter.predicate.Predicates;
|
||||||
|
|
@ -45,7 +46,6 @@ public final class CalixDestinysHand extends CardImpl {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final FilterPermanent filter3 = new FilterControlledEnchantmentPermanent();
|
private static final FilterPermanent filter3 = new FilterControlledEnchantmentPermanent();
|
||||||
private static final FilterCard filter4 = new FilterEnchantmentCard("enchantment cards");
|
|
||||||
|
|
||||||
public CalixDestinysHand(UUID ownerId, CardSetInfo setInfo) {
|
public CalixDestinysHand(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{G}{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{G}{W}");
|
||||||
|
|
@ -70,7 +70,7 @@ public final class CalixDestinysHand extends CardImpl {
|
||||||
this.addAbility(ability);
|
this.addAbility(ability);
|
||||||
|
|
||||||
// −7: Return all enchantment cards from your graveyard to the battlefield.
|
// −7: Return all enchantment cards from your graveyard to the battlefield.
|
||||||
this.addAbility(new LoyaltyAbility(new ReturnFromYourGraveyardToBattlefieldAllEffect(filter4), -7));
|
this.addAbility(new LoyaltyAbility(new ReturnFromYourGraveyardToBattlefieldAllEffect(StaticFilters.FILTER_CARD_ENCHANTMENTS), -7));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CalixDestinysHand(final CalixDestinysHand card) {
|
private CalixDestinysHand(final CalixDestinysHand card) {
|
||||||
|
|
|
||||||
|
|
@ -1,46 +1,44 @@
|
||||||
|
|
||||||
package mage.cards.c;
|
package mage.cards.c;
|
||||||
|
|
||||||
import java.util.UUID;
|
|
||||||
import mage.abilities.TriggeredAbility;
|
|
||||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
|
||||||
import mage.abilities.common.SimpleStaticAbility;
|
import mage.abilities.common.SimpleStaticAbility;
|
||||||
import mage.abilities.condition.common.RevoltCondition;
|
import mage.abilities.condition.common.RevoltCondition;
|
||||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
import mage.abilities.dynamicvalue.DynamicValue;
|
||||||
import mage.abilities.dynamicvalue.common.CountersSourceCount;
|
import mage.abilities.dynamicvalue.common.CountersSourceCount;
|
||||||
import mage.abilities.effects.Effect;
|
|
||||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||||
|
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||||
import mage.cards.CardImpl;
|
import mage.cards.CardImpl;
|
||||||
import mage.cards.CardSetInfo;
|
import mage.cards.CardSetInfo;
|
||||||
|
import mage.constants.AbilityWord;
|
||||||
import mage.constants.CardType;
|
import mage.constants.CardType;
|
||||||
import mage.constants.Duration;
|
import mage.constants.Duration;
|
||||||
import mage.constants.Zone;
|
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
import mage.filter.StaticFilters;
|
import mage.filter.StaticFilters;
|
||||||
import mage.watchers.common.RevoltWatcher;
|
import mage.watchers.common.RevoltWatcher;
|
||||||
|
|
||||||
|
import java.util.UUID;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
|
||||||
* @author fireshoes
|
* @author fireshoes
|
||||||
*/
|
*/
|
||||||
public final class CallForUnity extends CardImpl {
|
public final class CallForUnity extends CardImpl {
|
||||||
|
|
||||||
private static final String ruleText = "<i>Revolt</i> — At the beginning of your end step, if a permanent you controlled left the battlefield this turn, "
|
private static final DynamicValue xValue = new CountersSourceCount(CounterType.UNITY);
|
||||||
+ "put a unity counter on {this}.";
|
|
||||||
|
|
||||||
public CallForUnity(UUID ownerId, CardSetInfo setInfo) {
|
public CallForUnity(UUID ownerId, CardSetInfo setInfo) {
|
||||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{W}");
|
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{W}{W}");
|
||||||
|
|
||||||
// <i>Revolt</i> — At the beginning of your end step, if a permanent you controlled left the battlefield this turn, put a unity counter on Call for Unity.
|
// <i>Revolt</i> — At the beginning of your end step, if a permanent you controlled left the battlefield this turn, put a unity counter on Call for Unity.
|
||||||
TriggeredAbility ability = new BeginningOfEndStepTriggeredAbility(new AddCountersSourceEffect(CounterType.UNITY.createInstance(), true));
|
this.addAbility(new BeginningOfEndStepTriggeredAbility(new AddCountersSourceEffect(CounterType.UNITY.createInstance()))
|
||||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(ability, RevoltCondition.instance, ruleText).addHint(RevoltCondition.getHint()), new RevoltWatcher());
|
.withInterveningIf(RevoltCondition.instance)
|
||||||
|
.setAbilityWord(AbilityWord.REVOLT)
|
||||||
|
.addHint(RevoltCondition.getHint()), new RevoltWatcher());
|
||||||
|
|
||||||
// Creatures you control get +1/+1 for each unity counter on Call for Unity.
|
// Creatures you control get +1/+1 for each unity counter on Call for Unity.
|
||||||
Effect effect = new BoostControlledEffect(new CountersSourceCount(CounterType.UNITY), new CountersSourceCount(CounterType.UNITY), Duration.WhileOnBattlefield,
|
this.addAbility(new SimpleStaticAbility(new BoostControlledEffect(
|
||||||
StaticFilters.FILTER_PERMANENT_CREATURE, false);
|
xValue, xValue, Duration.WhileOnBattlefield,
|
||||||
effect.setText("Creatures you control get +1/+1 for each unity counter on {this}");
|
StaticFilters.FILTER_PERMANENT_CREATURE, false
|
||||||
this.addAbility(new SimpleStaticAbility(effect));
|
).setText("creatures you control get +1/+1 for each unity counter on {this}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CallForUnity(final CallForUnity card) {
|
private CallForUnity(final CallForUnity card) {
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ class CamouflageEffect extends ContinuousRuleModifyingEffectImpl {
|
||||||
// (This temporarily manipulates Blocking values to "test" how many blockers the creature has still left to assign)
|
// (This temporarily manipulates Blocking values to "test" how many blockers the creature has still left to assign)
|
||||||
List<Permanent> spentBlockers = new ArrayList<>();
|
List<Permanent> spentBlockers = new ArrayList<>();
|
||||||
for (Permanent possibleBlocker : list) {
|
for (Permanent possibleBlocker : list) {
|
||||||
if (possibleBlocker.getMaxBlocks() != 0 && possibleBlocker.getBlocking() >= possibleBlocker.getMaxBlocks()) {
|
if (possibleBlocker.getMaxBlocks() > 0 && possibleBlocker.getBlocking() >= possibleBlocker.getMaxBlocks()) {
|
||||||
spentBlockers.add(possibleBlocker);
|
spentBlockers.add(possibleBlocker);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,11 @@ import mage.constants.CardType;
|
||||||
import mage.constants.Outcome;
|
import mage.constants.Outcome;
|
||||||
import mage.constants.Zone;
|
import mage.constants.Zone;
|
||||||
import mage.counters.CounterType;
|
import mage.counters.CounterType;
|
||||||
|
import mage.filter.StaticFilters;
|
||||||
import mage.game.Game;
|
import mage.game.Game;
|
||||||
import mage.game.permanent.Permanent;
|
import mage.game.permanent.Permanent;
|
||||||
import mage.players.Player;
|
import mage.players.Player;
|
||||||
import mage.target.common.TargetCreaturePermanentSameController;
|
import mage.target.common.TargetPermanentSameController;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
|
@ -28,7 +29,7 @@ public final class Cannibalize extends CardImpl {
|
||||||
|
|
||||||
// Choose two target creatures controlled by the same player. Exile one of the creatures and put two +1/+1 counters on the other.
|
// Choose two target creatures controlled by the same player. Exile one of the creatures and put two +1/+1 counters on the other.
|
||||||
this.getSpellAbility().addEffect(new CannibalizeEffect());
|
this.getSpellAbility().addEffect(new CannibalizeEffect());
|
||||||
this.getSpellAbility().addTarget(new TargetCreaturePermanentSameController(2));
|
this.getSpellAbility().addTarget(new TargetPermanentSameController(StaticFilters.FILTER_PERMANENT_CREATURES));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Cannibalize(final Cannibalize card) {
|
private Cannibalize(final Cannibalize card) {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue