Merge pull request #6 from magefree/master

Update Master
This commit is contained in:
Danny Plenge 2018-04-03 12:06:22 +02:00 committed by GitHub
commit ab6208745e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 917 additions and 140 deletions

View file

@ -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

View file

@ -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

View file

@ -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)|

View file

@ -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)|

View file

@ -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)|

View file

@ -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) {

View file

@ -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;
}
}

View file

@ -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();
}
}

View file

@ -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;
/**
*

View file

@ -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);
}

View file

@ -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()) {

View file

@ -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();
}
}

View 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);
}
}

View file

@ -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) {

View file

@ -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) {

View 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;
}
}

View file

@ -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) {

View 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);
}
}

View file

@ -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;

View file

@ -93,6 +93,7 @@ class NaturesWillEffect extends OneShotEffect {
land.untap(game);
}
}
return true;
}
return false;
}

View 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 &mdash; 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;
}
}

View file

@ -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));

View file

@ -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));

View file

@ -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));

View file

@ -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);
}
}

View file

@ -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);
}
}

View file

@ -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()));

View file

@ -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");

View file

@ -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.