mirror of
https://github.com/correl/mage.git
synced 2024-11-16 11:09:29 +00:00
Merge pull request #48 from magefree/master
Merge https://github.com/magefree/mage
This commit is contained in:
commit
e92df28fbf
41 changed files with 1461 additions and 304 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
|
||||
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)|
|
|
@ -104,6 +104,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
private static final String LITE_MODE_ARG = "-lite";
|
||||
private static final String GRAY_MODE_ARG = "-gray";
|
||||
private static final String FILL_SCREEN_ARG = "-fullscreen";
|
||||
private static final String SKIP_DONE_SYMBOLS = "-skipDoneSymbols";
|
||||
|
||||
private static final String NOT_CONNECTED_TEXT = "<not connected>";
|
||||
private static MageFrame instance;
|
||||
|
@ -121,6 +122,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
//TODO: make gray theme, implement theme selector in preferences dialog
|
||||
private static boolean grayMode = false;
|
||||
private static boolean fullscreenMode = false;
|
||||
private static boolean skipSmallSymbolGenerationForExisting = false;
|
||||
|
||||
private static final Map<UUID, ChatPanelBasic> CHATS = new HashMap<>();
|
||||
private static final Map<UUID, GamePanel> GAMES = new HashMap<>();
|
||||
|
@ -153,6 +155,10 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
return grayMode;
|
||||
}
|
||||
|
||||
public static boolean isSkipSmallSymbolGenerationForExisting() {
|
||||
return skipSmallSymbolGenerationForExisting;
|
||||
}
|
||||
|
||||
@Override
|
||||
public MageVersion getVersion() {
|
||||
return VERSION;
|
||||
|
@ -1191,6 +1197,9 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
if (arg.startsWith(FILL_SCREEN_ARG)) {
|
||||
fullscreenMode = true;
|
||||
}
|
||||
if (arg.startsWith(SKIP_DONE_SYMBOLS)) {
|
||||
skipSmallSymbolGenerationForExisting = true;
|
||||
}
|
||||
}
|
||||
if (!liteMode) {
|
||||
final SplashScreen splash = SplashScreen.getSplashScreen();
|
||||
|
|
|
@ -1084,6 +1084,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) {
|
||||
l.showAll();
|
||||
|
@ -1705,6 +1721,10 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
|
|||
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) {
|
||||
JMenuItem duplicateSelection = new JMenuItem("Duplicate Selection");
|
||||
|
|
|
@ -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 final Color centerTextColor = Color.YELLOW;
|
||||
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) {
|
||||
|
@ -175,6 +197,10 @@ public class HoverButton extends JPanel implements MouseListener {
|
|||
}
|
||||
}
|
||||
|
||||
public void setCenterColor(Color c) {
|
||||
centerTextColor = c;
|
||||
}
|
||||
|
||||
private int calculateOffset(Graphics2D g2d) {
|
||||
if (textOffsetX == -1) { // calculate once
|
||||
FontRenderContext frc = g2d.getFontRenderContext();
|
||||
|
@ -345,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
|
||||
|
@ -354,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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -113,6 +113,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
private int avatarId = -1;
|
||||
private String flagName;
|
||||
private String basicTooltipText;
|
||||
private static final Map<UUID, Integer> playerLives = new HashMap<>();
|
||||
|
||||
private PriorityTimer timer;
|
||||
|
||||
|
@ -175,9 +176,30 @@ public class PlayerPanelExt extends javax.swing.JPanel {
|
|||
|
||||
public void update(PlayerView player) {
|
||||
this.player = player;
|
||||
int pastLife = player.getLife();
|
||||
if (playerLives != null) {
|
||||
if (playerLives.containsKey(player.getPlayerId())) {
|
||||
pastLife = playerLives.get(player.getPlayerId());
|
||||
}
|
||||
playerLives.put(player.getPlayerId(), player.getLife());
|
||||
}
|
||||
int playerLife = player.getLife();
|
||||
avatar.setCenterText("true".equals(MageFrame.getPreferences().get(PreferencesDialog.KEY_DISPLAY_LIVE_ON_AVATAR, "true"))
|
||||
? String.valueOf(playerLife) : null);
|
||||
|
||||
boolean displayLife = "true".equals(MageFrame.getPreferences().get(PreferencesDialog.KEY_DISPLAY_LIVE_ON_AVATAR, "true"));
|
||||
avatar.setCenterText(displayLife ? String.valueOf(playerLife) : null);
|
||||
|
||||
if (displayLife) {
|
||||
if (playerLife != pastLife) {
|
||||
if (playerLife > pastLife) {
|
||||
avatar.gainLifeDisplay();
|
||||
} else if (playerLife < pastLife) {
|
||||
avatar.loseLifeDisplay();
|
||||
}
|
||||
} else if (playerLife == pastLife) {
|
||||
avatar.stopLifeDisplay();
|
||||
}
|
||||
}
|
||||
|
||||
updateAvatar();
|
||||
|
||||
if (playerLife > 99) {
|
||||
|
|
|
@ -60,6 +60,7 @@ import mage.client.util.ButtonColumn;
|
|||
import mage.client.util.GUISizeHelper;
|
||||
import mage.client.util.IgnoreList;
|
||||
import mage.client.util.MageTableRowSorter;
|
||||
import mage.client.util.URLHandler;
|
||||
import mage.client.util.gui.GuiDisplayUtil;
|
||||
import mage.client.util.gui.TableUtil;
|
||||
import mage.constants.*;
|
||||
|
@ -579,7 +580,7 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
this.jPanelBottom.setVisible(false);
|
||||
} else {
|
||||
this.jPanelBottom.setVisible(true);
|
||||
this.jLabelFooterText.setText(serverMessages.get(0));
|
||||
URLHandler.handleMessage(serverMessages.get(0), this.jLabelFooterText);
|
||||
this.jButtonFooterNext.setVisible(serverMessages.size() > 1);
|
||||
}
|
||||
}
|
||||
|
@ -1283,7 +1284,9 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
if (currentMessage >= messages.size()) {
|
||||
currentMessage = 0;
|
||||
}
|
||||
this.jLabelFooterText.setText(messages.get(currentMessage));
|
||||
|
||||
URLHandler.RemoveMouseAdapter(jLabelFooterText);
|
||||
URLHandler.handleMessage(messages.get(currentMessage), this.jLabelFooterText);
|
||||
}
|
||||
}
|
||||
}//GEN-LAST:event_jButtonFooterNextActionPerformed
|
||||
|
|
117
Mage.Client/src/main/java/mage/client/util/URLHandler.java
Normal file
117
Mage.Client/src/main/java/mage/client/util/URLHandler.java
Normal file
|
@ -0,0 +1,117 @@
|
|||
/*
|
||||
* To change this license header, choose License Headers in Project Properties.
|
||||
* To change this template file, choose Tools | Templates
|
||||
* and open the template in the editor.
|
||||
*/
|
||||
package mage.client.util;
|
||||
|
||||
import java.awt.Desktop;
|
||||
import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import javax.swing.JLabel;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Dahny
|
||||
*/
|
||||
public class URLHandler {
|
||||
|
||||
private static MouseAdapter currentMouseAdapter;
|
||||
|
||||
/**
|
||||
* This method makes a URL in a message click-able and converts the message
|
||||
* into HTML.
|
||||
*
|
||||
* @param message: The selected message
|
||||
* @param label: The message of the day label
|
||||
*/
|
||||
public static void handleMessage(String message, JLabel label) {
|
||||
String url = detectURL(message);
|
||||
|
||||
if (!url.equals("")) {
|
||||
label.addMouseListener(createMouseAdapter(url));
|
||||
}
|
||||
|
||||
label.setText(convertToHTML(message));
|
||||
}
|
||||
|
||||
public static void RemoveMouseAdapter(JLabel label) {
|
||||
label.removeMouseListener(currentMouseAdapter);
|
||||
currentMouseAdapter = null;
|
||||
}
|
||||
|
||||
private static MouseAdapter createMouseAdapter(String url) {
|
||||
currentMouseAdapter = new MouseAdapter() {
|
||||
public void mouseClicked(MouseEvent e) {
|
||||
if (e.getClickCount() > 0) {
|
||||
if (Desktop.isDesktopSupported()) {
|
||||
Desktop desktop = Desktop.getDesktop();
|
||||
try {
|
||||
URI uri = new URI(url);
|
||||
desktop.browse(uri);
|
||||
} catch (IOException | URISyntaxException ex) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
return currentMouseAdapter;
|
||||
}
|
||||
|
||||
public static String convertToHTML(String input) {
|
||||
String s = input;
|
||||
String output = "<html>";
|
||||
// separate the input by spaces
|
||||
String[] parts = s.split("\\s+");
|
||||
|
||||
for (String item : parts) {
|
||||
try {
|
||||
URL url = new URL(item);
|
||||
// The item is already a valid URL
|
||||
output = output + "<a href=\"" + url + "\">" + url + "</a> ";
|
||||
|
||||
} catch (MalformedURLException e) {
|
||||
//The item might still be a URL
|
||||
if (item.startsWith("www.")) {
|
||||
output = output + "<a href=\"" + item + "\">" + item + "</a> ";
|
||||
} else {
|
||||
output = output + item + " ";
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
output = output + "</html>";
|
||||
return output;
|
||||
}
|
||||
|
||||
public static String detectURL(String input) {
|
||||
String s = input;
|
||||
String output = "";
|
||||
// separate the input by spaces
|
||||
String[] parts = s.split("\\s+");
|
||||
|
||||
for (String item : parts) {
|
||||
try {
|
||||
URL url = new URL(item);
|
||||
// The item is already a valid URL
|
||||
output = url.toString();
|
||||
} catch (MalformedURLException e) {
|
||||
//The item might still be a URL
|
||||
if (item.startsWith("www.")) {
|
||||
output = "http://" + item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
}
|
|
@ -625,9 +625,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
|
|||
if (gameCard.hideInfo()) {
|
||||
return;
|
||||
}
|
||||
if (this.contains(e.getPoint())) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (tooltipShowing) {
|
||||
synchronized (this) {
|
||||
if (tooltipShowing) {
|
||||
|
|
|
@ -31,10 +31,14 @@ import java.util.HashSet;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.IntStream;
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.swing.*;
|
||||
import mage.cards.repository.ExpansionRepository;
|
||||
import mage.client.MageFrame;
|
||||
import mage.client.constants.Constants;
|
||||
import mage.client.constants.Constants.ResourceSetSize;
|
||||
import mage.client.constants.Constants.ResourceSymbolSize;
|
||||
|
@ -57,7 +61,7 @@ public final class ManaSymbols {
|
|||
private static final Logger LOGGER = Logger.getLogger(ManaSymbols.class);
|
||||
private static final Map<Integer, Map<String, BufferedImage>> manaImages = new HashMap<>();
|
||||
|
||||
private static final Map<String, Map<String, Image>> setImages = new HashMap<>();
|
||||
private static final Map<String, Map<String, Image>> setImages = new ConcurrentHashMap<>();
|
||||
|
||||
private static final HashSet<String> onlyMythics = new HashSet<>();
|
||||
private static final HashSet<String> withoutSymbols = new HashSet<>();
|
||||
|
@ -77,7 +81,7 @@ public final class ManaSymbols {
|
|||
}
|
||||
private static final Map<String, Dimension> setImagesExist = new HashMap<>();
|
||||
private static final Pattern REPLACE_SYMBOLS_PATTERN = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}");
|
||||
private static String cachedPath;
|
||||
|
||||
private static final String[] symbols = new String[]{"0", "1", "10", "11", "12", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9",
|
||||
"B", "BG", "BR", "BP", "2B",
|
||||
"G", "GU", "GW", "GP", "2G",
|
||||
|
@ -174,8 +178,10 @@ public final class ManaSymbols {
|
|||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
|
||||
String pathRoot = getResourceSetsPath(ResourceSetSize.SMALL) + set;
|
||||
for (String code : codes) {
|
||||
File newFile = new File(pathRoot + '-' + code + ".png");
|
||||
if(!(MageFrame.isSkipSmallSymbolGenerationForExisting() && newFile.exists())){// skip if option enabled and file already exists
|
||||
file = new File(getResourceSetsPath(ResourceSetSize.MEDIUM) + set + '-' + code + ".png");
|
||||
if (file.exists()) {
|
||||
continue;
|
||||
|
@ -192,7 +198,6 @@ public final class ManaSymbols {
|
|||
}
|
||||
Rectangle r = new Rectangle(15 + dx, (int) (height * (15.0f + dx) / width));
|
||||
BufferedImage resized = ImageHelper.getResizedImage(BufferedImageBuilder.bufferImage(image, BufferedImage.TYPE_INT_ARGB), r);
|
||||
File newFile = new File(getResourceSetsPath(ResourceSetSize.SMALL) + set + '-' + code + ".png");
|
||||
ImageIO.write(resized, "png", newFile);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
|
@ -201,11 +206,11 @@ public final class ManaSymbols {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
|
||||
// mark loaded images
|
||||
// TODO: delete that code, images draw-show must dynamicly
|
||||
File file;
|
||||
|
@ -226,7 +231,6 @@ public final class ManaSymbols {
|
|||
}
|
||||
|
||||
public static BufferedImage loadSVG(File svgFile, int resizeToWidth, int resizeToHeight, boolean useShadow) throws IOException {
|
||||
|
||||
// debug: disable shadow gen, need to test it
|
||||
useShadow = false;
|
||||
|
||||
|
@ -424,17 +428,17 @@ public final class ManaSymbols {
|
|||
}
|
||||
|
||||
private static boolean loadSymbolImages(int size) {
|
||||
// load all symbols to cash
|
||||
// load all symbols to cache
|
||||
// priority: SVG -> GIF
|
||||
// gif remain for backward compatibility
|
||||
|
||||
boolean fileErrors = false;
|
||||
|
||||
HashMap<String, BufferedImage> sizedSymbols = new HashMap<>();
|
||||
for (String symbol : symbols) {
|
||||
|
||||
//boolean fileErrors = false;
|
||||
AtomicBoolean fileErrors = new AtomicBoolean(false);
|
||||
Map<String, BufferedImage> sizedSymbols = new ConcurrentHashMap<>();
|
||||
IntStream.range(0, symbols.length).parallel().forEach(i-> {
|
||||
String symbol = symbols[i];
|
||||
BufferedImage image = null;
|
||||
File file = null;
|
||||
File file;
|
||||
|
||||
// svg
|
||||
file = getSymbolFileNameAsSVG(symbol);
|
||||
|
@ -456,13 +460,13 @@ public final class ManaSymbols {
|
|||
if (image != null) {
|
||||
sizedSymbols.put(symbol, image);
|
||||
} else {
|
||||
fileErrors = true;
|
||||
fileErrors.set(true);
|
||||
LOGGER.warn("SVG or GIF symbol can't be load: " + symbol);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
manaImages.put(size, sizedSymbols);
|
||||
return !fileErrors;
|
||||
return !fileErrors.get();
|
||||
}
|
||||
|
||||
private static void renameSymbols(String path) {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package org.mage.card.arcane;
|
||||
|
||||
import mage.util.StreamUtils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.DatagramPacket;
|
||||
import java.net.DatagramSocket;
|
||||
|
@ -42,7 +44,7 @@ public final class Util {
|
|||
socket = new DatagramSocket();
|
||||
broadcast(socket, data, port, NetworkInterface.getNetworkInterfaces());
|
||||
} finally {
|
||||
socket.close();
|
||||
StreamUtils.closeQuietly(socket);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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,7 +38,7 @@ 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;
|
||||
|
|
|
@ -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()) {
|
||||
|
|
|
@ -90,8 +90,8 @@ public class ChatSession {
|
|||
String userName = clients.get(userId);
|
||||
if (reason != DisconnectReason.LostConnection) { // for lost connection the user will be reconnected or session expire so no removeUserFromAllTablesAndChat of chat yet
|
||||
final Lock w = lock.writeLock();
|
||||
try {
|
||||
w.lock();
|
||||
try {
|
||||
clients.remove(userId);
|
||||
} finally {
|
||||
w.unlock();
|
||||
|
|
|
@ -231,8 +231,8 @@ public enum UserManager {
|
|||
}
|
||||
logger.debug("Users to remove " + toRemove.size());
|
||||
final Lock w = lock.readLock();
|
||||
try {
|
||||
w.lock();
|
||||
try {
|
||||
for (User user : toRemove) {
|
||||
users.remove(user.getId());
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
@ -50,16 +47,14 @@ public class DeepAnalysis extends CardImpl {
|
|||
public DeepAnalysis(UUID ownerId, CardSetInfo setInfo) {
|
||||
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.ELVES);
|
||||
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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
152
Mage.Sets/src/mage/cards/r/Retribution.java
Normal file
152
Mage.Sets/src/mage/cards/r/Retribution.java
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* 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.r;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author jeffwadsworth
|
||||
*/
|
||||
public class Retribution extends CardImpl {
|
||||
|
||||
public Retribution(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}{R}");
|
||||
|
||||
// Choose two target creatures an opponent controls. That player chooses and sacrifices one of those creatures. Put a -1/-1 counter on the other.
|
||||
this.getSpellAbility().addEffect(new RetributionEffect());
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanentOpponentSameController(2, 2, StaticFilters.FILTER_PERMANENT_CREATURE, false));
|
||||
|
||||
}
|
||||
|
||||
public Retribution(final Retribution card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Retribution copy() {
|
||||
return new Retribution(this);
|
||||
}
|
||||
}
|
||||
|
||||
class RetributionEffect extends OneShotEffect {
|
||||
|
||||
public RetributionEffect() {
|
||||
super(Outcome.Detriment);
|
||||
this.staticText = "Choose two target creatures an opponent controls. That player chooses and sacrifices one of those creatures. Put a -1/-1 counter on the other";
|
||||
}
|
||||
|
||||
public RetributionEffect(final RetributionEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetributionEffect copy() {
|
||||
return new RetributionEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
if (sourceObject != null) {
|
||||
boolean sacrificeDone = false;
|
||||
int count = 0;
|
||||
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
|
||||
Permanent creature = game.getPermanent(targetId);
|
||||
if (creature != null) {
|
||||
Player controllerOfCreature = game.getPlayer(creature.getControllerId());
|
||||
if ((count == 0
|
||||
&& controllerOfCreature.chooseUse(Outcome.Sacrifice, "Sacrifice " + creature.getLogName() + '?', source, game))
|
||||
|| (count == 1
|
||||
&& !sacrificeDone)) {
|
||||
creature.sacrifice(source.getId(), game);
|
||||
sacrificeDone = true;
|
||||
} else {
|
||||
creature.addCounters(CounterType.M1M1.createInstance(), source, game);
|
||||
}
|
||||
count++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class TargetCreaturePermanentOpponentSameController extends TargetCreaturePermanent {
|
||||
|
||||
public TargetCreaturePermanentOpponentSameController(int minNumTargets, int maxNumTargets, FilterCreaturePermanent filter, boolean notTarget) {
|
||||
super(minNumTargets, maxNumTargets, filter, notTarget);
|
||||
}
|
||||
|
||||
public TargetCreaturePermanentOpponentSameController(final TargetCreaturePermanentOpponentSameController target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
|
||||
if (super.canTarget(controllerId, id, source, game)) {
|
||||
Permanent firstTargetPermanent = game.getPermanent(id);
|
||||
if (firstTargetPermanent != null
|
||||
&& game.getOpponents(controllerId).contains(firstTargetPermanent.getControllerId())) {
|
||||
for (Object object : getTargets()) {
|
||||
UUID targetId = (UUID) object;
|
||||
Permanent targetPermanent = game.getPermanent(targetId);
|
||||
if (targetPermanent != null) {
|
||||
if (!firstTargetPermanent.getId().equals(targetPermanent.getId())) {
|
||||
if (!firstTargetPermanent.getControllerId().equals(targetPermanent.getOwnerId())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TargetCreaturePermanentOpponentSameController copy() {
|
||||
return new TargetCreaturePermanentOpponentSameController(this);
|
||||
}
|
||||
}
|
|
@ -156,6 +156,7 @@ public class Homelands extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Reef Pirates", 45, Rarity.COMMON, ReefPirates.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Reef Pirates", 46, Rarity.COMMON, ReefPirates.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Renewal", 66, Rarity.COMMON, mage.cards.r.Renewal.class));
|
||||
cards.add(new SetCardInfo("Retribution", 99, Rarity.UNCOMMON, mage.cards.r.Retribution.class));
|
||||
cards.add(new SetCardInfo("Reveka, Wizard Savant", 47, Rarity.RARE, mage.cards.r.RevekaWizardSavant.class));
|
||||
cards.add(new SetCardInfo("Roots", 68, Rarity.UNCOMMON, mage.cards.r.Roots.class));
|
||||
cards.add(new SetCardInfo("Root Spider", 67, Rarity.UNCOMMON, mage.cards.r.RootSpider.class));
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -215,6 +215,7 @@ public class MastersEditionII extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Red Cliffs Armada", 62, Rarity.COMMON, mage.cards.r.RedCliffsArmada.class));
|
||||
cards.add(new SetCardInfo("Reinforcements", 28, Rarity.COMMON, mage.cards.r.Reinforcements.class));
|
||||
cards.add(new SetCardInfo("Reprisal", 29, Rarity.COMMON, mage.cards.r.Reprisal.class));
|
||||
cards.add(new SetCardInfo("Retribution", 148, Rarity.UNCOMMON, mage.cards.r.Retribution.class));
|
||||
cards.add(new SetCardInfo("Righteous Fury", 30, Rarity.RARE, mage.cards.r.RighteousFury.class));
|
||||
cards.add(new SetCardInfo("Ritual of Subdual", 174, Rarity.RARE, mage.cards.r.RitualOfSubdual.class));
|
||||
cards.add(new SetCardInfo("Ritual of the Machine", 109, Rarity.RARE, mage.cards.r.RitualOfTheMachine.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()));
|
||||
|
|
|
@ -123,6 +123,7 @@ public enum SubType {
|
|||
ELEMENTAL("Elemental", SubTypeSet.CreatureType),
|
||||
ELEPHANT("Elephant", SubTypeSet.CreatureType),
|
||||
ELF("Elf", SubTypeSet.CreatureType),
|
||||
ELVES("Elves", SubTypeSet.CreatureType),
|
||||
ELK("Elk", SubTypeSet.CreatureType),
|
||||
EYE("Eye", SubTypeSet.CreatureType),
|
||||
EWOK("Ewok", SubTypeSet.CreatureType, true), // Star Wars
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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