mirror of
https://github.com/correl/mage.git
synced 2024-12-26 11:09:27 +00:00
commit
ab6208745e
29 changed files with 917 additions and 140 deletions
|
@ -80,7 +80,7 @@
|
|||
1 [C16:104] Windfall
|
||||
1 [C16:148] Far Wanderings
|
||||
1 [C16:46] Thrasios, Triton Hero
|
||||
1 [C16:346] Mountain
|
||||
2 [C16:346] Mountain
|
||||
2 [C16:347] Mountain
|
||||
1 [C16:348] Mountain
|
||||
1 [C16:105] Army of the Damned
|
||||
|
|
|
@ -66,6 +66,7 @@
|
|||
1 [C17:158] Soul's Majesty
|
||||
1 [C17:73] Spirit of the Hearth
|
||||
1 [C17:224] Staff of Nin
|
||||
1 [C17:7] Stalking Leonin
|
||||
1 [C17:281] Stirring Wildwood
|
||||
1 [C17:75] Sunspear Shikari
|
||||
1 [C17:226] Swiftfoot Boots
|
||||
|
|
|
@ -1,23 +1,8 @@
|
|||
NAME:Mormir Basic
|
||||
3 [BFZ:259] Island
|
||||
3 [BFZ:261] Swamp
|
||||
3 [BFZ:250] Plains
|
||||
3 [BFZ:272] Forest
|
||||
3 [BFZ:260] Swamp
|
||||
3 [BFZ:271] Forest
|
||||
3 [BFZ:270] Forest
|
||||
3 [BFZ:265] Mountain
|
||||
2 [BFZ:254] Plains
|
||||
3 [BFZ:264] Swamp
|
||||
3 [BFZ:274] Forest
|
||||
1 [BFZ:252] Plains
|
||||
3 [BFZ:262] Swamp
|
||||
3 [BFZ:251] Plains
|
||||
2 [BFZ:273] Forest
|
||||
3 [BFZ:258] Island
|
||||
2 [BFZ:269] Mountain
|
||||
3 [BFZ:268] Mountain
|
||||
3 [BFZ:257] Island
|
||||
3 [BFZ:267] Mountain
|
||||
3 [BFZ:266] Mountain
|
||||
2 [BFZ:255] Island
|
||||
NAME:Mormir Basic
|
||||
12 [BFZ:250a] Plains
|
||||
12 [BFZ:260a] Swamp
|
||||
12 [BFZ:270a] Forest
|
||||
12 [BFZ:265a] Mountain
|
||||
12 [BFZ:255a] Island
|
||||
LAYOUT MAIN:(1,5)(COLOR_IDENTITY,true,5)|([BFZ:270a],[BFZ:270a],[BFZ:270a],[BFZ:270a],[BFZ:270a],[BFZ:270a],[BFZ:270a],[BFZ:270a],[BFZ:270a],[BFZ:270a],[BFZ:270a],[BFZ:270a])([BFZ:265a],[BFZ:265a],[BFZ:265a],[BFZ:265a],[BFZ:265a],[BFZ:265a],[BFZ:265a],[BFZ:265a],[BFZ:265a],[BFZ:265a],[BFZ:265a],[BFZ:265a])([BFZ:260a],[BFZ:260a],[BFZ:260a],[BFZ:260a],[BFZ:260a],[BFZ:260a],[BFZ:260a],[BFZ:260a],[BFZ:260a],[BFZ:260a],[BFZ:260a],[BFZ:260a])([BFZ:255a],[BFZ:255a],[BFZ:255a],[BFZ:255a],[BFZ:255a],[BFZ:255a],[BFZ:255a],[BFZ:255a],[BFZ:255a],[BFZ:255a],[BFZ:255a],[BFZ:255a])([BFZ:250a],[BFZ:250a],[BFZ:250a],[BFZ:250a],[BFZ:250a],[BFZ:250a],[BFZ:250a],[BFZ:250a],[BFZ:250a],[BFZ:250a],[BFZ:250a],[BFZ:250a])
|
||||
LAYOUT SIDEBOARD:(0,0)(COLOR,true,5)|
|
||||
|
|
|
@ -0,0 +1,28 @@
|
|||
2 [XLN:31] Raptor Companion
|
||||
1 [XLN:135] Burning Sun's Avatar
|
||||
2 [XLN:38] Slash of Talons
|
||||
2 [XLN:36] Shining Aerosaur
|
||||
1 [XLN:13] Goring Ceratops
|
||||
3 [XLN:274] Mountain
|
||||
3 [XLN:273] Mountain
|
||||
2 [XLN:133] Bonded Horncrest
|
||||
2 [XLN:275] Mountain
|
||||
2 [XLN:30] Rallying Roar
|
||||
3 [XLN:272] Mountain
|
||||
2 [XLN:28] Pterodon Knight
|
||||
2 [XLN:149] Lightning Strike
|
||||
2 [XLN:146] Frenzied Raptor
|
||||
4 [XLN:289] Stone Quarry
|
||||
2 [XLN:288] Sun-Blessed Mount
|
||||
2 [XLN:169] Tilonalli's Knight
|
||||
1 [XLN:285] Huatli, Dinosaur Knight
|
||||
2 [XLN:263] Plains
|
||||
3 [XLN:262] Plains
|
||||
3 [XLN:287] Huatli's Spurring
|
||||
4 [XLN:286] Huatli's Snubhorn
|
||||
2 [XLN:41] Territorial Hammerskull
|
||||
3 [XLN:261] Plains
|
||||
3 [XLN:260] Plains
|
||||
2 [XLN:18] Kinjalli's Caller
|
||||
LAYOUT MAIN:(1,4)(CARD_TYPE,false,50)|([XLN:13],[XLN:288],[XLN:288],[XLN:135],[XLN:18],[XLN:18],[XLN:286],[XLN:286],[XLN:286],[XLN:286],[XLN:31],[XLN:31],[XLN:169],[XLN:169],[XLN:146],[XLN:146],[XLN:41],[XLN:41],[XLN:133],[XLN:133],[XLN:28],[XLN:28],[XLN:36],[XLN:36])([XLN:287],[XLN:287],[XLN:287],[XLN:38],[XLN:38],[XLN:149],[XLN:149],[XLN:30],[XLN:30])([XLN:289],[XLN:289],[XLN:289],[XLN:289],[XLN:260],[XLN:260],[XLN:260],[XLN:261],[XLN:261],[XLN:261],[XLN:262],[XLN:262],[XLN:262],[XLN:263],[XLN:263],[XLN:272],[XLN:272],[XLN:272],[XLN:273],[XLN:273],[XLN:273],[XLN:274],[XLN:274],[XLN:274],[XLN:275],[XLN:275])([XLN:285])
|
||||
LAYOUT SIDEBOARD:(0,0)(NONE,false,50)|
|
|
@ -0,0 +1,23 @@
|
|||
4 [HOU:193] Island
|
||||
4 [HOU:192] Island
|
||||
2 [AKH:41] Angler Drake
|
||||
2 [HOU:29] Aerial Guide
|
||||
2 [AKH:209] Weaver of Currents
|
||||
2 [HOU:30] Aven Reedstalker
|
||||
2 [AKH:219] Spring // Mind
|
||||
4 [HOU:204] Woodland Stream
|
||||
3 [HOU:54] Unsummon
|
||||
3 [HOU:201] Avid Reclaimer
|
||||
3 [AKH:179] Pouncing Cheetah
|
||||
1 [HOU:200] Nissa, Genesis Mage
|
||||
2 [HOU:203] Nissa's Encouragement
|
||||
2 [HOU:115] Feral Prowler
|
||||
4 [HOU:202] Brambleweft Behemoth
|
||||
1 [AKH:196] Bounty of the Luxa
|
||||
7 [HOU:199] Forest
|
||||
1 [HOU:154] Reason // Believe
|
||||
2 [HOU:143] River Hoopoe
|
||||
2 [HOU:110] Ambuscade
|
||||
7 [HOU:198] Forest
|
||||
LAYOUT MAIN:(1,7)(CARD_TYPE,false,50)|([HOU:115],[HOU:115],[HOU:143],[HOU:143],[HOU:201],[HOU:201],[HOU:201],[AKH:179],[AKH:179],[AKH:179],[HOU:29],[HOU:29],[AKH:209],[AKH:209],[HOU:30],[HOU:30],[HOU:202],[HOU:202],[HOU:202],[HOU:202],[AKH:41],[AKH:41])([AKH:196])([AKH:219],[AKH:219])([HOU:54],[HOU:54],[HOU:54],[HOU:110],[HOU:110])([HOU:204],[HOU:204],[HOU:204],[HOU:204],[HOU:192],[HOU:192],[HOU:192],[HOU:192],[HOU:193],[HOU:193],[HOU:193],[HOU:193],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:198],[HOU:199],[HOU:199],[HOU:199],[HOU:199],[HOU:199],[HOU:199],[HOU:199])([HOU:200])([HOU:154],[HOU:203],[HOU:203])
|
||||
LAYOUT SIDEBOARD:(0,0)(NONE,false,50)|
|
|
@ -1083,6 +1083,22 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void chooseMatching() {
|
||||
Collection<CardView> toMatch = dragCardList();
|
||||
|
||||
for (DragCardGridListener l : listeners) {
|
||||
for (CardView card : allCards) {
|
||||
for (CardView aMatch : toMatch) {
|
||||
if (card.getName().equals(aMatch.getName())) {
|
||||
card.setSelected(true);
|
||||
cardViews.get(card.getId()).update(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
repaint();
|
||||
}
|
||||
|
||||
private void showAll() {
|
||||
for (DragCardGridListener l : listeners) {
|
||||
|
@ -1704,6 +1720,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
JMenuItem invertSelection = new JMenuItem("Invert Selection");
|
||||
invertSelection.addActionListener(e2 -> invertSelection());
|
||||
menu.add(invertSelection);
|
||||
|
||||
JMenuItem chooseMatching = new JMenuItem("Choose Matching");
|
||||
chooseMatching.addActionListener(e2 -> chooseMatching());
|
||||
menu.add(chooseMatching);
|
||||
|
||||
// Show 'Duplicate Selection' for FREE_BUILDING
|
||||
if (this.mode == Constants.DeckEditorMode.FREE_BUILDING) {
|
||||
|
|
|
@ -8,10 +8,15 @@ import java.awt.Graphics;
|
|||
import java.awt.Graphics2D;
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.RenderingHints;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.awt.font.FontRenderContext;
|
||||
import java.awt.font.GlyphVector;
|
||||
import javax.swing.JPanel;
|
||||
import javax.swing.Timer;
|
||||
import mage.client.util.Command;
|
||||
|
||||
/**
|
||||
|
@ -43,6 +48,7 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
private Image topTextImageRight;
|
||||
private String centerText;
|
||||
|
||||
private boolean wasHovered = false;
|
||||
private boolean isHovered = false;
|
||||
private boolean isSelected = false;
|
||||
private boolean drawSet = false;
|
||||
|
@ -52,7 +58,8 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
private Command onHover = null;
|
||||
private Color textColor = Color.white;
|
||||
private final Rectangle centerTextArea = new Rectangle(5, 18, 75, 40);
|
||||
private Color centerTextColor = new Color(200, 190, 0, 180);
|
||||
private Color centerTextColor = new Color(200, 210, 0, 200);
|
||||
private Color origCenterTextColor = new Color(200, 210, 0, 200);
|
||||
private final Color textBGColor = Color.black;
|
||||
|
||||
static final Font textFont = new Font("Arial", Font.PLAIN, 12);
|
||||
|
@ -64,6 +71,13 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
|
||||
private boolean alignTextLeft = false;
|
||||
|
||||
Timer faderGainLife = null;
|
||||
Timer faderLoseLife = null;
|
||||
private int loseX = 0;
|
||||
private int gainX = 0;
|
||||
private boolean doLoseFade = true;
|
||||
private boolean doGainFade = true;
|
||||
|
||||
public HoverButton(String text, Image image, Rectangle size) {
|
||||
this(text, image, image, null, image, size);
|
||||
if (image == null) {
|
||||
|
@ -95,6 +109,10 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
Graphics2D g2d = (Graphics2D) g;
|
||||
if (isEnabled()) {
|
||||
if (isHovered || textAlwaysVisible) {
|
||||
if (isHovered) {
|
||||
wasHovered = true;
|
||||
setCenterColor(Color.YELLOW);
|
||||
}
|
||||
g.drawImage(hoverImage, 0, 0, imageSize.width, imageSize.height, this);
|
||||
if (text != null) {
|
||||
if (textColor != null) {
|
||||
|
@ -109,6 +127,10 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
g2d.drawString(text, textOffsetX, textOffsetY);
|
||||
}
|
||||
} else {
|
||||
if (wasHovered) {
|
||||
wasHovered = false;
|
||||
setCenterColor(origCenterTextColor);
|
||||
}
|
||||
g.drawImage(image, 0, 0, imageSize.width, imageSize.height, this);
|
||||
}
|
||||
if (isSelected) {
|
||||
|
@ -151,7 +173,7 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
} else if (val > 99) {
|
||||
fontSize = 34;
|
||||
}
|
||||
drawCenteredString(g2d, centerText, centerTextArea, new Font("Arial", Font.BOLD, fontSize));
|
||||
drawCenteredStringWOutline(g2d, centerText, centerTextArea, new Font("Arial", Font.BOLD, fontSize));
|
||||
}
|
||||
g2d.setColor(textColor);
|
||||
if (overlayImage != null) {
|
||||
|
@ -174,7 +196,7 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
g2d.drawString(set, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void setCenterColor(Color c) {
|
||||
centerTextColor = c;
|
||||
}
|
||||
|
@ -349,7 +371,7 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
* @param rect The Rectangle to center the text in.
|
||||
* @param font
|
||||
*/
|
||||
public void drawCenteredString(Graphics g, String text, Rectangle rect, Font font) {
|
||||
public void drawCenteredStringWOutline(Graphics2D g, String text, Rectangle rect, Font font) {
|
||||
// Get the FontMetrics
|
||||
FontMetrics metrics = g.getFontMetrics(font);
|
||||
// Determine the X coordinate for the text
|
||||
|
@ -358,7 +380,91 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
int y = rect.y + ((rect.height - metrics.getHeight()) / 2) + metrics.getAscent();
|
||||
// Set the font
|
||||
g.setFont(font);
|
||||
// Draw the String
|
||||
g.drawString(text, x, y);
|
||||
|
||||
GlyphVector gv = font.createGlyphVector(g.getFontRenderContext(), text);
|
||||
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
|
||||
RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
|
||||
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
|
||||
RenderingHints.VALUE_FRACTIONALMETRICS_ON);
|
||||
g.drawGlyphVector(gv, x, y);
|
||||
|
||||
g.translate(x - 1, y - 1);
|
||||
for (int i = 0; i < text.length(); i++) {
|
||||
g.setColor(Color.BLACK);
|
||||
g.draw(gv.getGlyphOutline(i));
|
||||
}
|
||||
g.translate(-x + 1, -y + 1);
|
||||
|
||||
}
|
||||
|
||||
public void gainLifeDisplay() {
|
||||
if (faderGainLife == null && doGainFade) {
|
||||
doGainFade = false;
|
||||
faderGainLife = new Timer(50, new ActionListener() {
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
gainX++;
|
||||
int alpha = Math.max(250 - gainX, 200);
|
||||
setCenterColor(new Color(2 * gainX, 210, 255, alpha));
|
||||
repaint();
|
||||
if (gainX >= 100) {
|
||||
setCenterColor(new Color(200, 210, 0, 200));
|
||||
gainX = 100;
|
||||
|
||||
if (faderGainLife != null) {
|
||||
faderGainLife.stop();
|
||||
faderGainLife.setRepeats(false);
|
||||
faderGainLife.setDelay(50000);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
gainX = 0;
|
||||
faderGainLife.setInitialDelay(25);
|
||||
faderGainLife.setRepeats(true);
|
||||
faderGainLife.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void loseLifeDisplay() {
|
||||
if (faderLoseLife == null && doLoseFade) {
|
||||
doLoseFade = false;
|
||||
faderLoseLife = new Timer(50, new ActionListener() {
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
loseX++;
|
||||
int alpha = Math.max(250 - loseX, 200);
|
||||
setCenterColor(new Color(250 - loseX / 2, 130 + loseX, 0, alpha));
|
||||
repaint();
|
||||
if (loseX >= 100) {
|
||||
setCenterColor(new Color(200, 210, 0, 200));
|
||||
loseX = 100;
|
||||
stopLifeDisplay();
|
||||
|
||||
if (faderLoseLife != null) {
|
||||
faderLoseLife.stop();
|
||||
faderLoseLife.setRepeats(false);
|
||||
faderLoseLife.setDelay(50000);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
loseX = 0;
|
||||
faderLoseLife.setInitialDelay(25);
|
||||
faderLoseLife.setRepeats(true);
|
||||
faderLoseLife.start();
|
||||
}
|
||||
}
|
||||
|
||||
public void stopLifeDisplay() {
|
||||
|
||||
if (faderGainLife != null && gainX >= 100) {
|
||||
faderGainLife.stop();
|
||||
faderGainLife = null;
|
||||
}
|
||||
doGainFade = true;
|
||||
if (faderLoseLife != null && loseX >= 100) {
|
||||
faderLoseLife.stop();
|
||||
faderLoseLife = null;
|
||||
}
|
||||
doLoseFade = true;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,6 @@ import java.awt.Dimension;
|
|||
import java.awt.Font;
|
||||
import java.awt.Image;
|
||||
import java.awt.Rectangle;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.awt.event.ActionListener;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
|
@ -55,7 +53,6 @@ import javax.swing.JLabel;
|
|||
import javax.swing.JPanel;
|
||||
import javax.swing.LayoutStyle.ComponentPlacement;
|
||||
import javax.swing.SwingConstants;
|
||||
import javax.swing.Timer;
|
||||
import javax.swing.border.Border;
|
||||
import javax.swing.border.LineBorder;
|
||||
import mage.cards.decks.importer.DckDeckImporter;
|
||||
|
@ -117,12 +114,6 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
private String flagName;
|
||||
private String basicTooltipText;
|
||||
private static final Map<UUID, Integer> playerLives = new HashMap<>();
|
||||
private int loseX;
|
||||
private boolean doLoseFade = true;
|
||||
private int gainX;
|
||||
private boolean doGainFade = true;
|
||||
Timer faderGainLife = null;
|
||||
Timer faderLoseLife = null;
|
||||
|
||||
private PriorityTimer timer;
|
||||
|
||||
|
@ -200,69 +191,12 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
if (displayLife) {
|
||||
if (playerLife != pastLife) {
|
||||
if (playerLife > pastLife) {
|
||||
if (faderGainLife == null && doGainFade) {
|
||||
doGainFade = false;
|
||||
faderGainLife = new Timer(50, new ActionListener() {
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
gainX++;
|
||||
int alpha = Math.max(250 - gainX, 180);
|
||||
avatar.setCenterColor(new Color(2 * gainX, 190, 255, alpha));
|
||||
avatar.repaint();
|
||||
if (gainX >= 100) {
|
||||
avatar.setCenterColor(new Color(200, 190, 0, 180));
|
||||
gainX = 100;
|
||||
|
||||
if (faderGainLife != null) {
|
||||
faderGainLife.stop();
|
||||
faderGainLife.setRepeats(false);
|
||||
faderGainLife.setDelay(50000);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
gainX = 0;
|
||||
faderGainLife.setInitialDelay(25);
|
||||
faderGainLife.setRepeats(true);
|
||||
faderGainLife.start();
|
||||
}
|
||||
avatar.gainLifeDisplay();
|
||||
} else if (playerLife < pastLife) {
|
||||
if (faderLoseLife == null && doLoseFade) {
|
||||
doLoseFade = false;
|
||||
faderLoseLife = new Timer(50, new ActionListener() {
|
||||
public void actionPerformed(ActionEvent ae) {
|
||||
loseX++;
|
||||
int alpha = Math.max(250 - loseX, 180);
|
||||
avatar.setCenterColor(new Color(250 - loseX / 2, 140 + loseX / 2, 0, alpha));
|
||||
avatar.repaint();
|
||||
if (loseX >= 100) {
|
||||
avatar.setCenterColor(new Color(200, 190, 0, 180));
|
||||
loseX = 100;
|
||||
|
||||
if (faderLoseLife != null) {
|
||||
faderLoseLife.stop();
|
||||
faderLoseLife.setRepeats(false);
|
||||
faderLoseLife.setDelay(50000);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
loseX = 0;
|
||||
faderLoseLife.setInitialDelay(25);
|
||||
faderLoseLife.setRepeats(true);
|
||||
faderLoseLife.start();
|
||||
}
|
||||
avatar.loseLifeDisplay();
|
||||
}
|
||||
} else if (playerLife == pastLife) {
|
||||
if (faderGainLife != null && gainX >= 100) {
|
||||
faderGainLife.stop();
|
||||
faderGainLife = null;
|
||||
}
|
||||
doGainFade = true;
|
||||
if (faderLoseLife != null && loseX >= 100) {
|
||||
faderLoseLife.stop();
|
||||
faderLoseLife = null;
|
||||
}
|
||||
doLoseFade = true;
|
||||
avatar.stopLifeDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -28,38 +28,21 @@
|
|||
package mage.game;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.LimitedTimesPerTurnActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.common.DiscardCardCost;
|
||||
import mage.abilities.costs.mana.VariableManaCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.cards.Sets;
|
||||
import mage.cards.repository.CardCriteria;
|
||||
import mage.cards.repository.CardInfo;
|
||||
import mage.cards.repository.CardRepository;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.MultiplayerAttackOption;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.constants.SetType;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.command.Emblem;
|
||||
import mage.game.command.emblems.MomirEmblem;
|
||||
import mage.game.match.MatchType;
|
||||
import mage.game.permanent.token.EmptyToken;
|
||||
import mage.game.turn.TurnMod;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
import mage.util.RandomUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
|
@ -38,14 +38,14 @@ public class MomirFreeForAllType extends MatchType {
|
|||
public MomirFreeForAllType() {
|
||||
this.name = "Momir Basic Free For All";
|
||||
this.maxPlayers = 10;
|
||||
this.minPlayers = 2;
|
||||
this.minPlayers = 3;
|
||||
this.numTeams = 0;
|
||||
this.useAttackOption = true;
|
||||
this.useRange = true;
|
||||
this.sideboardingAllowed = false;
|
||||
}
|
||||
|
||||
protected MomirFreeForAllType(final MomirFreeForAllType matchType){
|
||||
protected MomirFreeForAllType(final MomirFreeForAllType matchType) {
|
||||
super(matchType);
|
||||
}
|
||||
|
||||
|
|
|
@ -244,6 +244,27 @@ public enum ChatManager {
|
|||
}
|
||||
return true;
|
||||
}
|
||||
if (command.startsWith("FIX")) {
|
||||
message += "<br/>" + GameManager.instance.getChatId(chatId);
|
||||
ChatSession session = chatSessions.get(chatId);
|
||||
if (session != null && session.getInfo() != null) {
|
||||
String gameId = session.getInfo();
|
||||
if (gameId.startsWith("Game ")) {
|
||||
UUID id = java.util.UUID.fromString(gameId.substring(5, gameId.length()));
|
||||
for (Entry<UUID, GameController> entry : GameManager.instance.getGameController().entrySet()) {
|
||||
if (entry.getKey().equals(id)) {
|
||||
GameController controller = entry.getValue();
|
||||
if (controller != null) {
|
||||
message += controller.attemptToFixGame();
|
||||
chatSessions.get(chatId).broadcastInfoToUser(user, message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (command.startsWith("CARD ")) {
|
||||
Matcher matchPattern = getCardTextPattern.matcher(message.toLowerCase(Locale.ENGLISH));
|
||||
if (matchPattern.find()) {
|
||||
|
|
|
@ -37,6 +37,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|||
import java.util.zip.GZIPOutputStream;
|
||||
import mage.MageException;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.PassAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.decks.Deck;
|
||||
|
@ -57,6 +58,7 @@ import mage.game.events.PlayerQueryEvent;
|
|||
import mage.game.events.TableEvent;
|
||||
import mage.game.match.MatchPlayer;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.turn.Phase;
|
||||
import mage.interfaces.Action;
|
||||
import mage.players.Player;
|
||||
import mage.server.*;
|
||||
|
@ -1150,13 +1152,13 @@ public class GameController implements GameCallback {
|
|||
sb.append(state.getPlayerList());
|
||||
sb.append("<br>getPlayers: ");
|
||||
sb.append(state.getPlayers());
|
||||
sb.append("<br>Player with Priority is: ");
|
||||
sb.append("<br><font color=orange>Player with Priority is: ");
|
||||
if (state.getPriorityPlayerId() != null) {
|
||||
sb.append(game.getPlayer(state.getPriorityPlayerId()).getName());
|
||||
} else {
|
||||
sb.append("noone!");
|
||||
}
|
||||
sb.append("<br>getRevealed: ");
|
||||
sb.append("</font><br>getRevealed: ");
|
||||
sb.append(state.getRevealed());
|
||||
sb.append("<br>getSpecialActions: ");
|
||||
sb.append(state.getSpecialActions());
|
||||
|
@ -1187,4 +1189,80 @@ public class GameController implements GameCallback {
|
|||
return sb.toString();
|
||||
}
|
||||
|
||||
public String attemptToFixGame() {
|
||||
if (game == null) {
|
||||
return "";
|
||||
}
|
||||
GameState state = game.getState();
|
||||
if (state == null) {
|
||||
return "";
|
||||
}
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<br/>Game State:<br/><font size=-2>");
|
||||
sb.append(state);
|
||||
boolean fixedAlready = false;
|
||||
|
||||
sb.append("<br>Active player is: ");
|
||||
sb.append(game.getPlayer(state.getActivePlayerId()).getName());
|
||||
PassAbility pass = new PassAbility();
|
||||
if (game.getPlayer(state.getActivePlayerId()).hasLeft()) {
|
||||
Phase currentPhase = game.getPhase();
|
||||
if (currentPhase != null) {
|
||||
currentPhase.getStep().skipStep(game, state.getActivePlayerId());
|
||||
sb.append("<br>Forcibly passing the phase!");
|
||||
fixedAlready = true;
|
||||
} else {
|
||||
sb.append("<br>Current phase null");
|
||||
}
|
||||
sb.append("<br>Active player has left");
|
||||
}
|
||||
|
||||
sb.append("<br>getChoosingPlayerId: ");
|
||||
if (state.getChoosingPlayerId() != null) {
|
||||
if (game.getPlayer(state.getChoosingPlayerId()).hasLeft()) {
|
||||
Phase currentPhase = game.getPhase();
|
||||
if (currentPhase != null && !fixedAlready) {
|
||||
currentPhase.getStep().endStep(game, state.getActivePlayerId());
|
||||
fixedAlready = true;
|
||||
sb.append("<br>Forcibly passing the phase!");
|
||||
} else if (currentPhase == null) {
|
||||
sb.append("<br>Current phase null");
|
||||
}
|
||||
sb.append("<br>Choosing player has left");
|
||||
}
|
||||
}
|
||||
|
||||
sb.append("<br><font color=orange>Player with Priority is: ");
|
||||
if (state.getPriorityPlayerId() != null) {
|
||||
if (game.getPlayer(state.getPriorityPlayerId()).hasLeft()) {
|
||||
Phase currentPhase = game.getPhase();
|
||||
if (currentPhase != null && !fixedAlready) {
|
||||
currentPhase.getStep().skipStep(game, state.getActivePlayerId());
|
||||
fixedAlready = true;
|
||||
sb.append("<br>Forcibly passing the phase!");
|
||||
}
|
||||
}
|
||||
sb.append(game.getPlayer(state.getPriorityPlayerId()).getName());
|
||||
sb.append("</font>");
|
||||
}
|
||||
|
||||
sb.append("<br>Future Timeout:");
|
||||
if (futureTimeout != null) {
|
||||
sb.append("Cancelled?=");
|
||||
sb.append(futureTimeout.isCancelled());
|
||||
sb.append(",,,Done?=");
|
||||
sb.append(futureTimeout.isDone());
|
||||
sb.append(",,,GetDelay?=");
|
||||
sb.append((int) futureTimeout.getDelay(TimeUnit.SECONDS));
|
||||
if ((int) futureTimeout.getDelay(TimeUnit.SECONDS) < 25) {
|
||||
game.endTurn(pass);
|
||||
sb.append("<br>Forcibly passing the turn!");
|
||||
}
|
||||
} else {
|
||||
sb.append("Not using future Timeout!");
|
||||
}
|
||||
sb.append("</font>");
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
122
Mage.Sets/src/mage/cards/b/BrutalSuppression.java
Normal file
122
Mage.Sets/src/mage/cards/b/BrutalSuppression.java
Normal file
|
@ -0,0 +1,122 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.common.SacrificeTargetCost;
|
||||
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.AbilityType;
|
||||
import mage.constants.CostModificationType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.filter.predicate.permanent.TokenPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class BrutalSuppression extends CardImpl {
|
||||
|
||||
public BrutalSuppression(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{R}");
|
||||
|
||||
// Activated abilities of nontoken Rebels cost an additional "Sacrifice a land" to activate.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BrutalSuppressionAdditionalCostEffect()));
|
||||
}
|
||||
|
||||
public BrutalSuppression(final BrutalSuppression card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrutalSuppression copy() {
|
||||
return new BrutalSuppression(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BrutalSuppressionAdditionalCostEffect extends CostModificationEffectImpl {
|
||||
|
||||
private static final FilterControlledPermanent filter = new FilterControlledPermanent("a land");
|
||||
static{
|
||||
filter.add(new CardTypePredicate(CardType.LAND));
|
||||
}
|
||||
|
||||
private static final FilterPermanent filter2 = new FilterPermanent("nontoken Rebels");
|
||||
static{
|
||||
filter2.add(new SubtypePredicate(SubType.REBEL));
|
||||
filter.add(Predicates.not(new TokenPredicate()));
|
||||
}
|
||||
|
||||
BrutalSuppressionAdditionalCostEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Benefit, CostModificationType.INCREASE_COST);
|
||||
this.staticText = "Activated abilities of nontoken Rebels cost an additional \"Sacrifice a land\" to activate";
|
||||
}
|
||||
|
||||
BrutalSuppressionAdditionalCostEffect(BrutalSuppressionAdditionalCostEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source, Ability abilityToModify) {
|
||||
TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true);
|
||||
target.setRequired(false);
|
||||
abilityToModify.addCost(new SacrificeTargetCost(target));
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(Ability abilityToModify, Ability source, Game game) {
|
||||
if (abilityToModify.getAbilityType() == AbilityType.ACTIVATED || abilityToModify.getAbilityType() == AbilityType.MANA) {
|
||||
Permanent rebelPermanent = game.getPermanent(abilityToModify.getSourceId());
|
||||
if (rebelPermanent != null) {
|
||||
return filter2.match(rebelPermanent, game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrutalSuppressionAdditionalCostEffect copy() {
|
||||
return new BrutalSuppressionAdditionalCostEffect(this);
|
||||
}
|
||||
}
|
|
@ -59,7 +59,7 @@ public class CabalSlaver extends CardImpl {
|
|||
this.toughness = new MageInt(1);
|
||||
|
||||
// Whenever a Goblin deals combat damage to a player, that player discards a card.
|
||||
this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility(new DiscardTargetEffect(1), filter, false, SetTargetPointer.PLAYER, true));
|
||||
this.addAbility(new DealsDamageToAPlayerAllTriggeredAbility(new DiscardTargetEffect(1), filter, false, SetTargetPointer.NONE, true, true));
|
||||
}
|
||||
|
||||
public CabalSlaver(final CabalSlaver card) {
|
||||
|
|
|
@ -28,9 +28,6 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.common.PayLifeCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.DrawCardTargetEffect;
|
||||
|
@ -48,18 +45,16 @@ import mage.target.TargetPlayer;
|
|||
public class DeepAnalysis extends CardImpl {
|
||||
|
||||
public DeepAnalysis(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{U}");
|
||||
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{U}");
|
||||
|
||||
// Target player draws two cards.
|
||||
this.getSpellAbility().addEffect(new DrawCardTargetEffect(2));
|
||||
this.getSpellAbility().addTarget(new TargetPlayer());
|
||||
|
||||
// Flashback-{1}{U}, Pay 3 life.
|
||||
Costs<Cost> costs = new CostsImpl<>();
|
||||
costs.add(new ManaCostsImpl("{1}{U}"));
|
||||
costs.add(new PayLifeCost(3));
|
||||
this.addAbility(new FlashbackAbility(costs, TimingRule.SORCERY));
|
||||
FlashbackAbility ability = new FlashbackAbility(new ManaCostsImpl("{1}{U}"), TimingRule.SORCERY);
|
||||
ability.addCost(new PayLifeCost(3));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public DeepAnalysis(final DeepAnalysis card) {
|
||||
|
|
99
Mage.Sets/src/mage/cards/e/ElvishImpersonators.java
Normal file
99
Mage.Sets/src/mage/cards/e/ElvishImpersonators.java
Normal file
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.e;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AsEntersBattlefieldAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.SetPowerToughnessSourceEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class ElvishImpersonators extends CardImpl {
|
||||
|
||||
public ElvishImpersonators(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{G}");
|
||||
this.subtype.add(SubType.ELF);
|
||||
this.power = new MageInt(0);
|
||||
this.toughness = new MageInt(0);
|
||||
|
||||
// As Elvish Impersonators enters the battlefield, roll a six-sided die twice. Its base power becomes the first result and its base toughness becomes the second result.
|
||||
this.addAbility(new AsEntersBattlefieldAbility(new ElvishImpersonatorsEffect()));
|
||||
}
|
||||
|
||||
public ElvishImpersonators(final ElvishImpersonators card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElvishImpersonators copy() {
|
||||
return new ElvishImpersonators(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ElvishImpersonatorsEffect extends OneShotEffect {
|
||||
|
||||
public ElvishImpersonatorsEffect() {
|
||||
super(Outcome.Neutral);
|
||||
staticText = "roll a six-sided die twice. Its base power becomes the first result and its base toughness becomes the second result";
|
||||
}
|
||||
|
||||
public ElvishImpersonatorsEffect(final ElvishImpersonatorsEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ElvishImpersonatorsEffect copy() {
|
||||
return new ElvishImpersonatorsEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
int firstRoll = controller.rollDice(game, 6);
|
||||
int secondRoll = controller.rollDice(game, 6);
|
||||
game.addEffect(new SetPowerToughnessSourceEffect(firstRoll, secondRoll, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -71,7 +71,7 @@ public class GrowthSpurt extends CardImpl {
|
|||
class GrowthSpurtEffect extends OneShotEffect {
|
||||
GrowthSpurtEffect() {
|
||||
super(Outcome.BoostCreature);
|
||||
this.staticText = "todo"; //TODO
|
||||
this.staticText = "Roll a six-sided die. Target creature gets +X/+X until end of turn, where X is the result";
|
||||
}
|
||||
|
||||
GrowthSpurtEffect(final GrowthSpurtEffect effect) {
|
||||
|
|
180
Mage.Sets/src/mage/cards/m/MineMineMine.java
Normal file
180
Mage.Sets/src/mage/cards/m/MineMineMine.java
Normal file
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.m;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.continuous.CantCastMoreThanOneSpellEffect;
|
||||
import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect.HandSizeModification;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class MineMineMine extends CardImpl {
|
||||
|
||||
public MineMineMine(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{G}{G}");
|
||||
|
||||
// When Mine, Mine, Mine enters the battlefield, each player puts his or her library into his or her hand.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new MineMineMineDrawEffect()));
|
||||
|
||||
// Players have no maximum hand size and don't lose the game for drawing from an empty library.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
|
||||
new MaximumHandSizeControllerEffect(Integer.MAX_VALUE, Duration.WhileOnBattlefield, HandSizeModification.SET, TargetController.ANY)
|
||||
.setText("Players have no maximum hand size and don't lose the game for drawing from an empty library")));
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new MineMineMineDontLoseEffect()));
|
||||
|
||||
// Each player can't cast more than one spell each turn.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantCastMoreThanOneSpellEffect(TargetController.ANY)));
|
||||
|
||||
// When Mine, Mine, Mine leaves the battlefield, each player shuffles his or her hand and graveyard into his or her library.
|
||||
this.addAbility(new LeavesBattlefieldTriggeredAbility(new MineMineMineShuffleEffect(), false));
|
||||
}
|
||||
|
||||
public MineMineMine(final MineMineMine card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MineMineMine copy() {
|
||||
return new MineMineMine(this);
|
||||
}
|
||||
}
|
||||
|
||||
class MineMineMineDrawEffect extends OneShotEffect {
|
||||
|
||||
MineMineMineDrawEffect() {
|
||||
super(Outcome.DrawCard);
|
||||
this.staticText = "each player puts his or her library into his or her hand";
|
||||
}
|
||||
|
||||
MineMineMineDrawEffect(final MineMineMineDrawEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
CardsImpl libraryCards = new CardsImpl();
|
||||
libraryCards.addAll(player.getLibrary().getCards(game));
|
||||
player.moveCards(libraryCards, Zone.HAND, source, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MineMineMineDrawEffect copy() {
|
||||
return new MineMineMineDrawEffect(this);
|
||||
}
|
||||
}
|
||||
|
||||
class MineMineMineDontLoseEffect extends ReplacementEffectImpl {
|
||||
|
||||
MineMineMineDontLoseEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Neutral);
|
||||
}
|
||||
|
||||
MineMineMineDontLoseEffect(final MineMineMineDontLoseEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MineMineMineDontLoseEffect copy() {
|
||||
return new MineMineMineDontLoseEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DRAW_CARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
Player player = game.getPlayer(event.getPlayerId());
|
||||
if (player != null && player.getLibrary().getCards(game).isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class MineMineMineShuffleEffect extends OneShotEffect {
|
||||
|
||||
public MineMineMineShuffleEffect() {
|
||||
super(Outcome.Neutral);
|
||||
staticText = "each player shuffles his or her hand and graveyard into his or her library";
|
||||
}
|
||||
|
||||
public MineMineMineShuffleEffect(final MineMineMineShuffleEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
player.moveCards(player.getHand(), Zone.LIBRARY, source, game);
|
||||
player.moveCards(player.getGraveyard(), Zone.LIBRARY, source, game);
|
||||
player.shuffleLibrary(source, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MineMineMineShuffleEffect copy() {
|
||||
return new MineMineMineShuffleEffect(this);
|
||||
}
|
||||
}
|
|
@ -41,7 +41,7 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.common.FilterInstantOrSorceryCard;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterInstantOrSorcerySpell;
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
|
@ -62,7 +62,7 @@ public class MizzixOfTheIzmagnus extends CardImpl {
|
|||
}
|
||||
|
||||
public MizzixOfTheIzmagnus(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{R}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{R}");
|
||||
addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.GOBLIN);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
|
@ -136,11 +136,11 @@ class MizzixOfTheIzmagnusCostReductionEffect extends CostModificationEffectImpl
|
|||
if (abilityToModify instanceof SpellAbility && abilityToModify.getControllerId().equals(source.getControllerId())) {
|
||||
Spell spell = (Spell) game.getStack().getStackObject(abilityToModify.getId());
|
||||
if (spell != null) {
|
||||
return new FilterInstantOrSorceryCard().match(spell, source.getSourceId(), source.getControllerId(), game);
|
||||
} else {
|
||||
// used at least for flashback ability because Flashback ability doesn't use stack or for getPlayables where spell is not cast yet
|
||||
return StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(spell, source.getSourceId(), source.getControllerId(), game);
|
||||
} else if (((SpellAbility) abilityToModify).isCheckPlayableMode()) {
|
||||
// Spell is not on the stack yet, but possible playable spells are determined
|
||||
Card sourceCard = game.getCard(abilityToModify.getSourceId());
|
||||
return sourceCard != null && new FilterInstantOrSorceryCard().match(sourceCard, source.getSourceId(), source.getControllerId(), game);
|
||||
return sourceCard != null && StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY.match(sourceCard, source.getSourceId(), source.getControllerId(), game);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -93,6 +93,7 @@ class NaturesWillEffect extends OneShotEffect {
|
|||
land.untap(game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
116
Mage.Sets/src/mage/cards/o/OnceMoreWithFeeling.java
Normal file
116
Mage.Sets/src/mage/cards/o/OnceMoreWithFeeling.java
Normal file
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.o;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DrawCardAllEffect;
|
||||
import mage.abilities.effects.common.ExileSpellEffect;
|
||||
import mage.abilities.effects.common.InfoEffect;
|
||||
import mage.abilities.effects.common.SetPlayerLifeAllEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author L_J
|
||||
*/
|
||||
public class OnceMoreWithFeeling extends CardImpl {
|
||||
|
||||
public OnceMoreWithFeeling(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{W}{W}{W}{W}");
|
||||
|
||||
// Exile all permanents and all cards from all graveyards. Each player shuffles his or her hand into his or her library, then draws seven cards. Each player's life total becomes 10. Exile Once More with Feeling.
|
||||
this.getSpellAbility().addEffect(new OnceMoreWithFeelingEffect());
|
||||
Effect effect = new DrawCardAllEffect(7);
|
||||
effect.setText(", then draws seven cards");
|
||||
this.getSpellAbility().addEffect(effect);
|
||||
this.getSpellAbility().addEffect(new SetPlayerLifeAllEffect(10));
|
||||
this.getSpellAbility().addEffect(ExileSpellEffect.getInstance());
|
||||
|
||||
// DCI ruling — A deck can have only one card named Once More with Feeling.
|
||||
// (according to rule 112.6m, this shouldn't do anything)
|
||||
this.getSpellAbility().addEffect(new InfoEffect("<br>DCI ruling — A deck can have only one card named {this}"));
|
||||
}
|
||||
|
||||
public OnceMoreWithFeeling(final OnceMoreWithFeeling card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnceMoreWithFeeling copy() {
|
||||
return new OnceMoreWithFeeling(this);
|
||||
}
|
||||
}
|
||||
|
||||
class OnceMoreWithFeelingEffect extends OneShotEffect {
|
||||
|
||||
public OnceMoreWithFeelingEffect() {
|
||||
super(Outcome.Detriment);
|
||||
staticText = "Exile all permanents and all cards from all graveyards. Each player shuffles his or her hand into his or her library";
|
||||
}
|
||||
|
||||
public OnceMoreWithFeelingEffect(final OnceMoreWithFeelingEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OnceMoreWithFeelingEffect copy() {
|
||||
return new OnceMoreWithFeelingEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
|
||||
permanent.moveToExile(null, "", source.getSourceId(), game);
|
||||
}
|
||||
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player != null) {
|
||||
for (UUID cid : player.getGraveyard().copy()) {
|
||||
Card c = game.getCard(cid);
|
||||
if (c != null) {
|
||||
c.moveToExile(null, null, source.getSourceId(), game);
|
||||
}
|
||||
}
|
||||
player.moveCards(player.getHand(), Zone.LIBRARY, source, game);
|
||||
player.shuffleLibrary(source, game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -206,7 +206,7 @@ public class MastersEdition extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Rabid Wombat", 126, Rarity.UNCOMMON, mage.cards.r.RabidWombat.class));
|
||||
cards.add(new SetCardInfo("Rainbow Vale", 179, Rarity.RARE, mage.cards.r.RainbowVale.class));
|
||||
cards.add(new SetCardInfo("Righteous Avengers", 25, Rarity.COMMON, mage.cards.r.RighteousAvengers.class));
|
||||
cards.add(new SetCardInfo("Ring of Ma'rûf", 163, Rarity.RARE, mage.cards.r.RingOfMaruf.class));
|
||||
cards.add(new SetCardInfo("Ring of Ma'ruf", 163, Rarity.RARE, mage.cards.r.RingOfMaruf.class));
|
||||
cards.add(new SetCardInfo("River Merfolk", 47, Rarity.COMMON, mage.cards.r.RiverMerfolk.class));
|
||||
cards.add(new SetCardInfo("Roots", 127, Rarity.COMMON, mage.cards.r.Roots.class));
|
||||
cards.add(new SetCardInfo("Scryb Sprites", 128, Rarity.COMMON, mage.cards.s.ScrybSprites.class));
|
||||
|
|
|
@ -69,6 +69,7 @@ public class Prophecy extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Bog Elemental", 57, Rarity.RARE, mage.cards.b.BogElemental.class));
|
||||
cards.add(new SetCardInfo("Bog Glider", 58, Rarity.COMMON, mage.cards.b.BogGlider.class));
|
||||
cards.add(new SetCardInfo("Branded Brawlers", 84, Rarity.COMMON, mage.cards.b.BrandedBrawlers.class));
|
||||
cards.add(new SetCardInfo("Brutal Suppression", 85, Rarity.UNCOMMON, mage.cards.b.BrutalSuppression.class));
|
||||
cards.add(new SetCardInfo("Calming Verse", 110, Rarity.COMMON, mage.cards.c.CalmingVerse.class));
|
||||
cards.add(new SetCardInfo("Celestial Convergence", 5, Rarity.RARE, mage.cards.c.CelestialConvergence.class));
|
||||
cards.add(new SetCardInfo("Chilling Apparition", 59, Rarity.UNCOMMON, mage.cards.c.ChillingApparition.class));
|
||||
|
|
|
@ -23,6 +23,7 @@ public class Unglued extends ExpansionSet {
|
|||
|
||||
cards.add(new SetCardInfo("Chicken Egg", 41, Rarity.COMMON, mage.cards.c.ChickenEgg.class));
|
||||
cards.add(new SetCardInfo("Chicken a la King", 17, Rarity.RARE, mage.cards.c.ChickenALaKing.class));
|
||||
cards.add(new SetCardInfo("Elvish Impersonators", 56, Rarity.COMMON, mage.cards.e.ElvishImpersonators.class));
|
||||
cards.add(new SetCardInfo("Forest", 88, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(FrameStyle.UGL_FULL_ART_BASIC, false)));
|
||||
cards.add(new SetCardInfo("Fowl Play", 24, Rarity.COMMON, mage.cards.f.FowlPlay.class));
|
||||
cards.add(new SetCardInfo("Goblin Tutor", 45, Rarity.UNCOMMON, mage.cards.g.GoblinTutor.class));
|
||||
|
@ -33,7 +34,9 @@ public class Unglued extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Jack-in-the-Mox", 75, Rarity.RARE, mage.cards.j.JackInTheMox.class));
|
||||
cards.add(new SetCardInfo("Jumbo Imp", 34, Rarity.UNCOMMON, mage.cards.j.JumboImp.class));
|
||||
cards.add(new SetCardInfo("Krazy Kow", 48, Rarity.COMMON, mage.cards.k.KrazyKow.class));
|
||||
cards.add(new SetCardInfo("Mine, Mine, Mine!", 65, Rarity.RARE, mage.cards.m.MineMineMine.class));
|
||||
cards.add(new SetCardInfo("Mountain", 87, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(FrameStyle.UGL_FULL_ART_BASIC, false)));
|
||||
cards.add(new SetCardInfo("Once More With Feeling", 11, Rarity.RARE, mage.cards.o.OnceMoreWithFeeling.class));
|
||||
cards.add(new SetCardInfo("Paper Tiger", 78, Rarity.COMMON, mage.cards.p.PaperTiger.class));
|
||||
cards.add(new SetCardInfo("Plains", 84, Rarity.LAND, mage.cards.basiclands.Plains.class, new CardGraphicInfo(FrameStyle.UGL_FULL_ART_BASIC, false)));
|
||||
cards.add(new SetCardInfo("Poultrygeist", 37, Rarity.COMMON, mage.cards.p.Poultrygeist.class));
|
||||
|
|
|
@ -30,6 +30,7 @@ package org.mage.test.cards.abilities.keywords;
|
|||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
|
@ -580,4 +581,35 @@ public class FlashbackTest extends CardTestPlayerBase {
|
|||
assertLife(playerA, 20);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cost reduction with mixed flashback costs
|
||||
*/
|
||||
@Test
|
||||
public void testReduceMixedFlashbackCosts() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 5);
|
||||
|
||||
// Whenever you cast an instant or sorcery spell with converted mana cost greater than the number of experience counters you have, you get an experience counter.
|
||||
// Instant and sorcery spells you cast cost {1} less to cast for each experience counter you have.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mizzix of the Izmagnus");// 2/2
|
||||
|
||||
// Target player draws two cards.
|
||||
// Flashback-{1}{U}, Pay 3 life.
|
||||
addCard(Zone.HAND, playerA, "Deep Analysis"); // {3}{U}
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Deep Analysis");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Deep Analysis", 0);
|
||||
assertExileCount(playerA, "Deep Analysis", 1);
|
||||
assertHandCount(playerA, 4);
|
||||
|
||||
assertCounterCount(playerA, CounterType.EXPERIENCE, 2);
|
||||
|
||||
assertLife(playerA, 17);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -120,4 +120,36 @@ public class MizzixOfTheIzmagnusTest extends CardTestPlayerBase {
|
|||
assertLife(playerB, 17);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test to reduce Flashback costs
|
||||
*/
|
||||
@Test
|
||||
public void testReduceFlashbackCosts() {
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
|
||||
|
||||
// Whenever you cast an instant or sorcery spell with converted mana cost greater than the number of experience counters you have, you get an experience counter.
|
||||
// Instant and sorcery spells you cast cost {1} less to cast for each experience counter you have.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mizzix of the Izmagnus");// 2/2
|
||||
|
||||
// Engulfing Flames deals 1 damage to target creature. It can't be regenerated this turn.
|
||||
// Flashback {3}{R} (You may cast this card from your graveyard for its flashback cost. Then exile it.)
|
||||
addCard(Zone.HAND, playerA, "Engulfing Flames"); // {R}
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");// 2/2
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Engulfing Flames", "Silvercoat Lion");
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Flashback", "Silvercoat Lion");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Engulfing Flames", 0);
|
||||
assertExileCount(playerA, "Engulfing Flames", 1);
|
||||
|
||||
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
||||
|
||||
assertCounterCount(playerA, CounterType.EXPERIENCE, 1);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,12 +47,18 @@ public class DealsDamageToAPlayerAllTriggeredAbility extends TriggeredAbilityImp
|
|||
private final FilterPermanent filter;
|
||||
private final SetTargetPointer setTargetPointer;
|
||||
private final boolean onlyCombat;
|
||||
private final boolean affectsDefendingPlayer;
|
||||
|
||||
public DealsDamageToAPlayerAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyCombat) {
|
||||
this(effect, filter, optional, setTargetPointer, onlyCombat, false);
|
||||
}
|
||||
|
||||
public DealsDamageToAPlayerAllTriggeredAbility(Effect effect, FilterPermanent filter, boolean optional, SetTargetPointer setTargetPointer, boolean onlyCombat, boolean affectsDefendingPlayer) {
|
||||
super(Zone.BATTLEFIELD, effect, optional);
|
||||
this.setTargetPointer = setTargetPointer;
|
||||
this.filter = filter;
|
||||
this.onlyCombat = onlyCombat;
|
||||
this.affectsDefendingPlayer = affectsDefendingPlayer;
|
||||
}
|
||||
|
||||
public DealsDamageToAPlayerAllTriggeredAbility(final DealsDamageToAPlayerAllTriggeredAbility ability) {
|
||||
|
@ -60,6 +66,7 @@ public class DealsDamageToAPlayerAllTriggeredAbility extends TriggeredAbilityImp
|
|||
this.setTargetPointer = ability.setTargetPointer;
|
||||
this.filter = ability.filter;
|
||||
this.onlyCombat = ability.onlyCombat;
|
||||
this.affectsDefendingPlayer = ability.affectsDefendingPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -81,6 +88,10 @@ public class DealsDamageToAPlayerAllTriggeredAbility extends TriggeredAbilityImp
|
|||
for (Effect effect : this.getEffects()) {
|
||||
effect.setValue("damage", event.getAmount());
|
||||
effect.setValue("sourceId", event.getSourceId());
|
||||
if (affectsDefendingPlayer) {
|
||||
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
|
||||
continue;
|
||||
}
|
||||
switch (setTargetPointer) {
|
||||
case PLAYER:
|
||||
effect.setTargetPointer(new FixedTarget(permanent.getControllerId()));
|
||||
|
|
|
@ -74,6 +74,12 @@ public final class StaticFilters {
|
|||
FILTER_CARD_A_NON_LAND.setLockedFilter(true);
|
||||
}
|
||||
|
||||
public static final FilterInstantOrSorceryCard FILTER_CARD_INSTANT_OR_SORCERY = new FilterInstantOrSorceryCard();
|
||||
|
||||
static {
|
||||
FILTER_CARD_INSTANT_OR_SORCERY.setLockedFilter(true);
|
||||
}
|
||||
|
||||
public static final FilterPermanent FILTER_PERMANENT = new FilterPermanent();
|
||||
|
||||
static {
|
||||
|
@ -236,7 +242,7 @@ public final class StaticFilters {
|
|||
static {
|
||||
FILTER_BASIC_LAND_CARD.setLockedFilter(true);
|
||||
}
|
||||
|
||||
|
||||
// Used for sacrifice targets that don't need the "you control" text
|
||||
public static final FilterControlledLandPermanent FILTER_CONTROLLED_LAND_SHORT_TEXT = new FilterControlledLandPermanent("a land");
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
[![Join the chat at https://gitter.im/magefree/mage](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/magefree/mage?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/magefree/mage.svg?branch=master)](https://travis-ci.org/magefree/mage)
|
||||
|
||||
XMage allows you to play Magic against one or more online players or computer opponents. It includes full rules enforcement for over **16950** unique cards (over 328000 counting all cards from different editions). Starting with *Morningtide*, all regular sets have nearly all the cards implemented.
|
||||
XMage allows you to play Magic against one or more online players or computer opponents. It includes full rules enforcement for over **16950** unique cards (over 32800 counting all cards from different editions). Starting with *Morningtide*, all regular sets have nearly all the cards implemented.
|
||||
|
||||
There are public servers where you can play XMage against other players. You can also host your own server to play against the AI and/or your friends.
|
||||
|
||||
|
|
Loading…
Reference in a new issue