mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 02:30:08 -08:00
Merge branch 'master' into addthefivedoctors
This commit is contained in:
commit
2aeb6420c6
736 changed files with 17075 additions and 6454 deletions
|
|
@ -114,12 +114,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
int cardWidth = getCardWidth();
|
||||
int cardHeight = getCardHeight();
|
||||
int cardTopHeight = CardRenderer.getCardTopHeight(cardWidth);
|
||||
int dx = x % (cardWidth + GRID_PADDING);
|
||||
int col = x / (cardWidth + GRID_PADDING);
|
||||
int dx = x % (cardWidth + getGridPadding());
|
||||
int col = x / (cardWidth + getGridPadding());
|
||||
int gridWidth = cardGrid.isEmpty() ? 0 : cardGrid.get(0).size();
|
||||
|
||||
int countLabelHeight = getCountLabelHeight();
|
||||
if (dx < GRID_PADDING && col < gridWidth) {
|
||||
if (dx < getGridPadding() && col < gridWidth) {
|
||||
// Which row to add to?
|
||||
int curY = countLabelHeight;
|
||||
int rowIndex = 0;
|
||||
|
|
@ -142,7 +142,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
// Insert between two columns
|
||||
insertArrow.setIcon(INSERT_COL_ICON);
|
||||
insertArrow.setSize(64, 64);
|
||||
insertArrow.setLocation((cardWidth + GRID_PADDING) * col + GRID_PADDING / 2 - 32, curY);
|
||||
insertArrow.setLocation((cardWidth + getGridPadding()) * col + getGridPadding() / 2 - 32, curY);
|
||||
} else {
|
||||
// Clamp to a new col one after the current last one
|
||||
col = Math.min(col, gridWidth);
|
||||
|
|
@ -184,7 +184,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
// Position arrow
|
||||
insertArrow.setIcon(INSERT_ROW_ICON);
|
||||
insertArrow.setSize(64, 32);
|
||||
insertArrow.setLocation((cardWidth + GRID_PADDING) * col + GRID_PADDING + cardWidth / 2 - 32, curY + stackInsertIndex * cardTopHeight - 32);
|
||||
insertArrow.setLocation((cardWidth + getGridPadding()) * col + getGridPadding() + cardWidth / 2 - 32, curY + stackInsertIndex * cardTopHeight - 32);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -225,12 +225,12 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
int cardWidth = getCardWidth();
|
||||
int cardHeight = getCardHeight();
|
||||
int cardTopHeight = CardRenderer.getCardTopHeight(cardWidth);
|
||||
int dx = x % (cardWidth + GRID_PADDING);
|
||||
int col = x / (cardWidth + GRID_PADDING);
|
||||
int dx = x % (cardWidth + getGridPadding());
|
||||
int col = x / (cardWidth + getGridPadding());
|
||||
int gridWidth = cardGrid.isEmpty() ? 0 : cardGrid.get(0).size();
|
||||
|
||||
int countLabelHeight = getCountLabelHeight();
|
||||
if (dx < GRID_PADDING && col < gridWidth) {
|
||||
if (dx < getGridPadding() && col < gridWidth) {
|
||||
// Which row to add to?
|
||||
int curY = countLabelHeight;
|
||||
int rowIndex = 0;
|
||||
|
|
@ -334,7 +334,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
// Add new cards to grid
|
||||
for (CardView card : cards) {
|
||||
card.setSelected(true);
|
||||
addCardView(card, false);
|
||||
addCardView(card, null);
|
||||
eventSource.fireEvent(card, ClientEventType.DECK_ADD_SPECIFIC_CARD);
|
||||
}
|
||||
layoutGrid();
|
||||
|
|
@ -575,7 +575,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
|
||||
// Constants
|
||||
private static final int DEFAULT_COUNT_LABEL_HEIGHT = 40; // can contain 1 or 2 lines
|
||||
public static final int GRID_PADDING = 10;
|
||||
public static final int GRID_PADDING = 20;
|
||||
|
||||
private static final ImageIcon INSERT_ROW_ICON = new ImageIcon(DragCardGrid.class.getClassLoader().getResource("editor_insert_row.png"));
|
||||
private static final ImageIcon INSERT_COL_ICON = new ImageIcon(DragCardGrid.class.getClassLoader().getResource("editor_insert_col.png"));
|
||||
|
|
@ -1044,7 +1044,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
selectBySearchPanelC.fill = GridBagConstraints.VERTICAL;
|
||||
|
||||
searchByTextField = new JTextField();
|
||||
searchByTextField.setToolTipText("Searches for card names, types, rarity, casting cost and rules text. NB: Mana symbols are written like {W},{U},{C} etc");
|
||||
searchByTextField.setToolTipText("Search cards by any data like name or mana symbols like {W}, {U}, {C}, etc (use quotes for exact search)");
|
||||
searchByTextField.addKeyListener(new KeyAdapter() {
|
||||
@Override
|
||||
public void keyReleased(KeyEvent e) {
|
||||
|
|
@ -1253,10 +1253,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
selectionPanel.setSize(x2 - x1, y2 - y1);
|
||||
|
||||
// First and last cols
|
||||
int col1 = x1 / (cardWidth + GRID_PADDING);
|
||||
int col2 = x2 / (cardWidth + GRID_PADDING);
|
||||
int offsetIntoCol2 = x2 % (cardWidth + GRID_PADDING);
|
||||
if (offsetIntoCol2 < GRID_PADDING) {
|
||||
int col1 = x1 / (cardWidth + getGridPadding());
|
||||
int col2 = x2 / (cardWidth + getGridPadding());
|
||||
int offsetIntoCol2 = x2 % (cardWidth + getGridPadding());
|
||||
if (offsetIntoCol2 < getGridPadding()) {
|
||||
--col2;
|
||||
}
|
||||
|
||||
|
|
@ -1322,7 +1322,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
|
||||
// re-insert
|
||||
for (CardView card : allCards) {
|
||||
sortIntoGrid(card);
|
||||
sortIntoGrid(card, null);
|
||||
}
|
||||
trimGrid();
|
||||
|
||||
|
|
@ -1717,7 +1717,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
|
||||
if (acard.getName().equals(card.getName())) {
|
||||
CardView pimpedCard = new CardView(acard);
|
||||
addCardView(pimpedCard, false);
|
||||
addCardView(pimpedCard, null);
|
||||
eventSource.fireEvent(pimpedCard, ClientEventType.DECK_ADD_SPECIFIC_CARD);
|
||||
pimpedCards.put(pimpedCard, 1);
|
||||
didModify = true;
|
||||
|
|
@ -1729,7 +1729,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
|
||||
if (didModify) {
|
||||
for (CardView c : pimpedCards.keySet()) {
|
||||
sortIntoGrid(c);
|
||||
sortIntoGrid(c, null);
|
||||
}
|
||||
trimGrid();
|
||||
|
||||
|
|
@ -1762,7 +1762,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
CardView oldestCardView = new CardView(oldestCardInfo.createMockCard());
|
||||
this.removeCardView(card);
|
||||
eventSource.fireEvent(card, ClientEventType.DECK_REMOVE_SPECIFIC_CARD);
|
||||
this.addCardView(oldestCardView, false);
|
||||
this.addCardView(oldestCardView, null);
|
||||
eventSource.fireEvent(oldestCardView, ClientEventType.DECK_ADD_SPECIFIC_CARD);
|
||||
newStack.add(oldestCardView);
|
||||
} else {
|
||||
|
|
@ -1814,10 +1814,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
for (CardView newCard : cardsView.values()) {
|
||||
if (!cardViews.containsKey(newCard.getId())) {
|
||||
// Is a new card
|
||||
addCardView(newCard, false);
|
||||
addCardView(newCard, null);
|
||||
|
||||
// Put it into the appropirate place in the grid given the current sort
|
||||
sortIntoGrid(newCard);
|
||||
sortIntoGrid(newCard, null);
|
||||
|
||||
// Mark
|
||||
didModify = true;
|
||||
|
|
@ -1837,7 +1837,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
for (CardView newCard : cardsView.values()) {
|
||||
if (!cardViews.containsKey(newCard.getId())) {
|
||||
// Add the new card
|
||||
addCardView(newCard, false);
|
||||
addCardView(newCard, null);
|
||||
|
||||
// Add the new card to tracking
|
||||
Map<String, List<CardView>> forSetCode;
|
||||
|
|
@ -1889,7 +1889,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
for (List<CardView> orphans : tracked.values()) {
|
||||
for (CardView orphan : orphans) {
|
||||
logger.info("Orphan when setting with layout: ");
|
||||
sortIntoGrid(orphan);
|
||||
sortIntoGrid(orphan, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1984,7 +1984,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
menu.show(e.getComponent(), e.getX(), e.getY());
|
||||
}
|
||||
|
||||
public void addCardView(final CardView card, boolean duplicated) {
|
||||
public void addCardView(final CardView card, final CardView duplicatedFromCard) {
|
||||
allCards.add(card);
|
||||
|
||||
// Update counts
|
||||
|
|
@ -2019,8 +2019,8 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
cardContent.add(cardPanel);
|
||||
cardViews.put(card.getId(), cardPanel);
|
||||
|
||||
if (duplicated) {
|
||||
sortIntoGrid(card);
|
||||
if (duplicatedFromCard != null) {
|
||||
sortIntoGrid(card, duplicatedFromCard);
|
||||
eventSource.fireEvent(card, ClientEventType.DECK_ADD_SPECIFIC_CARD);
|
||||
|
||||
// clear grid from empty rows
|
||||
|
|
@ -2094,7 +2094,21 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
*
|
||||
* @param newCard Card to add to the cardGrid array.
|
||||
*/
|
||||
private void sortIntoGrid(CardView newCard) {
|
||||
private void sortIntoGrid(CardView newCard, CardView duplicatedFromCard) {
|
||||
// fast put duplicated card to the same place as original
|
||||
if (duplicatedFromCard != null) {
|
||||
for (List<List<CardView>> gridRow : cardGrid) {
|
||||
for (List<CardView> gridStack : gridRow) {
|
||||
for (int i = 0; i < gridStack.size(); i++) {
|
||||
if (gridStack.get(i).equals(duplicatedFromCard)) {
|
||||
gridStack.add(i, newCard);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// row 1 must exists
|
||||
if (cardGrid.isEmpty()) {
|
||||
cardGrid.add(0, new ArrayList<>());
|
||||
|
|
@ -2336,7 +2350,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
} else {
|
||||
String description = cardSort.getComparator().getCategoryName(stack.get(0));
|
||||
DragCardGrid.updateCountLabel(countLabel, stack.size(), description);
|
||||
countLabel.setLocation(GRID_PADDING + (cardWidth + GRID_PADDING) * colIndex, currentY - countLabelHeight);
|
||||
countLabel.setLocation(getGridPadding() + (cardWidth + getGridPadding()) * colIndex, currentY - countLabelHeight);
|
||||
countLabel.setSize(cardWidth, countLabelHeight);
|
||||
countLabel.setVisible(true);
|
||||
}
|
||||
|
|
@ -2348,7 +2362,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
for (int i = 0; i < stack.size(); ++i) {
|
||||
CardView card = stack.get(i);
|
||||
MageCard view = cardViews.get(card.getId());
|
||||
int x = GRID_PADDING + (cardWidth + GRID_PADDING) * colIndex;
|
||||
int x = getGridPadding() + (cardWidth + getGridPadding()) * colIndex;
|
||||
int y = currentY + i * cardTopHeight;
|
||||
view.setCardBounds(x, y, cardWidth, cardHeight);
|
||||
cardContent.setLayer(view, layerIndex++);
|
||||
|
|
@ -2356,14 +2370,18 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
}
|
||||
|
||||
// Update the max stack size for this row and the max width
|
||||
maxWidth = Math.max(maxWidth, GRID_PADDING + (GRID_PADDING + cardWidth) * gridRow.size());
|
||||
maxWidth = Math.max(maxWidth, getGridPadding() + (getGridPadding() + cardWidth) * gridRow.size());
|
||||
maxStackSize.set(rowIndex, rowMaxStackSize);
|
||||
currentY += (cardTopHeight * (rowMaxStackSize - 1) + cardHeight) + countLabelHeight;
|
||||
}
|
||||
|
||||
// Resize card container
|
||||
cardContent.setPreferredSize(new Dimension(maxWidth, currentY - countLabelHeight + GRID_PADDING));
|
||||
//cardContent.setSize(maxWidth, currentY - COUNT_LABEL_HEIGHT + GRID_PADDING);
|
||||
cardContent.setPreferredSize(new Dimension(maxWidth, currentY - countLabelHeight + getGridPadding()));
|
||||
//cardContent.setSize(maxWidth, currentY - COUNT_LABEL_HEIGHT + getGridPadding());
|
||||
}
|
||||
|
||||
private int getGridPadding() {
|
||||
return Math.max(GRID_PADDING, Math.round(cardSizeMod * GRID_PADDING * GUISizeHelper.dialogGuiScale));
|
||||
}
|
||||
|
||||
public static int getCountLabelHeight() {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,343 @@
|
|||
package mage.client.components;
|
||||
|
||||
import mage.MageObject;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
|
||||
import java.awt.*;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* Inject bracket level inside validation panel
|
||||
* See more details at <a href="https://mtg.wiki/page/Commander_Brackets">wiki</a>
|
||||
* <p>
|
||||
* Support:
|
||||
* - [x] game changers
|
||||
* - [ ] infinite combos
|
||||
* - [x] mass land destruction
|
||||
* - [x] extra turns
|
||||
* - [x] tutors
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class BracketLegalityLabel extends LegalityLabel {
|
||||
|
||||
private static final String GROUP_GAME_CHANGES = "Game Changers";
|
||||
private static final String GROUP_INFINITE_COMBOS = "Infinite Combos (unsupported)";
|
||||
private static final String GROUP_MASS_LAND_DESTRUCTION = "Mass Land Destruction";
|
||||
private static final String GROUP_EXTRA_TURN = "Extra Turns";
|
||||
private static final String GROUP_TUTORS = "Tutors";
|
||||
|
||||
private final BracketLevel level;
|
||||
|
||||
private final List<String> foundGameChangers = new ArrayList<>();
|
||||
private final List<String> foundInfiniteCombos = new ArrayList<>();
|
||||
private final List<String> foundMassLandDestruction = new ArrayList<>();
|
||||
private final List<String> foundExtraTurn = new ArrayList<>();
|
||||
private final List<String> foundTutors = new ArrayList<>();
|
||||
|
||||
private final List<String> badCards = new ArrayList<>();
|
||||
private final List<String> fullGameChanges = new ArrayList<>();
|
||||
|
||||
public enum BracketLevel {
|
||||
BRACKET_1("Bracket 1"),
|
||||
BRACKET_2_3("Bracket 2-3"),
|
||||
BRACKET_4_5("Bracket 4-5");
|
||||
|
||||
private final String name;
|
||||
|
||||
BracketLevel(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return this.name;
|
||||
}
|
||||
}
|
||||
|
||||
public BracketLegalityLabel(BracketLevel level) {
|
||||
super(level.toString(), null);
|
||||
this.level = level;
|
||||
setPreferredSize(DIM_PREFERRED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectCards() {
|
||||
return new ArrayList<>(this.badCards);
|
||||
}
|
||||
|
||||
private void validateBracketLevel() {
|
||||
this.badCards.clear();
|
||||
switch (this.level) {
|
||||
case BRACKET_1:
|
||||
// No cards from the Game Changer list.
|
||||
// No intentional two-card infinite combos.
|
||||
// No mass land destruction.
|
||||
// No extra turn cards.
|
||||
// Tutors should be sparse.
|
||||
this.badCards.addAll(this.foundGameChangers);
|
||||
this.badCards.addAll(this.foundInfiniteCombos);
|
||||
this.badCards.addAll(this.foundMassLandDestruction);
|
||||
this.badCards.addAll(this.foundExtraTurn);
|
||||
if (this.foundTutors.size() > 3) {
|
||||
this.badCards.addAll(this.foundTutors);
|
||||
}
|
||||
break;
|
||||
case BRACKET_2_3:
|
||||
// 2
|
||||
// No cards from the Game Changer list.
|
||||
// No intentional two-card infinite combos.
|
||||
// No mass land destruction.
|
||||
// Extra turn cards should only appear in low quantities and should not be chained in succession or looped.
|
||||
// Tutors should be sparse.
|
||||
// 3
|
||||
// Up to three (3) cards from the Game Changer list.
|
||||
// No intentional early game two-card infinite combos.
|
||||
// No mass land destruction.
|
||||
// Extra turn cards should only appear in low quantities and should not be chained in succession or looped.
|
||||
if (this.foundGameChangers.size() > 3) {
|
||||
this.badCards.addAll(this.foundGameChangers);
|
||||
}
|
||||
this.badCards.addAll(this.foundInfiniteCombos);
|
||||
this.badCards.addAll(this.foundMassLandDestruction);
|
||||
if (this.foundExtraTurn.size() > 3) {
|
||||
this.badCards.addAll(this.foundExtraTurn);
|
||||
}
|
||||
// this.badCards.addAll(this.foundTutors); // allow any amount
|
||||
break;
|
||||
case BRACKET_4_5:
|
||||
// allow any cards
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported level: " + this.level);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateDeck(Deck deck) {
|
||||
collectAll(deck);
|
||||
validateBracketLevel();
|
||||
|
||||
int infoFontSize = Math.round(GUISizeHelper.cardTooltipFont.getSize() * 0.6f);
|
||||
|
||||
// show all found cards in any use cases
|
||||
Color showColor = this.badCards.isEmpty() ? COLOR_LEGAL : COLOR_NOT_LEGAL;
|
||||
|
||||
List<String> showInfo = new ArrayList<>();
|
||||
if (this.badCards.isEmpty()) {
|
||||
showInfo.add("<p>Deck is <span style='color:green;font-weight:bold;'>GOOD</span> for " + this.level + "</p>");
|
||||
} else {
|
||||
showInfo.add("<p>Deck is <span style='color:#BF544A;font-weight:bold;'>BAD</span> for " + this.level + "</p>");
|
||||
showInfo.add("<p>(click here to select all bad cards)</p>");
|
||||
}
|
||||
|
||||
Map<String, List<String>> groups = new LinkedHashMap<>();
|
||||
groups.put(GROUP_GAME_CHANGES, this.foundGameChangers);
|
||||
groups.put(GROUP_INFINITE_COMBOS, this.foundInfiniteCombos);
|
||||
groups.put(GROUP_MASS_LAND_DESTRUCTION, this.foundMassLandDestruction);
|
||||
groups.put(GROUP_EXTRA_TURN, this.foundExtraTurn);
|
||||
groups.put(GROUP_TUTORS, this.foundTutors);
|
||||
groups.forEach((group, cards) -> {
|
||||
showInfo.add("<br>");
|
||||
showInfo.add("<br>");
|
||||
showInfo.add("<span style='font-weight:bold;'>" + group + ": " + cards.size() + "</span>");
|
||||
if (!cards.isEmpty()) {
|
||||
showInfo.add("<ul style=\"font-size: " + infoFontSize + "px; width: " + TOOLTIP_TABLE_WIDTH + "px; padding-left: 10px; margin: 0;\">");
|
||||
cards.forEach(s -> showInfo.add(String.format("<li style=\"margin-bottom: 2px;\">%s</li>", s)));
|
||||
showInfo.add("</ul>");
|
||||
}
|
||||
});
|
||||
|
||||
String showText = "<html><body>" + String.join("\n", showInfo) + "</body></html>";
|
||||
showState(showColor, showText, false);
|
||||
}
|
||||
|
||||
private void collectAll(Deck deck) {
|
||||
collectGameChangers(deck);
|
||||
collectInfiniteCombos(deck);
|
||||
collectMassLandDestruction(deck);
|
||||
collectExtraTurn(deck);
|
||||
collectTutors(deck);
|
||||
}
|
||||
|
||||
private void collectGameChangers(Deck deck) {
|
||||
this.foundGameChangers.clear();
|
||||
|
||||
if (fullGameChanges.isEmpty()) {
|
||||
// https://mtg.wiki/page/Game_Changers
|
||||
// TODO: share list with AbstractCommander and edh power level
|
||||
fullGameChanges.addAll(Arrays.asList(
|
||||
"Ad Nauseam",
|
||||
"Ancient Tomb",
|
||||
"Aura Shards",
|
||||
"Bolas's Citadel",
|
||||
"Braids, Cabal Minion",
|
||||
"Demonic Tutor",
|
||||
"Drannith Magistrate",
|
||||
"Chrome Mox",
|
||||
"Coalition Victory",
|
||||
"Consecrated Sphinx",
|
||||
"Crop Rotation",
|
||||
"Cyclonic Rift",
|
||||
"Deflecting Swat",
|
||||
"Enlightened Tutor",
|
||||
"Expropriate",
|
||||
"Field of the Dead",
|
||||
"Fierce Guardianship",
|
||||
"Food Chain",
|
||||
"Force of Will",
|
||||
"Gaea's Cradle",
|
||||
"Gamble",
|
||||
"Gifts Ungiven",
|
||||
"Glacial Chasm",
|
||||
"Grand Arbiter Augustin IV",
|
||||
"Grim Monolith",
|
||||
"Humility",
|
||||
"Imperial Seal",
|
||||
"Intuition",
|
||||
"Jeska's Will",
|
||||
"Jin-Gitaxias, Core Augur",
|
||||
"Kinnan, Bonder Prodigy",
|
||||
"Lion's Eye Diamond",
|
||||
"Mana Vault",
|
||||
"Mishra's Workshop",
|
||||
"Mox Diamond",
|
||||
"Mystical Tutor",
|
||||
"Narset, Parter of Veils",
|
||||
"Natural Order",
|
||||
"Necropotence",
|
||||
"Notion Thief",
|
||||
"Rhystic Study",
|
||||
"Opposition Agent",
|
||||
"Orcish Bowmasters",
|
||||
"Panoptic Mirror",
|
||||
"Seedborn Muse",
|
||||
"Serra's Sanctum",
|
||||
"Smothering Tithe",
|
||||
"Survival of the Fittest",
|
||||
"Sway of the Stars",
|
||||
"Teferi's Protection",
|
||||
"Tergrid, God of Fright",
|
||||
"Thassa's Oracle",
|
||||
"The One Ring",
|
||||
"The Tabernacle at Pendrell Vale",
|
||||
"Underworld Breach",
|
||||
"Urza, Lord High Artificer",
|
||||
"Vampiric Tutor",
|
||||
"Vorinclex, Voice of Hunger",
|
||||
"Yuriko, the Tiger's Shadow",
|
||||
"Winota, Joiner of Forces",
|
||||
"Worldly Tutor"
|
||||
));
|
||||
}
|
||||
|
||||
Stream.concat(deck.getCards().stream(), deck.getSideboard().stream())
|
||||
.map(MageObject::getName)
|
||||
.filter(fullGameChanges::contains)
|
||||
.sorted()
|
||||
.forEach(this.foundGameChangers::add);
|
||||
}
|
||||
|
||||
private void collectInfiniteCombos(Deck deck) {
|
||||
// TODO: implement
|
||||
this.foundInfiniteCombos.clear();
|
||||
}
|
||||
|
||||
private void collectMassLandDestruction(Deck deck) {
|
||||
// https://mtg.wiki/page/Land_destruction
|
||||
// https://draftsim.com/mtg-mass-land-destruction/
|
||||
this.foundMassLandDestruction.clear();
|
||||
Stream.concat(deck.getCards().stream(), deck.getSideboard().stream())
|
||||
.filter(card -> card.getRules().stream()
|
||||
.map(s -> s.toLowerCase(Locale.ENGLISH))
|
||||
.anyMatch(s -> (s.contains("destroy") || s.contains("sacrifice"))
|
||||
&& (s.contains("all") || s.contains("x target") || s.contains("{x} target"))
|
||||
&& isTextContainsLandName(s)
|
||||
)
|
||||
)
|
||||
.map(Card::getName)
|
||||
.sorted()
|
||||
.forEach(this.foundMassLandDestruction::add);
|
||||
}
|
||||
|
||||
private void collectExtraTurn(Deck deck) {
|
||||
this.foundExtraTurn.clear();
|
||||
Stream.concat(deck.getCards().stream(), deck.getSideboard().stream())
|
||||
.filter(card -> card.getRules().stream()
|
||||
.map(s -> s.toLowerCase(Locale.ENGLISH))
|
||||
.anyMatch(s -> s.contains("extra turn"))
|
||||
)
|
||||
.map(Card::getName)
|
||||
.sorted()
|
||||
.forEach(this.foundExtraTurn::add);
|
||||
}
|
||||
|
||||
private void collectTutors(Deck deck) {
|
||||
// edh power level uses search for land and non-land card, but bracket need only non-land cards searching
|
||||
this.foundTutors.clear();
|
||||
Stream.concat(deck.getCards().stream(), deck.getSideboard().stream())
|
||||
.filter(card -> card.getRules().stream()
|
||||
.map(s -> s.toLowerCase(Locale.ENGLISH))
|
||||
.anyMatch(s -> s.contains("search your library") && !isTextContainsLandCard(s))
|
||||
)
|
||||
.map(Card::getName)
|
||||
.sorted()
|
||||
.forEach(this.foundTutors::add);
|
||||
}
|
||||
|
||||
private boolean isTextContainsLandCard(String lowerText) {
|
||||
// TODO: share code with AbstractCommander and edh power level
|
||||
// TODO: add tests
|
||||
return lowerText.contains("basic ")
|
||||
|| lowerText.contains("plains card")
|
||||
|| lowerText.contains("island card")
|
||||
|| lowerText.contains("swamp card")
|
||||
|| lowerText.contains("mountain card")
|
||||
|| lowerText.contains("forest card");
|
||||
}
|
||||
|
||||
private boolean isTextContainsLandName(String lowerText) {
|
||||
// TODO: add tests to find all cards from https://mtg.wiki/page/Land_destruction
|
||||
// TODO: add tests
|
||||
/*
|
||||
// mass land destruction
|
||||
Ajani Vengeant
|
||||
Armageddon
|
||||
Avalanche
|
||||
Bend or Break
|
||||
Boil
|
||||
Boiling Seas
|
||||
Boom // Bust
|
||||
Burning of Xinye
|
||||
Catastrophe
|
||||
Decree of Annihilation
|
||||
Desolation Angel
|
||||
Devastation
|
||||
Fall of the Thran
|
||||
From the Ashes
|
||||
Impending Disaster
|
||||
Jokulhaups
|
||||
Myojin of Infinite Rage
|
||||
Numot, the Devastator
|
||||
Obliterate
|
||||
Orcish Settlers
|
||||
Ravages of War
|
||||
Ruination
|
||||
Rumbling Crescendo
|
||||
Scorched Earth
|
||||
Tsunami
|
||||
Wake of Destruction
|
||||
Wildfire
|
||||
*/
|
||||
return lowerText.contains("lands")
|
||||
|| lowerText.contains("plains")
|
||||
|| lowerText.contains("island")
|
||||
|| lowerText.contains("swamp")
|
||||
|| lowerText.contains("mountain")
|
||||
|| lowerText.contains("forest");
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package mage.client.components;
|
||||
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.gui.GuiDisplayUtil;
|
||||
import mage.deck.Commander;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* Inject power level info inside validation panel
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class EdhPowerLevelLegalityLabel extends LegalityLabel {
|
||||
|
||||
private final Commander commanderDeckType = new Commander();
|
||||
private final List<String> foundPowerCards = new ArrayList<>();
|
||||
|
||||
public EdhPowerLevelLegalityLabel() {
|
||||
super("EDH Power Level: ?", null);
|
||||
setPreferredSize(DIM_PREFERRED_X3);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> selectCards() {
|
||||
// choose cards with power level
|
||||
return this.foundPowerCards;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void validateDeck(Deck deck) {
|
||||
// find and save power level and card hints
|
||||
|
||||
List<String> foundInfo = new ArrayList<>();
|
||||
int level = this.commanderDeckType.getEdhPowerLevel(deck, foundPowerCards, foundInfo);
|
||||
this.setText(String.format("EDH Power Level: %d", level));
|
||||
|
||||
// sort by score "+5 from xxx"
|
||||
Pattern pattern = Pattern.compile("\\+(\\d+)");
|
||||
foundInfo.sort((o1, o2) -> {
|
||||
Matcher matcher = pattern.matcher(o1);
|
||||
int score1 = matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
|
||||
matcher = pattern.matcher(o2);
|
||||
int score2 = matcher.find() ? Integer.parseInt(matcher.group(1)) : 0;
|
||||
if (score1 != score2) {
|
||||
return Integer.compare(score2, score1);
|
||||
}
|
||||
return o1.compareTo(o2);
|
||||
});
|
||||
|
||||
showStateInfo(formatCardsInfoTooltip(level, foundInfo));
|
||||
}
|
||||
|
||||
private String formatCardsInfoTooltip(int level, List<String> foundInfo) {
|
||||
// use 60% font for better and compatible list
|
||||
int infoFontSize = Math.round(GUISizeHelper.cardTooltipFont.getSize() * 0.6f);
|
||||
int maxLimit = 25;
|
||||
String extraInfo = this.foundPowerCards.size() <= maxLimit ? "" : String.format("<li style=\"margin-bottom: 2px;\">and %d more cards</li>", this.foundPowerCards.size() - maxLimit);
|
||||
return foundInfo.stream()
|
||||
.limit(maxLimit)
|
||||
.reduce("<html><body>"
|
||||
+ "<p>EDH Power Level: <span style='color:#b8860b;font-weight:bold;'>" + level + "</span></p>"
|
||||
+ "<br>"
|
||||
+ "<u>Found <span style='font-weight:bold;'>" + this.foundPowerCards.size() + "</span> cards with power levels (click to select it)</u>"
|
||||
+ "<br>"
|
||||
+ "<ul style=\"font-size: " + infoFontSize + "px; width: " + TOOLTIP_TABLE_WIDTH + "px; padding-left: 10px; margin: 0;\">",
|
||||
(str, info) -> str + String.format("<li style=\"margin-bottom: 2px;\">%s</li>", info), String::concat)
|
||||
+ extraInfo
|
||||
+ "</ul>"
|
||||
+ "</body></html>";
|
||||
}
|
||||
}
|
||||
|
|
@ -3,17 +3,18 @@ package mage.client.components;
|
|||
import mage.cards.decks.Deck;
|
||||
import mage.cards.decks.DeckValidator;
|
||||
import mage.cards.decks.DeckValidatorError;
|
||||
import mage.cards.decks.importer.DeckImporter;
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
import org.unbescape.html.HtmlEscapeLevel;
|
||||
import org.unbescape.html.HtmlEscapeType;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.awt.*;
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author Elandril
|
||||
* @author Elandril, JayDi85
|
||||
*/
|
||||
public class LegalityLabel extends JLabel {
|
||||
|
||||
|
|
@ -25,8 +26,10 @@ public class LegalityLabel extends JLabel {
|
|||
protected static final Dimension DIM_MINIMUM = new Dimension(75, 25);
|
||||
protected static final Dimension DIM_MAXIMUM = new Dimension(150, 75);
|
||||
protected static final Dimension DIM_PREFERRED = new Dimension(75, 25);
|
||||
protected static final Dimension DIM_PREFERRED_X2 = new Dimension(DIM_PREFERRED.width * 2 + 5, 25);
|
||||
protected static final Dimension DIM_PREFERRED_X3 = new Dimension(DIM_PREFERRED.width * 3 + 5 + 5, 25);
|
||||
|
||||
protected static final int TOOLTIP_TABLE_WIDTH = 300; // size of the label's tooltip
|
||||
protected static final int TOOLTIP_TABLE_WIDTH = 400; // size of the label's tooltip
|
||||
protected static final int TOOLTIP_MAX_ERRORS = 20; // max errors to show in tooltip
|
||||
|
||||
protected Deck currentDeck;
|
||||
|
|
@ -92,19 +95,6 @@ public class LegalityLabel extends JLabel {
|
|||
return button;
|
||||
}
|
||||
|
||||
public String getErrorMessage() {
|
||||
return errorMessage;
|
||||
}
|
||||
|
||||
public DeckValidator getValidator() {
|
||||
return validator;
|
||||
}
|
||||
|
||||
public void setValidator(DeckValidator validator) {
|
||||
this.validator = validator;
|
||||
revalidateDeck();
|
||||
}
|
||||
|
||||
protected String escapeHtml(String string) {
|
||||
return HtmlEscape.escapeHtml(string, HtmlEscapeType.HTML4_NAMED_REFERENCES_DEFAULT_TO_HEXA, HtmlEscapeLevel.LEVEL_0_ONLY_MARKUP_SIGNIFICANT_EXCEPT_APOS);
|
||||
}
|
||||
|
|
@ -146,25 +136,33 @@ public class LegalityLabel extends JLabel {
|
|||
setBackground(color);
|
||||
}
|
||||
|
||||
public void showState(Color color, String tooltip) {
|
||||
public void showState(Color color, String tooltip, boolean useErrors) {
|
||||
setBackground(color);
|
||||
if (useErrors) {
|
||||
setToolTipText(appendErrorMessage(tooltip));
|
||||
} else {
|
||||
setToolTipText(tooltip);
|
||||
}
|
||||
}
|
||||
|
||||
public void showStateInfo(String tooltip) {
|
||||
showState(COLOR_LEGAL, tooltip, false);
|
||||
}
|
||||
|
||||
public void showStateUnknown(String tooltip) {
|
||||
showState(COLOR_UNKNOWN, tooltip);
|
||||
showState(COLOR_UNKNOWN, tooltip, true);
|
||||
}
|
||||
|
||||
public void showStateLegal(String tooltip) {
|
||||
showState(COLOR_LEGAL, tooltip);
|
||||
showState(COLOR_LEGAL, tooltip, true);
|
||||
}
|
||||
|
||||
public void showStatePartlyLegal(String tooltip) {
|
||||
showState(COLOR_PARTLY_LEGAL, tooltip);
|
||||
showState(COLOR_PARTLY_LEGAL, tooltip, true);
|
||||
}
|
||||
|
||||
public void showStateNotLegal(String tooltip) {
|
||||
showState(COLOR_NOT_LEGAL, tooltip);
|
||||
showState(COLOR_NOT_LEGAL, tooltip, true);
|
||||
}
|
||||
|
||||
public void validateDeck(Deck deck) {
|
||||
|
|
@ -191,28 +189,13 @@ public class LegalityLabel extends JLabel {
|
|||
}
|
||||
}
|
||||
|
||||
public void validateDeck(File deckFile) {
|
||||
deckFile = deckFile.getAbsoluteFile();
|
||||
if (!deckFile.exists()) {
|
||||
errorMessage = String.format("Deck file '%s' does not exist.", deckFile.getAbsolutePath());
|
||||
showStateUnknown("<html><body><b>No Deck loaded!</b></body></html>");
|
||||
return;
|
||||
public java.util.List<String> selectCards() {
|
||||
if (this.validator == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
try {
|
||||
StringBuilder errorMessages = new StringBuilder();
|
||||
Deck deck = Deck.load(DeckImporter.importDeckFromFile(deckFile.getAbsolutePath(), errorMessages, false), true, true);
|
||||
errorMessage = errorMessages.toString();
|
||||
validateDeck(deck);
|
||||
} catch (Exception ex) {
|
||||
errorMessage = String.format("Error importing deck from file '%s'!", deckFile.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
public void revalidateDeck() {
|
||||
validateDeck(currentDeck);
|
||||
}
|
||||
|
||||
public void validateDeck(String deckFile) {
|
||||
validateDeck(new File(deckFile));
|
||||
return this.validator.getErrorsList().stream()
|
||||
.map(DeckValidatorError::getCardName)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,20 +5,21 @@ import mage.cards.decks.Deck;
|
|||
import mage.cards.repository.CardCriteria;
|
||||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.ExpansionRepository;
|
||||
import mage.client.dialog.PreferencesDialog;
|
||||
import mage.client.util.sets.ConstructedFormats;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ColoredManaSymbol;
|
||||
import mage.constants.SuperType;
|
||||
import mage.util.RandomUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Generates random card pool and builds a deck.
|
||||
*
|
||||
* @author nantuko
|
||||
* @author Simown
|
||||
* @author nantuko, Simown, JayDi85
|
||||
*/
|
||||
public final class DeckGenerator {
|
||||
|
||||
|
|
@ -124,7 +125,7 @@ public final class DeckGenerator {
|
|||
|
||||
genPool = new DeckGeneratorPool(deckSize, genDialog.getCreaturePercentage(), genDialog.getNonCreaturePercentage(),
|
||||
genDialog.getLandPercentage(), allowedColors, genDialog.isSingleton(), genDialog.isColorless(),
|
||||
genDialog.isAdvanced(), genDialog.getDeckGeneratorCMC());
|
||||
genDialog.isCommander(), genDialog.isAdvanced(), genDialog.getDeckGeneratorCMC());
|
||||
|
||||
final String[] sets = setsToUse.toArray(new String[setsToUse.size()]);
|
||||
|
||||
|
|
@ -155,8 +156,8 @@ public final class DeckGenerator {
|
|||
// Generate basic land cards
|
||||
Map<String, List<CardInfo>> basicLands = DeckGeneratorPool.generateBasicLands(setsToUse);
|
||||
|
||||
DeckGeneratorPool.generateSpells(creatureCriteria, genPool.getCreatureCount());
|
||||
DeckGeneratorPool.generateSpells(nonCreatureCriteria, genPool.getNonCreatureCount());
|
||||
DeckGeneratorPool.generateSpells(creatureCriteria, genPool.getCreatureCount(), genPool.getCommandersCount());
|
||||
DeckGeneratorPool.generateSpells(nonCreatureCriteria, genPool.getNonCreatureCount(), 0);
|
||||
DeckGeneratorPool.generateLands(genDialog.useNonBasicLand(), nonBasicLandCriteria, basicLands);
|
||||
|
||||
// Reconstructs the final deck and adjusts for Math rounding and/or missing cards
|
||||
|
|
|
|||
|
|
@ -1,46 +1,76 @@
|
|||
|
||||
package mage.client.deck.generator;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Mana value distribution between cards in diff deck sizes
|
||||
*/
|
||||
public enum DeckGeneratorCMC {
|
||||
|
||||
Low(ImmutableList.<CMC>builder()
|
||||
Low(
|
||||
// 100
|
||||
ImmutableList.<CMC>builder()
|
||||
.add(new CMC(0, 2, 0.55f))
|
||||
.add(new CMC(3, 4, 0.30f))
|
||||
.add(new CMC(5, 6, 0.15f)).build(),
|
||||
// 60
|
||||
ImmutableList.<CMC>builder()
|
||||
.add(new CMC(0, 2, 0.60f))
|
||||
.add(new CMC(3, 4, 0.30f))
|
||||
.add(new CMC(5, 6, 0.10f)).build(),
|
||||
// 40
|
||||
ImmutableList.<CMC>builder()
|
||||
.add(new CMC(0, 2, 0.65f))
|
||||
.add(new CMC(3, 4, 0.30f))
|
||||
.add(new CMC(5, 5, 0.05f)).build()),
|
||||
Default(ImmutableList.<CMC>builder()
|
||||
Default(
|
||||
// 100
|
||||
ImmutableList.<CMC>builder()
|
||||
.add(new CMC(0, 2, 0.15f))
|
||||
.add(new CMC(3, 5, 0.50f))
|
||||
.add(new CMC(6, 7, 0.30f))
|
||||
.add(new CMC(8, 100, 0.05f)).build(),
|
||||
// 60
|
||||
ImmutableList.<CMC>builder()
|
||||
.add(new CMC(0, 2, 0.20f))
|
||||
.add(new CMC(3, 5, 0.50f))
|
||||
.add(new CMC(6, 7, 0.25f))
|
||||
.add(new CMC(8, 100, 0.05f)).build(),
|
||||
// 40
|
||||
ImmutableList.<CMC>builder()
|
||||
.add(new CMC(0, 2, 0.30f))
|
||||
.add(new CMC(3, 4, 0.45f))
|
||||
.add(new CMC(5, 6, 0.20f))
|
||||
.add(new CMC(7, 100, 0.05f)).build()),
|
||||
|
||||
High(ImmutableList.<CMC>builder().
|
||||
High(
|
||||
// 100
|
||||
ImmutableList.<CMC>builder().
|
||||
add(new CMC(0, 2, 0.05f))
|
||||
.add(new CMC(3, 5, 0.40f))
|
||||
.add(new CMC(6, 7, 0.40f))
|
||||
.add(new CMC(8, 100, 0.15f)).build(),
|
||||
// 60
|
||||
ImmutableList.<CMC>builder().
|
||||
add(new CMC(0, 2, 0.05f))
|
||||
.add(new CMC(3, 5, 0.35f))
|
||||
.add(new CMC(6, 7, 0.40f))
|
||||
.add(new CMC(8, 100, 0.15f)).build(),
|
||||
// 40
|
||||
ImmutableList.<CMC>builder().
|
||||
add(new CMC(0, 2, 0.10f))
|
||||
.add(new CMC(3, 4, 0.30f))
|
||||
.add(new CMC(5, 6, 0.45f))
|
||||
.add(new CMC(7, 100, 0.15f)).build());
|
||||
|
||||
private final List<CMC> poolCMCs100;
|
||||
private final List<CMC> poolCMCs60;
|
||||
private final List<CMC> poolCMCs40;
|
||||
|
||||
DeckGeneratorCMC(List<CMC> CMCs60, List<CMC> CMCs40) {
|
||||
DeckGeneratorCMC(List<CMC> CMCs100, List<CMC> CMCs60, List<CMC> CMCs40) {
|
||||
this.poolCMCs100 = CMCs100;
|
||||
this.poolCMCs60 = CMCs60;
|
||||
this.poolCMCs40 = CMCs40;
|
||||
}
|
||||
|
|
@ -53,6 +83,10 @@ public enum DeckGeneratorCMC {
|
|||
return this.poolCMCs60;
|
||||
}
|
||||
|
||||
public List<CMC> get100CardPoolCMC() {
|
||||
return this.poolCMCs100;
|
||||
}
|
||||
|
||||
static class CMC {
|
||||
public final int min;
|
||||
public final int max;
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public class DeckGeneratorDialog {
|
|||
private static String selectedColors;
|
||||
private static JComboBox cbSets, cbDeckSize, cbCMC;
|
||||
private static JButton btnGenerate, btnCancel, btnReset;
|
||||
private static JCheckBox cArtifacts, cSingleton, cNonBasicLands, cColorless, cAdvanced;
|
||||
private static JCheckBox cArtifacts, cSingleton, cNonBasicLands, cColorless, cAdvanced, cCommander;
|
||||
private static JLabel averageCMCLabel;
|
||||
private static SimpleDateFormat dateFormat;
|
||||
private static RatioAdjustingSliderPanel adjustingSliderPanel;
|
||||
|
|
@ -63,7 +63,7 @@ public class DeckGeneratorDialog {
|
|||
c.insets = new Insets(5, 10, 0, 10);
|
||||
c.gridx = 1;
|
||||
c.gridy = 0;
|
||||
String chosen = MageFrame.getPreferences().get("genDeckColor", "u");
|
||||
String chosen = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORS, "u");
|
||||
final ColorsChooser colorsChooser = new ColorsChooser(chosen);
|
||||
mainPanel.add(colorsChooser, c);
|
||||
|
||||
|
|
@ -135,7 +135,7 @@ public class DeckGeneratorDialog {
|
|||
c.ipadx = 30;
|
||||
c.insets = new Insets(5, 10, 0, 10);
|
||||
c.weightx = 0.90;
|
||||
cbDeckSize = new JComboBox<>(new String[]{"40", "60"});
|
||||
cbDeckSize = new JComboBox<>(new String[]{"40", "60", "100"});
|
||||
cbDeckSize.setSelectedIndex(0);
|
||||
cbDeckSize.setAlignmentX(Component.LEFT_ALIGNMENT);
|
||||
mainPanel.add(cbDeckSize, c);
|
||||
|
|
@ -148,31 +148,33 @@ public class DeckGeneratorDialog {
|
|||
JPanel jCheckBoxes = new JPanel(new FlowLayout(FlowLayout.LEFT));
|
||||
|
||||
// Singletons
|
||||
boolean commanderEnabled = Boolean.parseBoolean(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COMMANDER, "false"));
|
||||
cSingleton = new JCheckBox("Singleton", false);
|
||||
cSingleton.setToolTipText("Allow only a single copy of each non-land card in your deck.");
|
||||
String singletonEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_SINGLETON, "false");
|
||||
cSingleton.setSelected(Boolean.valueOf(singletonEnabled));
|
||||
cSingleton.setSelected(Boolean.parseBoolean(singletonEnabled));
|
||||
jCheckBoxes.add(cSingleton);
|
||||
cSingleton.setEnabled(!commanderEnabled);
|
||||
|
||||
// Artifacts
|
||||
cArtifacts = new JCheckBox("Artifacts", false);
|
||||
cArtifacts.setToolTipText("Use artifacts and artifact creatures in your deck.");
|
||||
String artifactEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ARTIFACTS, "false");
|
||||
cArtifacts.setSelected(Boolean.valueOf(artifactEnabled));
|
||||
cArtifacts.setSelected(Boolean.parseBoolean(artifactEnabled));
|
||||
jCheckBoxes.add(cArtifacts);
|
||||
|
||||
// Non-basic lands
|
||||
cNonBasicLands = new JCheckBox("Non-basic Lands", false);
|
||||
cNonBasicLands.setToolTipText("Use non-basic lands in your deck (if applicable).");
|
||||
String nonBasicEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_NON_BASIC_LANDS, "false");
|
||||
cNonBasicLands.setSelected(Boolean.valueOf(nonBasicEnabled));
|
||||
cNonBasicLands.setSelected(Boolean.parseBoolean(nonBasicEnabled));
|
||||
jCheckBoxes.add(cNonBasicLands);
|
||||
|
||||
// Colorless mana
|
||||
cColorless = new JCheckBox("Colorless mana", false);
|
||||
cColorless.setToolTipText("Allow cards with colorless mana cost.");
|
||||
String colorlessEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORLESS, "false");
|
||||
cColorless.setSelected(Boolean.valueOf(colorlessEnabled));
|
||||
cColorless.setSelected(Boolean.parseBoolean(colorlessEnabled));
|
||||
jCheckBoxes.add(cColorless);
|
||||
c.ipadx = 0;
|
||||
c.gridx = 0;
|
||||
|
|
@ -181,12 +183,28 @@ public class DeckGeneratorDialog {
|
|||
c.gridwidth = 3;
|
||||
mainPanel.add(jCheckBoxes, c);
|
||||
|
||||
// Commander
|
||||
cCommander = new JCheckBox("Commander", false);
|
||||
cCommander.setToolTipText("Add legendary creature as commander");
|
||||
cCommander.setSelected(commanderEnabled);
|
||||
jCheckBoxes.add(cCommander);
|
||||
c.ipadx = 0;
|
||||
c.gridx = 0;
|
||||
c.gridy = 3;
|
||||
c.weightx = 1;
|
||||
c.gridwidth = 3;
|
||||
mainPanel.add(jCheckBoxes, c);
|
||||
cCommander.addItemListener(itemEvent -> {
|
||||
// commander require singletone mode
|
||||
cSingleton.setEnabled(!cCommander.isSelected());
|
||||
});
|
||||
|
||||
// Create the advanced configuration panel
|
||||
JPanel advancedPanel = createAdvancedPanel();
|
||||
|
||||
// Advanced checkbox (enable/disable advanced configuration)
|
||||
cAdvanced = new JCheckBox("Advanced");
|
||||
cAdvanced.setToolTipText("Enable advanced configuration options");
|
||||
cAdvanced = new JCheckBox("Customize distribution");
|
||||
cAdvanced.setToolTipText("Customize cards distribution due mana values and types");
|
||||
cAdvanced.addItemListener(itemEvent -> {
|
||||
boolean enable = cAdvanced.isSelected();
|
||||
enableAdvancedPanel(enable);
|
||||
|
|
@ -194,7 +212,7 @@ public class DeckGeneratorDialog {
|
|||
|
||||
// Advanced Checkbox
|
||||
String advancedSavedValue = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, "false");
|
||||
boolean advancedEnabled = Boolean.valueOf(advancedSavedValue);
|
||||
boolean advancedEnabled = Boolean.parseBoolean(advancedSavedValue);
|
||||
enableAdvancedPanel(advancedEnabled);
|
||||
cAdvanced.setSelected(advancedEnabled);
|
||||
c.gridy = 4;
|
||||
|
|
@ -212,7 +230,7 @@ public class DeckGeneratorDialog {
|
|||
colorsChooser.setEnabled(false);
|
||||
selectedColors = (String) colorsChooser.getSelectedItem();
|
||||
dlg.setVisible(false);
|
||||
MageFrame.getPreferences().put("genDeckColor", selectedColors);
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORS, selectedColors);
|
||||
});
|
||||
btnCancel = new JButton("Cancel");
|
||||
btnCancel.addActionListener(e -> {
|
||||
|
|
@ -297,7 +315,7 @@ public class DeckGeneratorDialog {
|
|||
c.gridwidth = 1;
|
||||
c.gridy = 2;
|
||||
btnReset = new JButton("Reset");
|
||||
btnReset.setToolTipText("Reset advanced dialog to default values");
|
||||
btnReset.setToolTipText("Reset custom cards distribution to default values");
|
||||
btnReset.addActionListener(actionEvent -> {
|
||||
cbCMC.setSelectedItem(DeckGeneratorCMC.Default);
|
||||
adjustingSliderPanel.resetValues();
|
||||
|
|
@ -374,6 +392,12 @@ public class DeckGeneratorDialog {
|
|||
return selected;
|
||||
}
|
||||
|
||||
public boolean isCommander() {
|
||||
boolean selected = cCommander.isSelected();
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COMMANDER, Boolean.toString(selected));
|
||||
return selected;
|
||||
}
|
||||
|
||||
public boolean isAdvanced() {
|
||||
boolean selected = cAdvanced.isSelected();
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ADVANCED, Boolean.toString(selected));
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
|
||||
package mage.client.deck.generator;
|
||||
|
||||
import mage.ObjectColor;
|
||||
import mage.abilities.Ability;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.decks.Deck;
|
||||
|
|
@ -11,16 +11,17 @@ import mage.constants.ColoredManaSymbol;
|
|||
import mage.constants.Rarity;
|
||||
import mage.util.RandomUtil;
|
||||
import mage.util.TournamentUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Simown
|
||||
* @author Simown, JayDi85
|
||||
*/
|
||||
public class DeckGeneratorPool
|
||||
{
|
||||
public class DeckGeneratorPool {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(DeckGeneratorPool.class);
|
||||
|
||||
public static final int DEFAULT_CREATURE_PERCENTAGE = 38;
|
||||
public static final int DEFAULT_NON_CREATURE_PERCENTAGE = 21;
|
||||
|
|
@ -31,6 +32,7 @@ public class DeckGeneratorPool
|
|||
private final List<DeckGeneratorCMC.CMC> poolCMCs;
|
||||
private final int creatureCount;
|
||||
private final int nonCreatureCount;
|
||||
private final int commandersCount;
|
||||
private final int landCount;
|
||||
private final boolean isSingleton;
|
||||
private final int deckSize;
|
||||
|
|
@ -56,57 +58,75 @@ public class DeckGeneratorPool
|
|||
* @param isSingleton if the deck only has 1 copy of each non-land card.
|
||||
* @param colorlessAllowed if colourless mana symbols are allowed in costs in the deck.
|
||||
* @param isAdvanced if the user has provided advanced options to generate the deck.
|
||||
* @param isCommander reserve commander card
|
||||
* @param deckGeneratorCMC the CMC curve to use for this deck
|
||||
*/
|
||||
public DeckGeneratorPool(final int deckSize, final int creaturePercentage, final int nonCreaturePercentage, final int landPercentage,
|
||||
final List<ColoredManaSymbol> allowedColors, boolean isSingleton, boolean colorlessAllowed, boolean isAdvanced, DeckGeneratorCMC deckGeneratorCMC)
|
||||
{
|
||||
final List<ColoredManaSymbol> allowedColors, boolean isSingleton, boolean colorlessAllowed, boolean isCommander,
|
||||
boolean isAdvanced, DeckGeneratorCMC deckGeneratorCMC) {
|
||||
this.deckSize = deckSize;
|
||||
this.allowedColors = allowedColors;
|
||||
this.isSingleton = isSingleton;
|
||||
this.colorlessAllowed = colorlessAllowed;
|
||||
this.commandersCount = isCommander ? 1 : 0;
|
||||
this.isSingleton = isSingleton || isCommander; // commander must use singleton mode only
|
||||
|
||||
this.deck = new Deck();
|
||||
|
||||
// Advanced (CMC Slider panel and curve drop-down in the dialog)
|
||||
if(isAdvanced) {
|
||||
this.creatureCount = (int)Math.ceil((deckSize / 100.0) * creaturePercentage);
|
||||
this.nonCreatureCount = (int)Math.ceil((deckSize / 100.0)* nonCreaturePercentage);
|
||||
this.landCount = (int)Math.ceil((deckSize / 100.0)* landPercentage);
|
||||
if(this.deckSize == 60) {
|
||||
if (isAdvanced) {
|
||||
this.creatureCount = (int) Math.ceil((deckSize / 100.0) * creaturePercentage);
|
||||
this.nonCreatureCount = (int) Math.ceil((deckSize / 100.0) * nonCreaturePercentage);
|
||||
this.landCount = (int) Math.ceil((deckSize / 100.0) * landPercentage);
|
||||
switch (this.deckSize) {
|
||||
case 100:
|
||||
this.poolCMCs = deckGeneratorCMC.get100CardPoolCMC();
|
||||
break;
|
||||
case 60:
|
||||
this.poolCMCs = deckGeneratorCMC.get60CardPoolCMC();
|
||||
} else {
|
||||
break;
|
||||
case 40:
|
||||
this.poolCMCs = deckGeneratorCMC.get40CardPoolCMC();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported deck size: " + this.deckSize);
|
||||
}
|
||||
} else {
|
||||
// Ignore the advanced group, just use defaults
|
||||
this.creatureCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_CREATURE_PERCENTAGE);
|
||||
this.nonCreatureCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_NON_CREATURE_PERCENTAGE);
|
||||
this.landCount = (int)Math.ceil((deckSize / 100.0) * DEFAULT_LAND_PERCENTAGE);
|
||||
if(this.deckSize == 60) {
|
||||
this.creatureCount = (int) Math.ceil((deckSize / 100.0) * DEFAULT_CREATURE_PERCENTAGE);
|
||||
this.nonCreatureCount = (int) Math.ceil((deckSize / 100.0) * DEFAULT_NON_CREATURE_PERCENTAGE);
|
||||
this.landCount = (int) Math.ceil((deckSize / 100.0) * DEFAULT_LAND_PERCENTAGE);
|
||||
switch (this.deckSize) {
|
||||
case 100:
|
||||
this.poolCMCs = DeckGeneratorCMC.Default.get100CardPoolCMC();
|
||||
break;
|
||||
case 60:
|
||||
this.poolCMCs = DeckGeneratorCMC.Default.get60CardPoolCMC();
|
||||
} else {
|
||||
break;
|
||||
case 40:
|
||||
this.poolCMCs = DeckGeneratorCMC.Default.get40CardPoolCMC();
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unsupported deck size: " + this.deckSize);
|
||||
}
|
||||
}
|
||||
|
||||
if(allowedColors.size() == 1) {
|
||||
if (allowedColors.size() == 1) {
|
||||
monoColored = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adjusts the number of spell cards that should be in a converted mana cost (CMC) range, given the amount of cards total.
|
||||
*
|
||||
* @param cardsCount the number of total cards.
|
||||
* @return a list of CMC ranges, with the amount of cards for each CMC range
|
||||
*/
|
||||
public List<DeckGeneratorCMC.CMC> getCMCsForSpellCount(int cardsCount) {
|
||||
List<DeckGeneratorCMC.CMC> adjustedCMCs = new ArrayList<>(this.poolCMCs);
|
||||
// For each CMC calculate how many spell cards are needed, given the total amount of cards
|
||||
for(DeckGeneratorCMC.CMC deckCMC : adjustedCMCs) {
|
||||
deckCMC.setAmount((int)Math.ceil(deckCMC.percentage * cardsCount));
|
||||
for (DeckGeneratorCMC.CMC deckCMC : adjustedCMCs) {
|
||||
deckCMC.setAmount((int) Math.ceil(deckCMC.percentage * cardsCount));
|
||||
}
|
||||
return adjustedCMCs;
|
||||
}
|
||||
|
|
@ -115,15 +135,15 @@ public class DeckGeneratorPool
|
|||
* Verifies if the spell card supplied is valid for this pool of cards.
|
||||
* Checks that there isn't too many copies of this card in the deck.
|
||||
* Checks that the card fits the chosen colors for this pool.
|
||||
*
|
||||
* @param card the spell card
|
||||
* @return if the spell card is valid for this pool.
|
||||
*/
|
||||
public boolean isValidSpellCard(Card card)
|
||||
{
|
||||
public boolean isValidSpellCard(Card card) {
|
||||
int cardCount = getCardCount((card.getName()));
|
||||
// Check it hasn't already got the maximum number of copies in a deck
|
||||
if(cardCount < (isSingleton ? 1 : 4)) {
|
||||
if(cardFitsChosenColors(card)) {
|
||||
if (cardCount < (isSingleton ? 1 : 4)) {
|
||||
if (cardFitsChosenColors(card)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -132,11 +152,11 @@ public class DeckGeneratorPool
|
|||
|
||||
/**
|
||||
* Verifies if the non-basic land card supplied is valid for this pool of cards.
|
||||
*
|
||||
* @param card the non-basic land card
|
||||
* @return if the land card generates the allowed colors for this pool.
|
||||
*/
|
||||
public boolean isValidLandCard(Card card)
|
||||
{
|
||||
public boolean isValidLandCard(Card card) {
|
||||
int cardCount = getCardCount((card.getName()));
|
||||
// No need to check if the land is valid for the colors chosen
|
||||
// They are all filtered before searching for lands to include in the deck.
|
||||
|
|
@ -146,22 +166,31 @@ public class DeckGeneratorPool
|
|||
|
||||
/**
|
||||
* Adds a card to the pool and updates the count of this card.
|
||||
*
|
||||
* @param card the card to add.
|
||||
*/
|
||||
public void addCard(Card card)
|
||||
{
|
||||
public void addCard(Card card) {
|
||||
Object cnt = cardCounts.get((card.getName()));
|
||||
if(cnt == null)
|
||||
if (cnt == null)
|
||||
cardCounts.put(card.getName(), 0);
|
||||
int existingCount = cardCounts.get((card.getName()));
|
||||
cardCounts.put(card.getName(), existingCount+1);
|
||||
cardCounts.put(card.getName(), existingCount + 1);
|
||||
deckCards.add(card);
|
||||
}
|
||||
|
||||
public void clearCards(boolean isClearReserve) {
|
||||
cardCounts.clear();
|
||||
deckCards.clear();
|
||||
if (isClearReserve) {
|
||||
reserveSpells.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a card to the reserve pool.
|
||||
* Reserve pool is used when the deck generation fails to build a complete deck, or
|
||||
* a partially complete deck (e.g. if there are no cards found that match a CMC)
|
||||
*
|
||||
* @param card the card to add
|
||||
* @param cardCMC the converted mana cost of the card
|
||||
*/
|
||||
|
|
@ -169,37 +198,24 @@ public class DeckGeneratorPool
|
|||
// Only cards with CMC < 7 and don't already exist in the deck
|
||||
// can be added to our reserve pool as not to overwhelm the curve
|
||||
// with high CMC cards and duplicates.
|
||||
if(cardCMC < 7 && getCardCount(card.getName()) == 0) {
|
||||
if (cardCMC < 7 && getCardCount(card.getName()) == 0) {
|
||||
this.reserveSpells.add(card);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the mana symbols in the card all match the allowed colors for this pool.
|
||||
* @param card the spell card to check.
|
||||
* @return if all the mana symbols fit the chosen colors.
|
||||
*/
|
||||
private boolean cardFitsChosenColors(Card card) {
|
||||
for (String symbol : card.getManaCostSymbols()) {
|
||||
boolean found = false;
|
||||
symbol = symbol.replace("{", "").replace("}", "");
|
||||
if (isColoredManaSymbol(symbol)) {
|
||||
for (ColoredManaSymbol allowed : allowedColors) {
|
||||
if (symbol.contains(allowed.toString())) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
Set<String> needColors = allowedColors.stream().map(ColoredManaSymbol::toString).collect(Collectors.toSet());
|
||||
List<ObjectColor> cardColors = card.getColorIdentity().getColors();
|
||||
for (ObjectColor cardColor : cardColors) {
|
||||
if (!needColors.contains(cardColor.toString())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (symbol.equals("C") && !colorlessAllowed) {
|
||||
if (cardColors.isEmpty() && !colorlessAllowed) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -208,6 +224,7 @@ public class DeckGeneratorPool
|
|||
* Calculates the percentage of colored mana symbols over all spell cards in the deck.
|
||||
* Used to balance the generation of basic lands so the amount of lands matches the
|
||||
* cards mana costs.
|
||||
*
|
||||
* @return a list of colored mana symbols and the percentage of symbols seen in cards mana costs.
|
||||
*/
|
||||
public Map<String, Double> calculateSpellColorPercentages() {
|
||||
|
|
@ -221,14 +238,14 @@ public class DeckGeneratorPool
|
|||
int totalCount = 0;
|
||||
|
||||
List<Card> fixedSpells = getFixedSpells();
|
||||
for(Card spell: fixedSpells) {
|
||||
for (Card spell : fixedSpells) {
|
||||
for (String symbol : spell.getManaCostSymbols()) {
|
||||
symbol = symbol.replace("{", "").replace("}", "");
|
||||
if (isColoredManaSymbol(symbol)) {
|
||||
for (ColoredManaSymbol allowed : allowedColors) {
|
||||
if (symbol.contains(allowed.toString())) {
|
||||
int cnt = colorCount.get(allowed.toString());
|
||||
colorCount.put(allowed.toString(), cnt+1);
|
||||
colorCount.put(allowed.toString(), cnt + 1);
|
||||
totalCount++;
|
||||
}
|
||||
}
|
||||
|
|
@ -236,7 +253,7 @@ public class DeckGeneratorPool
|
|||
}
|
||||
}
|
||||
final Map<String, Double> percentages = new HashMap<>();
|
||||
for(Map.Entry<String, Integer> singleCount: colorCount.entrySet()) {
|
||||
for (Map.Entry<String, Integer> singleCount : colorCount.entrySet()) {
|
||||
String color = singleCount.getKey();
|
||||
int count = singleCount.getValue();
|
||||
// Calculate the percentage this color has out of the total color counts
|
||||
|
|
@ -248,20 +265,20 @@ public class DeckGeneratorPool
|
|||
|
||||
/**
|
||||
* Calculates how many of each mana the non-basic lands produce.
|
||||
*
|
||||
* @param deckLands the non-basic lands which will be used in the deck.
|
||||
* @return a mapping of colored mana symbol to the amount that can be produced.
|
||||
*/
|
||||
public Map<String,Integer> countManaProduced(List<Card> deckLands)
|
||||
{
|
||||
public Map<String, Integer> countManaProduced(List<Card> deckLands) {
|
||||
Map<String, Integer> manaCounts = new HashMap<>();
|
||||
for (final ColoredManaSymbol color : ColoredManaSymbol.values()) {
|
||||
manaCounts.put(color.toString(), 0);
|
||||
}
|
||||
for(Card land: deckLands) {
|
||||
for(Ability landAbility: land.getAbilities()) {
|
||||
for (Card land : deckLands) {
|
||||
for (Ability landAbility : land.getAbilities()) {
|
||||
for (ColoredManaSymbol symbol : allowedColors) {
|
||||
String abilityString = landAbility.getRule();
|
||||
if(landTapsForAllowedColor(abilityString, symbol.toString())) {
|
||||
if (landTapsForAllowedColor(abilityString, symbol.toString())) {
|
||||
Integer count = manaCounts.get(symbol.toString());
|
||||
manaCounts.put(symbol.toString(), count + 1);
|
||||
}
|
||||
|
|
@ -271,15 +288,17 @@ public class DeckGeneratorPool
|
|||
return manaCounts;
|
||||
}
|
||||
|
||||
/** Filter all the non-basic lands retrieved from the database.
|
||||
/**
|
||||
* Filter all the non-basic lands retrieved from the database.
|
||||
*
|
||||
* @param landCardsInfo information about all the cards.
|
||||
* @return a list of cards that produce the allowed colors for this pool.
|
||||
*/
|
||||
public List<Card> filterLands(List<CardInfo> landCardsInfo) {
|
||||
List<Card> matchingLandList = new ArrayList<>();
|
||||
for(CardInfo landCardInfo: landCardsInfo) {
|
||||
for (CardInfo landCardInfo : landCardsInfo) {
|
||||
Card landCard = landCardInfo.createMockCard();
|
||||
if(landProducesChosenColors(landCard)) {
|
||||
if (landProducesChosenColors(landCard)) {
|
||||
matchingLandList.add(landCard);
|
||||
}
|
||||
}
|
||||
|
|
@ -288,11 +307,12 @@ public class DeckGeneratorPool
|
|||
|
||||
/**
|
||||
* Returns the card name that represents the basic land for this color.
|
||||
*
|
||||
* @param symbolString the colored mana symbol.
|
||||
* @return the name of a basic land card.
|
||||
*/
|
||||
public static String getBasicLandName(String symbolString) {
|
||||
switch(symbolString) {
|
||||
switch (symbolString) {
|
||||
case "B":
|
||||
return "Swamp";
|
||||
case "G":
|
||||
|
|
@ -311,25 +331,50 @@ public class DeckGeneratorPool
|
|||
|
||||
/**
|
||||
* Returns a complete deck.
|
||||
*
|
||||
* @return the deck.
|
||||
*/
|
||||
public Deck getDeck() {
|
||||
Set<Card> actualDeck = deck.getCards();
|
||||
actualDeck.addAll(deckCards);
|
||||
deck.getCards().clear();
|
||||
deck.getSideboard().clear();
|
||||
|
||||
List<Card> useCards = new ArrayList<>(deckCards);
|
||||
List<Card> useCommanders = new ArrayList<>();
|
||||
|
||||
// take random commanders
|
||||
if (commandersCount > 0) {
|
||||
List<Card> possibleCommanders = deckCards.stream()
|
||||
.filter(this::isValidCommander)
|
||||
.collect(Collectors.toList());
|
||||
Card nextCommander = RandomUtil.randomFromCollection(possibleCommanders);
|
||||
while (nextCommander != null && useCommanders.size() < commandersCount) {
|
||||
useCards.remove(nextCommander);
|
||||
useCommanders.add(nextCommander);
|
||||
}
|
||||
}
|
||||
|
||||
deck.getCards().addAll(useCards);
|
||||
deck.getSideboard().addAll(useCommanders);
|
||||
return deck;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the number of creatures needed in this pool.
|
||||
*
|
||||
* @return the number of creatures.
|
||||
*/
|
||||
public int getCreatureCount() {
|
||||
return creatureCount;
|
||||
}
|
||||
|
||||
public int getCommandersCount() {
|
||||
return commandersCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the number of non-creatures needed in this pool.
|
||||
*
|
||||
* @return the number of non-creatures.
|
||||
*/
|
||||
public int getNonCreatureCount() {
|
||||
|
|
@ -338,6 +383,7 @@ public class DeckGeneratorPool
|
|||
|
||||
/**
|
||||
* Returns the number of lands (basic + non-basic) needed in this pool.
|
||||
*
|
||||
* @return the number of lands.
|
||||
*/
|
||||
public int getLandCount() {
|
||||
|
|
@ -346,6 +392,7 @@ public class DeckGeneratorPool
|
|||
|
||||
/**
|
||||
* Returns if this pool only uses one color.
|
||||
*
|
||||
* @return if this pool is monocolored.
|
||||
*/
|
||||
public boolean isMonoColoredDeck() {
|
||||
|
|
@ -354,6 +401,7 @@ public class DeckGeneratorPool
|
|||
|
||||
/**
|
||||
* Returns the size of the deck to generate from this pool.
|
||||
*
|
||||
* @return the deck size.
|
||||
*/
|
||||
public int getDeckSize() {
|
||||
|
|
@ -364,20 +412,20 @@ public class DeckGeneratorPool
|
|||
* Fixes undersized or oversized decks that have been generated.
|
||||
* Removes random cards from an oversized deck until it is the correct size.
|
||||
* Uses the reserve pool to fill up and undersized deck with cards.
|
||||
*
|
||||
* @return a fixed list of cards for this deck.
|
||||
*/
|
||||
private List<Card> getFixedSpells()
|
||||
{
|
||||
private List<Card> getFixedSpells() {
|
||||
int spellSize = deckCards.size();
|
||||
int nonLandSize = (deckSize - landCount);
|
||||
|
||||
// Less spells than needed
|
||||
if(spellSize < nonLandSize) {
|
||||
if (spellSize < nonLandSize) {
|
||||
|
||||
int spellsNeeded = nonLandSize-spellSize;
|
||||
int spellsNeeded = nonLandSize - spellSize;
|
||||
|
||||
// If we haven't got enough spells in reserve to fulfil the amount we need, skip adding any.
|
||||
if(reserveSpells.size() >= spellsNeeded) {
|
||||
if (reserveSpells.size() >= spellsNeeded) {
|
||||
|
||||
List<Card> spellsToAdd = new ArrayList<>(spellsNeeded);
|
||||
|
||||
|
|
@ -398,16 +446,16 @@ public class DeckGeneratorPool
|
|||
}
|
||||
|
||||
// More spells than needed
|
||||
else if(spellSize > (deckSize - landCount)) {
|
||||
int spellsRemoved = (spellSize)-(deckSize-landCount);
|
||||
for(int i = 0; i < spellsRemoved; ++i) {
|
||||
else if (spellSize > (deckSize - landCount)) {
|
||||
int spellsRemoved = (spellSize) - (deckSize - landCount);
|
||||
for (int i = 0; i < spellsRemoved; ++i) {
|
||||
deckCards.remove(RandomUtil.nextInt(deckCards.size()));
|
||||
}
|
||||
}
|
||||
|
||||
// Check we have exactly the right amount of cards for a deck.
|
||||
if(deckCards.size() != nonLandSize) {
|
||||
throw new IllegalStateException("Not enough cards found to generate deck.");
|
||||
if (deckCards.size() != nonLandSize) {
|
||||
logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors");
|
||||
}
|
||||
// Return the fixed amount
|
||||
return deckCards;
|
||||
|
|
@ -416,6 +464,7 @@ public class DeckGeneratorPool
|
|||
/**
|
||||
* Returns if this land taps for the given color.
|
||||
* Basic string matching to check the ability adds one of the chosen mana when tapped.
|
||||
*
|
||||
* @param ability MockAbility of the land card
|
||||
* @param symbol colored mana symbol.
|
||||
* @return if the ability is tapping to produce the mana the symbol represents.
|
||||
|
|
@ -426,6 +475,7 @@ public class DeckGeneratorPool
|
|||
|
||||
/**
|
||||
* Returns if this land will produce the chosen colors for this pool.
|
||||
*
|
||||
* @param card a non-basic land card.
|
||||
* @return if this land card taps to produces the colors chosen.
|
||||
*/
|
||||
|
|
@ -434,32 +484,49 @@ public class DeckGeneratorPool
|
|||
// and other Abilities so we have to do some basic string matching on land cards for now.
|
||||
List<Ability> landAbilities = card.getAbilities();
|
||||
int count = 0;
|
||||
for(Ability ability : landAbilities) {
|
||||
for (Ability ability : landAbilities) {
|
||||
String abilityString = ability.getRule();
|
||||
// Lands that tap to produce mana of the chosen colors
|
||||
for(ColoredManaSymbol symbol : allowedColors) {
|
||||
if(landTapsForAllowedColor(abilityString, symbol.toString())) {
|
||||
for (ColoredManaSymbol symbol : allowedColors) {
|
||||
if (landTapsForAllowedColor(abilityString, symbol.toString())) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if(count > 1) {
|
||||
if (count > 1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isValidCommander(Card card) {
|
||||
// commander must be legendary creature
|
||||
if (!card.isCreature() || !card.isLegendary()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// commander must have all chosen colors
|
||||
for (ColoredManaSymbol symbol : allowedColors) {
|
||||
if (!card.getColor().contains(new ObjectColor(symbol.toString()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns if the symbol is a colored mana symbol.
|
||||
*
|
||||
* @param symbol the symbol to check.
|
||||
* @return If it is a basic mana symbol or a hybrid mana symbol.
|
||||
*/
|
||||
private static boolean isColoredManaSymbol(String symbol) {
|
||||
// Hybrid mana
|
||||
if(symbol.contains("/")) {
|
||||
if (symbol.contains("/")) {
|
||||
return true;
|
||||
}
|
||||
for(ColoredManaSymbol c: ColoredManaSymbol.values()) {
|
||||
for (ColoredManaSymbol c : ColoredManaSymbol.values()) {
|
||||
if (symbol.charAt(0) == (c.toString().charAt(0))) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -470,12 +537,13 @@ public class DeckGeneratorPool
|
|||
/**
|
||||
* Returns how many of this card is in the pool.
|
||||
* If there are none in the pool it will initalise the card count.
|
||||
*
|
||||
* @param cardName the name of the card to check.
|
||||
* @return the number of cards in the pool of this name.
|
||||
*/
|
||||
private int getCardCount(String cardName) {
|
||||
Object cC = cardCounts.get((cardName));
|
||||
if(cC == null)
|
||||
if (cC == null)
|
||||
cardCounts.put(cardName, 0);
|
||||
return cardCounts.get((cardName));
|
||||
}
|
||||
|
|
@ -542,22 +610,49 @@ public class DeckGeneratorPool
|
|||
* reasonable mix of both.
|
||||
*
|
||||
* @param criteria the criteria to search for in the database.
|
||||
* @param spellCount the number of spells that match the criteria needed in
|
||||
* @param needCardsCount the number of spells that match the criteria needed in
|
||||
* the deck.
|
||||
* @param needCommandersCount make sure it contains commander creature (must be uses on first generateSpells only)
|
||||
*/
|
||||
protected static void generateSpells(CardCriteria criteria, int spellCount) {
|
||||
protected static void generateSpells(CardCriteria criteria, int needCardsCount, int needCommandersCount) {
|
||||
DeckGeneratorPool genPool = DeckGenerator.genPool;
|
||||
if (needCommandersCount > 0 && !genPool.cardCounts.isEmpty()) {
|
||||
throw new IllegalArgumentException("Wrong code usage: generateSpells with creatures and commanders must be called as first");
|
||||
}
|
||||
List<CardInfo> cardPool = CardRepository.instance.findCards(criteria);
|
||||
int retrievedCount = cardPool.size();
|
||||
List<DeckGeneratorCMC.CMC> deckCMCs = genPool.getCMCsForSpellCount(spellCount);
|
||||
List<DeckGeneratorCMC.CMC> deckCMCs = genPool.getCMCsForSpellCount(needCardsCount);
|
||||
int count = 0;
|
||||
int validCommanders = 0;
|
||||
int reservesAdded = 0;
|
||||
boolean added;
|
||||
if (retrievedCount > 0 && retrievedCount >= spellCount) {
|
||||
if (cardPool.size() > 0 && cardPool.size() >= needCardsCount) {
|
||||
int tries = 0;
|
||||
while (count < spellCount) {
|
||||
Card card = cardPool.get(RandomUtil.nextInt(retrievedCount)).createMockCard();
|
||||
if (genPool.isValidSpellCard(card)) {
|
||||
while (true) {
|
||||
tries++;
|
||||
|
||||
// can't finish deck, stop and use reserved cards later
|
||||
if (tries > DeckGenerator.MAX_TRIES) {
|
||||
logger.info("Can't generate full deck for selected settings - try again or choose more sets and less colors");
|
||||
break;
|
||||
}
|
||||
|
||||
// can finish deck - but make sure it has commander
|
||||
if (count >= needCardsCount) {
|
||||
if (validCommanders < needCommandersCount) {
|
||||
// reset deck search from scratch (except reserved cards)
|
||||
count = 0;
|
||||
validCommanders = 0;
|
||||
deckCMCs = genPool.getCMCsForSpellCount(needCardsCount);
|
||||
genPool.clearCards(false);
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Card card = cardPool.get(RandomUtil.nextInt(cardPool.size())).createMockCard();
|
||||
if (!genPool.isValidSpellCard(card)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int cardCMC = card.getManaValue();
|
||||
for (DeckGeneratorCMC.CMC deckCMC : deckCMCs) {
|
||||
if (cardCMC >= deckCMC.min && cardCMC <= deckCMC.max) {
|
||||
|
|
@ -566,21 +661,18 @@ public class DeckGeneratorPool
|
|||
deckCMC.setAmount(currentAmount - 1);
|
||||
genPool.addCard(card.copy());
|
||||
count++;
|
||||
// make sure it has compatible commanders
|
||||
if (genPool.isValidCommander(card)) {
|
||||
validCommanders++;
|
||||
}
|
||||
}
|
||||
} else if (reservesAdded < (genPool.getDeckSize() / 2)) {
|
||||
added = genPool.tryAddReserve(card, cardCMC);
|
||||
if (added) {
|
||||
if (genPool.tryAddReserve(card, cardCMC)) {
|
||||
reservesAdded++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
tries++;
|
||||
if (tries > DeckGenerator.MAX_TRIES) {
|
||||
// Break here, we'll fill in random missing ones later
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new IllegalStateException("Not enough cards to generate deck.");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -780,7 +780,7 @@
|
|||
</Component>
|
||||
<Component class="javax.swing.JTextField" name="jTextFieldSearch">
|
||||
<Properties>
|
||||
<Property name="toolTipText" type="java.lang.String" value="Searches for card names and in the rule text of the card."/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="Search cards by any data like name or mana symbols like {W}, {U}, {C}, etc (use quotes for exact search)"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="chkNames">
|
||||
|
|
|
|||
|
|
@ -1085,7 +1085,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
|
|||
}
|
||||
});
|
||||
|
||||
jTextFieldSearch.setToolTipText("Searches for card names and in the rule text of the card.");
|
||||
jTextFieldSearch.setToolTipText("Search cards by any data like name or mana symbols like {W}, {U}, {C}, etc (use quotes for exact search)");
|
||||
|
||||
chkNames.setSelected(true);
|
||||
chkNames.setText("Names");
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ public class DeckArea extends javax.swing.JPanel {
|
|||
sideboardList.deselectAll();
|
||||
for (CardView card : cards) {
|
||||
CardView newCard = new CardView(card);
|
||||
deckList.addCardView(newCard, true);
|
||||
deckList.addCardView(newCard, card);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -150,7 +150,7 @@ public class DeckArea extends javax.swing.JPanel {
|
|||
deckList.deselectAll();
|
||||
for (CardView card : cards) {
|
||||
CardView newCard = new CardView(card);
|
||||
sideboardList.addCardView(newCard, true);
|
||||
sideboardList.addCardView(newCard, card);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -533,10 +533,10 @@
|
|||
<Component class="mage.client.deckeditor.DeckLegalityPanel" name="deckLegalityDisplay">
|
||||
<Properties>
|
||||
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[245, 155]"/>
|
||||
<Dimension value="[245, 255]"/>
|
||||
</Property>
|
||||
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
|
||||
<Dimension value="[85, 155]"/>
|
||||
<Dimension value="[85, 255]"/>
|
||||
</Property>
|
||||
<Property name="opaque" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
|
|
|
|||
|
|
@ -121,12 +121,8 @@ public class DeckEditorPanel extends javax.swing.JPanel {
|
|||
if (!SwingUtilities.isLeftMouseButton(e)) {
|
||||
return;
|
||||
}
|
||||
List<String> cardNames = new ArrayList<>();
|
||||
LegalityLabel label = (LegalityLabel) e.getComponent();
|
||||
label.getValidator().getErrorsList().stream()
|
||||
.map(DeckValidatorError::getCardName)
|
||||
.filter(Objects::nonNull)
|
||||
.forEach(cardNames::add);
|
||||
List<String> cardNames = new ArrayList<>(label.selectCards());
|
||||
deckArea.getDeckList().deselectAll();
|
||||
deckArea.getDeckList().selectByName(cardNames);
|
||||
deckArea.getSideboardList().deselectAll();
|
||||
|
|
@ -1290,8 +1286,8 @@ public class DeckEditorPanel extends javax.swing.JPanel {
|
|||
|
||||
panelInfo.setOpaque(false);
|
||||
|
||||
deckLegalityDisplay.setMaximumSize(new java.awt.Dimension(245, 155));
|
||||
deckLegalityDisplay.setMinimumSize(new java.awt.Dimension(85, 155));
|
||||
deckLegalityDisplay.setMaximumSize(new java.awt.Dimension(245, 255));
|
||||
deckLegalityDisplay.setMinimumSize(new java.awt.Dimension(85, 255));
|
||||
deckLegalityDisplay.setOpaque(false);
|
||||
deckLegalityDisplay.setVisible(false);
|
||||
|
||||
|
|
@ -1313,7 +1309,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
|
|||
panelInfoLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
|
||||
.addGroup(panelInfoLayout.createSequentialGroup()
|
||||
.addContainerGap()
|
||||
.addComponent(deckLegalityDisplay, javax.swing.GroupLayout.PREFERRED_SIZE, 155, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addComponent(deckLegalityDisplay, javax.swing.GroupLayout.PREFERRED_SIZE, 255, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
|
||||
.addComponent(bigCard, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap())
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import mage.cards.decks.Deck;
|
|||
import mage.cards.decks.DeckValidator;
|
||||
import mage.cards.mock.MockCard;
|
||||
import mage.cards.mock.MockSplitCard;
|
||||
import mage.client.components.BracketLegalityLabel;
|
||||
import mage.client.components.EdhPowerLevelLegalityLabel;
|
||||
import mage.client.components.LegalityLabel;
|
||||
import mage.deck.*;
|
||||
import org.apache.log4j.Logger;
|
||||
|
|
@ -15,7 +17,7 @@ import java.util.stream.Stream;
|
|||
|
||||
|
||||
/**
|
||||
* @author Elandril
|
||||
* @author Elandril, JayDi85
|
||||
*/
|
||||
public class DeckLegalityPanel extends javax.swing.JPanel {
|
||||
|
||||
|
|
@ -101,6 +103,14 @@ public class DeckLegalityPanel extends javax.swing.JPanel {
|
|||
new Frontier(), new HistoricalType2(), new PennyDreadfulCommander(), new EuropeanHighlander(), new CanadianHighlander()
|
||||
// not used: new Eternal(), new Momir(), new TinyLeaders()
|
||||
).forEach(this::addLegalityLabel);
|
||||
|
||||
// extra buttons like score
|
||||
this.add(new EdhPowerLevelLegalityLabel());
|
||||
// only 3 buttons allowed for one line
|
||||
this.add(new BracketLegalityLabel(BracketLegalityLabel.BracketLevel.BRACKET_1));
|
||||
this.add(new BracketLegalityLabel(BracketLegalityLabel.BracketLevel.BRACKET_2_3));
|
||||
this.add(new BracketLegalityLabel(BracketLegalityLabel.BracketLevel.BRACKET_4_5));
|
||||
|
||||
addHidePanelButton();
|
||||
|
||||
revalidate();
|
||||
|
|
@ -147,5 +157,4 @@ public class DeckLegalityPanel extends javax.swing.JPanel {
|
|||
.map(LegalityLabel.class::cast)
|
||||
.forEach(label -> label.validateDeck(deckToValidate));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2183,7 +2183,7 @@
|
|||
<Component id="panelCardStyles" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="panelCardImages" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="306" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="309" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
|
|
@ -2224,7 +2224,7 @@
|
|||
<Component id="cbPreferredImageLanguage" min="-2" pref="153" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="480" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
|
|
@ -2283,7 +2283,6 @@
|
|||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="cbPreferredImageLanguage">
|
||||
<Properties>
|
||||
<Property name="maximumRowCount" type="int" value="20"/>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="4">
|
||||
<StringItem index="0" value="Item 1"/>
|
||||
|
|
@ -2318,15 +2317,48 @@
|
|||
</Property>
|
||||
</Properties>
|
||||
|
||||
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
|
||||
<Property name="axis" type="int" value="1"/>
|
||||
<Layout>
|
||||
<DimensionLayout dim="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="cbCardRenderIconsForAbilities" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderIconsForPlayable" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator1" min="-2" pref="775" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderShowReminderText" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderHideSetSymbol" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderShowAbilityTextOverlay" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
|
||||
<Component id="labelRenderMode" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderImageFallback" min="-2" pref="122" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
<DimensionLayout dim="1">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Group type="102" attributes="0">
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="labelRenderMode" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderImageFallback" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderIconsForAbilities" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderIconsForPlayable" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="jSeparator1" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderShowReminderText" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderHideSetSymbol" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
|
||||
<Component id="cbCardRenderShowAbilityTextOverlay" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
</Layout>
|
||||
<SubComponents>
|
||||
<Component class="javax.swing.JCheckBox" name="cbCardRenderImageFallback">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Render mode: MTGO style (off) or IMAGE style (on)"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JCheckBox" name="cbCardRenderIconsForAbilities">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Enable card icons for abilities (example: flying, deathtouch)"/>
|
||||
|
|
@ -2354,6 +2386,28 @@
|
|||
<Property name="text" type="java.lang.String" value="Show ability text as overlay in big card view"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="labelRenderMode">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Render Mode:"/>
|
||||
<Property name="toolTipText" type="java.lang.String" value="<HTML>Image - Renders card image with text overlay<br> MTGO - Renders card frame around card art<br> Forced M15 - Renders all cards in the modern frame<br> Forced Retro - Renders all cards in the retro frame"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="cbCardRenderImageFallback">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="4">
|
||||
<StringItem index="0" value="Item 1"/>
|
||||
<StringItem index="1" value="Item 2"/>
|
||||
<StringItem index="2" value="Item 3"/>
|
||||
<StringItem index="3" value="Item 4"/>
|
||||
</StringArray>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" value="<HTML>Image - Renders card image with text overlay<br> MTGO - Renders card frame around card art<br> Forced M15 - Renders all cards in the MTGO style with the modern frame<br> Forced Retro - Renders all cards in the MTGO style with the retro frame"/>
|
||||
</Properties>
|
||||
<AuxValues>
|
||||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
</SubComponents>
|
||||
|
|
|
|||
|
|
@ -4,10 +4,7 @@ import mage.client.MageFrame;
|
|||
import mage.client.SessionHandler;
|
||||
import mage.client.components.KeyBindButton;
|
||||
import mage.client.themes.ThemeType;
|
||||
import mage.client.util.CardLanguage;
|
||||
import mage.client.util.ClientDefaultSettings;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.ImageHelper;
|
||||
import mage.client.util.*;
|
||||
import mage.client.util.audio.MusicPlayer;
|
||||
import mage.client.util.gui.BufferedImageBuilder;
|
||||
import mage.client.util.gui.GuiDisplayUtil;
|
||||
|
|
@ -37,7 +34,6 @@ import java.util.concurrent.locks.ReadWriteLock;
|
|||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.prefs.BackingStoreException;
|
||||
import java.util.prefs.Preferences;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static mage.client.constants.Constants.AUTO_TARGET_NON_FEEL_BAD;
|
||||
import static mage.constants.Constants.*;
|
||||
|
|
@ -89,8 +85,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
public static final String KEY_CARD_IMAGES_SAVE_TO_ZIP = "cardImagesSaveToZip";
|
||||
public static final String KEY_CARD_IMAGES_PREF_LANGUAGE = "cardImagesPreferredImageLaguage";
|
||||
|
||||
public static final String KEY_CARD_RENDERING_IMAGE_MODE = "cardRenderingFallback";
|
||||
public static final String KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES = "cardRenderingRetroFrames";
|
||||
public static final String KEY_CARD_RENDERING_IMAGE_MODE = "cardRenderingMode";
|
||||
public static final String KEY_CARD_RENDERING_ICONS_FOR_ABILITIES = "cardRenderingIconsForAbilities";
|
||||
public static final String KEY_CARD_RENDERING_ICONS_FOR_PLAYABLE = "cardRenderingIconsForPlayable";
|
||||
public static final String KEY_CARD_RENDERING_REMINDER_TEXT = "cardRenderingReminderText";
|
||||
|
|
@ -268,12 +263,14 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
public static final String KEY_AUTO_TARGET_LEVEL = "autoTargetLevel";
|
||||
|
||||
// pref setting for deck generator
|
||||
public static final String KEY_NEW_DECK_GENERATOR_COLORS = "newDeckGeneratorDeckColors";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_DECK_SIZE = "newDeckGeneratorDeckSize";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_SET = "newDeckGeneratorSet";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_SINGLETON = "newDeckGeneratorSingleton";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_ARTIFACTS = "newDeckGeneratorArtifacts";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_NON_BASIC_LANDS = "newDeckGeneratorNonBasicLands";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_COLORLESS = "newDeckGeneratorColorless";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_COMMANDER = "newDeckGeneratorCommander";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_ADVANCED = "newDeckGeneratorAdvanced";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_CREATURE_PERCENTAGE = "newDeckGeneratorCreaturePercentage";
|
||||
public static final String KEY_NEW_DECK_GENERATOR_NON_CREATURE_PERCENTAGE = "newDeckGeneratorNonCreaturePercentage";
|
||||
|
|
@ -761,6 +758,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
}
|
||||
|
||||
cbPreferredImageLanguage.setModel(new DefaultComboBoxModel<>(CardLanguage.toList()));
|
||||
cbCardRenderImageFallback.setModel(new DefaultComboBoxModel<>(CardRenderMode.toList()));
|
||||
}
|
||||
|
||||
private void createSizeSetting(Integer position, String key, Integer defaultValue, boolean useExample, String name, String hint) {
|
||||
|
|
@ -962,14 +960,14 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
cbPreferredImageLanguage = new javax.swing.JComboBox<>();
|
||||
labelPreferredImageLanguage = new javax.swing.JLabel();
|
||||
panelCardStyles = new javax.swing.JPanel();
|
||||
cbCardRenderImageFallback = new javax.swing.JCheckBox();
|
||||
cbCardRenderRetroFrames = new javax.swing.JCheckBox();
|
||||
cbCardRenderIconsForAbilities = new javax.swing.JCheckBox();
|
||||
cbCardRenderIconsForPlayable = new javax.swing.JCheckBox();
|
||||
jSeparator1 = new javax.swing.JSeparator();
|
||||
cbCardRenderShowReminderText = new javax.swing.JCheckBox();
|
||||
cbCardRenderHideSetSymbol = new javax.swing.JCheckBox();
|
||||
cbCardRenderShowAbilityTextOverlay = new javax.swing.JCheckBox();
|
||||
labelRenderMode = new javax.swing.JLabel();
|
||||
cbCardRenderImageFallback = new javax.swing.JComboBox<>();
|
||||
tabPhases = new javax.swing.JPanel();
|
||||
jLabelHeadLine = new javax.swing.JLabel();
|
||||
jLabelYourTurn = new javax.swing.JLabel();
|
||||
|
|
@ -2279,7 +2277,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
}
|
||||
});
|
||||
|
||||
cbPreferredImageLanguage.setMaximumRowCount(20);
|
||||
cbPreferredImageLanguage.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
|
||||
|
||||
labelPreferredImageLanguage.setText("Default images language:");
|
||||
|
|
@ -2305,7 +2302,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
.add(labelPreferredImageLanguage)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(cbPreferredImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
|
||||
.add(0, 480, Short.MAX_VALUE)))
|
||||
.add(0, 0, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
panelCardImagesLayout.setVerticalGroup(
|
||||
|
|
@ -2325,29 +2322,59 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
);
|
||||
|
||||
panelCardStyles.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Card styles (restart xmage to apply new settings)"));
|
||||
panelCardStyles.setLayout(new javax.swing.BoxLayout(panelCardStyles, javax.swing.BoxLayout.Y_AXIS));
|
||||
|
||||
cbCardRenderImageFallback.setText("Render mode: MTGO style (off) or IMAGE style (on)");
|
||||
panelCardStyles.add(cbCardRenderImageFallback);
|
||||
|
||||
cbCardRenderRetroFrames.setText("Force retro frames (MTGO render mode will use old border for all cards)");
|
||||
panelCardStyles.add(cbCardRenderRetroFrames);
|
||||
|
||||
cbCardRenderIconsForAbilities.setText("Enable card icons for abilities (example: flying, deathtouch)");
|
||||
panelCardStyles.add(cbCardRenderIconsForAbilities);
|
||||
|
||||
cbCardRenderIconsForPlayable.setText("Enable card icons for playable abilities (example: if you can activate card's ability then show a special icon in the corner)");
|
||||
panelCardStyles.add(cbCardRenderIconsForPlayable);
|
||||
panelCardStyles.add(jSeparator1);
|
||||
|
||||
cbCardRenderShowReminderText.setText("Show reminder text in rendered card textboxes");
|
||||
panelCardStyles.add(cbCardRenderShowReminderText);
|
||||
|
||||
cbCardRenderHideSetSymbol.setText("Hide set symbols on cards (more space on the type line for card types)");
|
||||
panelCardStyles.add(cbCardRenderHideSetSymbol);
|
||||
|
||||
cbCardRenderShowAbilityTextOverlay.setText("Show ability text as overlay in big card view");
|
||||
panelCardStyles.add(cbCardRenderShowAbilityTextOverlay);
|
||||
|
||||
labelRenderMode.setText("Render Mode:");
|
||||
labelRenderMode.setToolTipText("<HTML>Image - Renders card image with text overlay<br> MTGO - Renders card frame around card art<br> Forced M15 - Renders all cards in the modern frame<br> Forced Retro - Renders all cards in the retro frame");
|
||||
|
||||
cbCardRenderImageFallback.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
|
||||
cbCardRenderImageFallback.setToolTipText("<HTML>Image - Renders card image with text overlay<br> MTGO - Renders card frame around card art<br> Forced M15 - Renders all cards in the MTGO style with the modern frame<br> Forced Retro - Renders all cards in the MTGO style with the retro frame");
|
||||
|
||||
org.jdesktop.layout.GroupLayout panelCardStylesLayout = new org.jdesktop.layout.GroupLayout(panelCardStyles);
|
||||
panelCardStyles.setLayout(panelCardStylesLayout);
|
||||
panelCardStylesLayout.setHorizontalGroup(
|
||||
panelCardStylesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(cbCardRenderIconsForAbilities)
|
||||
.add(cbCardRenderIconsForPlayable)
|
||||
.add(jSeparator1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 775, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.add(cbCardRenderShowReminderText)
|
||||
.add(cbCardRenderHideSetSymbol)
|
||||
.add(cbCardRenderShowAbilityTextOverlay)
|
||||
.add(panelCardStylesLayout.createSequentialGroup()
|
||||
.add(6, 6, 6)
|
||||
.add(labelRenderMode)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(cbCardRenderImageFallback, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 122, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
|
||||
);
|
||||
panelCardStylesLayout.setVerticalGroup(
|
||||
panelCardStylesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(panelCardStylesLayout.createSequentialGroup()
|
||||
.add(0, 0, 0)
|
||||
.add(panelCardStylesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
|
||||
.add(labelRenderMode)
|
||||
.add(cbCardRenderImageFallback, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
|
||||
.add(0, 0, 0)
|
||||
.add(cbCardRenderIconsForAbilities)
|
||||
.add(0, 0, 0)
|
||||
.add(cbCardRenderIconsForPlayable)
|
||||
.add(0, 0, 0)
|
||||
.add(jSeparator1, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.add(0, 0, 0)
|
||||
.add(cbCardRenderShowReminderText)
|
||||
.add(0, 0, 0)
|
||||
.add(cbCardRenderHideSetSymbol)
|
||||
.add(0, 0, 0)
|
||||
.add(cbCardRenderShowAbilityTextOverlay))
|
||||
);
|
||||
|
||||
org.jdesktop.layout.GroupLayout tabGuiImagesLayout = new org.jdesktop.layout.GroupLayout(tabGuiImages);
|
||||
tabGuiImages.setLayout(tabGuiImagesLayout);
|
||||
|
|
@ -2367,7 +2394,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
.add(panelCardStyles, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(panelCardImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(306, Short.MAX_VALUE))
|
||||
.addContainerGap(309, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
tabsPanel.addTab("GUI Images", tabGuiImages);
|
||||
|
|
@ -3038,8 +3065,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
save(prefs, dialog.cbUseRandomBattleImage, KEY_BATTLEFIELD_IMAGE_RANDOM, "true", "false");
|
||||
|
||||
// rendering
|
||||
save(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_IMAGE_MODE, "true", "false");
|
||||
save(prefs, dialog.cbCardRenderRetroFrames, KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES, "true", "false");
|
||||
save(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_IMAGE_MODE);
|
||||
save(prefs, dialog.cbCardRenderIconsForAbilities, KEY_CARD_RENDERING_ICONS_FOR_ABILITIES, "true", "false");
|
||||
save(prefs, dialog.cbCardRenderIconsForPlayable, KEY_CARD_RENDERING_ICONS_FOR_PLAYABLE, "true", "false");
|
||||
save(prefs, dialog.cbCardRenderHideSetSymbol, KEY_CARD_RENDERING_SET_SYMBOL, "true", "false");
|
||||
|
|
@ -3490,8 +3516,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
dialog.cbPreferredImageLanguage.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_IMAGES_PREF_LANGUAGE, CardLanguage.ENGLISH.getCode()));
|
||||
|
||||
// rendering settings
|
||||
load(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_IMAGE_MODE, "true", "false");
|
||||
load(prefs, dialog.cbCardRenderRetroFrames, KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES, "true", "false");
|
||||
dialog.cbCardRenderImageFallback.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_RENDERING_IMAGE_MODE, CardRenderMode.MTGO.toString()));
|
||||
load(prefs, dialog.cbCardRenderIconsForAbilities, KEY_CARD_RENDERING_ICONS_FOR_ABILITIES, "true", "true");
|
||||
load(prefs, dialog.cbCardRenderIconsForPlayable, KEY_CARD_RENDERING_ICONS_FOR_PLAYABLE, "true", "true");
|
||||
load(prefs, dialog.cbCardRenderHideSetSymbol, KEY_CARD_RENDERING_SET_SYMBOL, "true");
|
||||
|
|
@ -3815,15 +3840,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
}
|
||||
|
||||
public static int getRenderMode() {
|
||||
if (getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_IMAGE_MODE, "false").equals("false")) {
|
||||
return 0; // mtgo
|
||||
} else {
|
||||
return 1; // image
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean getRenderRetroFrames() {
|
||||
return (getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_ENABLE_RETRO_FRAMES, "true").endsWith("true"));
|
||||
return CardRenderMode.fromString(getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_IMAGE_MODE, CardRenderMode.MTGO.toString())).getId();
|
||||
}
|
||||
|
||||
public static boolean getRenderIconsForAbilities() {
|
||||
|
|
@ -4053,8 +4070,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
private javax.swing.JCheckBox cbCardRenderHideSetSymbol;
|
||||
private javax.swing.JCheckBox cbCardRenderIconsForAbilities;
|
||||
private javax.swing.JCheckBox cbCardRenderIconsForPlayable;
|
||||
private javax.swing.JCheckBox cbCardRenderImageFallback;
|
||||
private javax.swing.JCheckBox cbCardRenderRetroFrames;
|
||||
private javax.swing.JComboBox<String> cbCardRenderImageFallback;
|
||||
private javax.swing.JCheckBox cbCardRenderShowAbilityTextOverlay;
|
||||
private javax.swing.JCheckBox cbCardRenderShowReminderText;
|
||||
private javax.swing.JCheckBox cbConfirmEmptyManaPool;
|
||||
|
|
@ -4186,6 +4202,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
private javax.swing.JLabel labelNextTurn;
|
||||
private javax.swing.JLabel labelPreferredImageLanguage;
|
||||
private javax.swing.JLabel labelPriorEnd;
|
||||
private javax.swing.JLabel labelRenderMode;
|
||||
private javax.swing.JLabel labelSizeGroup1;
|
||||
private javax.swing.JLabel labelSizeGroup2;
|
||||
private javax.swing.JLabel labelSkipStep;
|
||||
|
|
|
|||
|
|
@ -116,11 +116,9 @@
|
|||
<Component class="javax.swing.JComboBox" name="comboRenderMode">
|
||||
<Properties>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="2">
|
||||
<StringItem index="0" value="MTGO"/>
|
||||
<StringItem index="1" value="Image"/>
|
||||
</StringArray>
|
||||
<StringArray count="0"/>
|
||||
</Property>
|
||||
<Property name="toolTipText" type="java.lang.String" value="<HTML>Image - Renders card image with text overlay<br> MTGO - Renders card frame around card art<br> Forced M15 - Renders all cards in the MTGO style with the modern frame<br> Forced Retro - Renders all cards in the MTGO style with the retro frame"/>
|
||||
</Properties>
|
||||
<Events>
|
||||
<EventHandler event="itemStateChanged" listener="java.awt.event.ItemListener" parameters="java.awt.event.ItemEvent" handler="comboRenderModeItemStateChanged"/>
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ import mage.client.cards.BigCard;
|
|||
import mage.client.game.PlayAreaPanel;
|
||||
import mage.client.game.PlayerPanelExt;
|
||||
import mage.client.themes.ThemeType;
|
||||
import mage.client.util.ClientEventType;
|
||||
import mage.client.util.*;
|
||||
import mage.client.util.Event;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.Listener;
|
||||
|
|
@ -82,6 +82,7 @@ public class TestCardRenderDialog extends MageDialog {
|
|||
getRootPane().setDefaultButton(buttonCancel);
|
||||
|
||||
// init render mode
|
||||
this.comboRenderMode.setModel(new DefaultComboBoxModel<>(CardRenderMode.toList()));
|
||||
this.comboRenderMode.setSelectedIndex(PreferencesDialog.getRenderMode());
|
||||
|
||||
// init themes list
|
||||
|
|
@ -431,6 +432,7 @@ public class TestCardRenderDialog extends MageDialog {
|
|||
cardViews.add(createPermanentCard(game, playerYou.getId(), "RNA", "185", 0, 0, 0, true, false, null)); // Judith, the Scourge Diva
|
||||
cardViews.add(createHandCard(game, playerYou.getId(), "DIS", "153")); // Odds // Ends (split card)
|
||||
cardViews.add(createHandCard(game, playerYou.getId(), "ELD", "38")); // Animating Faerie (adventure card)
|
||||
cardViews.add(createHandCard(game, playerYou.getId(), "LEA", "278")); // Bayou (retro frame)
|
||||
cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, false, false)); // face down
|
||||
cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", true, false, true)); // morphed
|
||||
cardViews.add(createFaceDownCard(game, playerOpponent.getId(), "ELD", "38", false, true, false)); // manifested
|
||||
|
|
@ -635,7 +637,7 @@ public class TestCardRenderDialog extends MageDialog {
|
|||
|
||||
labelRenderMode.setText("Render mode:");
|
||||
|
||||
comboRenderMode.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "MTGO", "Image" }));
|
||||
comboRenderMode.setToolTipText("<HTML>Image - Renders card image with text overlay<br> MTGO - Renders card frame around card art<br> Forced M15 - Renders all cards in the MTGO style with the modern frame<br> Forced Retro - Renders all cards in the MTGO style with the retro frame");
|
||||
comboRenderMode.addItemListener(new java.awt.event.ItemListener() {
|
||||
public void itemStateChanged(java.awt.event.ItemEvent evt) {
|
||||
comboRenderModeItemStateChanged(evt);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
package mage.client.util;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public enum CardRenderMode {
|
||||
|
||||
MTGO("MTGO", 0),
|
||||
IMAGE("Image", 1),
|
||||
FORCED_M15("Forced M15", 2),
|
||||
FORCED_RETRO("Forced Retro", 3);
|
||||
|
||||
private final String text;
|
||||
private final int id;
|
||||
|
||||
CardRenderMode(String text, int id) {
|
||||
this.text = text;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return text;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static String[] toList() {
|
||||
List<String> list = new ArrayList<>();
|
||||
for (CardRenderMode mode : CardRenderMode.values()) {
|
||||
list.add(mode.toString());
|
||||
}
|
||||
return list.toArray(new String[0]);
|
||||
}
|
||||
|
||||
public static CardRenderMode fromId(int id) {
|
||||
for (CardRenderMode mode : CardRenderMode.values()) {
|
||||
if (mode.getId() == id) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
return MTGO;
|
||||
}
|
||||
|
||||
public static CardRenderMode fromString(String text) {
|
||||
for (CardRenderMode mode : CardRenderMode.values()) {
|
||||
if (mode.text.equals(text)) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
return MTGO;
|
||||
}
|
||||
}
|
||||
|
|
@ -19,6 +19,7 @@ public class CardViewEDHPowerLevelComparator implements CardViewComparator {
|
|||
return "EDH: " + getPowerLevel(sample);
|
||||
}
|
||||
|
||||
// TODO: it's outdated code, must migrate to shared code from AbstractCommander
|
||||
private int getPowerLevel(CardView card) {
|
||||
|
||||
int thisMaxPower = 0;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ public class CardPanelRenderModeMTGO extends CardPanel {
|
|||
private CardRenderer cardRenderer;
|
||||
|
||||
private int updateArtImageStamp;
|
||||
private final int cardRenderMode;
|
||||
|
||||
private static class ImageKey {
|
||||
final BufferedImage artImage;
|
||||
|
|
@ -84,7 +85,7 @@ public class CardPanelRenderModeMTGO extends CardPanel {
|
|||
sb.append((char) (this.view.isCanAttack() ? 1 : 0));
|
||||
sb.append((char) (this.view.isCanBlock() ? 1 : 0));
|
||||
sb.append((char) (this.view.isFaceDown() ? 1 : 0));
|
||||
sb.append((char) this.view.getFrameStyle().ordinal());
|
||||
sb.append((char) (this.view.getFrameStyle() != null ? this.view.getFrameStyle().ordinal() : -1));
|
||||
if (this.view instanceof PermanentView) {
|
||||
sb.append((char) (((PermanentView) this.view).hasSummoningSickness() ? 1 : 0));
|
||||
sb.append((char) (((PermanentView) this.view).getDamage()));
|
||||
|
|
@ -143,12 +144,13 @@ public class CardPanelRenderModeMTGO extends CardPanel {
|
|||
}
|
||||
|
||||
public CardPanelRenderModeMTGO(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback,
|
||||
final boolean foil, Dimension dimension, boolean needFullPermanentRender) {
|
||||
final boolean foil, Dimension dimension, boolean needFullPermanentRender, int renderMode) {
|
||||
// Call to super
|
||||
super(newGameCard, gameId, loadImage, callback, foil, dimension, needFullPermanentRender);
|
||||
|
||||
// Renderer
|
||||
cardRenderer = cardRendererFactory.create(getGameCard());
|
||||
cardRenderMode = renderMode;
|
||||
cardRenderer = cardRendererFactory.create(getGameCard(), cardRenderMode);
|
||||
|
||||
// Draw the parts
|
||||
initialDraw();
|
||||
|
|
@ -265,7 +267,7 @@ public class CardPanelRenderModeMTGO extends CardPanel {
|
|||
|
||||
// Update renderer
|
||||
cardImage = null;
|
||||
cardRenderer = cardRendererFactory.create(getGameCard());
|
||||
cardRenderer = cardRendererFactory.create(getGameCard(), cardRenderMode);
|
||||
cardRenderer.setArtImage(artImage);
|
||||
|
||||
// Repaint
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ package org.mage.card.arcane;
|
|||
|
||||
import mage.cards.FrameStyle;
|
||||
import mage.client.dialog.PreferencesDialog;
|
||||
import mage.client.util.CardRenderMode;
|
||||
import mage.view.CardView;
|
||||
|
||||
/**
|
||||
|
|
@ -13,12 +14,27 @@ public class CardRendererFactory {
|
|||
}
|
||||
|
||||
public CardRenderer create(CardView card) {
|
||||
return create(card, -1);
|
||||
}
|
||||
|
||||
public CardRenderer create(CardView card, int renderModeOverride) {
|
||||
if (card.isSplitCard()) {
|
||||
return new ModernSplitCardRenderer(card);
|
||||
} else if (card.getFrameStyle().equals(FrameStyle.RETRO) || card.getFrameStyle().equals(FrameStyle.LEA_ORIGINAL_DUAL_LAND_ART_BASIC) || PreferencesDialog.getRenderRetroFrames()) {
|
||||
} else if (shouldRenderRetro(card, renderModeOverride)) {
|
||||
// TODO: implement split card renderer for retro cards
|
||||
return new RetroCardRenderer(card);
|
||||
} else {
|
||||
return new ModernCardRenderer(card);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldRenderRetro(CardView card, int renderModeOverride) {
|
||||
int renderMode = PreferencesDialog.getRenderMode();
|
||||
if (renderModeOverride != -1) {
|
||||
renderMode = renderModeOverride;
|
||||
}
|
||||
boolean renderMTGO = (card.getFrameStyle().equals(FrameStyle.RETRO) || card.getFrameStyle().equals(FrameStyle.LEA_ORIGINAL_DUAL_LAND_ART_BASIC)) && renderMode == CardRenderMode.MTGO.ordinal();
|
||||
boolean forcedRetro = renderMode == CardRenderMode.FORCED_RETRO.ordinal();
|
||||
return renderMTGO || forcedRetro;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package org.mage.plugins.card;
|
|||
import mage.cards.MageCard;
|
||||
import mage.cards.MagePermanent;
|
||||
import mage.cards.action.ActionCallback;
|
||||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.*;
|
||||
import mage.interfaces.plugin.CardPlugin;
|
||||
import mage.view.CardView;
|
||||
import mage.view.CounterView;
|
||||
|
|
@ -35,6 +35,8 @@ import java.util.List;
|
|||
import java.util.*;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static mage.client.util.CardRenderMode.*;
|
||||
|
||||
/**
|
||||
* {@link CardPlugin} implementation.
|
||||
*
|
||||
|
|
@ -102,16 +104,19 @@ public class CardPluginImpl implements CardPlugin {
|
|||
* yet, so use old component based rendering for the split cards.
|
||||
*/
|
||||
private CardPanel makeCardPanel(CardView view, UUID gameId, boolean loadImage, ActionCallback callback,
|
||||
boolean isFoil, Dimension dimension, int renderMode, boolean needFullPermanentRender) {
|
||||
switch (renderMode) {
|
||||
case 0:
|
||||
boolean isFoil, Dimension dimension, int renderModeId, boolean needFullPermanentRender) {
|
||||
CardRenderMode cardRenderMode = CardRenderMode.fromId(renderModeId);
|
||||
switch (cardRenderMode) {
|
||||
case MTGO:
|
||||
case FORCED_M15:
|
||||
case FORCED_RETRO:
|
||||
return new CardPanelRenderModeMTGO(view, gameId, loadImage, callback, isFoil, dimension,
|
||||
needFullPermanentRender);
|
||||
case 1:
|
||||
needFullPermanentRender, renderModeId);
|
||||
case IMAGE:
|
||||
return new CardPanelRenderModeImage(view, gameId, loadImage, callback, isFoil, dimension,
|
||||
needFullPermanentRender);
|
||||
default:
|
||||
throw new IllegalStateException("Unknown render mode " + renderMode);
|
||||
throw new IllegalStateException("Unknown render mode " + cardRenderMode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import java.util.*;
|
|||
import java.util.stream.Stream;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
* @author TheElk801, JayDi85
|
||||
*/
|
||||
public abstract class AbstractCommander extends Constructed {
|
||||
|
||||
|
|
@ -88,13 +88,13 @@ public abstract class AbstractCommander extends Constructed {
|
|||
boolean valid = true;
|
||||
for (Card card : deck.getCards()) {
|
||||
if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) {
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')', true);
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + colorIdentity + ", but get " + card.getColorIdentity() + ")", true);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
for (Card card : deck.getSideboard()) {
|
||||
if (!ManaUtil.isColorIdentityCompatible(colorIdentity, card.getColorIdentity())) {
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')', true);
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + colorIdentity + ", but get " + card.getColorIdentity() + ")", true);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -258,14 +258,18 @@ public abstract class AbstractCommander extends Constructed {
|
|||
}
|
||||
|
||||
@Override
|
||||
public int getEdhPowerLevel(Deck deck) {
|
||||
if (deck == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public int getEdhPowerLevel(Deck deck, List<String> foundPowerCards, List<String> foundInfo) {
|
||||
// calculate power level and find all related cards with it to show in hints (see deck validation in deck editor)
|
||||
// example: https://edhpowerlevel.com
|
||||
int edhPowerLevel = 0;
|
||||
int commanderColors = 0;
|
||||
int numberInfinitePieces = 0;
|
||||
foundPowerCards.clear();
|
||||
foundInfo.clear();
|
||||
|
||||
if (deck == null) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
for (Card card : deck.getCards()) {
|
||||
|
||||
|
|
@ -330,6 +334,7 @@ public abstract class AbstractCommander extends Constructed {
|
|||
boolean yourOpponentsControl = false;
|
||||
boolean whenYouCast = false;
|
||||
|
||||
List<String> cardStates = new ArrayList<>();
|
||||
for (String str : card.getRules()) {
|
||||
String s = str.toLowerCase(Locale.ENGLISH);
|
||||
annihilator |= s.contains("annihilator");
|
||||
|
|
@ -406,178 +411,236 @@ public abstract class AbstractCommander extends Constructed {
|
|||
|
||||
if (extraTurns) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 7);
|
||||
cardStates.add(String.format("extraTurns %d", 7));
|
||||
}
|
||||
if (buyback) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 6);
|
||||
cardStates.add(String.format("buyback %d", 6));
|
||||
}
|
||||
if (tutor) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 6);
|
||||
cardStates.add(String.format("tutor %d", 6));
|
||||
}
|
||||
if (annihilator) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
cardStates.add(String.format("annihilator %d", 5));
|
||||
}
|
||||
if (cantUntap) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
cardStates.add(String.format("cantUntap %d", 5));
|
||||
}
|
||||
if (costLessEach) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
cardStates.add(String.format("costLessEach %d", 5));
|
||||
}
|
||||
if (infect) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
cardStates.add(String.format("infect %d", 5));
|
||||
}
|
||||
if (overload) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
cardStates.add(String.format("overload %d", 5));
|
||||
}
|
||||
if (twiceAs) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
cardStates.add(String.format("twiceAs %d", 5));
|
||||
}
|
||||
if (cascade) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("cascade %d", 4));
|
||||
}
|
||||
if (doesntUntap) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("doesntUntap %d", 4));
|
||||
}
|
||||
if (each) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("each %d", 4));
|
||||
}
|
||||
if (exileAll) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("exileAll %d", 4));
|
||||
}
|
||||
if (flash) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("flash %d", 4));
|
||||
}
|
||||
if (flashback) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("flashback %d", 4));
|
||||
}
|
||||
if (flicker) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("flicker %d", 4));
|
||||
}
|
||||
if (gainControl) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("gainControl %d", 4));
|
||||
}
|
||||
if (lifeTotalBecomes) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("lifeTotalBecomes %d", 4));
|
||||
}
|
||||
if (mayCastForFree) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("mayCastForFree %d", 4));
|
||||
}
|
||||
if (preventDamage) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("preventDamage %d", 4));
|
||||
}
|
||||
if (proliferate) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("proliferate %d", 4));
|
||||
}
|
||||
if (protection) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("protection %d", 4));
|
||||
}
|
||||
if (putUnderYourControl) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("putUnderYourControl %d", 4));
|
||||
}
|
||||
if (returnFromYourGY) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("returnFromYourGY %d", 4));
|
||||
}
|
||||
if (sacrifice) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 2);
|
||||
cardStates.add(String.format("sacrifice %d", 2));
|
||||
}
|
||||
if (skip) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("skip %d", 4));
|
||||
}
|
||||
if (storm) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("storm %d", 4));
|
||||
}
|
||||
if (unblockable) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("unblockable %d", 4));
|
||||
}
|
||||
if (whenCounterThatSpell) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("whenCounterThatSpell %d", 4));
|
||||
}
|
||||
if (wheneverEnters) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("wheneverEnters %d", 4));
|
||||
}
|
||||
if (xCost) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("xCost %d", 4));
|
||||
}
|
||||
if (youControlTarget) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("youControlTarget %d", 4));
|
||||
}
|
||||
if (yourOpponentsControl) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("yourOpponentsControl %d", 4));
|
||||
}
|
||||
if (whenYouCast) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
cardStates.add(String.format("whenYouCast %d", 4));
|
||||
}
|
||||
if (anyNumberOfTarget) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 3);
|
||||
cardStates.add(String.format("anyNumberOfTarget %d", 4));
|
||||
}
|
||||
if (createToken) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 3);
|
||||
cardStates.add(String.format("createToken %d", 3));
|
||||
}
|
||||
if (destroyAll) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 3);
|
||||
cardStates.add(String.format("destroyAll %d", 3));
|
||||
}
|
||||
if (dredge) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 3);
|
||||
cardStates.add(String.format("dredge %d", 3));
|
||||
}
|
||||
if (hexproof) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 3);
|
||||
cardStates.add(String.format("hexproof %d", 3));
|
||||
}
|
||||
if (shroud) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 3);
|
||||
cardStates.add(String.format("shroud %d", 3));
|
||||
}
|
||||
if (undying) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 3);
|
||||
cardStates.add(String.format("undying %d", 3));
|
||||
}
|
||||
if (persist) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 3);
|
||||
cardStates.add(String.format("persist %d", 3));
|
||||
}
|
||||
if (cantBe) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 2);
|
||||
cardStates.add(String.format("cantBe %d", 2));
|
||||
}
|
||||
if (evoke) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 2);
|
||||
cardStates.add(String.format("evoke %d", 2));
|
||||
}
|
||||
if (exile) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 2);
|
||||
cardStates.add(String.format("exile %d", 2));
|
||||
}
|
||||
if (menace) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 2);
|
||||
cardStates.add(String.format("menace %d", 2));
|
||||
}
|
||||
if (miracle) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 2);
|
||||
cardStates.add(String.format("miracle %d", 2));
|
||||
}
|
||||
if (sliver) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 2);
|
||||
cardStates.add(String.format("sliver %d", 2));
|
||||
}
|
||||
if (untapTarget) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 2);
|
||||
cardStates.add(String.format("untapTarget %d", 2));
|
||||
}
|
||||
if (copy) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 1);
|
||||
cardStates.add(String.format("copy %d", 1));
|
||||
}
|
||||
if (counter) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 1);
|
||||
cardStates.add(String.format("counter %d", 1));
|
||||
}
|
||||
if (destroy) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 1);
|
||||
cardStates.add(String.format("destroy %d", 1));
|
||||
}
|
||||
if (drawCards) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 1);
|
||||
cardStates.add(String.format("drawCards %d", 1));
|
||||
}
|
||||
if (exalted) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 1);
|
||||
cardStates.add(String.format("exalted %d", 1));
|
||||
}
|
||||
if (retrace) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 1);
|
||||
cardStates.add(String.format("retrace %d", 1));
|
||||
}
|
||||
if (trample) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 1);
|
||||
cardStates.add(String.format("trample %d", 1));
|
||||
}
|
||||
if (tutorBasic) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 1);
|
||||
cardStates.add(String.format("tutorBasic %d", 1));
|
||||
}
|
||||
|
||||
if (card.isPlaneswalker()) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 6);
|
||||
cardStates.add(String.format("isPlaneswalker %d", 6));
|
||||
}
|
||||
|
||||
String cn = card.getName().toLowerCase(Locale.ENGLISH);
|
||||
|
|
@ -845,6 +908,7 @@ public abstract class AbstractCommander extends Constructed {
|
|||
|| cn.equals("zealous conscripts")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 12);
|
||||
cardStates.add(String.format("saltiest card %d", 12));
|
||||
}
|
||||
|
||||
// Parts of infinite combos
|
||||
|
|
@ -906,6 +970,7 @@ public abstract class AbstractCommander extends Constructed {
|
|||
|| cn.equals("worthy cause") || cn.equals("yawgmoth's will")
|
||||
|| cn.equals("zealous conscripts")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 15);
|
||||
cardStates.add(String.format("infinite combo %d", 15));
|
||||
numberInfinitePieces++;
|
||||
}
|
||||
|
||||
|
|
@ -951,11 +1016,26 @@ public abstract class AbstractCommander extends Constructed {
|
|||
|| cn.equals("winota, joiner of forces")
|
||||
|| cn.equals("yuriko, the tiger's shadow")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 20);
|
||||
cardStates.add(String.format("game changer %d", 20));
|
||||
}
|
||||
|
||||
// keep card's level
|
||||
if (!cardStates.isEmpty()) {
|
||||
foundInfo.add(String.format("+%d from <b>%s</b> (%s)",
|
||||
thisMaxPower,
|
||||
card.getName(),
|
||||
String.join(", ", cardStates)
|
||||
));
|
||||
foundPowerCards.add(card.getName());
|
||||
}
|
||||
|
||||
edhPowerLevel += thisMaxPower;
|
||||
|
||||
} // cards list
|
||||
|
||||
ObjectColor color = null;
|
||||
for (Card commander : deck.getSideboard()) {
|
||||
List<String> commanderStates = new ArrayList<>();
|
||||
int thisMaxPower = 0;
|
||||
String cn = commander.getName().toLowerCase(Locale.ENGLISH);
|
||||
if (color == null) {
|
||||
|
|
@ -1024,6 +1104,7 @@ public abstract class AbstractCommander extends Constructed {
|
|||
|| cn.equals("vorinclex, voice of hunger")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 25);
|
||||
commanderStates.add(String.format("not fun commander (+%d)", 25));
|
||||
}
|
||||
|
||||
// Saltiest commanders
|
||||
|
|
@ -1060,12 +1141,30 @@ public abstract class AbstractCommander extends Constructed {
|
|||
|| cn.equals("xanathar, guild kingpin")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 20);
|
||||
commanderStates.add(String.format("saltiest commander (+%d)", 20));
|
||||
}
|
||||
|
||||
// keep commander's level
|
||||
if (!commanderStates.isEmpty()) {
|
||||
foundInfo.add(String.format("+%d from <b>%s</b> (%s)",
|
||||
thisMaxPower,
|
||||
commander.getName(),
|
||||
String.join(", ", commanderStates)
|
||||
));
|
||||
foundPowerCards.add(commander.getName());
|
||||
}
|
||||
|
||||
edhPowerLevel += thisMaxPower;
|
||||
}
|
||||
|
||||
if (numberInfinitePieces > 0) {
|
||||
edhPowerLevel += numberInfinitePieces * 18;
|
||||
edhPowerLevel = Math.round(edhPowerLevel / 10);
|
||||
foundInfo.add(String.format("+%d from <b>%d infinite pieces</b>", numberInfinitePieces * 18, numberInfinitePieces));
|
||||
}
|
||||
|
||||
// block colored decks by table's edh power level were disabled
|
||||
// it's better to show real edh power level and allow for direct values control
|
||||
if (false) {
|
||||
if (edhPowerLevel >= 100) {
|
||||
edhPowerLevel = 99;
|
||||
}
|
||||
|
|
@ -1076,6 +1175,7 @@ public abstract class AbstractCommander extends Constructed {
|
|||
edhPowerLevel += (color.isRed() ? 10000 : 0);
|
||||
edhPowerLevel += (color.isGreen() ? 1000 : 0);
|
||||
}
|
||||
}
|
||||
return edhPowerLevel;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -122,7 +122,7 @@ public class Brawl extends Constructed {
|
|||
&& !(colorIdentity.isColorless()
|
||||
&& basicsInDeck.size() == 1
|
||||
&& basicsInDeck.contains(card.getName()))) {
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')', true);
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + colorIdentity + ", but get " + card.getColorIdentity() + ")", true);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
|
@ -131,7 +131,7 @@ public class Brawl extends Constructed {
|
|||
&& !(colorIdentity.isColorless()
|
||||
&& basicsInDeck.size() == 1
|
||||
&& basicsInDeck.contains(card.getName()))) {
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + colorIdentity.toString() + ')', true);
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + colorIdentity + ", but get " + card.getColorIdentity() + ")", true);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -189,7 +189,7 @@ public class Oathbreaker extends Constructed {
|
|||
|
||||
for (Card card : deck.getCards()) {
|
||||
if (!ManaUtil.isColorIdentityCompatible(allCommandersColor, card.getColorIdentity())) {
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (" + card.getColorIdentity() + ')', true);
|
||||
addError(DeckValidatorErrorType.OTHER, card.getName(), "Invalid color (need " + allCommandersColor + ", but get " + card.getColorIdentity() + ")", true);
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -153,11 +153,11 @@ public class Session {
|
|||
if (userName.length() > config.getMaxUserNameLength()) {
|
||||
return "User name may not be longer than " + config.getMaxUserNameLength() + " characters";
|
||||
}
|
||||
if (userName.length() <= 3) {
|
||||
return "User name is too short (3 characters or fewer)";
|
||||
if (userName.length() <= 2) {
|
||||
return "User name is too short (2 characters or fewer)";
|
||||
}
|
||||
if (userName.length() >= 500) {
|
||||
return "User name is too long (500 characters or more)";
|
||||
if (userName.length() >= 250) {
|
||||
return "User name is too long (250 characters or more)";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -325,7 +325,7 @@ public class TableController {
|
|||
// user - restrict by deck power level and cards colors (see edh power level for details)
|
||||
int edhPowerLevel = table.getMatch().getOptions().getEdhPowerLevel();
|
||||
if (edhPowerLevel > 0 && table.getValidator().getName().toLowerCase(Locale.ENGLISH).equals("commander")) {
|
||||
int deckEdhPowerLevel = table.getValidator().getEdhPowerLevel(deck);
|
||||
int deckEdhPowerLevel = table.getValidator().getEdhPowerLevel(deck, new ArrayList<>(), new ArrayList<>());
|
||||
if (deckEdhPowerLevel % 100 > edhPowerLevel) {
|
||||
String message = new StringBuilder("Your deck appears to be too powerful for this table.\n\nReduce the number of extra turn cards, infect, counters, fogs, reconsider your commander. ")
|
||||
.append("\nThe table requirement has a maximum power level of ").append(edhPowerLevel).append(" whilst your deck has a calculated power level of ")
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.common.CantBeCounteredSourceAbility;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
|
|
@ -10,10 +8,11 @@ import mage.constants.CardType;
|
|||
import mage.constants.ComparisonType;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.filter.predicate.mageobject.ManaValuePredicate;
|
||||
import mage.target.common.TargetNonlandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class AbruptDecay extends CardImpl {
|
||||
|
|
@ -25,14 +24,14 @@ public final class AbruptDecay extends CardImpl {
|
|||
}
|
||||
|
||||
public AbruptDecay(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{B}{G}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}{G}");
|
||||
|
||||
// This spell can't be countered.
|
||||
this.addAbility(new CantBeCounteredSourceAbility().setRuleAtTheTop(true));
|
||||
|
||||
// Destroy target nonland permanent with converted mana cost 3 or less.
|
||||
this.getSpellAbility().addEffect(new DestroyTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetNonlandPermanent(filter));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||
}
|
||||
|
||||
private AbruptDecay(final AbruptDecay card) {
|
||||
|
|
|
|||
|
|
@ -6,8 +6,9 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
|||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue;
|
||||
import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
|
@ -53,7 +54,7 @@ public final class AbzanMonument extends CardImpl {
|
|||
);
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addCost(new SacrificeSourceCost());
|
||||
this.addAbility(ability.addHint(GreatestToughnessAmongControlledCreaturesValue.ALL.getHint()));
|
||||
this.addAbility(ability.addHint(GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES.getHint()));
|
||||
}
|
||||
|
||||
private AbzanMonument(final AbzanMonument card) {
|
||||
|
|
@ -85,8 +86,7 @@ class AbzanMonumentEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return new SpiritXXToken(
|
||||
GreatestToughnessAmongControlledCreaturesValue.ALL.calculate(game, source, this)
|
||||
).putOntoBattlefield(1, game, source);
|
||||
int value = GreatestAmongPermanentsValue.TOUGHNESS_CONTROLLED_CREATURES.calculate(game, source, this);
|
||||
return new CreateTokenEffect(new SpiritXXToken(value)).apply(game, source);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.HighestManaValueCount;
|
||||
import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
|
@ -10,19 +8,23 @@ import mage.constants.CardType;
|
|||
import mage.constants.Duration;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author nigelzor
|
||||
*/
|
||||
public final class AcceleratedMutation extends CardImpl {
|
||||
|
||||
private static final DynamicValue xValue = new HighestManaValueCount();
|
||||
|
||||
public AcceleratedMutation(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{G}{G}");
|
||||
|
||||
// Target creature gets +X/+X until end of turn, where X is the highest converted mana cost among permanents you control.
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(xValue, xValue, Duration.EndOfTurn));
|
||||
// Target creature gets +X/+X until end of turn, where X is the greatest converted mana cost among permanents you control.
|
||||
this.getSpellAbility().addEffect(new BoostTargetEffect(
|
||||
GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS,
|
||||
GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS,
|
||||
Duration.EndOfTurn
|
||||
));
|
||||
this.getSpellAbility().addHint(GreatestAmongPermanentsValue.MANAVALUE_CONTROLLED_PERMANENTS.getHint());
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,10 @@ package mage.cards.a;
|
|||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.condition.common.AttachedAttackingCondition;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.decorator.ConditionalRequirementEffect;
|
||||
import mage.abilities.effects.common.combat.MustBeBlockedByAtLeastOneAttachedEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostEquippedEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||
|
|
@ -14,8 +15,6 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -27,12 +26,14 @@ public final class AcesBaseballBat extends CardImpl {
|
|||
|
||||
private static final FilterControlledCreaturePermanent filterLegendary
|
||||
= new FilterControlledCreaturePermanent("legendary creature");
|
||||
|
||||
static {
|
||||
filterLegendary.add(SuperType.LEGENDARY.getPredicate());
|
||||
}
|
||||
|
||||
private static final FilterControlledCreaturePermanent filterDalek
|
||||
= new FilterControlledCreaturePermanent("a Dalek");
|
||||
|
||||
static {
|
||||
filterDalek.add(SubType.DALEK.getPredicate());
|
||||
}
|
||||
|
|
@ -48,12 +49,19 @@ public final class AcesBaseballBat extends CardImpl {
|
|||
// As long as equipped creature is attacking, it has first strike and must be blocked by a Dalek if able.
|
||||
Ability ability = new SimpleStaticAbility(new ConditionalContinuousEffect(
|
||||
new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.EQUIPMENT),
|
||||
AttachedToAttackingCondition.instance, "As long as equipped creature is attacking, it has first strike"));
|
||||
ability.addEffect(new MustBeBlockedByAtLeastOneAttachedEffect(filterDalek).concatBy("and"));
|
||||
AttachedAttackingCondition.instance, "as long as equipped creature is attacking, it has first strike"
|
||||
));
|
||||
ability.addEffect(new ConditionalRequirementEffect(
|
||||
new MustBeBlockedByAtLeastOneAttachedEffect(filterDalek),
|
||||
AttachedAttackingCondition.instance, "and must be blocked by a Dalek if able"
|
||||
));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Equip legendary creature (1)
|
||||
this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(1), new TargetControlledCreaturePermanent(filterLegendary), false));
|
||||
this.addAbility(new EquipAbility(
|
||||
Outcome.AddAbility, new GenericManaCost(1),
|
||||
new TargetControlledCreaturePermanent(filterLegendary), false
|
||||
));
|
||||
|
||||
// Equip {3}
|
||||
this.addAbility(new EquipAbility(Outcome.AddAbility, new GenericManaCost(3), false));
|
||||
|
|
@ -68,19 +76,3 @@ public final class AcesBaseballBat extends CardImpl {
|
|||
return new AcesBaseballBat(this);
|
||||
}
|
||||
}
|
||||
enum AttachedToAttackingCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent attachment = game.getPermanent(source.getSourceId());
|
||||
if (attachment == null || attachment.getAttachedTo() == null) {
|
||||
return false;
|
||||
}
|
||||
Permanent attachedTo = game.getPermanent(attachment.getAttachedTo());
|
||||
if (attachedTo == null) {
|
||||
return false;
|
||||
}
|
||||
return attachedTo.isAttacking();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -82,7 +83,7 @@ class AchHansRunEffect extends OneShotEffect {
|
|||
if (card == null || !controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
|
||||
return false;
|
||||
}
|
||||
Permanent creature = game.getPermanent(card.getId());
|
||||
Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (creature == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
|
|
@ -12,29 +9,25 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Loki
|
||||
*/
|
||||
public final class AcidWebSpider extends CardImpl {
|
||||
private static final FilterPermanent filter = new FilterPermanent("Equipment");
|
||||
|
||||
static {
|
||||
filter.add(SubType.EQUIPMENT.getPredicate());
|
||||
}
|
||||
|
||||
public AcidWebSpider (UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}{G}");
|
||||
public AcidWebSpider(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{G}{G}");
|
||||
this.subtype.add(SubType.SPIDER);
|
||||
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(5);
|
||||
this.addAbility(ReachAbility.getInstance());
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), true);
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.*;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.continuous.BoostAllEffect;
|
||||
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
|
||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
|
|
@ -18,11 +17,12 @@ import mage.game.Game;
|
|||
import mage.game.events.DamagedPlayerEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetNonlandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AdmiralBeckettBrass extends CardImpl {
|
||||
|
|
@ -33,7 +33,7 @@ public final class AdmiralBeckettBrass extends CardImpl {
|
|||
static {
|
||||
filter.add(SubType.PIRATE.getPredicate());
|
||||
filter.add(TargetController.YOU.getControllerPredicate());
|
||||
filter2.add(new ControllerDealtDamageByPiratesPredicate());
|
||||
filter2.add(ControllerDealtDamageByPiratesPredicate.instance);
|
||||
}
|
||||
|
||||
public AdmiralBeckettBrass(UUID ownerId, CardSetInfo setInfo) {
|
||||
|
|
@ -50,7 +50,7 @@ public final class AdmiralBeckettBrass extends CardImpl {
|
|||
|
||||
// At the beginning of your end step, gain control of target nonland permanent controlled by a player who was dealt combat damage by three or more Pirates this turn.
|
||||
Ability ability = new BeginningOfEndStepTriggeredAbility(new GainControlTargetEffect(Duration.Custom, true));
|
||||
ability.addTarget(new TargetNonlandPermanent(filter2));
|
||||
ability.addTarget(new TargetPermanent(filter2));
|
||||
this.addAbility(ability, new DamagedByPiratesWatcher());
|
||||
}
|
||||
|
||||
|
|
@ -101,7 +101,8 @@ class DamagedByPiratesWatcher extends Watcher {
|
|||
}
|
||||
}
|
||||
|
||||
class ControllerDealtDamageByPiratesPredicate implements Predicate<Permanent> {
|
||||
enum ControllerDealtDamageByPiratesPredicate implements Predicate<Permanent> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
|
|
|
|||
45
Mage.Sets/src/mage/cards/a/AdventurersAirship.java
Normal file
45
Mage.Sets/src/mage/cards/a/AdventurersAirship.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.effects.common.DrawDiscardControllerEffect;
|
||||
import mage.abilities.keyword.CrewAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AdventurersAirship extends CardImpl {
|
||||
|
||||
public AdventurersAirship(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
|
||||
|
||||
this.subtype.add(SubType.VEHICLE);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// When this Vehicle attacks, draw a card then discard a card.
|
||||
this.addAbility(new AttacksTriggeredAbility(new DrawDiscardControllerEffect(1, 1)));
|
||||
|
||||
// Crew 2
|
||||
this.addAbility(new CrewAbility(2));
|
||||
}
|
||||
|
||||
private AdventurersAirship(final AdventurersAirship card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdventurersAirship copy() {
|
||||
return new AdventurersAirship(this);
|
||||
}
|
||||
}
|
||||
45
Mage.Sets/src/mage/cards/a/AerithRescueMission.java
Normal file
45
Mage.Sets/src/mage/cards/a/AerithRescueMission.java
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.TapTargetEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.permanent.token.HeroToken;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AerithRescueMission extends CardImpl {
|
||||
|
||||
public AerithRescueMission(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{W}");
|
||||
|
||||
// Choose one--
|
||||
// * Take the Elevator -- Create three 1/1 colorless Hero creature tokens.
|
||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new HeroToken(), 3));
|
||||
this.getSpellAbility().withFirstModeFlavorWord("Take the Elevator");
|
||||
|
||||
// * Take 59 Flights of Stairs -- Tap up to three target creatures. Put a stun counter on one of them.
|
||||
this.getSpellAbility().addMode(new Mode(new TapTargetEffect())
|
||||
.addEffect(new AddCountersTargetEffect(CounterType.STUN.createInstance())
|
||||
.setText("Put a stun counter on one of them"))
|
||||
.addTarget(new TargetCreaturePermanent(0, 3))
|
||||
.withFlavorWord("Take 59 Flights of Stairs"));
|
||||
}
|
||||
|
||||
private AerithRescueMission(final AerithRescueMission card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AerithRescueMission copy() {
|
||||
return new AerithRescueMission(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,12 +14,11 @@ import mage.constants.SubType;
|
|||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.permanent.token.BirdToken;
|
||||
import mage.target.common.TargetNonlandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author weirddan455
|
||||
*/
|
||||
public final class AetherChanneler extends CardImpl {
|
||||
|
|
@ -44,7 +43,7 @@ public final class AetherChanneler extends CardImpl {
|
|||
// * Draw a card.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BirdToken()));
|
||||
Mode mode = new Mode(new ReturnToHandTargetEffect());
|
||||
mode.addTarget(new TargetNonlandPermanent(filter));
|
||||
mode.addTarget(new TargetPermanent(filter));
|
||||
ability.addMode(mode);
|
||||
ability.addMode(new Mode(new DrawCardSourceControllerEffect(1)));
|
||||
this.addAbility(ability);
|
||||
|
|
|
|||
|
|
@ -1,26 +1,25 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.effects.common.ReturnToHandTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.common.TargetNonlandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class AetherGale extends CardImpl {
|
||||
|
||||
public AetherGale(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}{U}");
|
||||
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}{U}");
|
||||
|
||||
// Return six target nonland permanents to their owners' hands.
|
||||
this.getSpellAbility().addEffect(new ReturnToHandTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetNonlandPermanent(6,6, StaticFilters.FILTER_PERMANENTS_NON_LAND, false));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(6, StaticFilters.FILTER_PERMANENTS_NON_LAND));
|
||||
}
|
||||
|
||||
private AetherGale(final AetherGale card) {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -73,7 +74,7 @@ class AethermagesTouchEffect extends OneShotEffect {
|
|||
cards.remove(card);
|
||||
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
|
||||
// It gains \"At the beginning of your end step, return this creature to its owner's hand.\"
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent != null) {
|
||||
Ability ability = new BeginningOfEndStepTriggeredAbility(TargetController.YOU, new ReturnToHandSourceEffect(true), false, null);
|
||||
ContinuousEffect effect = new GainAbilityTargetEffect(ability, Duration.Custom);
|
||||
|
|
|
|||
78
Mage.Sets/src/mage/cards/a/AettirAndPriwen.java
Normal file
78
Mage.Sets/src/mage/cards/a/AettirAndPriwen.java
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.keyword.EquipAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Controllable;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AettirAndPriwen extends CardImpl {
|
||||
|
||||
public AettirAndPriwen(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{6}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.EQUIPMENT);
|
||||
|
||||
// Equipped creature has base power and toughness X/X, where X is your life total.
|
||||
this.addAbility(new SimpleStaticAbility(new AettirAndPriwenEffect()));
|
||||
|
||||
// Equip {5}
|
||||
this.addAbility(new EquipAbility(5));
|
||||
}
|
||||
|
||||
private AettirAndPriwen(final AettirAndPriwen card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AettirAndPriwen copy() {
|
||||
return new AettirAndPriwen(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AettirAndPriwenEffect extends ContinuousEffectImpl {
|
||||
|
||||
AettirAndPriwenEffect() {
|
||||
super(Duration.WhileOnBattlefield, Layer.PTChangingEffects_7, SubLayer.SetPT_7b, Outcome.Benefit);
|
||||
staticText = "equipped creature has base power and toughness X/X, where X is your life total";
|
||||
}
|
||||
|
||||
private AettirAndPriwenEffect(final AettirAndPriwenEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AettirAndPriwenEffect copy() {
|
||||
return new AettirAndPriwenEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
int life = Optional
|
||||
.ofNullable(source)
|
||||
.map(Controllable::getControllerId)
|
||||
.map(game::getPlayer)
|
||||
.map(Player::getLife)
|
||||
.orElse(0);
|
||||
permanent.getPower().setModifiedBaseValue(life);
|
||||
permanent.getToughness().setModifiedBaseValue(life);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,31 +1,26 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.constants.SubType;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
|
||||
import mage.abilities.condition.common.DidNotAttackThisTurnEnchantedCondition;
|
||||
import mage.abilities.decorator.ConditionalTriggeredAbility;
|
||||
import mage.abilities.decorator.ConditionalOneShotEffect;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.DestroyAttachedToEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||
import mage.constants.Outcome;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.abilities.triggers.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AttachmentType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.watchers.common.AttackedThisTurnWatcher;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public final class Aggression extends CardImpl {
|
||||
|
|
@ -45,27 +40,25 @@ public final class Aggression extends CardImpl {
|
|||
TargetPermanent auraTarget = new TargetPermanent(filter);
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
|
||||
Ability ability = new EnchantAbility(auraTarget);
|
||||
this.addAbility(ability);
|
||||
this.addAbility(new EnchantAbility(auraTarget));
|
||||
|
||||
// Enchanted creature has first strike and trample.
|
||||
Ability ability2 = new SimpleStaticAbility(
|
||||
new GainAbilityAttachedEffect(
|
||||
FirstStrikeAbility.getInstance(),
|
||||
AttachmentType.AURA));
|
||||
ability2.addEffect(new GainAbilityAttachedEffect(
|
||||
TrampleAbility.getInstance(),
|
||||
AttachmentType.AURA).setText("and trample"));
|
||||
this.addAbility(ability2);
|
||||
Ability ability = new SimpleStaticAbility(
|
||||
new GainAbilityAttachedEffect(FirstStrikeAbility.getInstance(), AttachmentType.AURA)
|
||||
);
|
||||
ability.addEffect(new GainAbilityAttachedEffect(
|
||||
TrampleAbility.getInstance(), AttachmentType.AURA
|
||||
).setText("and trample"));
|
||||
this.addAbility(ability);
|
||||
|
||||
// At the beginning of the end step of enchanted creature's controller, destroy that creature if it didn't attack this turn.
|
||||
this.addAbility(new ConditionalTriggeredAbility(
|
||||
new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
|
||||
new DestroyAttachedToEffect("enchanted"),
|
||||
TargetController.CONTROLLER_ATTACHED_TO),
|
||||
DidNotAttackThisTurnEnchantedCondition.instance,
|
||||
"At the beginning of the end step of enchanted creature's controller, destroy that creature if it didn't attack this turn."));
|
||||
|
||||
this.addAbility(new BeginningOfEndStepTriggeredAbility(
|
||||
TargetController.CONTROLLER_ATTACHED_TO,
|
||||
new ConditionalOneShotEffect(
|
||||
new DestroyAttachedToEffect(""), DidNotAttackThisTurnEnchantedCondition.instance,
|
||||
"destroy that creature if it didn't attack this turn"
|
||||
), false
|
||||
));
|
||||
}
|
||||
|
||||
private Aggression(final Aggression card) {
|
||||
|
|
|
|||
52
Mage.Sets/src/mage/cards/a/Ahriman.java
Normal file
52
Mage.Sets/src/mage/cards/a/Ahriman.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.keyword.DeathtouchAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class Ahriman extends CardImpl {
|
||||
|
||||
public Ahriman(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
|
||||
|
||||
this.subtype.add(SubType.EYE);
|
||||
this.subtype.add(SubType.HORROR);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Deathtouch
|
||||
this.addAbility(DeathtouchAbility.getInstance());
|
||||
|
||||
// {3}, Sacrifice another creature or artifact: Draw a card.
|
||||
Ability ability = new SimpleActivatedAbility(new DrawCardSourceControllerEffect(1), new GenericManaCost(3));
|
||||
ability.addCost(new SacrificeTargetCost(StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE_OR_ARTIFACT));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private Ahriman(final Ahriman card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Ahriman copy() {
|
||||
return new Ahriman(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -76,7 +76,7 @@ class AidFromTheCowlEffect extends OneShotEffect {
|
|||
if (card.isPermanent(game) && controller.chooseUse(Outcome.Neutral, "Put " + card.getIdName() + " onto the 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)) {
|
||||
controller.putCardsOnBottomOfLibrary(card, game, source, false);
|
||||
controller.putCardsOnBottomOfLibrary(card, game, source);
|
||||
} else {
|
||||
game.informPlayers(controller.getLogName() + " puts the revealed card back to the top of the library.");
|
||||
}
|
||||
|
|
|
|||
55
Mage.Sets/src/mage/cards/a/AirshipCrash.java
Normal file
55
Mage.Sets/src/mage/cards/a/AirshipCrash.java
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
import mage.abilities.keyword.CyclingAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AirshipCrash extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter
|
||||
= new FilterPermanent("artifact, enchantment, or creature with flying");
|
||||
|
||||
static {
|
||||
filter.add(Predicates.or(
|
||||
CardType.ARTIFACT.getPredicate(),
|
||||
CardType.ENCHANTMENT.getPredicate(),
|
||||
Predicates.and(
|
||||
CardType.CREATURE.getPredicate(),
|
||||
new AbilityPredicate(FlyingAbility.class)
|
||||
)
|
||||
));
|
||||
}
|
||||
|
||||
public AirshipCrash(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}");
|
||||
|
||||
// Destroy target artifact, enchantment, or creature with flying.
|
||||
this.getSpellAbility().addEffect(new DestroyTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||
|
||||
// Cycling {2}
|
||||
this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}")));
|
||||
}
|
||||
|
||||
private AirshipCrash(final AirshipCrash card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AirshipCrash copy() {
|
||||
return new AirshipCrash(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -90,7 +90,7 @@ class AjaniSleeperAgentEffect extends OneShotEffect {
|
|||
if (card.isCreature(game) || card.isPlaneswalker(game)) {
|
||||
controller.moveCards(card, Zone.HAND, source, game);
|
||||
} else if (controller.chooseUse(Outcome.Neutral, "Put " + card.getName() + " on the bottom of your library?", source, game)) {
|
||||
controller.putCardsOnBottomOfLibrary(card, game, source, true);
|
||||
controller.putCardsOnBottomOfLibrary(card, game, source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,9 +13,8 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterEquipmentPermanent;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.predicate.permanent.AttachedToPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DefenderAttackedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
|
|
@ -103,21 +102,11 @@ class AkiriFearlessVoyagerTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
class AkiriFearlessVoyagerEffect extends OneShotEffect {
|
||||
|
||||
private static enum AkiriFearlessVoyagerPredicate implements ObjectSourcePlayerPredicate<Permanent> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
|
||||
return game.getPermanent(input.getObject().getAttachedTo()) != null
|
||||
&& game.getControllerId(input.getObject().getAttachedTo()).equals(input.getPlayerId());
|
||||
}
|
||||
}
|
||||
|
||||
private static final FilterPermanent filter
|
||||
= new FilterEquipmentPermanent("equipment attached to a creature you control");
|
||||
= new FilterPermanent(SubType.EQUIPMENT, "equipment attached to a creature you control");
|
||||
|
||||
static {
|
||||
filter.add(AkiriFearlessVoyagerPredicate.instance);
|
||||
filter.add(new AttachedToPredicate(StaticFilters.FILTER_CONTROLLED_CREATURE));
|
||||
}
|
||||
|
||||
AkiriFearlessVoyagerEffect() {
|
||||
|
|
|
|||
|
|
@ -1,34 +1,38 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.MageObjectReference;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.abilities.keyword.PartnerAbility;
|
||||
import mage.abilities.mana.DynamicManaAbility;
|
||||
import mage.constants.*;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.watchers.Watcher;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.EnteredThisTurnPredicate;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AlenaKessigTrapper extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledCreaturePermanent("creatures you control that entered this turn");
|
||||
|
||||
static {
|
||||
filter.add(EnteredThisTurnPredicate.instance);
|
||||
}
|
||||
|
||||
private static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.Power, filter);
|
||||
private static final Hint hint = xValue.getHint();
|
||||
|
||||
public AlenaKessigTrapper(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}");
|
||||
|
||||
|
|
@ -43,9 +47,9 @@ public final class AlenaKessigTrapper extends CardImpl {
|
|||
|
||||
// {T}: Add an amount of {R} equal to the greatest power among creatures you control that entered the battlefield this turn.
|
||||
this.addAbility(new DynamicManaAbility(
|
||||
Mana.RedMana(1), AlenaKessigTrapperValue.instance, new TapSourceCost(), "Add an amount of {R} " +
|
||||
"equal to the greatest power among creatures you control that entered the battlefield this turn."
|
||||
), new AlenaKessigTrapperWatcher());
|
||||
Mana.RedMana(1), xValue, new TapSourceCost(),
|
||||
"Add an amount of {R} equal to the greatest power among creatures you control that entered this turn."
|
||||
).addHint(hint));
|
||||
|
||||
// Partner
|
||||
this.addAbility(PartnerAbility.getInstance());
|
||||
|
|
@ -60,63 +64,3 @@ public final class AlenaKessigTrapper extends CardImpl {
|
|||
return new AlenaKessigTrapper(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum AlenaKessigTrapperValue implements DynamicValue {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
AlenaKessigTrapperWatcher watcher = game.getState().getWatcher(AlenaKessigTrapperWatcher.class);
|
||||
if (watcher == null) {
|
||||
return 0;
|
||||
}
|
||||
return watcher.getPower(sourceAbility.getControllerId(), game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlenaKessigTrapperValue copy() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
class AlenaKessigTrapperWatcher extends Watcher {
|
||||
|
||||
private final Set<MageObjectReference> enteredThisTurn = new HashSet<>();
|
||||
|
||||
public AlenaKessigTrapperWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.ZONE_CHANGE
|
||||
&& ((ZoneChangeEvent) event).getToZone() == Zone.BATTLEFIELD) {
|
||||
enteredThisTurn.add(new MageObjectReference(event.getTargetId(), game));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
enteredThisTurn.clear();
|
||||
super.reset();
|
||||
}
|
||||
|
||||
int getPower(UUID playerId, Game game) {
|
||||
return enteredThisTurn
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(mor -> mor.getPermanent(game))
|
||||
.filter(Objects::nonNull)
|
||||
.filter(permanent1 -> permanent1.isCreature(game))
|
||||
.filter(permanent -> permanent.isControlledBy(playerId))
|
||||
.map(MageObject::getPower)
|
||||
.mapToInt(MageInt::getValue)
|
||||
.max()
|
||||
.orElse(0);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,22 +4,24 @@ import mage.MageInt;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.effects.keyword.ScryEffect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterControlledArtifactPermanent;
|
||||
import mage.filter.predicate.permanent.TappedPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetAnyTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -29,6 +31,15 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class AlibouAncientWitness extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledArtifactPermanent("tapped artifacts you control");
|
||||
|
||||
static {
|
||||
filter.add(TappedPredicate.TAPPED);
|
||||
}
|
||||
|
||||
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter, null);
|
||||
private static final Hint hint = new ValueHint("Tapped artifacts you control", xValue);
|
||||
|
||||
public AlibouAncientWitness(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{R}{W}");
|
||||
|
||||
|
|
@ -45,11 +56,12 @@ public final class AlibouAncientWitness extends CardImpl {
|
|||
|
||||
// Whenever one or more artifact creatures you control attack, Alibou, Ancient Witness deals X damage to any target and you scry X, where X is the number of tapped artifacts you control.
|
||||
Ability ability = new AttacksWithCreaturesTriggeredAbility(
|
||||
new AlibouAncientWitnessEffect(), 1,
|
||||
StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE
|
||||
new DamageTargetEffect(xValue).setText("{this} deals X damage to any target"),
|
||||
1, StaticFilters.FILTER_PERMANENTS_ARTIFACT_CREATURE
|
||||
).setTriggerPhrase("Whenever one or more artifact creatures you control attack, ");
|
||||
ability.addEffect(new ScryEffect(xValue).concatBy("and you"));
|
||||
ability.addTarget(new TargetAnyTarget());
|
||||
this.addAbility(ability.addHint(AlibouAncientWitnessEffect.getHint()));
|
||||
this.addAbility(ability.addHint(hint));
|
||||
}
|
||||
|
||||
private AlibouAncientWitness(final AlibouAncientWitness card) {
|
||||
|
|
@ -61,56 +73,3 @@ public final class AlibouAncientWitness extends CardImpl {
|
|||
return new AlibouAncientWitness(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AlibouAncientWitnessEffect extends OneShotEffect {
|
||||
|
||||
private static final FilterPermanent filter = new FilterControlledArtifactPermanent();
|
||||
|
||||
static {
|
||||
filter.add(TappedPredicate.TAPPED);
|
||||
}
|
||||
|
||||
private static final Hint hint = new ValueHint(
|
||||
"Tapped artifacts you control", new PermanentsOnBattlefieldCount(filter)
|
||||
);
|
||||
|
||||
AlibouAncientWitnessEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "{this} deals X damage to any target and you scry X, " +
|
||||
"where X is the number of tapped artifacts you control";
|
||||
}
|
||||
|
||||
private AlibouAncientWitnessEffect(final AlibouAncientWitnessEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlibouAncientWitnessEffect copy() {
|
||||
return new AlibouAncientWitnessEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
int xValue = game.getBattlefield().count(filter, source.getControllerId(), source, game);
|
||||
if (xValue < 1) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(source.getFirstTarget());
|
||||
if (permanent != null) {
|
||||
permanent.damage(xValue, source.getSourceId(), source, game);
|
||||
}
|
||||
Player player = game.getPlayer(source.getFirstTarget());
|
||||
if (player != null) {
|
||||
player.damage(xValue, source.getSourceId(), source, game);
|
||||
}
|
||||
player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
player.scry(xValue, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static Hint getHint() {
|
||||
return hint;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ class AmassTheComponentsEffect extends OneShotEffect {
|
|||
if (player.choose(Outcome.Detriment, player.getHand(), target, source, game)) {
|
||||
Card card = player.getHand().get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
return player.putCardsOnBottomOfLibrary(card, game, source, true);
|
||||
return player.putCardsOnBottomOfLibrary(card, game, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,10 +39,12 @@ public final class AmbitiousDragonborn extends CardImpl {
|
|||
this.toughness = new MageInt(0);
|
||||
|
||||
// Ambitious Dragonborn enters the battlefield with X +1/+1 counters on it, where X is the greatest power among creatures you control and creature cards in your graveyard.
|
||||
this.addAbility(new EntersBattlefieldAbility(new AddCountersSourceEffect(
|
||||
this.addAbility(new EntersBattlefieldAbility(
|
||||
new AddCountersSourceEffect(
|
||||
CounterType.P1P1.createInstance(), AmbitiousDragonbornValue.instance, false
|
||||
), "with X +1/+1 counters on it, where X is the greatest power " +
|
||||
"among creatures you control and creature cards in your graveyard"));
|
||||
"among creatures you control and creature cards in your graveyard"
|
||||
).addHint(hint));
|
||||
}
|
||||
|
||||
private AmbitiousDragonborn(final AmbitiousDragonborn card) {
|
||||
|
|
|
|||
148
Mage.Sets/src/mage/cards/a/AncientAdamantoise.java
Normal file
148
Mage.Sets/src/mage/cards/a/AncientAdamantoise.java
Normal file
|
|
@ -0,0 +1,148 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.DiesSourceTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.ExileSourceEffect;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.abilities.keyword.WardAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DamageEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.TreasureToken;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AncientAdamantoise extends CardImpl {
|
||||
|
||||
public AncientAdamantoise(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}{G}");
|
||||
|
||||
this.subtype.add(SubType.TURTLE);
|
||||
this.power = new MageInt(8);
|
||||
this.toughness = new MageInt(20);
|
||||
|
||||
// Vigilance
|
||||
this.addAbility(VigilanceAbility.getInstance());
|
||||
|
||||
// Ward {3}
|
||||
this.addAbility(new WardAbility(new ManaCostsImpl<>("{3}")));
|
||||
|
||||
// Damage isn't removed from this creature during cleanup steps.
|
||||
this.addAbility(new SimpleStaticAbility(new AncientAdamantoiseDamageEffect()));
|
||||
|
||||
// All damage that would be dealt to you and other permanents you control is dealt to this creature instead.
|
||||
this.addAbility(new SimpleStaticAbility(new AncientAdamantoiseRedirectEffect()));
|
||||
|
||||
// When this creature dies, exile it and create ten tapped Treasure tokens.
|
||||
Ability ability = new DiesSourceTriggeredAbility(new ExileSourceEffect().setText("exile it"));
|
||||
ability.addEffect(new CreateTokenEffect(new TreasureToken(), 10, true).concatBy("and"));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private AncientAdamantoise(final AncientAdamantoise card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AncientAdamantoise copy() {
|
||||
return new AncientAdamantoise(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AncientAdamantoiseDamageEffect extends ContinuousRuleModifyingEffectImpl {
|
||||
|
||||
AncientAdamantoiseDamageEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit);
|
||||
staticText = "damage isn't removed from {this} during cleanup steps";
|
||||
}
|
||||
|
||||
private AncientAdamantoiseDamageEffect(final AncientAdamantoiseDamageEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.REMOVE_DAMAGE_EOT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return event.getTargetId().equals(source.getSourceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public AncientAdamantoiseDamageEffect copy() {
|
||||
return new AncientAdamantoiseDamageEffect(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AncientAdamantoiseRedirectEffect extends ReplacementEffectImpl {
|
||||
|
||||
AncientAdamantoiseRedirectEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.RedirectDamage);
|
||||
staticText = "all damage that would be dealt to you and other " +
|
||||
"permanents you control is dealt to this creature instead";
|
||||
}
|
||||
|
||||
private AncientAdamantoiseRedirectEffect(final AncientAdamantoiseRedirectEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AncientAdamantoiseRedirectEffect copy() {
|
||||
return new AncientAdamantoiseRedirectEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
DamageEvent damageEvent = (DamageEvent) event;
|
||||
Permanent permanent = source.getSourcePermanentOrLKI(game);
|
||||
if (permanent != null) {
|
||||
permanent.damage(
|
||||
damageEvent.getAmount(), event.getSourceId(), source, game,
|
||||
damageEvent.isCombatDamage(), damageEvent.isPreventable()
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
switch (event.getType()) {
|
||||
case DAMAGE_PLAYER:
|
||||
case DAMAGE_PERMANENT:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
switch (event.getType()) {
|
||||
case DAMAGE_PLAYER:
|
||||
return source.isControlledBy(event.getTargetId());
|
||||
case DAMAGE_PERMANENT:
|
||||
return !event.getTargetId().equals(source.getSourceId())
|
||||
&& source.isControlledBy(game.getControllerId(event.getTargetId()));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +77,7 @@ class AnimistsAwakeningEffect extends OneShotEffect {
|
|||
|
||||
if (SpellMasteryCondition.instance.apply(game, source)) {
|
||||
for (Card card : toBattlefield) {
|
||||
Permanent land = game.getPermanent(card.getId());
|
||||
Permanent land = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (land != null) {
|
||||
land.untap(game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ class AnzragsRampageEffect extends OneShotEffect {
|
|||
}
|
||||
|
||||
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent == null) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -80,7 +81,7 @@ class ApprenticeNecromancerEffect extends OneShotEffect {
|
|||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null && card != null) {
|
||||
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
|
||||
Permanent creature = game.getPermanent(card.getId());
|
||||
Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (creature != null) {
|
||||
// Gains haste
|
||||
ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.Custom);
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -86,7 +87,7 @@ class ArbiterOfTheIdealEffect extends OneShotEffect {
|
|||
controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game);
|
||||
if (filter.match(card, game) && controller.chooseUse(outcome, "Put " + card.getName() + " onto the battlefield?", source, game)) {
|
||||
controller.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent != null) {
|
||||
permanent.addCounters(CounterType.MANIFESTATION.createInstance(), source.getControllerId(), source, game);
|
||||
ContinuousEffect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ENCHANTMENT);
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ package mage.cards.a;
|
|||
import mage.MageInt;
|
||||
import mage.Mana;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue;
|
||||
import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue;
|
||||
import mage.abilities.mana.AnyColorManaAbility;
|
||||
import mage.abilities.mana.DynamicManaAbility;
|
||||
import mage.cards.CardImpl;
|
||||
|
|
@ -31,10 +31,10 @@ public final class ArborAdherent extends CardImpl {
|
|||
|
||||
// {T}: Add X mana of any one color, where X is the greatest toughness among other creatures you control.
|
||||
this.addAbility(new DynamicManaAbility(
|
||||
Mana.AnyMana(1), GreatestToughnessAmongControlledCreaturesValue.OTHER,
|
||||
Mana.AnyMana(1), GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES,
|
||||
new TapSourceCost(), "add X mana of any one color, where X is the " +
|
||||
"greatest toughness among other creatures you control", true
|
||||
).addHint(GreatestToughnessAmongControlledCreaturesValue.OTHER.getHint()));
|
||||
).addHint(GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES.getHint()));
|
||||
}
|
||||
|
||||
private ArborAdherent(final ArborAdherent card) {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
|
||||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
|
|
@ -12,20 +9,20 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterLandPermanent;
|
||||
import mage.target.common.TargetLandPermanent;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public final class ArborElf extends CardImpl {
|
||||
|
||||
private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.FOREST, "Forest");
|
||||
private static final FilterPermanent filter = new FilterPermanent(SubType.FOREST, "Forest");
|
||||
|
||||
public ArborElf(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}");
|
||||
this.subtype.add(SubType.ELF);
|
||||
this.subtype.add(SubType.DRUID);
|
||||
|
||||
|
|
@ -34,8 +31,7 @@ public final class ArborElf extends CardImpl {
|
|||
|
||||
// (T): Untap target Forest.
|
||||
Ability ability = new SimpleActivatedAbility(new UntapTargetEffect(), new TapSourceCost());
|
||||
TargetLandPermanent target = new TargetLandPermanent(filter);
|
||||
ability.addTarget(target);
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
@ -47,5 +43,4 @@ public final class ArborElf extends CardImpl {
|
|||
public ArborElf copy() {
|
||||
return new ArborElf(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
|
|
@ -13,10 +12,11 @@ import mage.constants.CardType;
|
|||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.target.common.TargetNonlandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class ArchonOfTheTriumvirate extends CardImpl {
|
||||
|
|
@ -28,7 +28,7 @@ public final class ArchonOfTheTriumvirate extends CardImpl {
|
|||
}
|
||||
|
||||
public ArchonOfTheTriumvirate(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{W}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{U}");
|
||||
this.subtype.add(SubType.ARCHON);
|
||||
|
||||
this.power = new MageInt(4);
|
||||
|
|
@ -40,7 +40,7 @@ public final class ArchonOfTheTriumvirate extends CardImpl {
|
|||
// Whenever Archon of the Triumvirate attacks, detain up to two target nonland permanents your opponents control.
|
||||
// (Until your next turn, those permanents can't attack or block and their activated abilities can't be activated.)
|
||||
Ability ability = new AttacksTriggeredAbility(new DetainTargetEffect(), false);
|
||||
ability.addTarget(new TargetNonlandPermanent(0,2,filter, false));
|
||||
ability.addTarget(new TargetPermanent(0, 2, filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ class ArdentDustspeakerCost extends CostImpl {
|
|||
if (controller != null && (controller.chooseTarget(Outcome.Benefit, this.getTargets().get(0), source, game))) {
|
||||
Card card = game.getCard(this.getTargets().get(0).getFirstTarget());
|
||||
if (card != null) {
|
||||
controller.putCardsOnBottomOfLibrary(card, game, source, true);
|
||||
controller.putCardsOnBottomOfLibrary(card, game, source);
|
||||
paid = true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import mage.filter.common.FilterNonlandPermanent;
|
|||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.target.common.TargetNonlandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ public final class ArgentDais extends CardImpl {
|
|||
);
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addCost(new RemoveCountersSourceCost(CounterType.OIL.createInstance(2)));
|
||||
ability.addTarget(new TargetNonlandPermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
ability.addEffect(new DrawCardTargetControllerEffect(2));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,27 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.common.FilterControlledArtifactPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.common.TargetAttackingCreature;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Plopman
|
||||
*/
|
||||
public final class ArmedResponse extends CardImpl {
|
||||
|
||||
private static final FilterControlledArtifactPermanent filter = new FilterControlledArtifactPermanent("Equipment you control");
|
||||
|
||||
static {
|
||||
filter.add(SubType.EQUIPMENT.getPredicate());
|
||||
}
|
||||
|
||||
|
||||
public ArmedResponse(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}");
|
||||
|
||||
// Armed Response deals damage to target attacking creature equal to the number of Equipment you control.
|
||||
Effect effect = new DamageTargetEffect(new PermanentsOnBattlefieldCount(filter));
|
||||
Effect effect = new DamageTargetEffect(new PermanentsOnBattlefieldCount(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT));
|
||||
effect.setText("{this} deals damage to target attacking creature equal to the number of Equipment you control");
|
||||
this.getSpellAbility().addEffect(effect);
|
||||
this.getSpellAbility().addTarget(new TargetAttackingCreature());
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import mage.players.Player;
|
|||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -94,7 +95,7 @@ class ArmoredSkyhunterEffect extends OneShotEffect {
|
|||
}
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
cards.removeIf(uuid -> game.getState().getZone(uuid) != Zone.LIBRARY);
|
||||
Permanent equipment = game.getPermanent(card.getId());
|
||||
Permanent equipment = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (equipment == null || !equipment.hasSubtype(SubType.EQUIPMENT, game)) {
|
||||
return player.putCardsOnBottomOfLibrary(cards, game, source, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
|
@ -21,12 +21,6 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class ArmoryAutomaton extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterPermanent("Equipment");
|
||||
|
||||
static {
|
||||
filter.add(SubType.EQUIPMENT.getPredicate());
|
||||
}
|
||||
|
||||
public ArmoryAutomaton(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}");
|
||||
|
||||
|
|
@ -36,7 +30,7 @@ public final class ArmoryAutomaton extends CardImpl {
|
|||
|
||||
// Whenever Armory Automaton enters or attacks, you may attach any number of target Equipment to it.
|
||||
Ability ability = new EntersBattlefieldOrAttacksSourceTriggeredAbility(new ArmoryAutomatonEffect(), true);
|
||||
ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, filter));
|
||||
ability.addTarget(new TargetPermanent(0, Integer.MAX_VALUE, StaticFilters.FILTER_PERMANENT_EQUIPMENT));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.MageInt;
|
|||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.SetBasePowerSourceEffect;
|
||||
import mage.abilities.keyword.BoastAbility;
|
||||
|
|
@ -11,11 +12,9 @@ import mage.abilities.keyword.HasteAbility;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -36,7 +35,8 @@ public final class ArniBrokenbrow extends CardImpl {
|
|||
this.addAbility(HasteAbility.getInstance());
|
||||
|
||||
// Boast — {1}: You may change Arni Brokenbrow's base power to 1 plus the greatest power among other creatures you control until end of turn.
|
||||
this.addAbility(new BoastAbility(new ArniBrokenbrowEffect(), new GenericManaCost(1)));
|
||||
this.addAbility(new BoastAbility(new ArniBrokenbrowEffect(), new GenericManaCost(1))
|
||||
.addHint(GreatestAmongPermanentsValue.POWER_OTHER_CONTROLLED_CREATURES.getHint()));
|
||||
}
|
||||
|
||||
private ArniBrokenbrow(final ArniBrokenbrow card) {
|
||||
|
|
@ -72,17 +72,7 @@ class ArniBrokenbrowEffect extends OneShotEffect {
|
|||
if (controller == null || mageObject == null) {
|
||||
return false;
|
||||
}
|
||||
int power = game
|
||||
.getBattlefield()
|
||||
.getActivePermanents(StaticFilters.FILTER_CONTROLLED_CREATURE, source.getControllerId(), game)
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.filter(permanent -> !permanent.getId().equals(source.getSourceId())
|
||||
|| permanent.getZoneChangeCounter(game) != source.getSourceObjectZoneChangeCounter())
|
||||
.map(MageObject::getPower)
|
||||
.mapToInt(MageInt::getValue)
|
||||
.max()
|
||||
.orElse(0);
|
||||
int power = GreatestAmongPermanentsValue.POWER_OTHER_CONTROLLED_CREATURES.calculate(game, source, this);
|
||||
power += 1;
|
||||
if (controller.chooseUse(outcome, "Change base power of " + mageObject.getLogName() + " to "
|
||||
+ power + " until end of turn?", source, game
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInHand;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -131,7 +132,7 @@ class ArniMetalbrowEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null);
|
||||
Permanent creature = game.getPermanent(card.getId());
|
||||
Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (creature != null) {
|
||||
game.getCombat().addAttackingCreature(creature.getId(), game);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package mage.cards.a;
|
|||
|
||||
import mage.Mana;
|
||||
import mage.abilities.common.SagaAbility;
|
||||
import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue;
|
||||
import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue;
|
||||
import mage.abilities.effects.common.FightTargetsEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
|
||||
|
|
@ -58,11 +58,11 @@ public final class ArniSlaysTheTroll extends CardImpl {
|
|||
|
||||
// III — You gain life equal to the greatest power among creatures you control.
|
||||
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III,
|
||||
new GainLifeEffect(GreatestPowerAmongControlledCreaturesValue.instance,
|
||||
new GainLifeEffect(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES,
|
||||
"You gain life equal to the greatest power among creatures you control"
|
||||
)
|
||||
);
|
||||
sagaAbility.addHint(GreatestPowerAmongControlledCreaturesValue.getHint());
|
||||
sagaAbility.addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint());
|
||||
this.addAbility(sagaAbility);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -111,7 +112,7 @@ class ArthurMarigoldKnightEffect extends OneShotEffect {
|
|||
)) {
|
||||
return player.putCardsOnBottomOfLibrary(cards, game, source, false);
|
||||
}
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent == null) {
|
||||
return player.putCardsOnBottomOfLibrary(cards, game, source, false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,34 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.abilities.triggers.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class ArtificersHex extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterPermanent("Equipment");
|
||||
static {
|
||||
filter.add(CardType.ARTIFACT.getPredicate());
|
||||
filter.add(SubType.EQUIPMENT.getPredicate());
|
||||
}
|
||||
|
||||
public ArtificersHex(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{B}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}");
|
||||
this.subtype.add(SubType.AURA);
|
||||
|
||||
|
||||
// Enchant Equipment
|
||||
TargetPermanent auraTarget = new TargetPermanent(filter);
|
||||
TargetPermanent auraTarget = new TargetPermanent(StaticFilters.FILTER_PERMANENT_EQUIPMENT);
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
|
||||
Ability ability = new EnchantAbility(auraTarget);
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import mage.target.TargetPermanent;
|
|||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -172,7 +173,7 @@ class AscentOfTheWorthyReturnEffect extends OneShotEffect {
|
|||
countersToAdd.addCounter(CounterType.FLYING.createInstance());
|
||||
game.setEnterWithCounters(card.getId(), countersToAdd);
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
127
Mage.Sets/src/mage/cards/a/AshadTheLoneCyberman.java
Normal file
127
Mage.Sets/src/mage/cards/a/AshadTheLoneCyberman.java
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SacrificePermanentTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledSpellsEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.abilities.keyword.CasualtyAbility;
|
||||
import mage.constants.*;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.common.FilterNonlandCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author padfoothelix
|
||||
*/
|
||||
public final class AshadTheLoneCyberman extends CardImpl {
|
||||
|
||||
private static final String rule = "The first nonlegendary artifact spell you cast each turn has casualty 2. " +
|
||||
"<i>(As you cast it, you may sacrifice a creature with power 2 or greater. When you do, copy it. " +
|
||||
"A copy of an artifact spell becomes a token.)</i>";
|
||||
private static final FilterNonlandCard filter = new FilterNonlandCard("the first nonlegendary artifact you cast each turn");
|
||||
static {
|
||||
filter.add(AshadTheLoneCybermanSpellPredicate.instance);
|
||||
}
|
||||
|
||||
public AshadTheLoneCyberman(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{U}{B}{R}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.CYBERMAN);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// The first nonlegendary artifact spell you cast each turn has casualty 2.
|
||||
this.addAbility(new SimpleStaticAbility(
|
||||
new GainAbilityControlledSpellsEffect(
|
||||
new CasualtyAbility(2),
|
||||
filter
|
||||
).setText(rule)),
|
||||
new AshadTheLoneCybermanWatcher()
|
||||
);
|
||||
|
||||
// Whenever you sacrifice another creature, put a +1/+1 counter on Ashad, the Lone Cyberman.
|
||||
this.addAbility(new SacrificePermanentTriggeredAbility(
|
||||
new AddCountersSourceEffect(CounterType.P1P1.createInstance()),
|
||||
StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE
|
||||
));
|
||||
}
|
||||
|
||||
private AshadTheLoneCyberman(final AshadTheLoneCyberman card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AshadTheLoneCyberman copy() {
|
||||
return new AshadTheLoneCyberman(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AshadTheLoneCybermanWatcher extends Watcher {
|
||||
|
||||
// Based on Peri Brown which is based on Conduit of Ruin
|
||||
|
||||
private final Map<UUID, Integer> nonlegendaryArtifactSpells; // player id -> number
|
||||
|
||||
public AshadTheLoneCybermanWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
nonlegendaryArtifactSpells = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
if (event.getType() == GameEvent.EventType.SPELL_CAST) {
|
||||
Spell spell = (Spell) game.getObject(event.getTargetId());
|
||||
if (spell != null
|
||||
&& !spell.isLegendary(game)
|
||||
&& spell.isArtifact(game)) {
|
||||
nonlegendaryArtifactSpells.put(event.getPlayerId(), nonlegendaryArtifactSpellsCastThisTurn(event.getPlayerId()) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public int nonlegendaryArtifactSpellsCastThisTurn(UUID playerId) {
|
||||
return nonlegendaryArtifactSpells.getOrDefault(playerId, 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reset() {
|
||||
super.reset();
|
||||
nonlegendaryArtifactSpells.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
enum AshadTheLoneCybermanSpellPredicate implements ObjectSourcePlayerPredicate<Card> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<Card> input, Game game) {
|
||||
if (input.getObject() != null
|
||||
&& !input.getObject().isLegendary(game)
|
||||
&& input.getObject().isArtifact(game)) {
|
||||
AshadTheLoneCybermanWatcher watcher = game.getState().getWatcher(AshadTheLoneCybermanWatcher.class);
|
||||
return watcher != null && watcher.nonlegendaryArtifactSpellsCastThisTurn(input.getPlayerId()) == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "The first nonlegendary artifact spell you cast each turn";
|
||||
}
|
||||
}
|
||||
46
Mage.Sets/src/mage/cards/a/AshePrincessOfDalmasca.java
Normal file
46
Mage.Sets/src/mage/cards/a/AshePrincessOfDalmasca.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.effects.common.LookLibraryAndPickControllerEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.PutCards;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AshePrincessOfDalmasca extends CardImpl {
|
||||
|
||||
public AshePrincessOfDalmasca(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.REBEL);
|
||||
this.subtype.add(SubType.NOBLE);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Whenever Ashe attacks, look at the top five cards of your library. You may reveal an artifact card from among them and put it into your hand. Put the rest on the bottom of your library in a random order.
|
||||
this.addAbility(new AttacksTriggeredAbility(new LookLibraryAndPickControllerEffect(
|
||||
5, 1, StaticFilters.FILTER_CARD_ARTIFACT_AN,
|
||||
PutCards.HAND, PutCards.BOTTOM_RANDOM
|
||||
)));
|
||||
}
|
||||
|
||||
private AshePrincessOfDalmasca(final AshePrincessOfDalmasca card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AshePrincessOfDalmasca copy() {
|
||||
return new AshePrincessOfDalmasca(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -125,7 +125,7 @@ class AshiokNightmareWeaverPutIntoPlayEffect extends OneShotEffect {
|
|||
return true;
|
||||
}
|
||||
controller.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent != null) {
|
||||
game.addEffect(new AddCardSubTypeTargetEffect(
|
||||
SubType.NIGHTMARE, Duration.EndOfTurn
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ import mage.constants.Zone;
|
|||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -81,7 +82,8 @@ class AspiringChampionEffect extends OneShotEffect {
|
|||
toReveal.retainZone(Zone.LIBRARY, game);
|
||||
player.putCardsOnBottomOfLibrary(toReveal, game, source, false);
|
||||
player.shuffleLibrary(source, game);
|
||||
Permanent creature = game.getPermanent(card.getId());
|
||||
game.processAction();
|
||||
Permanent creature = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (creature == null
|
||||
|| !creature.hasSubtype(SubType.DEMON, game)
|
||||
|| creature.getPower().getValue() < 1) {
|
||||
|
|
|
|||
|
|
@ -1,42 +1,30 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.AttachTargetToTargetEffect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.common.FilterEnchantmentPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.Target;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public final class AuraFinesse extends CardImpl {
|
||||
|
||||
private static final FilterEnchantmentPermanent filter = new FilterEnchantmentPermanent("Aura you control");
|
||||
|
||||
static {
|
||||
filter.add(TargetController.YOU.getControllerPredicate());
|
||||
filter.add(SubType.AURA.getPredicate());
|
||||
}
|
||||
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.AURA);
|
||||
|
||||
public AuraFinesse(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
|
||||
|
||||
// Attach target Aura you control to target creature.
|
||||
this.getSpellAbility().addEffect(new AuraFinesseEffect());
|
||||
this.getSpellAbility().addEffect(new AttachTargetToTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
|
||||
|
|
@ -53,47 +41,3 @@ public final class AuraFinesse extends CardImpl {
|
|||
return new AuraFinesse(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AuraFinesseEffect extends OneShotEffect {
|
||||
|
||||
AuraFinesseEffect() {
|
||||
super(Outcome.BoostCreature);
|
||||
this.staticText = "Attach target Aura you control to target creature";
|
||||
}
|
||||
|
||||
private AuraFinesseEffect(final AuraFinesseEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AuraFinesseEffect copy() {
|
||||
return new AuraFinesseEffect(this);
|
||||
}
|
||||
|
||||
// 15/06/2010 As Aura Finesse resolves, if either target is illegal,
|
||||
// the spell resolves but the Aura doesn’t move. You still draw a card.
|
||||
// If both targets are illegal, Aura Finesse doesn’t resolve and you don’t draw a card.
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Permanent aura = game.getPermanent(source.getFirstTarget());
|
||||
Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget());
|
||||
if (aura != null && creature != null) {
|
||||
Permanent oldCreature = game.getPermanent(aura.getAttachedTo());
|
||||
if (oldCreature != null && !oldCreature.equals(creature)) {
|
||||
Target auraTarget = aura.getSpellAbility().getTargets().get(0);
|
||||
if (!auraTarget.canTarget(creature.getId(), game)) {
|
||||
game.informPlayers(aura.getLogName() + " was not attched to " +creature.getLogName() + " because it's no legal target for the aura" );
|
||||
} else if (oldCreature.removeAttachment(aura.getId(), source, game)) {
|
||||
game.informPlayers(aura.getLogName() + " was unattached from " + oldCreature.getLogName() + " and attached to " + creature.getLogName());
|
||||
creature.addAttachment(aura.getId(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,39 +1,28 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.AttachTargetToTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Plopman
|
||||
*/
|
||||
public final class AuriokWindwalker extends CardImpl {
|
||||
|
||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent("Equipment you control");
|
||||
|
||||
static {
|
||||
filter.add(SubType.EQUIPMENT.getPredicate());
|
||||
}
|
||||
|
||||
public AuriokWindwalker(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
this.power = new MageInt(2);
|
||||
|
|
@ -41,9 +30,10 @@ public final class AuriokWindwalker extends CardImpl {
|
|||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// {T}: Attach target Equipment you control to target creature you control.
|
||||
Ability ability = new SimpleActivatedAbility(new AttachTargetEquipmentEffect(), new TapSourceCost());
|
||||
ability.addTarget(new TargetControlledPermanent(filter));
|
||||
Ability ability = new SimpleActivatedAbility(new AttachTargetToTargetEffect(), new TapSourceCost());
|
||||
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT));
|
||||
ability.addTarget(new TargetControlledCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
@ -57,30 +47,3 @@ public final class AuriokWindwalker extends CardImpl {
|
|||
return new AuriokWindwalker(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AttachTargetEquipmentEffect extends OneShotEffect {
|
||||
|
||||
AttachTargetEquipmentEffect() {
|
||||
super(Outcome.BoostCreature);
|
||||
staticText = "Attach target Equipment you control to target creature you control";
|
||||
}
|
||||
|
||||
private AttachTargetEquipmentEffect(final AttachTargetEquipmentEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AttachTargetEquipmentEffect copy() {
|
||||
return new AttachTargetEquipmentEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent equipment = game.getPermanent(source.getFirstTarget());
|
||||
Permanent creature = game.getPermanent(source.getTargets().get(1).getFirstTarget());
|
||||
if (creature != null && equipment != null) {
|
||||
return creature.addAttachment(equipment.getId(), source, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +1,6 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.continuous.BecomesCreatureAttachedEffect;
|
||||
|
|
@ -11,37 +8,38 @@ import mage.abilities.keyword.EnchantAbility;
|
|||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterLandPermanent;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.permanent.token.TokenImpl;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetLandPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public final class AwakenTheAncient extends CardImpl {
|
||||
|
||||
private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.MOUNTAIN, "Mountain");
|
||||
private static final FilterPermanent filter = new FilterPermanent(SubType.MOUNTAIN, "Mountain");
|
||||
|
||||
public AwakenTheAncient(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{R}{R}{R}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}{R}{R}");
|
||||
this.subtype.add(SubType.AURA);
|
||||
|
||||
|
||||
// Enchant Mountain
|
||||
TargetPermanent auraTarget = new TargetLandPermanent(filter);
|
||||
TargetPermanent auraTarget = new TargetPermanent(filter);
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.PutCreatureInPlay));
|
||||
Ability ability = new EnchantAbility(auraTarget);
|
||||
this.addAbility(ability);
|
||||
this.addAbility(new EnchantAbility(auraTarget));
|
||||
|
||||
// Enchanted Mountain is a 7/7 red Giant creature with haste. It's still a land.
|
||||
Ability ability2 = new SimpleStaticAbility(new BecomesCreatureAttachedEffect(
|
||||
new GiantToken(), "Enchanted Mountain is a 7/7 red Giant creature with haste. It's still a land", Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.COLOR));
|
||||
this.addAbility(ability2);
|
||||
|
||||
this.addAbility(new SimpleStaticAbility(new BecomesCreatureAttachedEffect(
|
||||
new GiantToken(), "Enchanted Mountain is a 7/7 red Giant creature with haste. It's still a land",
|
||||
Duration.WhileOnBattlefield, BecomesCreatureAttachedEffect.LoseType.COLOR
|
||||
)));
|
||||
}
|
||||
|
||||
private AwakenTheAncient(final AwakenTheAncient card) {
|
||||
|
|
|
|||
|
|
@ -1,32 +1,29 @@
|
|||
|
||||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.common.continuous.BecomesCreatureTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterLandPermanent;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.TokenImpl;
|
||||
import mage.target.common.TargetLandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public final class AwakenerDruid extends CardImpl {
|
||||
|
||||
private static final FilterLandPermanent filter = new FilterLandPermanent(SubType.FOREST, "Forest");
|
||||
private static final FilterPermanent filter = new FilterPermanent(SubType.FOREST, "Forest");
|
||||
|
||||
public AwakenerDruid(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.DRUID);
|
||||
|
|
@ -35,7 +32,7 @@ public final class AwakenerDruid extends CardImpl {
|
|||
|
||||
// When Awakener Druid enters the battlefield, target Forest becomes a 4/5 green Treefolk creature for as long as Awakener Druid remains on the battlefield. It's still a land.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new AwakenerDruidBecomesCreatureEffect(), false);
|
||||
ability.addTarget(new TargetLandPermanent(filter));
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
@ -87,6 +84,7 @@ class AwakenerDruidToken extends TokenImpl {
|
|||
power = new MageInt(4);
|
||||
toughness = new MageInt(5);
|
||||
}
|
||||
|
||||
private AwakenerDruidToken(final AwakenerDruidToken token) {
|
||||
super(token);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,9 +15,11 @@ import mage.constants.*;
|
|||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -90,17 +92,18 @@ class AyaraFurnaceQueenEffect extends OneShotEffect {
|
|||
if (player == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
if (game.getPermanent(card.getId()) == null) {
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
game.addEffect(new GainAbilityTargetEffect(
|
||||
HasteAbility.getInstance(), Duration.Custom
|
||||
).setTargetPointer(new FixedTarget(card.getId(), game)), source);
|
||||
).setTargetPointer(new FixedTarget(permanent.getId(), game)), source);
|
||||
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
|
||||
new ExileTargetEffect().setText("exile it")
|
||||
.setTargetPointer(new FixedTarget(card.getId(), game)),
|
||||
.setTargetPointer(new FixedTarget(permanent.getId(), game)),
|
||||
TargetController.ANY
|
||||
), source);
|
||||
return true;}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -79,7 +80,7 @@ class BackForMoreEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
player.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
71
Mage.Sets/src/mage/cards/b/BahamutWardenOfLight.java
Normal file
71
Mage.Sets/src/mage/cards/b/BahamutWardenOfLight.java
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SagaAbility;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
import mage.abilities.effects.common.ExileSourceAndReturnFaceUpEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersAllEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BahamutWardenOfLight extends CardImpl {
|
||||
|
||||
public BahamutWardenOfLight(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.DRAGON);
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(5);
|
||||
this.nightCard = true;
|
||||
this.color.setWhite(true);
|
||||
|
||||
// (As this Saga enters and after your draw step, add a lore counter.)
|
||||
SagaAbility sagaAbility = new SagaAbility(this);
|
||||
|
||||
// I, II -- Wings of Light -- Put a +1/+1 counter on each other creature you control. Those creatures gain flying until end of turn.
|
||||
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_II, ability -> {
|
||||
ability.addEffect(new AddCountersAllEffect(
|
||||
CounterType.P1P1.createInstance(), StaticFilters.FILTER_OTHER_CONTROLLED_CREATURE
|
||||
));
|
||||
ability.addEffect(new GainAbilityControlledEffect(
|
||||
FlyingAbility.getInstance(), Duration.EndOfTurn,
|
||||
StaticFilters.FILTER_CONTROLLED_CREATURE
|
||||
).setText("Those creatures gain flying until end of turn"));
|
||||
ability.withFlavorWord("Wings of Light");
|
||||
});
|
||||
|
||||
// III -- Gigaflare -- Destroy target permanent. Exile Bahamut, then return it to the battlefield.
|
||||
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III, ability -> {
|
||||
ability.addEffect(new DestroyTargetEffect());
|
||||
ability.addEffect(new ExileSourceAndReturnFaceUpEffect());
|
||||
ability.addTarget(new TargetPermanent());
|
||||
ability.withFlavorWord("Gigaflare");
|
||||
});
|
||||
this.addAbility(sagaAbility);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
}
|
||||
|
||||
private BahamutWardenOfLight(final BahamutWardenOfLight card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BahamutWardenOfLight copy() {
|
||||
return new BahamutWardenOfLight(this);
|
||||
}
|
||||
}
|
||||
48
Mage.Sets/src/mage/cards/b/BalambGardenAirborne.java
Normal file
48
Mage.Sets/src/mage/cards/b/BalambGardenAirborne.java
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.keyword.CrewAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
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 BalambGardenAirborne extends CardImpl {
|
||||
|
||||
public BalambGardenAirborne(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.VEHICLE);
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(4);
|
||||
this.nightCard = true;
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Whenever Balamb Garden attacks, draw a card.
|
||||
this.addAbility(new AttacksTriggeredAbility(new DrawCardSourceControllerEffect(1)));
|
||||
|
||||
// Crew 1
|
||||
this.addAbility(new CrewAbility(1));
|
||||
}
|
||||
|
||||
private BalambGardenAirborne(final BalambGardenAirborne card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BalambGardenAirborne copy() {
|
||||
return new BalambGardenAirborne(this);
|
||||
}
|
||||
}
|
||||
88
Mage.Sets/src/mage/cards/b/BalambGardenSeeDAcademy.java
Normal file
88
Mage.Sets/src/mage/cards/b/BalambGardenSeeDAcademy.java
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTappedAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.CostAdjuster;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.abilities.mana.BlueManaAbility;
|
||||
import mage.abilities.mana.GreenManaAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BalambGardenSeeDAcademy extends CardImpl {
|
||||
|
||||
public BalambGardenSeeDAcademy(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||
|
||||
this.subtype.add(SubType.TOWN);
|
||||
this.secondSideCardClazz = mage.cards.b.BalambGardenAirborne.class;
|
||||
|
||||
// This land enters tapped.
|
||||
this.addAbility(new EntersBattlefieldTappedAbility());
|
||||
|
||||
// Add {G} or {U}.
|
||||
this.addAbility(new GreenManaAbility());
|
||||
this.addAbility(new BlueManaAbility());
|
||||
|
||||
// {5}{G}{U}, {T}: Transform this land. This ability costs {1} less to activate for each other Town you control.
|
||||
this.addAbility(new TransformAbility());
|
||||
Ability ability = new SimpleActivatedAbility(new TransformSourceEffect(), new ManaCostsImpl<>("{5}{G}{U}"));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addEffect(new InfoEffect("This ability costs {1} less to activate for each other Town you control"));
|
||||
this.addAbility(ability
|
||||
.setCostAdjuster(BalambGardenSeeDAcademyAdjuster.instance)
|
||||
.addHint(BalambGardenSeeDAcademyAdjuster.getHint()));
|
||||
}
|
||||
|
||||
private BalambGardenSeeDAcademy(final BalambGardenSeeDAcademy card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BalambGardenSeeDAcademy copy() {
|
||||
return new BalambGardenSeeDAcademy(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum BalambGardenSeeDAcademyAdjuster implements CostAdjuster {
|
||||
instance;
|
||||
private static final FilterPermanent filter = new FilterControlledPermanent(SubType.TOWN);
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
private static final DynamicValue xValue = new PermanentsOnBattlefieldCount(filter);
|
||||
private static final Hint hint = new ValueHint("Other Towns you control", xValue);
|
||||
|
||||
static Hint getHint() {
|
||||
return hint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reduceCost(Ability ability, Game game) {
|
||||
int count = xValue.calculate(game, ability, null);
|
||||
CardUtil.reduceCost(ability, count);
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,8 @@ import mage.MageInt;
|
|||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.EquippedMultipleSourceCondition;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
|
|
@ -15,6 +16,8 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.predicate.permanent.AttachedToSourcePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
|
|
@ -25,7 +28,14 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class BalanWanderingKnight extends CardImpl {
|
||||
|
||||
private static final String rule = "{this} has double strike as long as two or more Equipment are attached to it.";
|
||||
private static final FilterPermanent filter = new FilterPermanent(SubType.EQUIPMENT, "");
|
||||
|
||||
static {
|
||||
filter.add(AttachedToSourcePredicate.instance);
|
||||
}
|
||||
|
||||
private static final Condition condition
|
||||
= new PermanentsOnTheBattlefieldCondition(filter, ComparisonType.MORE_THAN, 1, false);
|
||||
|
||||
public BalanWanderingKnight(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}{W}");
|
||||
|
|
@ -38,8 +48,10 @@ public final class BalanWanderingKnight extends CardImpl {
|
|||
this.addAbility(FirstStrikeAbility.getInstance());
|
||||
|
||||
// Balan, Wandering Knight has double strike as long as two or more Equipment are attached to it.
|
||||
ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()), EquippedMultipleSourceCondition.instance, rule);
|
||||
this.addAbility(new SimpleStaticAbility(effect));
|
||||
this.addAbility(new SimpleStaticAbility(new ConditionalContinuousEffect(
|
||||
new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()), condition,
|
||||
"{this} has double strike as long as two or more Equipment are attached to it."
|
||||
)));
|
||||
|
||||
// {1}{W}: Attach all Equipment you control to Balan.
|
||||
this.addAbility(new SimpleActivatedAbility(new BalanWanderingKnightEffect(), new ManaCostsImpl<>("{1}{W}")));
|
||||
|
|
@ -54,11 +66,13 @@ public final class BalanWanderingKnight extends CardImpl {
|
|||
return new BalanWanderingKnight(this);
|
||||
}
|
||||
|
||||
static class BalanWanderingKnightEffect extends OneShotEffect {
|
||||
}
|
||||
|
||||
public BalanWanderingKnightEffect() {
|
||||
class BalanWanderingKnightEffect extends OneShotEffect {
|
||||
|
||||
BalanWanderingKnightEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Attach all Equipment you control to {this}.";
|
||||
this.staticText = "attach all Equipment you control to {this}.";
|
||||
}
|
||||
|
||||
private BalanWanderingKnightEffect(final BalanWanderingKnightEffect effect) {
|
||||
|
|
@ -72,21 +86,15 @@ public final class BalanWanderingKnight extends CardImpl {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent balan = game.getPermanent(source.getSourceId());
|
||||
if (balan != null) {
|
||||
FilterPermanent filter = new FilterPermanent();
|
||||
filter.add(SubType.EQUIPMENT.getPredicate());
|
||||
for (Permanent equipment : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(), game)) {
|
||||
if (equipment != null) {
|
||||
//If an Equipment can't equip, it isn't attached, and it doesn't become unattached (if it's attached to a creature).
|
||||
if (!balan.cantBeAttachedBy(equipment, source, game, false)) {
|
||||
balan.addAttachment(equipment.getId(), source, game);
|
||||
}
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
for (Permanent equipment : game.getBattlefield().getActivePermanents(
|
||||
StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT, source.getControllerId(), source, game
|
||||
)) {
|
||||
permanent.addAttachment(equipment.getId(), source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ import mage.game.permanent.Permanent;
|
|||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -98,7 +99,7 @@ class BalduvianAtrocityEffect extends OneShotEffect {
|
|||
return false;
|
||||
}
|
||||
controller.moveCards(card, Zone.BATTLEFIELD, source, game);
|
||||
Permanent permanent = game.getPermanent(card.getId());
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
54
Mage.Sets/src/mage/cards/b/BardsBow.java
Normal file
54
Mage.Sets/src/mage/cards/b/BardsBow.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
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.abilities.keyword.JobSelectAbility;
|
||||
import mage.abilities.keyword.ReachAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.AttachmentType;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BardsBow extends CardImpl {
|
||||
|
||||
public BardsBow(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}{G}");
|
||||
|
||||
this.subtype.add(SubType.EQUIPMENT);
|
||||
|
||||
// Job select
|
||||
this.addAbility(new JobSelectAbility());
|
||||
|
||||
// Equipped creature gets +2/+2, has reach, and is a Bard in addition to its other types.
|
||||
Ability ability = new SimpleStaticAbility(new BoostEquippedEffect(2, 2));
|
||||
ability.addEffect(new GainAbilityAttachedEffect(
|
||||
ReachAbility.getInstance(), AttachmentType.EQUIPMENT
|
||||
).setText(", has reach"));
|
||||
ability.addEffect(new AddCardSubtypeAttachedEffect(
|
||||
SubType.BARD, AttachmentType.EQUIPMENT
|
||||
).setText(", and is a Bard in addition to its other types"));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Perseus's Bow-- Equip {6}
|
||||
this.addAbility(new EquipAbility(6).withFlavorWord("Perseus's Bow"));
|
||||
}
|
||||
|
||||
private BardsBow(final BardsBow card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BardsBow copy() {
|
||||
return new BardsBow(this);
|
||||
}
|
||||
}
|
||||
39
Mage.Sets/src/mage/cards/b/BaronAirshipKingdom.java
Normal file
39
Mage.Sets/src/mage/cards/b/BaronAirshipKingdom.java
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.abilities.common.EntersBattlefieldTappedAbility;
|
||||
import mage.abilities.mana.BlueManaAbility;
|
||||
import mage.abilities.mana.RedManaAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BaronAirshipKingdom extends CardImpl {
|
||||
|
||||
public BaronAirshipKingdom(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
|
||||
|
||||
this.subtype.add(SubType.TOWN);
|
||||
|
||||
// This land enters tapped.
|
||||
this.addAbility(new EntersBattlefieldTappedAbility());
|
||||
|
||||
// {T}: Add {U} or {R}.
|
||||
this.addAbility(new BlueManaAbility());
|
||||
this.addAbility(new RedManaAbility());
|
||||
}
|
||||
|
||||
private BaronAirshipKingdom(final BaronAirshipKingdom card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaronAirshipKingdom copy() {
|
||||
return new BaronAirshipKingdom(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -3,35 +3,28 @@ 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.AttachTargetToTargetEffect;
|
||||
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.StaticFilters;
|
||||
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) {
|
||||
|
|
@ -48,12 +41,12 @@ public final class BarretAvalancheLeader extends CardImpl {
|
|||
|
||||
// Avalanche! -- Whenever an Equipment you control enters, create a 2/2 red Rebel creature token.
|
||||
this.addAbility(new EntersBattlefieldAllTriggeredAbility(
|
||||
new CreateTokenEffect(new RebelRedToken()), filter
|
||||
new CreateTokenEffect(new RebelRedToken()), StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT
|
||||
).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 ability = new BeginningOfCombatTriggeredAbility(new AttachTargetToTargetEffect());
|
||||
ability.addTarget(new TargetPermanent(0, 1, StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT));
|
||||
ability.addTarget(new TargetPermanent(filter2));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
@ -67,33 +60,3 @@ public final class BarretAvalancheLeader extends CardImpl {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,8 @@ import mage.abilities.keyword.CyclingAbility;
|
|||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.filter.common.FilterEnchantmentPermanent;
|
||||
import mage.target.common.TargetEnchantmentPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -21,8 +21,9 @@ public final class BarrierBreach extends CardImpl {
|
|||
|
||||
// Exile up to three target enchantments.
|
||||
this.getSpellAbility().addEffect(new ExileTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetEnchantmentPermanent(0, 3,
|
||||
new FilterEnchantmentPermanent("enchantments"), false));
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(
|
||||
0, 3, StaticFilters.FILTER_PERMANENT_ENCHANTMENTS
|
||||
));
|
||||
|
||||
// Cycling {2}
|
||||
this.addAbility(new CyclingAbility(new ManaCostsImpl<>("{2}")));
|
||||
|
|
|
|||
|
|
@ -1,28 +1,26 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.CostAdjuster;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.token.Wurm44Token;
|
||||
import mage.util.CardUtil;
|
||||
|
|
@ -34,11 +32,9 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class BaruWurmspeaker extends CardImpl {
|
||||
|
||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent(SubType.WURM, "Wurms");
|
||||
private static final FilterPermanent filter2 = new FilterPermanent(SubType.WURM, "");
|
||||
private static final Hint hint = new ValueHint(
|
||||
"Highest power among Wurms you control", BaruWurmspeakerValue.instance
|
||||
);
|
||||
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent(SubType.WURM, "Wurms you control");
|
||||
static final GreatestAmongPermanentsValue xValue = new GreatestAmongPermanentsValue(GreatestAmongPermanentsValue.Quality.Power, filter);
|
||||
private static final Hint hint = xValue.getHint();
|
||||
|
||||
public BaruWurmspeaker(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{G}{G}");
|
||||
|
|
@ -54,7 +50,7 @@ public final class BaruWurmspeaker extends CardImpl {
|
|||
2, 2, Duration.WhileOnBattlefield, filter, false
|
||||
));
|
||||
ability.addEffect(new GainAbilityControlledEffect(
|
||||
TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter2
|
||||
TrampleAbility.getInstance(), Duration.WhileOnBattlefield, filter
|
||||
).setText("and have trample"));
|
||||
this.addAbility(ability);
|
||||
|
||||
|
|
@ -64,6 +60,7 @@ public final class BaruWurmspeaker extends CardImpl {
|
|||
ability.addEffect(new InfoEffect("this ability costs {X} less to activate, " +
|
||||
"where X is the greatest power among Wurms you control"));
|
||||
ability.setCostAdjuster(BaruWurmspeakerAdjuster.instance);
|
||||
ability.addHint(hint);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
@ -77,44 +74,12 @@ public final class BaruWurmspeaker extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
enum BaruWurmspeakerValue implements DynamicValue {
|
||||
instance;
|
||||
private static final FilterPermanent filter = new FilterControlledCreaturePermanent(SubType.WURM);
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return game
|
||||
.getBattlefield()
|
||||
.getActivePermanents(filter, sourceAbility.getControllerId(), sourceAbility, game)
|
||||
.stream()
|
||||
.map(MageObject::getPower)
|
||||
.mapToInt(MageInt::getValue)
|
||||
.max()
|
||||
.orElse(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BaruWurmspeakerValue copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
enum BaruWurmspeakerAdjuster implements CostAdjuster {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public void reduceCost(Ability ability, Game game) {
|
||||
int value = BaruWurmspeakerValue.instance.calculate(game, ability, null);
|
||||
int value = BaruWurmspeaker.xValue.calculate(game, ability, null);
|
||||
if (value > 0) {
|
||||
CardUtil.reduceCost(ability, value);
|
||||
}
|
||||
|
|
|
|||
62
Mage.Sets/src/mage/cards/b/BattleMenu.java
Normal file
62
Mage.Sets/src/mage/cards/b/BattleMenu.java
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||
import mage.game.permanent.token.WaylayToken;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BattleMenu extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent("creature with power 4 or greater");
|
||||
|
||||
static {
|
||||
filter.add(new PowerPredicate(ComparisonType.MORE_THAN, 3));
|
||||
}
|
||||
|
||||
public BattleMenu(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
|
||||
|
||||
// Choose one --
|
||||
// * Attack -- Create a 2/2 white Knight creature token.
|
||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new WaylayToken()));
|
||||
this.getSpellAbility().withFirstModeFlavorWord("Attack");
|
||||
|
||||
// * Ability -- Target creature gets +0/+4 until end of turn.
|
||||
this.getSpellAbility().addMode(new Mode(new BoostTargetEffect(0, 4))
|
||||
.addTarget(new TargetCreaturePermanent())
|
||||
.withFlavorWord("Ability"));
|
||||
|
||||
// * Magic -- Destroy target creature with power 4 or greater.
|
||||
this.getSpellAbility().addMode(new Mode(new DestroyTargetEffect())
|
||||
.addTarget(new TargetPermanent(filter))
|
||||
.withFlavorWord("Magic"));
|
||||
|
||||
// * Item -- You gain 4 life.
|
||||
this.getSpellAbility().addMode(new Mode(new GainLifeEffect(4)).withFlavorWord("Item"));
|
||||
}
|
||||
|
||||
private BattleMenu(final BattleMenu card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BattleMenu copy() {
|
||||
return new BattleMenu(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -12,12 +12,13 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.Choice;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterLandPermanent;
|
||||
import mage.filter.predicate.permanent.TappedPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetLandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
|
|
@ -26,7 +27,7 @@ import java.util.*;
|
|||
*/
|
||||
public final class BenthicExplorers extends CardImpl {
|
||||
|
||||
private static final FilterLandPermanent filter = new FilterLandPermanent("tapped land an opponent controls");
|
||||
private static final FilterPermanent filter = new FilterLandPermanent("tapped land an opponent controls");
|
||||
|
||||
static {
|
||||
filter.add(TargetController.OPPONENT.getControllerPredicate());
|
||||
|
|
@ -43,9 +44,7 @@ public final class BenthicExplorers extends CardImpl {
|
|||
|
||||
// {T}, Untap a tapped land an opponent controls: Add one mana of any type that land could produce.
|
||||
Ability ability = new BenthicExplorersManaAbility();
|
||||
ability.addCost(new UntapTargetCost(
|
||||
new TargetLandPermanent(filter)
|
||||
));
|
||||
ability.addCost(new UntapTargetCost(new TargetPermanent(filter)));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,7 @@ import mage.Mana;
|
|||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.dynamicvalue.common.GreatestPowerAmongControlledCreaturesValue;
|
||||
import mage.abilities.dynamicvalue.common.GreatestToughnessAmongControlledCreaturesValue;
|
||||
import mage.abilities.dynamicvalue.common.GreatestAmongPermanentsValue;
|
||||
import mage.abilities.effects.common.GainLifeEffect;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.abilities.mana.DynamicManaAbility;
|
||||
|
|
@ -35,16 +34,16 @@ public final class BighornerRancher extends CardImpl {
|
|||
|
||||
// {T}: Add an amount of {G} equal to the greatest power among creatures you control.
|
||||
this.addAbility(new DynamicManaAbility(
|
||||
Mana.GreenMana(1), GreatestPowerAmongControlledCreaturesValue.instance, new TapSourceCost(),
|
||||
Mana.GreenMana(1), GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES, new TapSourceCost(),
|
||||
"Add an amount of {G} equal to the greatest power among creatures you control."
|
||||
).addHint(GreatestPowerAmongControlledCreaturesValue.getHint()));
|
||||
).addHint(GreatestAmongPermanentsValue.POWER_CONTROLLED_CREATURES.getHint()));
|
||||
|
||||
// Sacrifice Bighorner Rancher: You gain life equal to the greatest toughness among other creatures you control.
|
||||
this.addAbility(new SimpleActivatedAbility(
|
||||
new GainLifeEffect(GreatestToughnessAmongControlledCreaturesValue.OTHER)
|
||||
new GainLifeEffect(GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES)
|
||||
.setText("You gain life equal to the greatest toughness among other creatures you control."),
|
||||
new SacrificeSourceCost()
|
||||
).addHint(GreatestToughnessAmongControlledCreaturesValue.OTHER.getHint()));
|
||||
).addHint(GreatestAmongPermanentsValue.TOUGHNESS_OTHER_CONTROLLED_CREATURES.getHint()));
|
||||
}
|
||||
|
||||
private BighornerRancher(final BighornerRancher card) {
|
||||
|
|
|
|||
|
|
@ -1,35 +1,30 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.abilities.common.SagaAbility;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
|
||||
import mage.abilities.keyword.DeathtouchAbility;
|
||||
import mage.constants.*;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SagaChapter;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterBySubtypeCard;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetNonlandPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author weirddan455
|
||||
*/
|
||||
public final class BindingTheOldGods extends CardImpl {
|
||||
|
||||
private static final FilterNonlandPermanent filter
|
||||
= new FilterNonlandPermanent("nonland permanent an opponent controls");
|
||||
private static final FilterBySubtypeCard filter2
|
||||
= new FilterBySubtypeCard(SubType.FOREST);
|
||||
|
||||
static {
|
||||
filter.add(TargetController.OPPONENT.getControllerPredicate());
|
||||
}
|
||||
private static final FilterCard filter = new FilterBySubtypeCard(SubType.FOREST);
|
||||
|
||||
public BindingTheOldGods(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{G}");
|
||||
|
|
@ -41,11 +36,11 @@ public final class BindingTheOldGods extends CardImpl {
|
|||
// I — Destroy target nonland permanent an opponent controls.
|
||||
sagaAbility.addChapterEffect(
|
||||
this, SagaChapter.CHAPTER_I, SagaChapter.CHAPTER_I,
|
||||
new DestroyTargetEffect(), new TargetNonlandPermanent(filter)
|
||||
new DestroyTargetEffect(), new TargetPermanent(StaticFilters.FILTER_OPPONENTS_PERMANENT_NON_LAND)
|
||||
);
|
||||
// II — Search your library for a Forest card, put it onto the battlefield tapped, then shuffle your library.
|
||||
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_II,
|
||||
new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter2), true)
|
||||
new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(filter), true)
|
||||
);
|
||||
// III — Creatures you control gain deathtouch until end of turn.
|
||||
sagaAbility.addChapterEffect(this, SagaChapter.CHAPTER_III,
|
||||
|
|
|
|||
|
|
@ -10,9 +10,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.WasDealtDamageThisTurnPredicate;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -22,14 +20,7 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class BitterDownfall extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter
|
||||
= new FilterCreaturePermanent("a creature that was dealt damage this turn");
|
||||
|
||||
static {
|
||||
filter.add(WasDealtDamageThisTurnPredicate.instance);
|
||||
}
|
||||
|
||||
private static final Condition condition = new SourceTargetsPermanentCondition(filter);
|
||||
private static final Condition condition = new SourceTargetsPermanentCondition(StaticFilters.FILTER_CREATURE_DAMAGED_THIS_TURN);
|
||||
|
||||
public BitterDownfall(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{B}");
|
||||
|
|
|
|||
54
Mage.Sets/src/mage/cards/b/BlackChocobo.java
Normal file
54
Mage.Sets/src/mage/cards/b/BlackChocobo.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.LandfallAbility;
|
||||
import mage.abilities.common.TransformIntoSourceTriggeredAbility;
|
||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BlackChocobo extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterPermanent(SubType.BIRD, "Birds");
|
||||
|
||||
public BlackChocobo(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "");
|
||||
|
||||
this.subtype.add(SubType.BIRD);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
this.nightCard = true;
|
||||
this.color.setGreen(true);
|
||||
|
||||
// When this permanent transforms into Black Chocobo, search your library for a land card, put it onto the battlefield tapped, then shuffle.
|
||||
this.addAbility(new TransformIntoSourceTriggeredAbility(
|
||||
new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(StaticFilters.FILTER_CARD_LAND_A), true)
|
||||
));
|
||||
|
||||
// Landfall -- Whenever a land you control enters, Birds you control get +1/+0 until end of turn.
|
||||
this.addAbility(new LandfallAbility(new BoostControlledEffect(
|
||||
1, 0, Duration.EndOfTurn, filter, false
|
||||
)));
|
||||
}
|
||||
|
||||
private BlackChocobo(final BlackChocobo card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlackChocobo copy() {
|
||||
return new BlackChocobo(this);
|
||||
}
|
||||
}
|
||||
52
Mage.Sets/src/mage/cards/b/BlackWaltzNo3.java
Normal file
52
Mage.Sets/src/mage/cards/b/BlackWaltzNo3.java
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||
import mage.abilities.effects.common.DamagePlayersEffect;
|
||||
import mage.abilities.keyword.DeathtouchAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BlackWaltzNo3 extends CardImpl {
|
||||
|
||||
public BlackWaltzNo3(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}{R}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Deathtouch
|
||||
this.addAbility(DeathtouchAbility.getInstance());
|
||||
|
||||
// Whenever you cast a noncreature spell, Black Waltz No. 3 deals 2 damage to each opponent.
|
||||
this.addAbility(new SpellCastControllerTriggeredAbility(
|
||||
new DamagePlayersEffect(2, TargetController.OPPONENT),
|
||||
StaticFilters.FILTER_SPELL_A_NON_CREATURE, false
|
||||
));
|
||||
}
|
||||
|
||||
private BlackWaltzNo3(final BlackWaltzNo3 card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlackWaltzNo3 copy() {
|
||||
return new BlackWaltzNo3(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,7 @@ import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
|||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.MyTurnCondition;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.AttachTargetToTargetEffect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.effects.common.continuous.GainClassAbilitySourceEffect;
|
||||
|
|
@ -16,19 +16,17 @@ import mage.abilities.keyword.HasteAbility;
|
|||
import mage.abilities.triggers.BeginningOfCombatTriggeredAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.common.FilterEquipmentPermanent;
|
||||
import mage.filter.predicate.permanent.EquippedPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.SwordToken;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.targetpointer.EachTargetPointer;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
|
|
@ -36,12 +34,10 @@ import java.util.UUID;
|
|||
*/
|
||||
public final class BlacksmithsTalent extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterEquipmentPermanent("equipment you control");
|
||||
private static final FilterPermanent filter2 = new FilterCreaturePermanent();
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent();
|
||||
|
||||
static {
|
||||
filter.add(TargetController.YOU.getControllerPredicate());
|
||||
filter2.add(EquippedPredicate.instance);
|
||||
filter.add(EquippedPredicate.instance);
|
||||
}
|
||||
|
||||
public BlacksmithsTalent(UUID ownerId, CardSetInfo setInfo) {
|
||||
|
|
@ -59,10 +55,8 @@ public final class BlacksmithsTalent extends CardImpl {
|
|||
this.addAbility(new ClassLevelAbility(2, "{2}{R}"));
|
||||
|
||||
// At the beginning of combat on your turn, attach target Equipment you control to up to one target creature you control.
|
||||
Ability ability = new BeginningOfCombatTriggeredAbility(
|
||||
new BlacksmithsTalentEffect()
|
||||
);
|
||||
ability.addTarget(new TargetPermanent(filter));
|
||||
Ability ability = new BeginningOfCombatTriggeredAbility(new AttachTargetToTargetEffect());
|
||||
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_CONTROLLED_PERMANENT_EQUIPMENT));
|
||||
ability.addTarget(new TargetControlledCreaturePermanent(0, 1));
|
||||
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 2)));
|
||||
|
||||
|
|
@ -71,10 +65,10 @@ public final class BlacksmithsTalent extends CardImpl {
|
|||
|
||||
// During your turn, equipped creatures you control have double strike and haste.
|
||||
ability = new SimpleStaticAbility(new ConditionalContinuousEffect(new GainAbilityControlledEffect(
|
||||
DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter2
|
||||
DoubleStrikeAbility.getInstance(), Duration.WhileOnBattlefield, filter
|
||||
), MyTurnCondition.instance, "during your turn, equipped creatures you control have double strike"));
|
||||
ability.addEffect(new ConditionalContinuousEffect(new GainAbilityControlledEffect(
|
||||
HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter2
|
||||
HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter
|
||||
), MyTurnCondition.instance, "and haste"));
|
||||
this.addAbility(new SimpleStaticAbility(new GainClassAbilitySourceEffect(ability, 3)));
|
||||
}
|
||||
|
|
@ -88,32 +82,3 @@ public final class BlacksmithsTalent extends CardImpl {
|
|||
return new BlacksmithsTalent(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BlacksmithsTalentEffect extends OneShotEffect {
|
||||
|
||||
BlacksmithsTalentEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.setTargetPointer(new EachTargetPointer());
|
||||
staticText = "attach target Equipment you control to up to one target creature you control";
|
||||
}
|
||||
|
||||
private BlacksmithsTalentEffect(final BlacksmithsTalentEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlacksmithsTalentEffect copy() {
|
||||
return new BlacksmithsTalentEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
List<UUID> targets = this.getTargetPointer().getTargets(game, source);
|
||||
if (targets.size() < 2) {
|
||||
return false;
|
||||
}
|
||||
Permanent equipment = game.getPermanent(targets.get(0));
|
||||
Permanent creature = game.getPermanent(targets.get(1));
|
||||
return equipment != null && creature != null && creature.addAttachment(equipment.getId(), source, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
57
Mage.Sets/src/mage/cards/b/BlazingBomb.java
Normal file
57
Mage.Sets/src/mage/cards/b/BlazingBomb.java
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.ActivateAsSorceryActivatedAbility;
|
||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.dynamicvalue.common.SourcePermanentPowerValue;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BlazingBomb extends CardImpl {
|
||||
|
||||
public BlazingBomb(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{R}");
|
||||
|
||||
this.subtype.add(SubType.ELEMENTAL);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Whenever you cast a noncreature spell, if at least four mana was spent to cast it, put a +1/+1 counter on this creature.
|
||||
this.addAbility(new SpellCastControllerTriggeredAbility(
|
||||
new AddCountersSourceEffect(CounterType.P1P1.createInstance()),
|
||||
StaticFilters.FILTER_NONCREATURE_SPELL_FOUR_MANA_SPENT, false
|
||||
));
|
||||
|
||||
// Blow Up -- {T}, Sacrifice this creature: It deals damage equal to its power to target creature. Activate only as a sorcery.
|
||||
Ability ability = new ActivateAsSorceryActivatedAbility(
|
||||
new DamageTargetEffect(SourcePermanentPowerValue.NOT_NEGATIVE, "it"), new TapSourceCost()
|
||||
);
|
||||
ability.addCost(new SacrificeSourceCost());
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability.withFlavorWord("Blow Up"));
|
||||
}
|
||||
|
||||
private BlazingBomb(final BlazingBomb card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlazingBomb copy() {
|
||||
return new BlazingBomb(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,9 +14,11 @@ import mage.constants.Outcome;
|
|||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -69,13 +71,14 @@ class BondOfRevivalEffect extends OneShotEffect {
|
|||
if (player == null || card == null) {
|
||||
return false;
|
||||
}
|
||||
ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.UntilYourNextTurn);
|
||||
effect.setTargetPointer(new FixedTarget(card.getId(), card.getZoneChangeCounter(game) + 1));
|
||||
if (player.moveCards(card, Zone.BATTLEFIELD, source, game)) {
|
||||
Permanent permanent = CardUtil.getPermanentFromCardPutToBattlefield(card, game);
|
||||
if (permanent != null) {
|
||||
ContinuousEffect effect = new GainAbilityTargetEffect(HasteAbility.getInstance(), Duration.UntilYourNextTurn);
|
||||
effect.setTargetPointer(new FixedTarget(permanent.getId(), game));
|
||||
game.addEffect(effect, source);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
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