Merge origin/master

This commit is contained in:
fireshoes 2016-03-04 10:51:50 -06:00
commit 8096d2f4aa
149 changed files with 3642 additions and 1338 deletions

View file

@ -4,6 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1
http://maven.apache.org/xsd/assembly-1.1.1.xsd">
<includeBaseDirectory>false</includeBaseDirectory>
<id>mage-client</id>
<formats>
<format>zip</format>
</formats>

View file

@ -682,6 +682,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
gamePane.watchGame(gameId);
setActive(gamePane);
} catch (PropertyVetoException ex) {
LOGGER.debug("Problem starting watching game " + gameId, ex);
}
}
@ -1068,9 +1069,11 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
if (setActive) {
setActive(tablesPane);
} else // if other panel was already shown, mamke sure it's topmost again
if (topPanebefore != null) {
{
if (topPanebefore != null) {
setActive(topPanebefore);
}
}
}
public void hideGames() {
@ -1417,6 +1420,10 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
break;
case CLIENT_STOP_WATCHING:
session.stopWatching(userRequestMessage.getGameId());
GamePanel gamePanel = getGame(userRequestMessage.getGameId());
if (gamePanel != null) {
gamePanel.removeGame();
}
removeGame(userRequestMessage.getGameId());
break;
case CLIENT_EXIT:

View file

@ -78,6 +78,7 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener,
public CardGrid() {
initComponents();
setGUISize();
setOpaque(false);
}
@ -90,6 +91,14 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener,
this.bigCard = null;
}
public void changeGUISize() {
setGUISize();
}
private void setGUISize() {
cardDimension = GUISizeHelper.editorCardDimension;
}
@Override
public void loadCards(CardsView showCards, SortSetting sortSetting, BigCard bigCard, UUID gameId) {
this.loadCards(showCards, sortSetting, bigCard, gameId, true);
@ -125,9 +134,6 @@ public class CardGrid extends javax.swing.JLayeredPane implements MouseListener,
}
private void addCard(CardView card, BigCard bigCard, UUID gameId, boolean drawImage) {
if (cardDimension == null) {
cardDimension = GUISizeHelper.editorCardDimension;
}
MageCard cardImg = Plugins.getInstance().getMageCard(card, bigCard, cardDimension, gameId, drawImage);
cards.put(card.getId(), cardImg);
cardImg.addMouseListener(this);

View file

@ -149,7 +149,6 @@ public class CardsList extends javax.swing.JPanel implements MouseListener, ICar
private void setGUISize() {
mainTable.getTableHeader().setFont(GUISizeHelper.tableFont);
mainTable.getTableHeader().setPreferredSize(new Dimension(GUISizeHelper.tableHeaderHeight, GUISizeHelper.tableHeaderHeight));
mainTable.setFont(GUISizeHelper.tableFont);
mainTable.setRowHeight(GUISizeHelper.getTableRowHeight());
cardDimension = GUISizeHelper.editorCardDimension;

View file

@ -2,13 +2,29 @@ package mage.client.components;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JEditorPane;
import javax.swing.JPanel;
import javax.swing.JTextPane;
import javax.swing.SwingUtilities;
import javax.swing.event.HyperlinkEvent;
import javax.swing.event.HyperlinkEvent.EventType;
import javax.swing.event.HyperlinkListener;
import javax.swing.text.html.HTMLDocument;
import javax.swing.text.html.HTMLEditorKit;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.client.MageFrame;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.gui.GuiDisplayUtil;
import mage.components.CardInfoPane;
import mage.utils.ThreadUtils;
import mage.view.CardView;
/**
* Enhanced {@link JTextPane} with text highlighting support.
*
@ -18,10 +34,68 @@ public class ColorPane extends JEditorPane {
HTMLEditorKit kit = new HTMLEditorKit();
HTMLDocument doc = new HTMLDocument();
private int tooltipDelay;
public ColorPane() {
this.setEditorKit(kit);
this.setDocument(doc);
addHyperlinkListener(new HyperlinkListener() {
@Override
public void hyperlinkUpdate(final HyperlinkEvent e) {
ThreadUtils.threadPool2.submit(new Runnable() {
@Override
public void run() {
tooltipDelay = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_TOOLTIPS_DELAY, 300);
if (tooltipDelay == 0) {
return;
}
String name = e.getDescription().substring(1);
CardInfo card = CardRepository.instance.findCard(name);
try {
final Component container = MageFrame.getUI().getComponent(MageComponents.POPUP_CONTAINER);
if (e.getEventType() == EventType.EXITED) {
setPopupVisibility(null, container, false);
} else {
CardInfoPane cardInfoPane = (CardInfoPane) MageFrame.getUI().getComponent(MageComponents.CARD_INFO_PANE);
cardInfoPane.setCard(new CardView(card.getMockCard()), container);
Point mousePosition = MageFrame.getDesktop().getMousePosition();
int popupY = 0;
if (mousePosition == null) { // switched to another window
popupY = getLocationOnScreen().y;
} else {
popupY = mousePosition.y;
}
Point location = new Point(getLocationOnScreen().x - container.getWidth(), popupY);
Component parentComponent = MageFrame.getInstance();
location = GuiDisplayUtil.keepComponentInsideParent(location, parentComponent.getLocationOnScreen(),
container, parentComponent);
setPopupVisibility(location, container, true);
}
} catch (InterruptedException e1) {
e1.printStackTrace();
}
}
});
}
private void setPopupVisibility(final Point location, final Component container, final boolean show)
throws InterruptedException {
final Component c = MageFrame.getUI().getComponent(MageComponents.DESKTOP_PANE);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (location != null) {
container.setLocation(location);
}
container.setVisible(show);
c.repaint();
}
});
}
});
}
/**
@ -45,6 +119,7 @@ public class ColorPane extends JEditorPane {
public void append(String text) {
try {
text = text.replaceAll("(<font color=[^>]*>([^<]*)) (\\[[0-9a-fA-F]*\\])</font>", "<a href='#$2'>$1</a> $3");
setEditable(true);
kit.insertHTML(doc, doc.getLength(), text, 0, 0, null);
setEditable(false);

View file

@ -14,6 +14,7 @@ import org.apache.log4j.Logger;
import org.jdesktop.layout.GroupLayout;
import org.jdesktop.layout.LayoutStyle;
import org.jdesktop.swingx.JXPanel;
import org.jsoup.Jsoup;
import org.mage.card.arcane.ManaSymbols;
import org.mage.card.arcane.UI;
@ -434,6 +435,7 @@ public class AbilityPicker extends JXPanel implements MouseWheelListener {
if (choice == null || choice.isEmpty()) {
return choice;
}
choice = Jsoup.parse(choice).text(); // decode HTML entities and strip tags
return choice.substring(0, 1).toUpperCase() + choice.substring(1);
}

View file

@ -54,6 +54,14 @@ import mage.util.TournamentUtil;
*/
public class DeckGenerator {
public static class DeckGeneratorException extends RuntimeException {
public DeckGeneratorException(String message) {
super(message);
}
}
private static final int MAX_TRIES = 8196;
private static DeckGeneratorDialog genDialog;
private static DeckGeneratorPool genPool;
@ -82,6 +90,9 @@ public class DeckGenerator {
String format = genDialog.getSelectedFormat();
List<String> setsToUse = ConstructedFormats.getSetsByFormat(format);
if (setsToUse == null) {
throw new DeckGeneratorException("Deck sets aren't initialized; please connect to a server to update the database.");
}
if (setsToUse.isEmpty()) {
// Default to using all sets
setsToUse = ExpansionRepository.instance.getSetCodes();
@ -144,7 +155,7 @@ public class DeckGenerator {
* @return the final deck to use.
*/
private static Deck generateDeck(int deckSize, List<ColoredManaSymbol> allowedColors, List<String> setsToUse) {
genPool = new DeckGeneratorPool(deckSize, allowedColors, genDialog.isSingleton());
genPool = new DeckGeneratorPool(deckSize, allowedColors, genDialog.isSingleton(), genDialog.isColorless());
final String[] sets = setsToUse.toArray(new String[setsToUse.size()]);

View file

@ -48,13 +48,13 @@ import java.util.Date;
*/
public class DeckGeneratorDialog {
private static JDialog dlg;
private static String selectedColors;
private static JComboBox cbSets;
private static JComboBox cbDeckSize;
private static JButton btnGenerate, btnCancel;
private static JCheckBox cArtifacts, cSingleton, cNonBasicLands;
private static SimpleDateFormat dateFormat;
private JDialog dlg;
private String selectedColors;
private JComboBox<String> cbSets;
private JComboBox<String> cbDeckSize;
private JButton btnGenerate, btnCancel;
private JCheckBox cArtifacts, cSingleton, cNonBasicLands, cColorless;
private SimpleDateFormat dateFormat;
public DeckGeneratorDialog()
{
@ -83,7 +83,7 @@ public class DeckGeneratorDialog {
p0.add(Box.createVerticalStrut(5));
JPanel jPanel = new JPanel();
JLabel text3 = new JLabel("Choose sets:");
cbSets = new JComboBox(ConstructedFormats.getTypes());
cbSets = new JComboBox<String>(ConstructedFormats.getTypes());
cbSets.setSelectedIndex(0);
cbSets.setPreferredSize(new Dimension(300, 25));
cbSets.setMaximumSize(new Dimension(300, 25));
@ -100,7 +100,7 @@ public class DeckGeneratorDialog {
p0.add(Box.createVerticalStrut(5));
JPanel jPanel2 = new JPanel();
JLabel textDeckSize = new JLabel("Deck size:");
cbDeckSize = new JComboBox(new String[]{"40","60"});
cbDeckSize = new JComboBox<String>(new String[] { "40", "60" });
cbDeckSize.setSelectedIndex(0);
cbDeckSize.setPreferredSize(new Dimension(300, 25));
cbDeckSize.setMaximumSize(new Dimension(300, 25));
@ -138,8 +138,15 @@ public class DeckGeneratorDialog {
cNonBasicLands.setSelected(Boolean.valueOf(nonBasicEnabled));
jCheckBoxes.add(cNonBasicLands);
jCheckBoxes.setPreferredSize(new Dimension(300, 25));
jCheckBoxes.setMaximumSize(new Dimension(300, 25));
// Non-basic lands
cColorless = new JCheckBox("Colorless mana", false);
cColorless.setToolTipText("Allow cards with colorless mana cost.");
String colorlessEnabled = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORLESS, "false");
cColorless.setSelected(Boolean.valueOf(colorlessEnabled));
jCheckBoxes.add(cColorless);
jCheckBoxes.setPreferredSize(new Dimension(450, 25));
jCheckBoxes.setMaximumSize(new Dimension(450, 25));
p0.add(jCheckBoxes);
btnGenerate = new JButton("Ok");
@ -168,7 +175,7 @@ public class DeckGeneratorDialog {
dlg.dispose();
}
public static void cleanUp() {
public void cleanUp() {
for (ActionListener al: btnGenerate.getActionListeners()) {
btnGenerate.removeActionListener(al);
}
@ -187,7 +194,7 @@ public class DeckGeneratorDialog {
tmp.createNewFile();
deck.setName(deckName);
Sets.saveDeck(tmp.getAbsolutePath(), deck.getDeckCardLists());
DeckGeneratorDialog.cleanUp();
cleanUp();
return tmp.getAbsolutePath();
} catch (Exception e) {
JOptionPane.showMessageDialog(null, "Couldn't generate deck. Try again.");
@ -205,6 +212,12 @@ public class DeckGeneratorDialog {
return selected;
}
public boolean isColorless() {
boolean selected = cColorless.isSelected();
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_COLORLESS, Boolean.toString(selected));
return selected;
}
public boolean useArtifacts() {
boolean selected = cArtifacts.isSelected();
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_DECK_GENERATOR_ARTIFACTS, Boolean.toString(selected));

View file

@ -51,6 +51,7 @@ public class DeckGeneratorPool
private static final int NONCREATURE_COUNT_60 = 13;
private final List<ColoredManaSymbol> allowedColors;
private boolean colorlessAllowed;
private final List<DeckGeneratorCMC> poolCMCs;
private final int creatureCount;
private final int nonCreatureCount;
@ -68,11 +69,12 @@ public class DeckGeneratorPool
private List<Card> reserveSpells = new ArrayList<>();
private Deck deck;
public DeckGeneratorPool(final int deckSize, final List<ColoredManaSymbol> allowedColors, boolean isSingleton)
public DeckGeneratorPool(final int deckSize, final List<ColoredManaSymbol> allowedColors, boolean isSingleton, boolean colorlessAllowed)
{
this.deckSize = deckSize;
this.allowedColors = allowedColors;
this.isSingleton = isSingleton;
this.colorlessAllowed = colorlessAllowed;
this.deck = new Deck();
@ -207,6 +209,9 @@ public class DeckGeneratorPool
return false;
}
}
if (symbol.equals("C") && !colorlessAllowed) {
return false;
}
}
return true;
}

View file

@ -35,7 +35,6 @@ package mage.client.deckeditor;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentEvent;
@ -183,11 +182,11 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
public void changeGUISize() {
setGUISize();
cardGrid.changeGUISize();
}
private void setGUISize() {
mainTable.getTableHeader().setFont(GUISizeHelper.tableFont);
mainTable.getTableHeader().setPreferredSize(new Dimension(GUISizeHelper.tableHeaderHeight, GUISizeHelper.tableHeaderHeight));
mainTable.setFont(GUISizeHelper.tableFont);
mainTable.setRowHeight(GUISizeHelper.getTableRowHeight());

View file

@ -83,6 +83,8 @@
<EmptySpace max="-2" attributes="0"/>
<Component id="btnImport" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnGenDeck" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnAddLand" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnSubmit" min="-2" max="-2" attributes="0"/>
@ -110,6 +112,7 @@
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btnImport" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnGenDeck" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnAddLand" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnSubmit" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>

View file

@ -60,9 +60,9 @@ import mage.client.MageFrame;
import mage.client.cards.BigCard;
import mage.client.cards.ICardGrid;
import mage.client.constants.Constants.DeckEditorMode;
import static mage.client.constants.Constants.DeckEditorMode.FREE_BUILDING;
import static mage.client.constants.Constants.DeckEditorMode.LIMITED_BUILDING;
import static mage.client.constants.Constants.DeckEditorMode.SIDEBOARDING;
import mage.client.deck.generator.DeckGenerator;
import mage.client.deck.generator.DeckGenerator.DeckGeneratorException;
import mage.client.dialog.AddLandDialog;
import mage.client.plugins.impl.Plugins;
import mage.client.util.Event;
@ -171,9 +171,9 @@ public class DeckEditorPanel extends javax.swing.JPanel {
}
// TODO: take from preferences
this.cardSelector.switchToGrid();
this.btnExit.setVisible(false);
this.btnImport.setVisible(false);
this.btnGenDeck.setVisible(false);
if (!MageFrame.getSession().isTestMode()) {
this.btnLoad.setVisible(false);
}
@ -196,6 +196,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
//this.cardTableSelector.loadCards(this.bigCard);
this.btnExit.setVisible(true);
this.btnImport.setVisible(true);
this.btnGenDeck.setVisible(true);
if (!MageFrame.getSession().isTestMode()) {
this.btnLoad.setVisible(true);
}
@ -505,6 +506,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
btnImport = new javax.swing.JButton();
btnSubmit = new javax.swing.JButton();
btnAddLand = new javax.swing.JButton();
btnGenDeck = new javax.swing.JButton();
txtTimeRemaining = new javax.swing.JTextField();
jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT);
@ -592,6 +594,15 @@ public class DeckEditorPanel extends javax.swing.JPanel {
btnAddLandActionPerformed(evt);
}
});
btnGenDeck.setText("Generate");
btnGenDeck.setName("btnGenDeck");
btnGenDeck.addActionListener(new java.awt.event.ActionListener() {
@Override
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnGenDeckActionPerformed(evt);
}
});
txtTimeRemaining.setEditable(false);
txtTimeRemaining.setForeground(java.awt.Color.red);
@ -627,6 +638,8 @@ public class DeckEditorPanel extends javax.swing.JPanel {
.addContainerGap()
.addComponent(btnImport)
.addContainerGap()
.addComponent(btnGenDeck)
.addContainerGap()
.addComponent(btnAddLand)
.addContainerGap()
.addComponent(btnSubmit))
@ -651,6 +664,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnImport)
.addComponent(btnGenDeck)
.addComponent(btnAddLand)
.addComponent(btnSubmit))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@ -802,6 +816,21 @@ public class DeckEditorPanel extends javax.swing.JPanel {
refreshDeck();
}//GEN-LAST:event_btnAddLandActionPerformed
private void btnGenDeckActionPerformed(ActionEvent evt) {
try {
setCursor(new Cursor(Cursor.WAIT_CURSOR));
String path = DeckGenerator.generateDeck();
deck = Deck.load(DeckImporterUtil.importDeck(path), true, true);
} catch (GameException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Error loading generated deck", JOptionPane.ERROR_MESSAGE);
}catch (DeckGeneratorException ex) {
JOptionPane.showMessageDialog(MageFrame.getDesktop(), ex.getMessage(), "Generator error", JOptionPane.ERROR_MESSAGE);
} finally {
setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
}
refreshDeck();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private mage.client.cards.BigCard bigCard;
private javax.swing.JButton btnExit;
@ -817,6 +846,7 @@ public class DeckEditorPanel extends javax.swing.JPanel {
private javax.swing.JTextField txtDeckName;
private javax.swing.JButton btnSubmit;
private javax.swing.JButton btnAddLand;
private javax.swing.JButton btnGenDeck;
private JComponent cardInfoPane;
private javax.swing.JTextField txtTimeRemaining;
// End of variables declaration//GEN-END:variables

View file

@ -66,6 +66,7 @@ import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_FLAG;
import mage.client.preference.MagePreferences;
import mage.client.util.Config;
import mage.client.util.gui.countryBox.CountryItemEditor;
import mage.client.util.sets.ConstructedFormats;
import mage.remote.Connection;
import org.apache.log4j.Logger;
@ -442,6 +443,7 @@ public class ConnectDialog extends MageDialog {
private void connected() {
this.saveSettings();
this.hideDialog();
ConstructedFormats.ensureLists();
}
private void keyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_keyTyped

View file

@ -235,6 +235,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_NEW_DECK_GENERATOR_SINGLETON = "newDeckGeneratorSingleton";
public static final String KEY_NEW_DECK_GENERATOR_ARTIFACTS = "newDeckGeneratorArtifacts";
public static final String KEY_NEW_DECK_GENERATOR_NON_BASIC_LANDS = "newDeckGeneratorNonBasicLands";
public static final String KEY_NEW_DECK_GENERATOR_COLORLESS = "newDeckGeneratorColorless";
// used to save and restore the settings for the cardArea (draft, sideboarding, deck builder)
public static final String KEY_DRAFT_VIEW = "draftView";

View file

@ -104,7 +104,6 @@ public class TableWaitingDialog extends MageDialog {
private void setGUISize() {
tableSeats.getTableHeader().setFont(GUISizeHelper.tableFont);
tableSeats.getTableHeader().setPreferredSize(new Dimension(GUISizeHelper.tableHeaderHeight, GUISizeHelper.tableHeaderHeight));
tableSeats.setFont(GUISizeHelper.tableFont);
tableSeats.setRowHeight(GUISizeHelper.getTableRowHeight());

View file

@ -226,7 +226,7 @@ public class BattlefieldPanel extends javax.swing.JLayeredPane {
}
public void sortLayout() {
if (battlefield == null) {
if (battlefield == null || this.getWidth() < 1) { // Can't do layout when panel is not sized yet
return;
}

View file

@ -608,9 +608,9 @@ public final class GamePanel extends javax.swing.JPanel {
}
}
PlayerView player = game.getPlayers().get(playerSeat);
PlayAreaPanel sessionPlayer = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this,
new PlayAreaPanelOptions(game.isPlayer(), true, game.isRollbackTurnsAllowed()));
players.put(player.getPlayerId(), sessionPlayer);
PlayAreaPanel playAreaPanel = new PlayAreaPanel(player, bigCard, gameId, game.getPriorityTime(), this,
new PlayAreaPanelOptions(game.isPlayer(), game.isPlayer(), game.isRollbackTurnsAllowed()));
players.put(player.getPlayerId(), playAreaPanel);
GridBagConstraints c = new GridBagConstraints();
c.fill = GridBagConstraints.BOTH;
c.weightx = 0.5;
@ -620,8 +620,8 @@ public final class GamePanel extends javax.swing.JPanel {
}
c.gridx = col;
c.gridy = row;
this.pnlBattlefield.add(sessionPlayer, c);
sessionPlayer.setVisible(true);
this.pnlBattlefield.add(playAreaPanel, c);
playAreaPanel.setVisible(true);
if (oddNumber) {
col++;
}

View file

@ -287,7 +287,7 @@ public class PlayerPanelExt extends javax.swing.JPanel {
avatar.setText(this.player.getName());
if (!player.getUserData().getFlagName().equals(flagName)) {
flagName = player.getUserData().getFlagName();
this.avatar.setTopTextImage(CountryUtil.getCountryFlagIcon(flagName).getImage());
this.avatar.setTopTextImage(CountryUtil.getCountryFlagIconSize(flagName, 11).getImage());
}
// TODO: Add the wins to the tooltiptext of the avatar
String countryname = CountryUtil.getCountryName(flagName);

View file

@ -189,27 +189,31 @@ public class MageActionCallback implements ActionCallback {
Point location = new Point((int) data.locationOnScreen.getX() + data.popupOffsetX - 40, (int) data.locationOnScreen.getY() + data.popupOffsetY - 40);
location = GuiDisplayUtil.keepComponentInsideParent(location, parentPoint, popup2, parentComponent);
location.translate(-parentPoint.x, -parentPoint.y);
popupContainer.setLocation(location);
ThreadUtils.sleep(200);
final Component c = MageFrame.getUI().getComponent(MageComponents.DESKTOP_PANE);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (!popupTextWindowOpen || !enlargedWindowState.equals(EnlargedWindowState.CLOSED)) {
return;
}
popupContainer.setVisible(true);
c.repaint();
}
}
);
showPopup(popupContainer, location);
} catch (InterruptedException e) {
LOGGER.warn(e.getMessage());
}
}
public void showPopup(final Component popupContainer, final Point location) throws InterruptedException {
final Component c = MageFrame.getUI().getComponent(MageComponents.DESKTOP_PANE);
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (!popupTextWindowOpen || !enlargedWindowState.equals(EnlargedWindowState.CLOSED)) {
return;
}
popupContainer.setLocation(location);
popupContainer.setVisible(true);
c.repaint();
}
}
);
}
});
}

View file

@ -123,7 +123,6 @@ public class PlayersChatPanel extends javax.swing.JPanel {
private void setGUISize() {
jTablePlayers.getTableHeader().setFont(GUISizeHelper.tableFont);
jTablePlayers.getTableHeader().setPreferredSize(new Dimension((int) jTablePlayers.getTableHeader().getPreferredSize().getWidth(), GUISizeHelper.tableHeaderHeight));
jTablePlayers.setFont(GUISizeHelper.tableFont);
jTablePlayers.setRowHeight(GUISizeHelper.getTableRowHeight());
jScrollPanePlayers.getVerticalScrollBar().setPreferredSize(new Dimension(GUISizeHelper.scrollBarSize, 0));

View file

@ -294,12 +294,10 @@ public class TablesPanel extends javax.swing.JPanel {
private void setGUISize() {
tableTables.getTableHeader().setFont(GUISizeHelper.tableFont);
tableTables.getTableHeader().setPreferredSize(new Dimension(GUISizeHelper.tableHeaderHeight, GUISizeHelper.tableHeaderHeight));
tableTables.setFont(GUISizeHelper.tableFont);
tableTables.setRowHeight(GUISizeHelper.getTableRowHeight());
tableCompleted.getTableHeader().setFont(GUISizeHelper.tableFont);
tableCompleted.getTableHeader().setPreferredSize(new Dimension(GUISizeHelper.tableHeaderHeight, GUISizeHelper.tableHeaderHeight));
tableCompleted.setFont(GUISizeHelper.tableFont);
tableCompleted.setRowHeight(GUISizeHelper.getTableRowHeight());

View file

@ -154,12 +154,10 @@ public class TournamentPanel extends javax.swing.JPanel {
private void setGUISize() {
tablePlayers.getTableHeader().setFont(GUISizeHelper.tableFont);
tablePlayers.getTableHeader().setPreferredSize(new Dimension(GUISizeHelper.tableHeaderHeight, GUISizeHelper.tableHeaderHeight));
tablePlayers.setFont(GUISizeHelper.tableFont);
tablePlayers.setRowHeight(GUISizeHelper.getTableRowHeight());
tableMatches.getTableHeader().setFont(GUISizeHelper.tableFont);
tableMatches.getTableHeader().setPreferredSize(new Dimension(GUISizeHelper.tableHeaderHeight, GUISizeHelper.tableHeaderHeight));
tableMatches.setFont(GUISizeHelper.tableFont);
tableMatches.setRowHeight(GUISizeHelper.getTableRowHeight());

View file

@ -111,7 +111,7 @@ public class ButtonColumn extends AbstractCellEditor implements TableCellRendere
@Override
public void actionPerformed(ActionEvent e) {
if (table.getRowCount() >= table.getEditingRow()) {
if (table.getRowCount() > 0 && table.getRowCount() >= table.getEditingRow()) {
int row = table.convertRowIndexToModel(table.getEditingRow());
fireEditingStopped();
ActionEvent event = new ActionEvent(table, ActionEvent.ACTION_PERFORMED, "" + row);

View file

@ -24,11 +24,9 @@
* 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.client.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@ -42,6 +40,7 @@ import org.apache.log4j.Logger;
*/
public class Config {
// TODO: Remove this class completely
private static final Logger logger = Logger.getLogger(Config.class);
public static final String remoteServer;
@ -65,33 +64,33 @@ public class Config {
} catch (IOException ex) {
logger.fatal("Config error ", ex);
}
serverName = p.getProperty("server-name");
port = Integer.parseInt(p.getProperty("port"));
remoteServer = p.getProperty("remote-server");
cardScalingFactor = Double.valueOf(p.getProperty("card-scaling-factor"));
cardScalingFactorEnlarged = Double.valueOf(p.getProperty("card-scaling-factor-enlarged"));
handScalingFactor = Double.valueOf(p.getProperty("hand-scaling-factor"));
defaultGameType = p.getProperty("default-game-type", "Human");
defaultDeckPath = p.getProperty("default-deck-path");
defaultOtherPlayerIndex = p.getProperty("default-other-player-index");
defaultComputerName = p.getProperty("default-computer-name");
dimensions = new CardDimensions(cardScalingFactor);
dimensionsEnlarged = new CardDimensions(cardScalingFactorEnlarged);
// activate instead this part, to run the UI editor for some panels without error
// serverName = "localhost";
// port = 17171;
// remoteServer = "mage-server";
// cardScalingFactor = Double.valueOf(0.4);
// cardScalingFactorEnlarged = Double.valueOf(0.5);
// handScalingFactor = Double.valueOf(1.3);
// serverName = p.getProperty("server-name");
// port = Integer.parseInt(p.getProperty("port"));
// remoteServer = p.getProperty("remote-server");
// cardScalingFactor = Double.valueOf(p.getProperty("card-scaling-factor"));
// cardScalingFactorEnlarged = Double.valueOf(p.getProperty("card-scaling-factor-enlarged"));
// handScalingFactor = Double.valueOf(p.getProperty("hand-scaling-factor"));
// defaultGameType = p.getProperty("default-game-type", "Human");
// defaultDeckPath = "";
// defaultOtherPlayerIndex = "1";
// defaultComputerName = "Computer";
// defaultDeckPath = p.getProperty("default-deck-path");
// defaultOtherPlayerIndex = p.getProperty("default-other-player-index");
// defaultComputerName = p.getProperty("default-computer-name");
//
// dimensions = new CardDimensions(cardScalingFactor);
// dimensionsEnlarged = new CardDimensions(cardScalingFactorEnlarged);
// activate instead this part, to run the UI editor for some panels without error
serverName = "localhost";
port = 17171;
remoteServer = "mage-server";
cardScalingFactor = 0.4;
cardScalingFactorEnlarged = 0.5;
handScalingFactor = 1.3;
defaultGameType = p.getProperty("default-game-type", "Human");
defaultDeckPath = "";
defaultOtherPlayerIndex = "1";
defaultComputerName = "Computer";
dimensions = new CardDimensions(cardScalingFactor);
dimensionsEnlarged = new CardDimensions(cardScalingFactorEnlarged);
}

View file

@ -47,8 +47,8 @@ public class EDTExceptionHandler implements Thread.UncaughtExceptionHandler {
public void handle(Throwable throwable) {
try {
logger.fatal(null, throwable);
JOptionPane.showMessageDialog(MageFrame.getDesktop(), throwable, "MAGE Client UI error", JOptionPane.ERROR_MESSAGE);
logger.fatal("MAGE Client UI error", throwable);
// JOptionPane.showMessageDialog(MageFrame.getDesktop(), throwable, "MAGE Client UI error", JOptionPane.ERROR_MESSAGE);
} catch (Throwable t) {}
}

View file

@ -27,7 +27,6 @@ public class CountryUtil {
public static ImageIcon getCountryFlagIcon(String countryCode) {
ImageIcon flagIcon = FLAG_ICON_CACHE.get(countryCode);
if (flagIcon == null) {
// URL url = CountryUtil.class.getResource("/flags/" + countryCode + (countryCode.endsWith(".png") ? "" : ".png"));
Image flagImage = ImageHelper.getImageFromResources("/flags/" + countryCode + (countryCode.endsWith(".png") ? "" : ".png"));
if (flagImage != null) {
if (GUISizeHelper.flagHeight > 11) {
@ -48,6 +47,22 @@ public class CountryUtil {
return flagIcon;
}
public static ImageIcon getCountryFlagIconSize(String countryCode, int height) {
ImageIcon flagIcon = null;
Image flagImage = ImageHelper.getImageFromResources("/flags/" + countryCode + (countryCode.endsWith(".png") ? "" : ".png"));
if (flagImage != null) {
if (height > 11) {
int width = Math.round(height * flagImage.getWidth(null) / flagImage.getHeight(null));
BufferedImage resized = ImageHelper.scale((BufferedImage) flagImage, BufferedImage.TYPE_4BYTE_ABGR, width, height);
flagIcon = new ImageIcon(resized);
} else {
flagIcon = new ImageIcon(flagImage);
}
}
return flagIcon;
}
public static void changeGUISize() {
FLAG_ICON_CACHE.clear();
}

View file

@ -50,6 +50,12 @@ public class ConstructedFormats {
}
public static void ensureLists() {
if (getSetsByFormat(ConstructedFormats.STANDARD) == null) {
buildLists();
}
}
private static void buildLists() {
GregorianCalendar cutoff;
// month is zero based so January = 0
@ -60,6 +66,7 @@ public class ConstructedFormats {
cutoff = new GregorianCalendar(calendar.get(Calendar.YEAR) - 2, Calendar.SEPTEMBER, 1);
}
final Map<String, ExpansionInfo> expansionInfo = new HashMap<>();
formats.clear(); // prevent NPE on sorting if this is not the first try
for (ExpansionInfo set : ExpansionRepository.instance.getAll()) {
expansionInfo.put(set.getName(), set);
formats.add(set.getName());
@ -207,10 +214,11 @@ public class ConstructedFormats {
}
});
formats.add(0, MODERN);
formats.add(0, EXTENDED);
formats.add(0, STANDARD);
if (!formats.isEmpty()) {
formats.add(0, MODERN);
formats.add(0, EXTENDED);
formats.add(0, STANDARD);
}
formats.add(0, ALL);
}

View file

@ -6,6 +6,16 @@ import java.awt.Image;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.FileSystems;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.PathMatcher;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -36,6 +46,7 @@ public class ManaSymbols {
"WP", "UP", "BP", "RP", "GP", "X", "C"};
public static void loadImages() {
renameSymbols(getSymbolsPath() + File.separator + "symbols");
smallSymbolsFound = loadSymbolsImages(15);
mediumSymbolsFound = loadSymbolsImages(25);
@ -120,14 +131,14 @@ public class ManaSymbols {
private static boolean loadSymbolsImages(int size) {
boolean fileErrors = false;
HashMap<String, BufferedImage> sizedSymbols = new HashMap<>();
String resourcePath = Constants.RESOURCE_PATH_MANA_SMALL;
if (size > 25) {
resourcePath = Constants.RESOURCE_PATH_MANA_LARGE;
} else if (size > 15) {
resourcePath = Constants.RESOURCE_PATH_MANA_MEDIUM;
}
for (String symbol : symbols) {
String resourcePath = Constants.RESOURCE_PATH_MANA_SMALL;
if (size > 25) {
resourcePath = Constants.RESOURCE_PATH_MANA_LARGE;
} else if (size > 15) {
resourcePath = Constants.RESOURCE_PATH_MANA_MEDIUM;
}
File file = new File(getSymbolsPath() + resourcePath + "/" + symbol + ".jpg");
File file = new File(getSymbolsPath() + resourcePath + "/" + symbol + ".gif");
try {
if (size == 15 || size == 25) {
@ -148,6 +159,24 @@ public class ManaSymbols {
return !fileErrors;
}
private static void renameSymbols(String path) {
final PathMatcher matcher = FileSystems.getDefault().getPathMatcher("glob:**/*.jpg");
try {
Files.walkFileTree(Paths.get(path), new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
if (matcher.matches(file)) {
Path gifPath = file.resolveSibling(file.getFileName().toString().replaceAll("\\.jpg$", ".gif"));
Files.move(file, gifPath, StandardCopyOption.REPLACE_EXISTING);
}
return FileVisitResult.CONTINUE;
}
});
} catch (IOException e) {
LOGGER.error("Couldn't rename mana symbols!");
}
}
private static String getSymbolsPath() {
return getSymbolsPath(false);
}
@ -247,9 +276,9 @@ public class ManaSymbols {
symbolFilesFound = mediumSymbolsFound;
}
if (symbolFilesFound) {
replaced = REPLACE_SYMBOLS_PATTERN.matcher(value).replaceAll("<img src='file:" + getSymbolsPath(true)
+ "/symbols/" + resourcePath + "/$1$2.jpg' alt='$1$2' width="
+ symbolSize + " height=" + symbolSize + ">");
replaced = REPLACE_SYMBOLS_PATTERN.matcher(value).replaceAll(
"<img src='file:" + getSymbolsPath(true) + "/symbols/" + resourcePath + "/$1$2.gif' alt='$1$2' width=" + symbolSize
+ " height=" + symbolSize + ">");
}
replaced = replaced.replace("|source|", "{source}");
replaced = replaced.replace("|this|", "{this}");

View file

@ -31,6 +31,7 @@ import net.xeoh.plugins.base.annotations.meta.Author;
import org.apache.log4j.Logger;
import org.mage.card.arcane.Animation;
import org.mage.card.arcane.CardPanel;
import org.mage.card.arcane.ManaSymbols;
import org.mage.plugins.card.dl.DownloadGui;
import org.mage.plugins.card.dl.DownloadJob;
import org.mage.plugins.card.dl.Downloader;
@ -540,6 +541,7 @@ public class CardPluginImpl implements CardPlugin {
@Override
public void windowClosing(WindowEvent e) {
g.getDownloader().dispose();
ManaSymbols.loadImages();
}
});
d.setLayout(new BorderLayout());

View file

@ -71,7 +71,7 @@ public class GathererSymbols implements Iterable<DownloadJob> {
return computeNext();
}
String symbol = sym.replaceAll("/", "");
File dst = new File(dir, symbol + ".jpg");
File dst = new File(dir, symbol + ".gif");
switch (symbol) {
case "T":

View file

@ -38,6 +38,9 @@ import java.net.URL;
import java.net.URLEncoder;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.prefs.Preferences;
import mage.client.MageFrame;
import mage.client.dialog.PreferencesDialog;
@ -246,11 +249,11 @@ public class WizardCardsImageSource implements CardImageSource {
languageAliases.put("fr", "French");
languageAliases.put("cn", "Chinese Simplified");
languageAliases.put("de", "German");
}
private Map<String, String> getSetLinks(String cardSet) {
Map<String, String> setLinks = new HashMap<>();
ConcurrentHashMap<String, String> setLinks = new ConcurrentHashMap<>();
ExecutorService executor = Executors.newFixedThreadPool(10);
try {
String setNames = setsAliases.get(cardSet);
String preferedLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en");
@ -262,12 +265,10 @@ public class WizardCardsImageSource implements CardImageSource {
while (page < 999) {
String searchUrl = "http://gatherer.wizards.com/Pages/Search/Default.aspx?page=" + page +"&output=spoiler&method=visual&action=advanced&set=+[%22" + URLSetName + "%22]";
Document doc = getDocument(searchUrl);
Elements cardsImages = doc.select("img[src^=../../Handlers/]");
if (cardsImages.isEmpty()) {
break;
}
for (int i = 0; i < cardsImages.size(); i++) {
Integer multiverseId = Integer.parseInt(cardsImages.get(i).attr("src").replaceAll("[^\\d]", ""));
if (i == 0) {
@ -278,12 +279,8 @@ public class WizardCardsImageSource implements CardImageSource {
}
String cardName = normalizeName(cardsImages.get(i).attr("alt"));
if (cardName != null && !cardName.isEmpty()) {
if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) {
setLinks.putAll(getLandVariations(multiverseId, cardName));
} else {
Integer preferedMultiverseId = getLocalizedMultiverseId(preferedLanguage, multiverseId);
setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId));
}
Runnable task = new GetImageLinkTask(multiverseId, cardName, preferedLanguage, setLinks);
executor.execute(task);
}
}
page++;
@ -292,6 +289,16 @@ public class WizardCardsImageSource implements CardImageSource {
} catch (IOException ex) {
System.out.println("Exception when parsing the wizards page: " + ex.getMessage());
}
executor.shutdown();
while (!executor.isTerminated()) {
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
}
}
return setLinks;
}
@ -440,4 +447,35 @@ public class WizardCardsImageSource implements CardImageSource {
public Float getAverageSize() {
return 60.0f;
}
private final class GetImageLinkTask implements Runnable {
private final Integer multiverseId;
private final String cardName;
private final String preferedLanguage;
private final ConcurrentHashMap setLinks;
public GetImageLinkTask(Integer multiverseId, String cardName, String preferedLanguage, ConcurrentHashMap setLinks) {
this.multiverseId = multiverseId;
this.cardName = cardName;
this.preferedLanguage = preferedLanguage;
this.setLinks = setLinks;
}
@Override
public void run() {
try {
if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) {
setLinks.putAll(getLandVariations(multiverseId, cardName));
} else {
Integer preferedMultiverseId = getLocalizedMultiverseId(preferedLanguage, multiverseId);
setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId));
}
} catch (IOException | NumberFormatException ex) {
System.out.println("Exception when parsing the wizards page: " + ex.getMessage());
}
}
}
}

View file

@ -312,14 +312,12 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
url.setFlippedSide(true);
allCardsUrls.add(url);
}
} else {
if (card.getCardNumber() < 1) {
System.err.println("There was a critical error!");
logger.error("Card has no collector ID and won't be sent to client: " + card);
} else if (card.getSetCode().isEmpty()) {
System.err.println("There was a critical error!");
logger.error("Card has no set name and won't be sent to client:" + card);
}
} else if (card.getCardNumber() < 1) {
System.err.println("There was a critical error!");
logger.error("Card has no collector ID and won't be sent to client: " + card);
} else if (card.getSetCode().isEmpty()) {
System.err.println("There was a critical error!");
logger.error("Card has no set name and won't be sent to client:" + card);
}
}
allCardsUrls.addAll(getTokenCardUrls());

View file

@ -59,33 +59,28 @@ public class CardInfoPaneImpl extends JEditorPane implements CardInfoPane {
}
currentCard = card;
ThreadUtils.threadPool.submit(new Runnable() {
@Override
public void run() {
try {
try {
if (!card.equals(currentCard)) {
return;
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (!card.equals(currentCard)) {
return;
}
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
if (!card.equals(currentCard)) {
return;
}
TextLines textLines = GuiDisplayUtil.getTextLinesfromCardView(card);
StringBuilder buffer = GuiDisplayUtil.getRulefromCardView(card, textLines);
resizeTooltipIfNeeded(container, textLines.basicTextLength, textLines.lines.size());
setText(buffer.toString());
setCaretPosition(0);
}
});
} catch (Exception e) {
e.printStackTrace();
TextLines textLines = GuiDisplayUtil.getTextLinesfromCardView(card);
StringBuilder buffer = GuiDisplayUtil.getRulefromCardView(card, textLines);
resizeTooltipIfNeeded(container, textLines.basicTextLength, textLines.lines.size());
setText(buffer.toString());
setCaretPosition(0);
}
}
});
});
} catch (Exception e) {
e.printStackTrace();
}
}
private void resizeTooltipIfNeeded(Component container, int ruleLength, int rules) {

View file

@ -41,7 +41,7 @@ public class MageVersion implements Serializable, Comparable<MageVersion> {
public final static int MAGE_VERSION_MAJOR = 1;
public final static int MAGE_VERSION_MINOR = 4;
public final static int MAGE_VERSION_PATCH = 9;
public final static String MAGE_VERSION_MINOR_PATCH = "v0";
public final static String MAGE_VERSION_MINOR_PATCH = "v1";
public final static String MAGE_VERSION_INFO = "";
private final int major;

View file

@ -52,7 +52,9 @@ public class RateCard {
*/
public static int rateCard(Card card, List<ColoredManaSymbol> allowedColors) {
if (allowedColors == null && rated.containsKey(card.getName())) {
return rated.get(card.getName());
int rate = rated.get(card.getName());
// log.info(card.getName() + " rate: " + rate);
return rate;
}
int type;
if (card.getCardType().contains(CardType.PLANESWALKER)) {
@ -136,7 +138,7 @@ public class RateCard {
*/
private synchronized static void readRatings() {
if (ratings == null) {
ratings = new HashMap<String, Integer>();
ratings = new HashMap<>();
readFromFile("/m13.csv");
}
}

View file

@ -1,16 +1,45 @@
#default levels
log4j.rootLogger=debug, console, logfile
#log4j.rootLogger=info, console, logfile
log4j.rootLogger=info, RollingAppender
log4j.logger.com.j256.ormlite=warn
log4j.logger.mage.player.ai=warn
#log4j.logger.mage.player.ai.ComputerPlayer6=debug
log4j.logger.mage.game=debug
log4j.logger.mage.game.GameImpl=debug
log4j.logger.mage.players.PlayerImpl=debug
log4j.logger.mage.server=debug
#log4j.logger.mage.server.UserManager=debug
#log4j.logger.mage.server.User=debug
#log4j.logger.mage.server.ChatSession=debug
#log4j.logger.mage.server.ChatManager=debug
#log4j.logger.mage.server.TableController=debug
#log4j.logger.mage.server.TableManager=debug
#log4j.logger.mage.server.tournament.TournamentManager=debug
#log4j.logger.mage.server.game.GameSession=debug
log4j.logger.mage.abilities.AbilityImpl=debug
log4j.logger.mage.cards.decks=debug
log4j.logger.mage.abilities.effects.common.continious.CommanderManaReplacementEffect=debug
#console log
log4j.appender.console=org.apache.log4j.ConsoleAppender
log4j.appender.console.layout=org.apache.log4j.PatternLayout
log4j.appender.console.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M%n
log4j.appender.console.Threshold=info
#log4j.appender.console=org.apache.log4j.ConsoleAppender
#log4j.appender.console.layout=org.apache.log4j.PatternLayout
#log4j.appender.console.layout.ConversionPattern=%-5p [%d{yyyy-MM-dd HH:mm [ss:SSS]}] %C{1}[%t]: %m%
#log4j.appender.console.Threshold=info
#file log
log4j.appender.logfile=org.apache.log4j.FileAppender
log4j.appender.logfile.File=mageserver.log
log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
log4j.appender.logfile.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M%n
#log4j.appender.logfile=org.apache.log4j.FileAppender
#log4j.appender.logfile.File=mageserver.log
#log4j.appender.logfile.layout=org.apache.log4j.PatternLayout
#log4j.appender.logfile.layout.ConversionPattern=%-5p [%d{yyyy-MM-dd HH:mm [ss:SSS]}] %C{1}[%t]: %m%n
log4j.appender.RollingAppender=org.apache.log4j.DailyRollingFileAppender
log4j.appender.RollingAppender.File=mageserver.log
log4j.appender.RollingAppender.DatePattern='.'yyyy-MM-dd
log4j.appender.RollingAppender.layout=org.apache.log4j.PatternLayout
log4j.appender.RollingAppender.layout.ConversionPattern=[%p] %d %c %M - %m%n
log4j.appender.RollingAppender.layout.ConversionPattern=%-5p %d{yyyy-MM-dd HH:mm:ss,SSS} %-90m =>[%t] %C{1}.%M%n
#diagnostic log for game core classes
#log4j.category.mage.server.game = INFO, diagfile
#log4j.additivity.mage.server.game = false
#log4j.appender.diagfile=org.apache.log4j.FileAppender
#log4j.appender.diagfile.File=magediag.log
#log4j.appender.diagfile.layout=org.apache.log4j.PatternLayout
#log4j.appender.diagfile.layout.ConversionPattern=%-5p [%d{yyyy-MM-dd HH:mm [ss:SSS]}] %C{1}[%t]: %m%n

View file

@ -4,6 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.1
http://maven.apache.org/xsd/assembly-1.1.1.xsd">
<includeBaseDirectory>false</includeBaseDirectory>
<id>mage-server</id>
<formats>
<format>zip</format>
</formats>

View file

@ -97,11 +97,7 @@ class AliFromCairoReplacementEffect extends ReplacementEffectImpl {
&& (controller.getLife() > 0) &&(controller.getLife() - event.getAmount()) < 1
&& event.getPlayerId().equals(controller.getId())
) {
return true;
//unsure how to make this comply with
// 10/1/2008: The ability doesn't change how much damage is dealt;
// it just changes how much life that damage makes you lose.
// An effect such as Spirit Link will see the full amount of damage being dealt.
return true;
}
}
return false;
@ -110,10 +106,17 @@ class AliFromCairoReplacementEffect extends ReplacementEffectImpl {
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
// 10/1/2008: The ability doesn't change how much damage is dealt;
// it just changes how much life that damage makes you lose.
// An effect such as Spirit Link will see the full amount of damage being dealt.
game.fireEvent(event);
if (controller != null) {
event.setAmount(controller.getLife() - 1);
controller.setLife(1, game);
}
return false;
return true;
}
}

View file

@ -77,7 +77,7 @@ class SecondSpellPredicate implements Predicate<Spell> {
@Override
public boolean apply(Spell input, Game game) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
if (watcher.getSpellOrder(new MageObjectReference(input.getId(), game), game) == 2) {
return true;

View file

@ -0,0 +1,125 @@
/*
* 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.sets.blessedvscursed;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnToBattlefieldUnderOwnerControlTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetControlledCreaturePermanent;
import mage.target.targetpointer.FixedTargets;
import mage.util.CardUtil;
/**
*
* @author LevelX2
*/
public class EerieInterlude extends CardImpl {
public EerieInterlude(UUID ownerId) {
super(ownerId, 8, "Eerie Interlude", Rarity.RARE, new CardType[]{CardType.INSTANT}, "{2}{W}");
this.expansionSetCode = "DDQ";
// Exile any number of target creatures you control. Return those cards to the battlefield under their owner's control at the beginning of the next end step.
this.getSpellAbility().addEffect(new EerieInterludeEffect());
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(0, Integer.MAX_VALUE, new FilterControlledCreaturePermanent(), false));
}
public EerieInterlude(final EerieInterlude card) {
super(card);
}
@Override
public EerieInterlude copy() {
return new EerieInterlude(this);
}
}
class EerieInterludeEffect extends OneShotEffect {
public EerieInterludeEffect() {
super(Outcome.Neutral);
staticText = "Exile any number of target creatures you control. Return those cards to the battlefield under their owner's control at the beginning of the next end step";
}
public EerieInterludeEffect(final EerieInterludeEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (sourceObject != null && controller != null) {
Set<Card> toExile = new HashSet<>();
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
Permanent targetCreature = game.getPermanent(targetId);
if (targetCreature != null) {
toExile.add(targetCreature);
}
}
UUID exileId = CardUtil.getExileZoneId(game, source.getSourceId(), source.getSourceObjectZoneChangeCounter());
controller.moveCardsToExile(toExile, source, game, true, exileId, sourceObject.getIdName());
Cards cardsToReturn = new CardsImpl();
for (Card exiled : toExile) {
if (((Permanent) exiled).getZoneChangeCounter(game) == game.getState().getZoneChangeCounter(exiled.getId()) - 1) {
cardsToReturn.add(exiled);
}
}
Effect effect = new ReturnToBattlefieldUnderOwnerControlTargetEffect();
effect.setTargetPointer(new FixedTargets(cardsToReturn, game));
AtTheBeginOfNextEndStepDelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect);
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}
return false;
}
@Override
public EerieInterludeEffect copy() {
return new EerieInterludeEffect(this);
}
}

View file

@ -35,7 +35,7 @@ import mage.MageInt;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.condition.common.SourceHasCounterCondition;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.decorator.ConditionalContinuousEffect;
@ -74,7 +74,7 @@ public class MyojinOfCleansingFire extends CardImpl {
this.getSpellAbility().addWatcher(new CastFromHandWatcher());
// Myojin of Cleansing Fire enters the battlefield with a divinity counter on it if you cast it from your hand.
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandSourceCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
// Myojin of Cleansing Fire is indestructible as long as it has a divinity counter on it.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it")));

View file

@ -35,7 +35,7 @@ import mage.MageInt;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.condition.common.SourceHasCounterCondition;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.decorator.ConditionalContinuousEffect;
@ -70,7 +70,7 @@ public class MyojinOfInfiniteRage extends CardImpl {
this.getSpellAbility().addWatcher(new CastFromHandWatcher());
// Myojin of Infinite Rage enters the battlefield with a divinity counter on it if you cast it from your hand.
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandSourceCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
// Myojin of Infinite Rage is indestructible as long as it has a divinity counter on it.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it")));

View file

@ -35,7 +35,7 @@ import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.condition.common.SourceHasCounterCondition;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.decorator.ConditionalContinuousEffect;
@ -75,7 +75,7 @@ public class MyojinOfLifesWeb extends CardImpl {
this.getSpellAbility().addWatcher(new CastFromHandWatcher());
// Myojin of Life's Web enters the battlefield with a divinity counter on it if you cast it from your hand.
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandSourceCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
// Myojin of Life's Web is indestructible as long as it has a divinity counter on it.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),

View file

@ -35,7 +35,7 @@ import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.condition.common.SourceHasCounterCondition;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.decorator.ConditionalContinuousEffect;
@ -68,7 +68,7 @@ public class MyojinOfNightsReach extends CardImpl {
this.getSpellAbility().addWatcher(new CastFromHandWatcher());
// Myojin of Night's Reach enters the battlefield with a divinity counter on it if you cast it from your hand.
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandSourceCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
// Myojin of Night's Reach is indestructible as long as it has a divinity counter on it.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it")));

View file

@ -35,7 +35,7 @@ import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.condition.common.SourceHasCounterCondition;
import mage.abilities.costs.common.RemoveCountersSourceCost;
import mage.abilities.decorator.ConditionalContinuousEffect;
@ -73,7 +73,7 @@ public class MyojinOfSeeingWinds extends CardImpl {
this.getSpellAbility().addWatcher(new CastFromHandWatcher());
// Myojin of Seeing Winds enters the battlefield with a divinity counter on it if you cast it from your hand.
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
this.addAbility(new EntersBattlefieldAbility(new ConditionalOneShotEffect(new AddCountersSourceEffect(CounterType.DIVINITY.createInstance()), new CastFromHandSourceCondition(), ""), "{this} enters the battlefield with a divinity counter on it if you cast it from your hand"));
// Myojin of Seeing Winds is indestructible as long as it has a divinity counter on it.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ConditionalContinuousEffect(new GainAbilitySourceEffect(IndestructibleAbility.getInstance(), Duration.WhileOnBattlefield),
new SourceHasCounterCondition(CounterType.DIVINITY), "{this} is indestructible as long as it has a divinity counter on it")));

View file

@ -0,0 +1,86 @@
/*
* 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.sets.commander;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.TriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.DestroyAllEffect;
import mage.abilities.effects.common.TapAllEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.watchers.common.CastFromHandWatcher;
/**
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
*/
public class DreadCacodemon extends CardImpl {
private static final FilterCreaturePermanent opponentsCreatures = new FilterCreaturePermanent("creatures your opponents control");
static {
opponentsCreatures.add(new ControllerPredicate(TargetController.OPPONENT));
}
private static final FilterCreaturePermanent otherCreaturesYouControl = new FilterCreaturePermanent("other creatures you control");
static {
otherCreaturesYouControl.add(new ControllerPredicate(TargetController.YOU));
otherCreaturesYouControl.add(new AnotherPredicate());
}
public DreadCacodemon(UUID ownerId) {
super(ownerId, 79, "Dread Cacodemon", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{7}{B}{B}{B}");
this.expansionSetCode = "CMD";
this.subtype.add("Demon");
this.power = new MageInt(8);
this.toughness = new MageInt(8);
// When Dread Cacodemon enters the battlefield,
// if you cast it from your hand, destroy all creatures your opponents control, then tap all other creatures you control.
TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(opponentsCreatures, false));
ability.addEffect(new TapAllEffect(otherCreaturesYouControl));
this.addAbility(new ConditionalTriggeredAbility(ability, new CastFromHandSourceCondition(),
"When {this} enters the battlefield, if you cast it from your hand, destroy all creatures your opponents control, then tap all other creatures you control."), new CastFromHandWatcher());
}
public DreadCacodemon(final DreadCacodemon card) {
super(card);
}
@Override
public DreadCacodemon copy() {
return new DreadCacodemon(this);
}
}

View file

@ -53,7 +53,6 @@ public class AEtherSnap extends CardImpl {
super(ownerId, 133, "AEther Snap", Rarity.RARE, new CardType[]{CardType.SORCERY}, "{3}{B}{B}");
this.expansionSetCode = "C14";
// Remove all counters from all permanents and exile all tokens.
this.getSpellAbility().addEffect(new AEtherSnapEffect());
}
@ -88,13 +87,13 @@ class AEtherSnapEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
for (Permanent permanent :game.getBattlefield().getActivePermanents(new FilterPermanent(), controller.getId(), source.getSourceId(), game)) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(new FilterPermanent(), controller.getId(), source.getSourceId(), game)) {
if (permanent instanceof PermanentToken) {
controller.moveCardToExileWithInfo(permanent, null, "", source.getSourceId(), game, Zone.BATTLEFIELD, true);
} else if (!permanent.getCounters().isEmpty()){
} else if (!permanent.getCounters().isEmpty()) {
Counters counters = permanent.getCounters().copy();
for (Counter counter: counters.values()) {
permanent.getCounters().removeCounter(counter.getName(), counter.getCount());
for (Counter counter : counters.values()) {
permanent.removeCounters(counter, game);
}
}
}

View file

@ -29,10 +29,9 @@ package mage.sets.commander2014;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.ExileAllEffect;
import mage.abilities.keyword.FlashAbility;
import mage.abilities.keyword.FlyingAbility;
@ -61,10 +60,11 @@ public class AngelOfTheDireHour extends CardImpl {
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Angel of the Dire Hour enters the battlefield, if you cast it from your hand, exile all attacking creatures.
Ability ability = new EntersBattlefieldTriggeredAbility(
new ConditionalOneShotEffect(new ExileAllEffect(new FilterAttackingCreature("attacking creatures")), new CastFromHandCondition(),
" if you cast it from your hand, exile all attacking creatures"));
this.addAbility(ability, new CastFromHandWatcher());
this.addAbility(new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new ExileAllEffect(new FilterAttackingCreature("attacking creatures")), false),
new CastFromHandSourceCondition(),
"When {this} enters the battlefield, if you cast it from your hand, exile all attacking creatures."),
new CastFromHandWatcher());
}
public AngelOfTheDireHour(final AngelOfTheDireHour card) {

View file

@ -32,8 +32,8 @@ import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
@ -64,10 +64,11 @@ public class BreachingLeviathan extends CardImpl {
this.toughness = new MageInt(9);
// When Breaching Leviathan enters the battlefield, if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps.
Ability ability = new EntersBattlefieldTriggeredAbility(
new ConditionalOneShotEffect(new BreachingLeviathanEffect(), new CastFromHandCondition(),
"if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps"));
this.addAbility(ability, new CastFromHandWatcher());
this.addAbility(new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new BreachingLeviathanEffect(), false),
new CastFromHandSourceCondition(),
"When {this} enters the battlefield, if you cast it from your hand, tap all nonblue creatures. Those creatures don't untap during their controllers' next untap steps."),
new CastFromHandWatcher());
}
public BreachingLeviathan(final BreachingLeviathan card) {
@ -104,7 +105,7 @@ class BreachingLeviathanEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
for (Permanent creature: game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
for (Permanent creature : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
creature.tap(game);
ContinuousEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(creature.getId()));

View file

@ -57,10 +57,10 @@ public class ThiefOfBlood extends CardImpl {
this.power = new MageInt(1);
this.toughness = new MageInt(1);
this.subtype.add("Vampire");
// Flying
this.addAbility(FlyingAbility.getInstance());
// As Thief of Blood enters the battlefield, remove all counters from all permanents. Thief of Blood enters the battlefield with a +1/+1 counter on it for each counter removed this way.
this.addAbility(new EntersBattlefieldAbility(new ThiefOfBloodEffect(), null, "As {this} enters the battlefield, remove all counters from all permanents. {this} enters the battlefield with a +1/+1 counter on it for each counter removed this way", null));
}
@ -76,33 +76,34 @@ public class ThiefOfBlood extends CardImpl {
}
class ThiefOfBloodEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent("permanent with a counter");
static {
filter.add(new CounterPredicate(null));
}
ThiefOfBloodEffect() {
super(Outcome.BoostCreature);
this.staticText = "remove all counters from all permanents. {this} enters the battlefield with a +1/+1 counter on it for each counter removed this way";
}
ThiefOfBloodEffect(final ThiefOfBloodEffect effect) {
super(effect);
}
@Override
public ThiefOfBloodEffect copy() {
return new ThiefOfBloodEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
int countersRemoved = 0;
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), game)) {
Counters counters = permanent.getCounters().copy();
for (Counter counter : counters.values()) {
permanent.getCounters().removeCounter(counter.getName(), counter.getCount());
permanent.removeCounters(counter.getName(), counter.getCount(), game);
countersRemoved += counter.getCount();
}
}

View file

@ -104,7 +104,7 @@ class CurseOfExhaustionEffect extends ContinuousRuleModifyingEffectImpl {
if (enchantment != null && enchantment.getAttachedTo() != null) {
Player player = game.getPlayer(enchantment.getAttachedTo());
if (player != null && event.getPlayerId().equals(player.getId())) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) > 0) {
return true;
}

View file

@ -29,10 +29,9 @@ package mage.sets.darksteel;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.ExileAllEffect;
import mage.abilities.keyword.AffinityForArtifactsAbility;
import mage.abilities.keyword.FlyingAbility;
@ -41,9 +40,6 @@ import mage.constants.CardType;
import mage.constants.Rarity;
import mage.filter.FilterPermanent;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.watchers.Watcher;
import mage.watchers.common.CastFromHandWatcher;
/**
@ -51,7 +47,7 @@ import mage.watchers.common.CastFromHandWatcher;
* @author fireshoes
*/
public class FurnaceDragon extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("artifacts");
static {
@ -67,12 +63,16 @@ public class FurnaceDragon extends CardImpl {
// Affinity for artifacts
this.addAbility(new AffinityForArtifactsAbility());
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Furnace Dragon enters the battlefield, if you cast it from your hand, exile all artifacts.
this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(new ExileAllEffect(filter), new FurnaceDragonCondition()), false), new CastFromHandWatcher());
this.addAbility(new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new ExileAllEffect(filter), false),
new CastFromHandSourceCondition(),
"When {this} enters the battlefield, if you cast it from your hand, exile all artifacts."),
new CastFromHandWatcher());
}
public FurnaceDragon(final FurnaceDragon card) {
@ -84,24 +84,3 @@ public class FurnaceDragon extends CardImpl {
return new FurnaceDragon(this);
}
}
class FurnaceDragonCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
boolean applies = false;
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) {
Watcher watcher = game.getState().getWatchers().get("CastFromHand", source.getSourceId());
if (watcher != null && watcher.conditionMet()) {
applies = true;
}
}
return applies;
}
@Override
public String toString() {
return "you cast it from your hand";
}
}

View file

@ -0,0 +1,60 @@
/*
* 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.sets.darksteel;
import java.util.UUID;
import mage.abilities.effects.common.TapAllEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.filter.common.FilterArtifactPermanent;
/**
*
* @author djbrez
*/
public class MetalFatigue extends CardImpl {
public MetalFatigue(UUID ownerId) {
super(ownerId, 8, "Metal Fatigue", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{2}{W}");
this.expansionSetCode = "DST";
// Tap all artifacts.
this.getSpellAbility().addEffect(new TapAllEffect(new FilterArtifactPermanent("artifacts")));
}
public MetalFatigue(final MetalFatigue card) {
super(card);
}
@Override
public MetalFatigue copy() {
return new MetalFatigue(this);
}
}

View file

@ -30,10 +30,9 @@ package mage.sets.divinevsdemonic;
import java.util.UUID;
import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.DestroyAllEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
@ -50,9 +49,9 @@ import mage.watchers.common.CastFromHandWatcher;
* @author daagar
*/
public class ReiverDemon extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("nonartifact, nonblack creatures");
static {
filter.add(Predicates.not(new CardTypePredicate(CardType.ARTIFACT)));
filter.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK)));
@ -67,12 +66,13 @@ public class ReiverDemon extends CardImpl {
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Reiver Demon enters the battlefield, if you cast it from your hand, destroy all nonartifact, nonblack creatures. They can't be regenerated.
Ability ability = new EntersBattlefieldTriggeredAbility(
new ConditionalOneShotEffect(new DestroyAllEffect(filter), new CastFromHandCondition(),
"if you cast it from your hand, destroy all nonartifact, nonblack creatures. They can't be regenerated"));
this.addAbility(ability, new CastFromHandWatcher());
this.addAbility(new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter, true), false),
new CastFromHandSourceCondition(),
"When {this} enters the battlefield, if you cast it from your hand, destroy all nonartifact, nonblack creatures. They can't be regenerated."),
new CastFromHandWatcher());
}
public ReiverDemon(final ReiverDemon card) {

View file

@ -25,15 +25,14 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.sets.dragonsmaze;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.PopulateEffect;
import mage.cards.CardImpl;
@ -46,11 +45,9 @@ import mage.watchers.common.CastFromHandWatcher;
*
* @author LevelX2
*/
public class ScionOfVituGhazi extends CardImpl {
public ScionOfVituGhazi (UUID ownerId) {
public ScionOfVituGhazi(UUID ownerId) {
super(ownerId, 7, "Scion of Vitu-Ghazi", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{W}{W}");
this.expansionSetCode = "DGM";
this.subtype.add("Elemental");
@ -58,15 +55,14 @@ public class ScionOfVituGhazi extends CardImpl {
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// When Scion of Vitu-Ghazi enters the battlefield, if you cast it from your hand, put a 1/1 white Bird creature token with flying onto the battlefield, then populate.
Ability ability = new EntersBattlefieldTriggeredAbility(
new ConditionalOneShotEffect(new CreateTokenEffect(new BirdToken()), new CastFromHandCondition(),
"if you cast it from your hand, put a 1/1 white Bird creature token with flying onto the battlefield,"));
TriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new CreateTokenEffect(new BirdToken()), false);
ability.addEffect(new PopulateEffect("then"));
this.addAbility(ability, new CastFromHandWatcher());
this.addAbility(new ConditionalTriggeredAbility(ability, new CastFromHandSourceCondition(),
"When {this} enters the battlefield, if you cast it from your hand, put a 1/1 white Bird creature token with flying onto the battlefield, then populate."),
new CastFromHandWatcher());
}
public ScionOfVituGhazi (final ScionOfVituGhazi card) {
public ScionOfVituGhazi(final ScionOfVituGhazi card) {
super(card);
}

View file

@ -32,7 +32,8 @@ import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.DestroyAllEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
@ -41,8 +42,6 @@ import mage.constants.Rarity;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.watchers.Watcher;
import mage.watchers.common.CastFromHandWatcher;
/**
@ -50,9 +49,9 @@ import mage.watchers.common.CastFromHandWatcher;
* @author jeffwadsworth
*/
public class DeathbringerRegent extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("other creatures");
static {
filter.add(new AnotherPredicate());
}
@ -66,10 +65,13 @@ public class DeathbringerRegent extends CardImpl {
// Flying
this.addAbility(FlyingAbility.getInstance());
// When Deathbringer Regent enters the battlefield, if you cast it from your hand and there are five or more other creatures on the battlefield, destroy all other creatures.
this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(new DestroyAllEffect(filter), new DeathbringerRegentCondition()), false), new CastFromHandWatcher());
this.addAbility(new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new DestroyAllEffect(filter), false),
new DeathbringerRegentCondition(),
"When {this} enters the battlefield, if you cast it from your hand and there are five or more other creatures on the battlefield, destroy all other creatures."),
new CastFromHandWatcher());
}
public DeathbringerRegent(final DeathbringerRegent card) {
@ -86,22 +88,7 @@ class DeathbringerRegentCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
boolean applies = false;
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) {
Watcher watcher = game.getState().getWatchers().get("CastFromHand", source.getSourceId());
if (watcher != null && watcher.conditionMet()) {
applies = true;
}
}
if (applies) {
applies = game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), game).size() >= 6;
}
return applies;
return new CastFromHandSourceCondition().apply(game, source)
&& game.getBattlefield().getAllActivePermanents(new FilterCreaturePermanent(), game).size() >= 6;
}
@Override
public String toString() {
return "you cast it from your hand and there are five or more other creatures on the battlefield";
}
}
}

View file

@ -88,7 +88,7 @@ class HardenedBerserkerSpellsCostReductionEffect extends CostModificationEffectI
@Override
public void init(Ability source, Game game) {
super.init(source, game);
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
if (watcher != null) {
spellsCast = watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId());
}
@ -102,7 +102,7 @@ class HardenedBerserkerSpellsCostReductionEffect extends CostModificationEffectI
@Override
public boolean applies(Ability abilityToModify, Ability source, Game game) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
if (watcher != null) {
if (watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(source.getControllerId()) > spellsCast) {
discard(); // only one use

View file

@ -29,7 +29,6 @@ package mage.sets.eventide;
import java.util.UUID;
import mage.MageInt;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.Condition;
@ -39,14 +38,9 @@ import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.WatcherScope;
import mage.filter.FilterSpell;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.stack.Spell;
import mage.watchers.Watcher;
import mage.watchers.common.SpellsCastWatcher;
/**
*
@ -69,7 +63,8 @@ public class DreamThief extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
// When Dream Thief enters the battlefield, draw a card if you've cast another blue spell this turn.
this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), new CastBlueSpellThisTurnCondition(), rule)), new DreamThiefWatcher(this.getId()));
this.addAbility(new EntersBattlefieldTriggeredAbility(new ConditionalOneShotEffect(new DrawCardSourceControllerEffect(1), new CastBlueSpellThisTurnCondition(), rule)),
new SpellsCastWatcher());
}
@ -87,55 +82,14 @@ class CastBlueSpellThisTurnCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
DreamThiefWatcher watcher = (DreamThiefWatcher) game.getState().getWatchers().get("DreamThiefWatcher", source.getControllerId());
SpellsCastWatcher watcher = (SpellsCastWatcher) game.getState().getWatchers().get(SpellsCastWatcher.class.getName());
if (watcher != null) {
return watcher.conditionMet();
for (Spell spell : watcher.getSpellsCastThisTurn(source.getControllerId())) {
if (!spell.getSourceId().equals(source.getSourceId()) && spell.getColor(game).isBlue()) {
return true;
}
}
}
return false;
}
}
class DreamThiefWatcher extends Watcher {
private static final FilterSpell filter = new FilterSpell();
static {
filter.add(new ColorPredicate(ObjectColor.BLUE));
}
private UUID cardId;
public DreamThiefWatcher(UUID cardId) {
super("DreamThiefWatcher", WatcherScope.PLAYER);
this.cardId = cardId;
}
public DreamThiefWatcher(final DreamThiefWatcher watcher) {
super(watcher);
this.cardId = watcher.cardId;
}
@Override
public DreamThiefWatcher copy() {
return new DreamThiefWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (condition == true) { //no need to check - condition has already occured
return;
}
if (event.getType() == EventType.SPELL_CAST
&& controllerId.equals(event.getPlayerId())) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (!spell.getSourceId().equals(cardId) && filter.match(spell, game)) {
condition = true;
}
}
}
@Override
public void reset() {
super.reset();
}
}

View file

@ -0,0 +1,74 @@
/*
* 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.sets.exodus;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.DealsDamageToACreatureTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.effects.common.SacrificeSourceUnlessPaysEffect;
import mage.abilities.keyword.FirstStrikeAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.TargetController;
/**
*
* @author djbrez
*/
public class PitSpawn extends CardImpl {
public PitSpawn(UUID ownerId) {
super(ownerId, 70, "Pit Spawn", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{4}{B}{B}{B}");
this.expansionSetCode = "EXO";
this.subtype.add("Demon");
this.power = new MageInt(6);
this.toughness = new MageInt(4);
// First strike
this.addAbility(FirstStrikeAbility.getInstance());
// At the beginning of your upkeep, sacrifice Pit Spawn unless you pay {B}{B}.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new SacrificeSourceUnlessPaysEffect(new ManaCostsImpl("{B}{B}")), TargetController.YOU, false));
// Whenever Pit Spawn deals damage to a creature, exile that creature.
this.addAbility(new DealsDamageToACreatureTriggeredAbility(new ExileTargetEffect("exile that creature"), false, false, true));
}
public PitSpawn(final PitSpawn card) {
super(card);
}
@Override
public PitSpawn copy() {
return new PitSpawn(this);
}
}

View file

@ -80,7 +80,7 @@ class OtherSpellsCastThisTurnCount implements DynamicValue {
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
return watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn() - 1;
}

View file

@ -95,7 +95,7 @@ class IncursionTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getPlayerId().equals(controllerId)) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2) {
return true;
}

View file

@ -28,9 +28,6 @@
package mage.sets.innistrad;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
@ -41,7 +38,9 @@ import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.keyword.DefenderAbility;
import mage.abilities.keyword.TransformAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
@ -83,6 +82,7 @@ public class LudevicsTestSubject extends CardImpl {
}
class LudevicsTestSubjectEffect extends OneShotEffect {
LudevicsTestSubjectEffect() {
super(Outcome.Benefit);
staticText = "Then if there are five or more hatchling counters on it, remove all of them and transform it";
@ -97,7 +97,7 @@ class LudevicsTestSubjectEffect extends OneShotEffect {
Permanent p = game.getPermanent(source.getSourceId());
if (p != null) {
if (p.getCounters().getCount(CounterType.HATCHLING) >= 5) {
p.getCounters().removeCounter(CounterType.HATCHLING, p.getCounters().getCount(CounterType.HATCHLING));
p.removeCounters(CounterType.HATCHLING.getName(), p.getCounters().getCount(CounterType.HATCHLING), game);
TransformSourceEffect effect = new TransformSourceEffect(true);
return effect.apply(game, source);
}
@ -109,4 +109,4 @@ class LudevicsTestSubjectEffect extends OneShotEffect {
public LudevicsTestSubjectEffect copy() {
return new LudevicsTestSubjectEffect(this);
}
}
}

View file

@ -83,21 +83,12 @@ class RevivingVaporsEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (controller == null || sourceObject == null) {
if (controller == null || sourceObject == null) {
return false;
}
Cards cards = new CardsImpl();
int count = Math.min(controller.getLibrary().size(), 3);
for (int i = 0; i < count; i++) {
Card card = controller.getLibrary().removeFromTop(game);
if (card != null) {
cards.add(card);
} else {
return false;
}
}
cards.addAll(controller.getLibrary().getTopCards(game, 3));
if (!cards.isEmpty()) {
controller.revealCards(sourceObject.getName(), cards, game);
Card card = null;
@ -112,9 +103,9 @@ class RevivingVaporsEffect extends OneShotEffect {
}
if (card != null) {
cards.remove(card);
controller.moveCards(card, Zone.LIBRARY, Zone.GRAVEYARD, source, game);
controller.moveCards(card, Zone.HAND, source, game);
}
controller.moveCards(cards, Zone.LIBRARY, Zone.GRAVEYARD, source, game);
controller.moveCards(cards, Zone.GRAVEYARD, source, game);
}
return true;
}

View file

@ -39,7 +39,6 @@ import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.SpiritWhiteToken;
import mage.game.permanent.token.Token;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
@ -55,7 +54,6 @@ public class CribSwap extends CardImpl {
this.expansionSetCode = "LRW";
this.subtype.add("Shapeshifter");
// Changeling
this.addAbility(ChangelingAbility.getInstance());
// Exile target creature. Its controller puts a 1/1 colorless Shapeshifter creature token with changeling onto the battlefield.
@ -112,7 +110,6 @@ class CribSwapShapeshifterWhiteToken extends Token {
this.setOriginalExpansionSetCode("LRW");
cardType.add(CardType.CREATURE);
subtype.add("Shapeshifter");
color.setWhite(true);
power = new MageInt(1);
toughness = new MageInt(1);
addAbility(ChangelingAbility.getInstance());

View file

@ -141,7 +141,7 @@ class AngelicArbiterCantAttackTargetEffect extends RestrictionEffect {
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
if (game.getActivePlayerId().equals(permanent.getControllerId()) && game.getOpponents(source.getControllerId()).contains(permanent.getControllerId())) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(permanent.getControllerId()) > 0) {
return true;
}

View file

@ -173,7 +173,7 @@ class AlhammarretHighArbiterCantCastEffect extends ContinuousRuleModifyingEffect
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == EventType.CAST_SPELL;
return event.getType() == EventType.CAST_SPELL_LATE;
}
@Override

View file

@ -28,9 +28,6 @@
package mage.sets.mirrodin;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.SacrificeSourceCost;
@ -39,7 +36,9 @@ import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.counter.AddCountersTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
@ -78,6 +77,7 @@ public class OblivionStone extends CardImpl {
}
class OblivionStoneEffect extends OneShotEffect {
OblivionStoneEffect() {
super(Outcome.DestroyPermanent);
staticText = "Destroy each nonland permanent without a fate counter on it, then remove all fate counters from all permanents";
@ -96,7 +96,7 @@ class OblivionStoneEffect extends OneShotEffect {
}
for (Permanent p : game.getBattlefield().getAllActivePermanents()) {
if (p.getCounters().containsKey(CounterType.FATE)) {
p.getCounters().removeCounter(CounterType.FATE, p.getCounters().getCount(CounterType.FATE));
p.removeCounters(CounterType.FATE.getName(), p.getCounters().getCount(CounterType.FATE), game);
}
}
return true;

View file

@ -34,7 +34,7 @@ import mage.abilities.Ability;
import mage.abilities.common.DiesTriggeredAbility;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.condition.InvertCondition;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.GainSuspendEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
@ -67,7 +67,7 @@ public class Epochrasite extends CardImpl {
// Epochrasite enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand.
this.addAbility(new EntersBattlefieldAbility(
new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)),
new InvertCondition(new CastFromHandCondition()),
new InvertCondition(new CastFromHandSourceCondition()),
"{this} enters the battlefield with three +1/+1 counters on it if you didn't cast it from your hand",""),
new CastFromHandWatcher());

View file

@ -28,8 +28,6 @@
package mage.sets.newphyrexia;
import java.util.UUID;
import mage.constants.*;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
@ -37,6 +35,7 @@ import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.*;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.CardTypePredicate;
@ -50,6 +49,7 @@ import mage.target.TargetPermanent;
* @author Loki
*/
public class ExclusionRitual extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("nonland permanent");
static {
@ -60,7 +60,6 @@ public class ExclusionRitual extends CardImpl {
super(ownerId, 10, "Exclusion Ritual", Rarity.UNCOMMON, new CardType[]{CardType.ENCHANTMENT}, "{4}{W}{W}");
this.expansionSetCode = "NPH";
// Imprint - When Exclusion Ritual enters the battlefield, exile target nonland permanent.
Ability ability = new EntersBattlefieldTriggeredAbility(new ExclusionRitualImprintEffect(), false);
ability.addTarget(new TargetPermanent(filter));
@ -80,6 +79,7 @@ public class ExclusionRitual extends CardImpl {
}
class ExclusionRitualImprintEffect extends OneShotEffect {
ExclusionRitualImprintEffect() {
super(Outcome.Exile);
staticText = "exile target nonland permanent";
@ -108,6 +108,7 @@ class ExclusionRitualImprintEffect extends OneShotEffect {
}
class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl {
ExclusionRitualReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment);
staticText = "Players can't cast spells with the same name as the exiled card";
@ -116,12 +117,12 @@ class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl
ExclusionRitualReplacementEffect(final ExclusionRitualReplacementEffect effect) {
super(effect);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CAST_SPELL;
return event.getType() == GameEvent.EventType.CAST_SPELL_LATE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId());

View file

@ -103,13 +103,13 @@ class HexParasiteEffect extends OneShotEffect {
for (String counterName : counterNames) {
if (player.chooseUse(Outcome.Neutral, "Do you want to remove " + counterName + " counters?", source, game)) {
if (permanent.getCounters().get(counterName).getCount() == 1 || toRemove == 1) {
permanent.getCounters().removeCounter(counterName, 1);
permanent.removeCounters(counterName, 1, game);
removed++;
} else {
int amount = player.getAmount(1, Math.min(permanent.getCounters().get(counterName).getCount(), toRemove - removed), "How many?", game);
if (amount > 0) {
removed += amount;
permanent.getCounters().removeCounter(counterName, amount);
permanent.removeCounters(counterName, amount, game);
}
}
}

View file

@ -92,7 +92,7 @@ class JoriEnTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getPlayerId().equals(controllerId)) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2) {
return true;
}

View file

@ -90,7 +90,7 @@ class PyromancersAssaultTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getPlayerId().equals(controllerId)) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 2) {
return true;
}

View file

@ -47,6 +47,7 @@ import mage.filter.predicate.permanent.ControllerPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.game.turn.Step;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
@ -110,7 +111,9 @@ class ReflectorMageEffect extends OneShotEffect {
Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source));
if (targetCreature != null) {
controller.moveCards(targetCreature, Zone.HAND, source, game);
game.addEffect(new ExclusionRitualReplacementEffect(targetCreature.getName(), targetCreature.getOwnerId()), source);
if (!targetCreature.getName().isEmpty()) { // if the creature had no name, no restrict effect will be created
game.addEffect(new ExclusionRitualReplacementEffect(targetCreature.getName(), targetCreature.getOwnerId()), source);
}
}
return true;
}
@ -138,13 +141,17 @@ class ExclusionRitualReplacementEffect extends ContinuousRuleModifyingEffectImpl
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.CAST_SPELL;
return event.getType() == GameEvent.EventType.CAST_SPELL_LATE;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Card card = game.getCard(event.getSourceId());
if (card != null) {
Spell spell = game.getState().getStack().getSpell(event.getSourceId());
if (spell != null && spell.isFaceDown(game)) {
return false; // Face Down cast spell (Morph creature) has no name
}
return card.getName().equals(creatureName);
}
return false;

View file

@ -0,0 +1,94 @@
/*
* 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.sets.onslaught;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.ReturnFromGraveyardToHandTargetEffect;
import mage.cards.CardImpl;
import mage.cards.repository.CardRepository;
import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.filter.common.FilterCreatureCard;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetCardInYourGraveyard;
/**
*
* @author Quercitron
*/
public class AphettoDredging extends CardImpl {
public AphettoDredging(UUID ownerId) {
super(ownerId, 125, "Aphetto Dredging", Rarity.COMMON, new CardType[]{CardType.SORCERY}, "{3}{B}");
this.expansionSetCode = "ONS";
// Return up to three target creature cards of the creature type of your choice from your graveyard to your hand.
Effect effect = new ReturnFromGraveyardToHandTargetEffect();
effect.setText("Return up to three target creature cards of the creature type of your choice from your graveyard to your hand");
this.getSpellAbility().addEffect(effect);
}
@Override
public void adjustTargets(Ability ability, Game game) {
if (ability instanceof SpellAbility) {
Player controller = game.getPlayer(ability.getControllerId());
if (controller != null) {
Choice typeChoice = new ChoiceImpl(true);
typeChoice.setMessage("Choose a creature type");
typeChoice.setChoices(CardRepository.instance.getCreatureTypes());
while (!controller.choose(Outcome.PutCreatureInPlay, typeChoice, game)) {
if (!controller.canRespond()) {
return;
}
}
String chosenType = typeChoice.getChoice();
FilterCreatureCard filter = new FilterCreatureCard(chosenType + " cards");
filter.add(new SubtypePredicate(chosenType));
ability.addTarget(new TargetCardInYourGraveyard(0, 3, filter));
}
}
}
public AphettoDredging(final AphettoDredging card) {
super(card);
}
@Override
public AphettoDredging copy() {
return new AphettoDredging(this);
}
}

View file

@ -0,0 +1,52 @@
/*
* 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.sets.pdsslivers;
import java.util.UUID;
/**
*
* @author Quercitron
*/
public class AphettoDredging extends mage.sets.onslaught.AphettoDredging {
public AphettoDredging(UUID ownerId) {
super(ownerId);
this.cardNumber = 28;
this.expansionSetCode = "PDS";
}
public AphettoDredging(final AphettoDredging card) {
super(card);
}
@Override
public AphettoDredging copy() {
return new AphettoDredging(this);
}
}

View file

@ -0,0 +1,52 @@
/*
* 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.sets.pdsslivers;
import java.util.UUID;
/**
*
* @author fenhl
*/
public class WildPair extends mage.sets.planarchaos.WildPair {
public WildPair(UUID ownerId) {
super(ownerId);
this.cardNumber = 30;
this.expansionSetCode = "H09";
}
public WildPair(final WildPair card) {
super(card);
}
@Override
public WildPair copy() {
return new WildPair(this);
}
}

View file

@ -0,0 +1,175 @@
/*
* 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.sets.planarchaos;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.SetTargetPointer;
import mage.constants.Zone;
import mage.filter.Filter;
import mage.filter.common.FilterCreatureCard;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.IntComparePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.players.Player;
import mage.target.common.TargetCardInLibrary;
import mage.watchers.common.CastFromHandWatcher;
/**
*
* @author fenhl
*/
public class WildPair extends CardImpl {
public WildPair(UUID ownerID) {
super(ownerID, 30, "Wild Pair", Rarity.RARE, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}{G}");
this.expansionSetCode = "PLC";
// Whenever a creature enters the battlefield, if you cast it from your hand, you may search your library for a creature card with the same total power and toughness and put it onto the battlefield. If you do, shuffle your library.
this.addAbility(new ConditionalTriggeredAbility(
new EntersBattlefieldAllTriggeredAbility(Zone.BATTLEFIELD, new WildPairEffect(), new FilterCreaturePermanent("a creature"), true, SetTargetPointer.PERMANENT, ""),
new CastFromHandTargetCondition(),
"Whenever a creature enters the battlefield, if you cast it from your hand, you may search your library for a creature card with the same total power and toughness and put it onto the battlefield. If you do, shuffle your library."
), new CastFromHandWatcher());
}
public WildPair(final WildPair card) {
super(card);
}
@Override
public WildPair copy() {
return new WildPair(this);
}
}
class WildPairEffect extends OneShotEffect {
public WildPairEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "search your library for a creature card with the same total power and toughness and put it onto the battlefield";
}
public WildPairEffect(final WildPairEffect effect) {
super(effect);
}
@Override
public WildPairEffect copy() {
return new WildPairEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
if (permanent != null) {
int totalPT = permanent.getPower().getValue() + permanent.getToughness().getValue();
FilterCreatureCard filter = new FilterCreatureCard("creature card with total power and toughness " + totalPT);
filter.add(new TotalPowerAndToughnessPredicate(Filter.ComparisonType.Equal, totalPT));
TargetCardInLibrary target = new TargetCardInLibrary(1, filter);
if (controller.searchLibrary(target, game)) {
if (target.getTargets().size() > 0) {
controller.moveCards(new CardsImpl(target.getTargets()), Zone.BATTLEFIELD, source, game);
}
}
controller.shuffleLibrary(game);
return true;
}
}
return false;
}
}
/**
*
* @author fenhl
*/
class TotalPowerAndToughnessPredicate extends IntComparePredicate<MageObject> {
public TotalPowerAndToughnessPredicate(Filter.ComparisonType type, int value) {
super(type, value);
}
@Override
protected int getInputValue(MageObject input) {
return input.getPower().getValue() + input.getToughness().getValue();
}
@Override
public String toString() {
return "TotalPowerAndToughness" + super.toString();
}
}
class CastFromHandTargetCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
UUID targetId = source.getEffects().get(0).getTargetPointer().getFirst(game, source);
Permanent permanent = game.getPermanentEntering(targetId);
int zccDiff = 0;
if (permanent == null) {
permanent = game.getPermanentOrLKIBattlefield(targetId); // can be alredy again removed from battlefield so also check LKI
zccDiff = -1;
}
if (permanent != null) {
// check that the spell is still in the LKI
Spell spell = game.getStack().getSpell(targetId);
if (spell == null || spell.getZoneChangeCounter(game) != permanent.getZoneChangeCounter(game) + zccDiff) {
if (game.getLastKnownInformation(targetId, Zone.STACK, permanent.getZoneChangeCounter(game) + zccDiff) == null) {
return false;
}
}
CastFromHandWatcher watcher = (CastFromHandWatcher) game.getState().getWatchers().get(CastFromHandWatcher.class.getName());
if (watcher != null && watcher.spellWasCastFromHand(targetId)) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "you cast it from your hand";
}
}

View file

@ -101,8 +101,8 @@ class DescendantOfMasumaroEffect extends OneShotEffect {
}
Player targetOpponent = game.getPlayer(getTargetPointer().getFirst(game, source));
if (targetOpponent != null && targetOpponent.getHand().size() > 0) {
sourcePermanent.getCounters().removeCounter(CounterType.P1P1, targetOpponent.getHand().size());
game.informPlayers(controller.getLogName() + " removes " + targetOpponent.getHand().size() + " +1/+1 counters from " + sourcePermanent.getLogName());
sourcePermanent.removeCounters(CounterType.P1P1.getName(), targetOpponent.getHand().size(), game);
game.informPlayers(controller.getLogName() + " removes " + targetOpponent.getHand().size() + " +1/+1 counters from " + sourcePermanent.getLogName());
}
return true;
}

View file

@ -103,7 +103,7 @@ class ErayoSoratamiAscendantTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
return watcher != null && watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn() == 4;
}
@ -152,7 +152,7 @@ class ErayosEssenceTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (game.getOpponents(getControllerId()).contains(event.getPlayerId())) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
if (watcher != null && watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(event.getPlayerId()) == 1) {
for (Effect effect : getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));

View file

@ -33,8 +33,8 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.DiesTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileSourceEffect;
@ -75,13 +75,14 @@ public class InameAsOne extends CardImpl {
this.toughness = new MageInt(8);
// When Iname as One enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle your library.
Ability ability = new EntersBattlefieldTriggeredAbility(
new ConditionalOneShotEffect(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, filter), false),
new CastFromHandCondition()));
this.addAbility(ability, new CastFromHandWatcher());
this.addAbility(new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInPlayEffect(new TargetCardInLibrary(0, 1, filter)), true),
new CastFromHandSourceCondition(),
"When {this} enters the battlefield, if you cast it from your hand, you may search your library for a Spirit permanent card, put it onto the battlefield, then shuffle your library."),
new CastFromHandWatcher());
// When Iname as One dies, you may exile it. If you do, return target Spirit permanent card from your graveyard to the battlefield.
ability = new DiesTriggeredAbility(new InameAsOneEffect(), false);
Ability ability = new DiesTriggeredAbility(new InameAsOneEffect(), false);
ability.addTarget(new TargetCardInYourGraveyard(filter));
this.addAbility(ability);
}

View file

@ -73,7 +73,7 @@ class ImpatienceCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get("CastSpellLastTurnWatcher");
CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getName());
return watcher.getAmountOfSpellsPlayerCastOnCurrentTurn(game.getActivePlayerId()) == 0;
}

View file

@ -0,0 +1,52 @@
/*
* 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.sets.shadowsoverinnistrad;
import java.util.UUID;
/**
*
* @author LevelX2
*/
public class EerieInterlude extends mage.sets.blessedvscursed.EerieInterlude {
public EerieInterlude(UUID ownerId) {
super(ownerId);
this.cardNumber = 994; //TODO: Fix card number
this.expansionSetCode = "SOI";
}
public EerieInterlude(final EerieInterlude card) {
super(card);
}
@Override
public EerieInterlude copy() {
return new EerieInterlude(this);
}
}

View file

@ -0,0 +1,130 @@
/*
* 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.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.condition.common.DeliriumCondition;
import mage.abilities.effects.common.search.SearchTargetGraveyardHandLibraryForCardNameAndExileEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.filter.FilterSpell;
import mage.filter.predicate.mageobject.CardTypePredicate;
import mage.game.Game;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.TargetSpell;
/**
*
* @author LevelX2
*/
public class InvasiveSurgery extends CardImpl {
private final static FilterSpell filter = new FilterSpell("sorcery spell");
static {
filter.add(new CardTypePredicate(CardType.SORCERY));
}
public InvasiveSurgery(UUID ownerId) {
super(ownerId, 68, "Invasive Surgery", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{U}");
this.expansionSetCode = "SOI";
// Counter target sorcery spell.
// <i>Delirium</i> &mdash; If there are four or more card types among cards in your graveyard, search the graveyard, hand, and library of that spell's controller for any number of cards with the same name as that spell, exile those cards, then that player shuffles his or her library.
this.getSpellAbility().addEffect(new InvasiveSurgeryEffect());
this.getSpellAbility().addTarget(new TargetSpell(filter));
}
public InvasiveSurgery(final InvasiveSurgery card) {
super(card);
}
@Override
public InvasiveSurgery copy() {
return new InvasiveSurgery(this);
}
}
class InvasiveSurgeryEffect extends SearchTargetGraveyardHandLibraryForCardNameAndExileEffect {
public InvasiveSurgeryEffect() {
super(true, "that spell's controller", "all cards with the same name as that spell");
}
public InvasiveSurgeryEffect(final InvasiveSurgeryEffect effect) {
super(effect);
}
@Override
public InvasiveSurgeryEffect copy() {
return new InvasiveSurgeryEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller == null) {
return false;
}
String cardName = "";
UUID spellController = null;
if (source.getTargets().get(0) instanceof TargetSpell) {
UUID objectId = source.getFirstTarget();
StackObject stackObject = game.getStack().getStackObject(objectId);
if (stackObject != null) {
MageObject targetObject = game.getObject(stackObject.getSourceId());
if (targetObject instanceof Card) {
cardName = targetObject.getName();
}
spellController = stackObject.getControllerId();
game.getStack().counter(objectId, source.getSourceId(), game);
}
}
// Check the Delirium condition
if (!DeliriumCondition.getInstance().apply(game, source)) {
return true;
}
return this.applySearchAndExile(game, source, cardName, spellController);
}
@Override
public String getText(Mode mode) {
return "Counter target sorcery spell.<br><br>"
+ "<i>Delirium</i> &mdash; If there are four or more card types among cards in your graveyard, "
+ "search the graveyard, hand, and library of that spell's controller for any number of cards "
+ "with the same name as that spell, exile those cards, then that player shuffles his or her library";
}
}

View file

@ -0,0 +1,82 @@
/*
* 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.sets.shadowsoverinnistrad;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.EntersBattlefieldTappedAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.abilities.effects.common.TapTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author LevelX2
*/
public class StichedMangler extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls");
static {
filter.add(new ControllerPredicate(TargetController.OPPONENT));
}
public StichedMangler(UUID ownerId) {
super(ownerId, 89, "Stiched Mangler", Rarity.COMMON, new CardType[]{CardType.CREATURE}, "{2}{U}");
this.expansionSetCode = "SOI";
this.subtype.add("Zombie");
this.subtype.add("Horror");
this.power = new MageInt(2);
this.toughness = new MageInt(3);
// Stitched Mangler enters the battlefield tapped.
this.addAbility(new EntersBattlefieldTappedAbility());
// When Stitched Mangler enters the battlefield, tap target creature an opponent controls. That creature doesn't untap during its controller's next untap step.
EntersBattlefieldTriggeredAbility ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect());
ability.addEffect(new DontUntapInControllersNextUntapStepTargetEffect());
ability.addTarget(new TargetCreaturePermanent(filter));
this.addAbility(ability);
}
public StichedMangler(final StichedMangler card) {
super(card);
}
@Override
public StichedMangler copy() {
return new StichedMangler(this);
}
}

View file

@ -30,16 +30,13 @@ package mage.sets.sorinvstibalt;
import java.util.UUID;
import mage.MageInt;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.decorator.ConditionalOneShotEffect;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.BasicManaEffect;
import mage.abilities.effects.common.ExileAllEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.filter.common.FilterAttackingCreature;
import mage.watchers.common.CastFromHandWatcher;
/**
@ -56,10 +53,11 @@ public class CoalStoker extends CardImpl {
this.toughness = new MageInt(3);
// When Coal Stoker enters the battlefield, if you cast it from your hand, add {R}{R}{R} to your mana pool.
Ability ability = new EntersBattlefieldTriggeredAbility(
new ConditionalOneShotEffect(new BasicManaEffect(new Mana(3, 0, 0, 0, 0, 0, 0, 0)), new CastFromHandCondition(),
" if you cast it from your hand, add {R}{R}{R} to your mana pool."));
this.addAbility(ability, new CastFromHandWatcher());
this.addAbility(new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new BasicManaEffect(new Mana(3, 0, 0, 0, 0, 0, 0, 0)), false),
new CastFromHandSourceCondition(),
"When {this} enters the battlefield, if you cast it from your hand, add {R}{R}{R} to your mana pool."),
new CastFromHandWatcher());
}
public CoalStoker(final CoalStoker card) {

View file

@ -73,10 +73,9 @@ public class CrovaxTheCursed extends CardImpl {
Ability ability = new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new CrovaxTheCursedEffect(), TargetController.YOU, false);
this.addAbility(ability);
// {B}: Crovax gains flying until end of turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new GainAbilitySourceEffect(FlyingAbility.getInstance(), Duration.EndOfTurn), new ManaCostsImpl("{B}")));
}
public CrovaxTheCursed(final CrovaxTheCursed card) {
@ -118,11 +117,9 @@ class CrovaxTheCursedEffect extends OneShotEffect {
game.informPlayers(controller.getLogName() + " puts a +1/+1 counter on " + sourceObject.getName());
}
}
} else {
if (sourceObject != null && sourceObject.getCounters().containsKey(CounterType.P1P1)) {
sourceObject.getCounters().removeCounter(CounterType.P1P1, 1);
game.informPlayers(controller.getLogName() + " removes a +1/+1 counter from " + sourceObject.getName());
}
} else if (sourceObject != null && sourceObject.getCounters().containsKey(CounterType.P1P1)) {
sourceObject.removeCounters(CounterType.P1P1.getName(), 1, game);
game.informPlayers(controller.getLogName() + " removes a +1/+1 counter from " + sourceObject.getName());
}
return true;
}

View file

@ -107,10 +107,10 @@ class MagmasaurEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent sourceObject = (Permanent)source.getSourceObjectIfItStillExists(game);
Permanent sourceObject = (Permanent) source.getSourceObjectIfItStillExists(game);
if (sourceObject != null && controller != null) {
if (controller.chooseUse(outcome, "Remove a +1/+1 counter from " + sourceObject.getLogName() + "?", source, game)) {
sourceObject.getCounters().removeCounter(CounterType.P1P1, 1);
sourceObject.removeCounters(CounterType.P1P1.getName(), 1, game);
} else {
int counters = sourceObject.getCounters().getCount(CounterType.P1P1);
sourceObject.sacrifice(source.getSourceId(), game);

View file

@ -33,7 +33,7 @@ import mage.abilities.common.DealsCombatDamageToACreatureTriggeredAbility;
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.condition.InvertCondition;
import mage.abilities.condition.common.CastFromHandCondition;
import mage.abilities.condition.common.CastFromHandSourceCondition;
import mage.abilities.decorator.ConditionalTriggeredAbility;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.LoseGameSourceControllerEffect;
@ -62,7 +62,7 @@ public class PhageTheUntouchable extends CardImpl {
// When Phage the Untouchable enters the battlefield, if you didn't cast it from your hand, you lose the game.
this.addAbility(new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new LoseGameSourceControllerEffect(), false),
new InvertCondition(new CastFromHandCondition()),
new InvertCondition(new CastFromHandSourceCondition()),
"When {this} enters the battlefield, if you didn't cast it from your hand, you lose the game"
), new CastFromHandWatcher());

View file

@ -0,0 +1,131 @@
/*
* 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.sets.timespiral;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.MorphAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetControlledCreaturePermanent;
/**
*
* @author djbrez
*/
public class LiegeOfThePit extends CardImpl {
public LiegeOfThePit(UUID ownerId) {
super(ownerId, 113, "Liege of the Pit", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{5}{B}{B}{B}");
this.expansionSetCode = "TSP";
this.subtype.add("Demon");
this.power = new MageInt(7);
this.toughness = new MageInt(7);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Trample
this.addAbility(TrampleAbility.getInstance());
// At the beginning of your upkeep, sacrifice a creature other than Liege of the Pit. If you can't, Liege of the Pit deals 7 damage to you.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new LiegeOfThePitEffect(), TargetController.YOU, false));
// Morph {B}{B}{B}{B}
this.addAbility(new MorphAbility(this, new ManaCostsImpl("{B}{B}{B}{B}")));
}
public LiegeOfThePit(final LiegeOfThePit card) {
super(card);
}
@Override
public LiegeOfThePit copy() {
return new LiegeOfThePit(this);
}
}
class LiegeOfThePitEffect extends OneShotEffect {
public LiegeOfThePitEffect() {
super(Outcome.Damage);
this.staticText = "Sacrifice a creature other than {this}. If you can't {this} deals 7 damage to you.";
}
public LiegeOfThePitEffect(final LiegeOfThePitEffect effect) {
super(effect);
}
@Override
public LiegeOfThePitEffect copy() {
return new LiegeOfThePitEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (sourcePermanent == null) {
sourcePermanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
}
if (player == null || sourcePermanent == null) {
return false;
}
FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creature other than " + sourcePermanent.getName());
filter.add(new AnotherPredicate());
Target target = new TargetControlledCreaturePermanent(1, 1, filter, true);
if (target.canChoose(source.getSourceId(), player.getId(), game)) {
player.choose(Outcome.Sacrifice, target, source.getSourceId(), game);
Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent != null) {
permanent.sacrifice(source.getSourceId(), game);
return true;
}
} else {
player.damage(7, source.getSourceId(), game, false, true);
return true;
}
return false;
}
}

View file

@ -0,0 +1,207 @@
/*
* 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.sets.timespiral;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.EntersBattlefieldAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.AsTurnedFaceUpEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CopyEffect;
import mage.abilities.effects.common.CopyPermanentEffect;
import mage.abilities.keyword.MorphAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
import mage.util.functions.ApplyToPermanent;
/**
*
* @author spjspj
*/
public class VesuvanShapeshifter extends CardImpl {
protected Ability turnFaceUpAbility = null;
private static final String effectText = "as a copy of any creature on the battlefield until {this} is turned faced down";
public VesuvanShapeshifter(UUID ownerId) {
super(ownerId, 90, "Vesuvan Shapeshifter", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{U}{U}");
this.expansionSetCode = "TSP";
this.subtype.add("Shapeshifter");
this.power = new MageInt(0);
this.toughness = new MageInt(0);
// Morph {1}{U}
this.addAbility(new MorphAbility(this, new ManaCostsImpl("{1}{U}")));
// As Vesuvan Shapeshifter turned face up, may choose another creature. If you do, until Vesuvan Shapeshifter is turned face down, it becomes a copy of that creature
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new AsTurnedFaceUpEffect(new VesuvanShapeshifterEffect(), false));
ability.setWorksFaceDown(true);
this.addAbility(ability);
// As Vesuvan Shapeshifter etbs, may choose another creature. If you do, until Vesuvan Shapeshifter is turned face down, it becomes a copy of that creature
Effect effect = new CopyPermanentEffect(new FilterCreaturePermanent());
effect.setText(effectText);
ability = new EntersBattlefieldAbility(effect, true);
ability.setWorksFaceDown(false);
this.addAbility(ability);
// At the beginning of your upkeep, you may turn this creature face down
effect = new VesuvanShapeshifterFaceDownEffect();
ability = new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, true);
this.addAbility(ability);
}
public VesuvanShapeshifter(final VesuvanShapeshifter card) {
super(card);
}
@Override
public VesuvanShapeshifter copy() {
return new VesuvanShapeshifter(this);
}
}
class VesuvanShapeshifterEffect extends OneShotEffect {
public VesuvanShapeshifterEffect() {
super(Outcome.Copy);
staticText = "have {this} become a copy of a creature and gain this ability";
}
public VesuvanShapeshifterEffect(final VesuvanShapeshifterEffect effect) {
super(effect);
}
@Override
public VesuvanShapeshifterEffect copy() {
return new VesuvanShapeshifterEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
ApplyToPermanent vesuvanShapeShifterFaceUpApplier = new ApplyToPermanent() {
@Override
public Boolean apply(Game game, Permanent permanent) {
Effect effect = new VesuvanShapeshifterFaceDownEffect();
Ability ability = new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, true);
permanent.getAbilities().add(ability);
permanent.addAbility(new MorphAbility(permanent, new ManaCostsImpl("{1}{U}")), permanent.getId(), game);
return true;
}
@Override
public Boolean apply(Game game, MageObject mageObject) {
Effect effect = new VesuvanShapeshifterFaceDownEffect();
Ability ability = new BeginningOfUpkeepTriggeredAbility(effect, TargetController.YOU, true);
mageObject.getAbilities().add(ability);
return true;
}
};
Permanent copyToCreature = game.getPermanent(source.getSourceId());
if (copyToCreature != null) {
FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature");
filter.add(new AnotherPredicate());
TargetCreaturePermanent target = new TargetCreaturePermanent(0, 1, filter, false);
if (controller.chooseTarget(Outcome.BecomeCreature, target, source, game) && !target.getTargets().isEmpty()) {
Permanent copyFromCreature = game.getPermanentOrLKIBattlefield(target.getFirstTarget());
if (copyFromCreature != null) {
game.copyPermanent(Duration.Custom, copyFromCreature, copyToCreature.getId(), source, vesuvanShapeShifterFaceUpApplier);
source.getTargets().clear();
return true;
}
}
}
return false;
}
}
class VesuvanShapeshifterFaceDownEffect extends OneShotEffect {
public VesuvanShapeshifterFaceDownEffect() {
super(Outcome.Copy);
staticText = "have {this} become a morphed, faced down creature";
}
public VesuvanShapeshifterFaceDownEffect(final VesuvanShapeshifterFaceDownEffect effect) {
super(effect);
}
@Override
public VesuvanShapeshifterFaceDownEffect copy() {
return new VesuvanShapeshifterFaceDownEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (controller != null && permanent != null) {
permanent.removeAllAbilities(source.getSourceId(), game);
// Set any previous copy effects to 'discarded'
for (Effect effect : game.getState().getContinuousEffects().getLayeredEffects(game)) {
if (effect instanceof CopyEffect) {
CopyEffect copyEffect = (CopyEffect) effect;
if (copyEffect.getSourceId().equals(permanent.getId())) {
copyEffect.discard();
}
}
}
permanent.turnFaceDown(game, source.getControllerId());
permanent.setManifested(false);
permanent.setMorphed(true);
return permanent.isFaceDown(game);
}
return false;
}
}

View file

@ -0,0 +1,74 @@
/*
* 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.sets.weatherlight;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.costs.common.SacrificeTargetCost;
import mage.abilities.effects.common.CounterTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.filter.common.FilterControlledPermanent;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.target.TargetSpell;
import mage.target.common.TargetControlledPermanent;
/**
*
* @author djbrez
*/
public class Abjure extends CardImpl {
private static final FilterControlledPermanent filter = new FilterControlledPermanent("a blue permanent");
static {
filter.add(new ColorPredicate(ObjectColor.BLUE));
}
public Abjure(UUID ownerId) {
super(ownerId, 31, "Abjure", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{U}");
this.expansionSetCode = "WTH";
// As an additional cost to cast Abjure, sacrifice a blue permanent.
this.getSpellAbility().addCost(new SacrificeTargetCost(new TargetControlledPermanent(1,1,filter, true)));
// Counter target spell.
this.getSpellAbility().addEffect(new CounterTargetEffect());
this.getSpellAbility().addTarget(new TargetSpell());
}
public Abjure(final Abjure card) {
super(card);
}
@Override
public Abjure copy() {
return new Abjure(this);
}
}

View file

@ -27,25 +27,20 @@
*/
package mage.sets.worldwake;
import java.util.List;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostImpl;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.ColoredManaCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DontUntapInControllersNextUntapStepTargetEffect;
import mage.abilities.effects.common.TapTargetEffect;
import mage.cards.CardImpl;
import mage.constants.*;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
import mage.watchers.Watcher;
import mage.watchers.common.PermanentsEnteredBattlefieldWatcher;
/**
*
@ -58,16 +53,13 @@ public class PermafrostTrap extends CardImpl {
this.expansionSetCode = "WWK";
this.subtype.add("Trap");
// If an opponent had a green creature enter the battlefield under his or her control this turn, you may pay {U} rather than pay Permafrost Trap's mana cost.
this.getSpellAbility().addAlternativeCost(new PermafrostTrapAlternativeCost());
this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{U}"), PermafrostTrapCondition.getInstance()), new PermanentsEnteredBattlefieldWatcher());
// Tap up to two target creatures. Those creatures don't untap during their controller's next untap step.
TargetCreaturePermanent target = new TargetCreaturePermanent(0, 2);
this.getSpellAbility().addTarget(target);
this.getSpellAbility().addEffect(new PermafrostTrapEffect());
this.getSpellAbility().addWatcher(new PermafrostTrapWatcher());
this.getSpellAbility().addEffect(new TapTargetEffect());
this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2));
this.getSpellAbility().addEffect(new DontUntapInControllersNextUntapStepTargetEffect());
}
public PermafrostTrap(final PermafrostTrap card) {
@ -80,99 +72,34 @@ public class PermafrostTrap extends CardImpl {
}
}
class PermafrostTrapWatcher extends Watcher {
class PermafrostTrapCondition implements Condition {
public PermafrostTrapWatcher() {
super("PermafrostTrapWatcher", WatcherScope.GAME);
}
private static final PermafrostTrapCondition fInstance = new PermafrostTrapCondition();
public PermafrostTrapWatcher(final PermafrostTrapWatcher watcher) {
super(watcher);
}
@Override
public PermafrostTrapWatcher copy() {
return new PermafrostTrapWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (condition == true) { // no need to check - condition has already occured
return;
}
if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) {
Permanent perm = game.getPermanent(event.getTargetId());
if (perm.getCardType().contains(CardType.CREATURE) && perm.getColor(game).contains(ObjectColor.GREEN) && !perm.getControllerId().equals(controllerId)) {
condition = true;
}
}
}
@Override
public void reset() {
super.reset();
condition = false;
}
}
class PermafrostTrapAlternativeCost extends AlternativeCostImpl<Cost> {
public PermafrostTrapAlternativeCost() {
super("you may pay {U} rather than pay Permafrost Trap's mana cost");
this.add(new ColoredManaCost(ColoredManaSymbol.U));
}
public PermafrostTrapAlternativeCost(final PermafrostTrapAlternativeCost cost) {
super(cost);
}
@Override
public PermafrostTrapAlternativeCost copy() {
return new PermafrostTrapAlternativeCost(this);
}
@Override
public boolean isAvailable(Game game, Ability source) {
PermafrostTrapWatcher watcher = (PermafrostTrapWatcher) game.getState().getWatchers().get("PermafrostTrapWatcher");
if (watcher != null && watcher.conditionMet()) {
return true;
}
return false;
}
@Override
public String getText() {
return "If an opponent had a green creature enter the battlefield under his or her control this turn, you may pay {U} rather than pay Permafrost Trap's mana cost";
}
}
class PermafrostTrapEffect extends OneShotEffect {
public PermafrostTrapEffect() {
super(Outcome.Detriment);
staticText = "Tap up to two target creatures. Those creatures don't untap during their controller's next untap step";
}
public PermafrostTrapEffect(final PermafrostTrapEffect effect) {
super(effect);
public static Condition getInstance() {
return fInstance;
}
@Override
public boolean apply(Game game, Ability source) {
for (UUID targetId : this.targetPointer.getTargets(game, source)) {
Permanent creature = game.getPermanent(targetId);
if (creature != null) {
creature.tap(game);
DontUntapInControllersNextUntapStepTargetEffect effect = new DontUntapInControllersNextUntapStepTargetEffect();
effect.setTargetPointer(new FixedTarget(targetId));
game.addEffect(effect, source);
PermanentsEnteredBattlefieldWatcher watcher = (PermanentsEnteredBattlefieldWatcher) game.getState().getWatchers().get(PermanentsEnteredBattlefieldWatcher.class.getName());
if (watcher != null) {
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
List<Permanent> permanents = watcher.getThisTurnEnteringPermanents(opponentId);
if (permanents != null) {
for (Permanent permanent : permanents) {
if (permanent.getCardType().contains(CardType.CREATURE) && permanent.getColor(game).isGreen()) {
return true;
}
}
}
}
}
return false;
}
@Override
public PermafrostTrapEffect copy() {
return new PermafrostTrapEffect(this);
public String toString() {
return "If an opponent had a green creature enter the battlefield under his or her control this turn";
}
}

View file

@ -27,13 +27,12 @@
*/
package mage.sets.worldwake;
import java.util.HashSet;
import java.util.Set;
import java.util.List;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostImpl;
import mage.abilities.costs.mana.ManaCost;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.PreventionEffectData;
import mage.abilities.effects.PreventionEffectImpl;
@ -42,7 +41,6 @@ import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Rarity;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
@ -51,7 +49,7 @@ import mage.game.stack.StackObject;
import mage.players.Player;
import mage.target.TargetSource;
import mage.target.common.TargetCreatureOrPlayer;
import mage.watchers.Watcher;
import mage.watchers.common.SpellsCastWatcher;
/**
*
@ -65,13 +63,11 @@ public class RefractionTrap extends CardImpl {
this.subtype.add("Trap");
// If an opponent cast a red instant or sorcery spell this turn, you may pay {W} rather than pay Refraction Trap's mana cost.
this.getSpellAbility().addAlternativeCost(new RefractionTrapAlternativeCost());
this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{W}"), RefractionTrapCondition.getInstance()), new SpellsCastWatcher());
// Prevent the next 3 damage that a source of your choice would deal to you and/or permanents you control this turn. If damage is prevented this way, Refraction Trap deals that much damage to target creature or player.
this.getSpellAbility().addEffect(new RefractionTrapPreventDamageEffect(Duration.EndOfTurn, 3));
this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());
this.getSpellAbility().addWatcher(new RefractionTrapWatcher());
}
public RefractionTrap(final RefractionTrap card) {
@ -84,89 +80,43 @@ public class RefractionTrap extends CardImpl {
}
}
class RefractionTrapWatcher extends Watcher {
class RefractionTrapCondition implements Condition {
Set<UUID> playersMetCondition = new HashSet<>();
private static final RefractionTrapCondition fInstance = new RefractionTrapCondition();
public RefractionTrapWatcher() {
super("RefractionTrapWatcher", WatcherScope.GAME);
}
public RefractionTrapWatcher(final RefractionTrapWatcher watcher) {
super(watcher);
this.playersMetCondition.addAll(watcher.playersMetCondition);
public static Condition getInstance() {
return fInstance;
}
@Override
public RefractionTrapWatcher copy() {
return new RefractionTrapWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.SPELL_CAST) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell.getColor(game).isRed()) {
if (spell.getCardType().contains(CardType.INSTANT)
|| spell.getCardType().contains(CardType.SORCERY)) {
playersMetCondition.add(event.getPlayerId());
}
}
}
}
public boolean conditionMetForAnOpponent(UUID controllerId, Game game) {
Player controller = game.getPlayer(controllerId);
if (controller != null) {
for (UUID playerId : playersMetCondition) {
if (controller.hasOpponent(playerId, game)) {
return true;
public boolean apply(Game game, Ability source) {
SpellsCastWatcher watcher = (SpellsCastWatcher) game.getState().getWatchers().get(SpellsCastWatcher.class.getName());
if (watcher != null) {
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
List<Spell> spells = watcher.getSpellsCastThisTurn(opponentId);
if (spells != null) {
for (Spell spell : spells) {
if ((spell.getCardType().contains(CardType.SORCERY) || spell.getCardType().contains(CardType.INSTANT))
&& spell.getColor(game).isRed()) {
return true;
}
}
}
}
}
return false;
}
@Override
public void reset() {
playersMetCondition.clear();
super.reset();
}
}
class RefractionTrapAlternativeCost extends AlternativeCostImpl {
public RefractionTrapAlternativeCost() {
super("You may pay {W} rather than pay Refraction Trap's mana cost");
this.add(new ManaCostsImpl<ManaCost>("{W}"));
}
public RefractionTrapAlternativeCost(final RefractionTrapAlternativeCost cost) {
super(cost);
}
@Override
public RefractionTrapAlternativeCost copy() {
return new RefractionTrapAlternativeCost(this);
}
@Override
public boolean isAvailable(Game game, Ability source) {
RefractionTrapWatcher watcher = (RefractionTrapWatcher) game.getState().getWatchers().get("RefractionTrapWatcher");
return watcher != null && watcher.conditionMetForAnOpponent(source.getControllerId(), game);
}
@Override
public String getText() {
return "If an opponent cast a red instant or sorcery spell this turn, you may pay {W} rather than pay {this} mana cost";
public String toString() {
return "If an opponent cast a red instant or sorcery spell this turn";
}
}
class RefractionTrapPreventDamageEffect extends PreventionEffectImpl {
private final TargetSource target;
private int amount;
private final int amount;
public RefractionTrapPreventDamageEffect(Duration duration, int amount) {
super(duration, amount, false, false);

View file

@ -27,26 +27,22 @@
*/
package mage.sets.worldwake;
import java.util.List;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.WatcherScope;
import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostImpl;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.ColoredManaCost;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.ChooseNewTargetsTargetEffect;
import mage.cards.CardImpl;
import mage.constants.ColoredManaSymbol;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.filter.FilterSpell;
import mage.filter.predicate.mageobject.NumberOfTargetsPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.stack.Spell;
import mage.target.TargetSpell;
import mage.watchers.Watcher;
import mage.watchers.common.SpellsCastWatcher;
/**
*
@ -65,15 +61,14 @@ public class RicochetTrap extends CardImpl {
this.expansionSetCode = "WWK";
this.subtype.add("Trap");
// If an opponent cast a blue spell this turn, you may pay {R} rather than pay Ricochet Trap's mana cost.
this.getSpellAbility().addAlternativeCost(new RicochetTrapAlternativeCost());
this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{R}"), RicochetTrapCondition.getInstance()));
// Change the target of target spell with a single target.
this.getSpellAbility().addEffect(new ChooseNewTargetsTargetEffect(true, true));
this.getSpellAbility().addTarget(new TargetSpell(filter));
this.getSpellAbility().addWatcher(new RicochetTrapWatcher());
this.getSpellAbility().addWatcher(new SpellsCastWatcher());
}
public RicochetTrap(final RicochetTrap card) {
@ -86,70 +81,34 @@ public class RicochetTrap extends CardImpl {
}
}
class RicochetTrapWatcher extends Watcher {
class RicochetTrapCondition implements Condition {
public RicochetTrapWatcher() {
super("RicochetTrapWatcher", WatcherScope.GAME);
}
private static final RicochetTrapCondition fInstance = new RicochetTrapCondition();
public RicochetTrapWatcher(final RicochetTrapWatcher watcher) {
super(watcher);
public static Condition getInstance() {
return fInstance;
}
@Override
public RicochetTrapWatcher copy() {
return new RicochetTrapWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (condition == true) //no need to check - condition has already occured
{
return;
}
if (event.getType() == EventType.SPELL_CAST
&& game.getOpponents(controllerId).contains(event.getPlayerId())) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell.getColor(game).isBlue()) {
condition = true;
public boolean apply(Game game, Ability source) {
SpellsCastWatcher watcher = (SpellsCastWatcher) game.getState().getWatchers().get(SpellsCastWatcher.class.getName());
if (watcher != null) {
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
List<Spell> spells = watcher.getSpellsCastThisTurn(opponentId);
if (spells != null) {
for (Spell spell : spells) {
if (spell.getColor(game).isBlue()) {
return true;
}
}
}
}
}
}
@Override
public void reset() {
super.reset();
condition = false;
}
}
class RicochetTrapAlternativeCost extends AlternativeCostImpl<Cost> {
public RicochetTrapAlternativeCost() {
super("You may pay {R} rather than pay Ricochet Trap's mana cost");
this.add(new ColoredManaCost(ColoredManaSymbol.R));
}
public RicochetTrapAlternativeCost(final RicochetTrapAlternativeCost cost) {
super(cost);
}
@Override
public RicochetTrapAlternativeCost copy() {
return new RicochetTrapAlternativeCost(this);
}
@Override
public boolean isAvailable(Game game, Ability source) {
RicochetTrapWatcher watcher = (RicochetTrapWatcher) game.getState().getWatchers().get("RicochetTrapWatcher");
if (watcher != null && watcher.conditionMet()) {
return true;
}
return false;
}
@Override
public String getText() {
return "If an opponent cast a blue spell this turn, you may pay {R} rather than pay {this} mana cost";
public String toString() {
return "If an opponent cast a blue spell this turn";
}
}

View file

@ -27,17 +27,16 @@
*/
package mage.sets.worldwake;
import java.util.List;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostImpl;
import mage.abilities.costs.Cost;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.filter.common.FilterAttackingCreature;
import mage.filter.predicate.mageobject.AbilityPredicate;
import mage.game.Game;
@ -61,9 +60,8 @@ public class SlingbowTrap extends CardImpl {
this.expansionSetCode = "WWK";
this.subtype.add("Trap");
// If a black creature with flying is attacking, you may pay {G} rather than pay Slingbow Trap's mana cost.
this.getSpellAbility().addAlternativeCost(new SlingbowTrapAlternativeCost());
this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{G}"), SlingbowTrapCondition.getInstance()));
// Destroy target attacking creature with flying.
this.getSpellAbility().addEffect(new DestroyTargetEffect());
@ -80,37 +78,29 @@ public class SlingbowTrap extends CardImpl {
}
}
class SlingbowTrapAlternativeCost extends AlternativeCostImpl<Cost> {
class SlingbowTrapCondition implements Condition {
public SlingbowTrapAlternativeCost() {
super("you may pay {G} rather than pay {this}'s mana cost");
this.add(new ManaCostsImpl("{G}"));
}
private static final SlingbowTrapCondition fInstance = new SlingbowTrapCondition();
public SlingbowTrapAlternativeCost(final SlingbowTrapAlternativeCost cost) {
super(cost);
public static Condition getInstance() {
return fInstance;
}
@Override
public SlingbowTrapAlternativeCost copy() {
return new SlingbowTrapAlternativeCost(this);
}
@Override
public boolean isAvailable(Game game, Ability source) {
List<UUID> attackers = game.getCombat().getAttackers();
for (UUID creatureId : attackers) {
Permanent creature = game.getPermanent(creatureId);
if (creature.getColor(game).isBlack()
&& creature.getAbilities().contains(FlyingAbility.getInstance())) {
return true;
public boolean apply(Game game, Ability source) {
for (UUID attackingCreatureId : game.getCombat().getAttackers()) {
Permanent attackingCreature = game.getPermanent(attackingCreatureId);
if (attackingCreature != null) {
if (attackingCreature.getColor(game).isBlack() && attackingCreature.hasAbility(FlyingAbility.getInstance().getId(), game)) {
return true;
}
}
}
return false;
}
@Override
public String getText() {
return "If a black creature with flying is attacking, you may pay {G} rather than pay Slingbow Trap's mana cost";
public String toString() {
return "If a black creature with flying is attacking";
}
}

View file

@ -29,7 +29,8 @@ package mage.sets.zendikar;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostImpl;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DamageMultiEffect;
import mage.cards.CardImpl;
@ -50,7 +51,7 @@ public class ArrowVolleyTrap extends CardImpl {
this.subtype.add("Trap");
// If four or more creatures are attacking, you may pay {1}{W} rather than pay Arrow Volley Trap's mana cost.
this.getSpellAbility().addAlternativeCost(new ArrowVolleyTrapAlternativeCost());
this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{1}{W}"), ArrowVolleyTrapCondition.getInstance()));
// Arrow Volley Trap deals 5 damage divided as you choose among any number of target attacking creatures.
this.getSpellAbility().addEffect(new DamageMultiEffect(5));
@ -68,29 +69,21 @@ public class ArrowVolleyTrap extends CardImpl {
}
}
class ArrowVolleyTrapAlternativeCost extends AlternativeCostImpl {
class ArrowVolleyTrapCondition implements Condition {
public ArrowVolleyTrapAlternativeCost() {
super("you may pay {1}{W} rather than pay Arrow Volley Trap's mana cost");
this.add(new ManaCostsImpl("{1}{W}"));
}
private static final ArrowVolleyTrapCondition fInstance = new ArrowVolleyTrapCondition();
public ArrowVolleyTrapAlternativeCost(final ArrowVolleyTrapAlternativeCost cost) {
super(cost);
public static Condition getInstance() {
return fInstance;
}
@Override
public ArrowVolleyTrapAlternativeCost copy() {
return new ArrowVolleyTrapAlternativeCost(this);
}
@Override
public boolean isAvailable(Game game, Ability source) {
public boolean apply(Game game, Ability source) {
return game.getCombat().getAttackers().size() > 3;
}
@Override
public String getText() {
return "If four or more creatures are attacking, you may pay {1}{W} rather than pay {this}'s mana cost";
public String toString() {
return "If four or more creatures are attacking";
}
}

View file

@ -27,20 +27,20 @@
*/
package mage.sets.zendikar;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostImpl;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.BeastToken2;
import mage.watchers.Watcher;
import mage.watchers.common.PermanentsEnteredBattlefieldWatcher;
/**
*
@ -54,8 +54,7 @@ public class BalothCageTrap extends CardImpl {
this.subtype.add("Trap");
// If an opponent had an artifact enter the battlefield under his or her control this turn, you may pay {1}{G} rather than pay Baloth Cage Trap's mana cost.
this.getSpellAbility().addAlternativeCost(new BalothCageTrapAlternativeCost());
this.getSpellAbility().addWatcher(new BalothCageTrapWatcher());
this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{1}{G}"), BalothCageTrapCondition.getInstance()), new PermanentsEnteredBattlefieldWatcher());
// Put a 4/4 green Beast creature token onto the battlefield.
this.getSpellAbility().addEffect(new CreateTokenEffect(new BeastToken2()));
@ -71,68 +70,34 @@ public class BalothCageTrap extends CardImpl {
}
}
class BalothCageTrapWatcher extends Watcher {
class BalothCageTrapCondition implements Condition {
public BalothCageTrapWatcher() {
super("BalothCageTrapWatcher", WatcherScope.GAME);
}
private static final BalothCageTrapCondition fInstance = new BalothCageTrapCondition();
public BalothCageTrapWatcher(final BalothCageTrapWatcher watcher) {
super(watcher);
public static Condition getInstance() {
return fInstance;
}
@Override
public BalothCageTrapWatcher copy() {
return new BalothCageTrapWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (condition == true) { // no need to check - condition has already occured
return;
}
if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) {
Permanent perm = game.getPermanent(event.getTargetId());
if (perm.getCardType().contains(CardType.ARTIFACT) && !perm.getControllerId().equals(controllerId)) {
condition = true;
public boolean apply(Game game, Ability source) {
PermanentsEnteredBattlefieldWatcher watcher = (PermanentsEnteredBattlefieldWatcher) game.getState().getWatchers().get(PermanentsEnteredBattlefieldWatcher.class.getName());
if (watcher != null) {
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
List<Permanent> permanents = watcher.getThisTurnEnteringPermanents(opponentId);
if (permanents != null) {
for (Permanent permanent : permanents) {
if (permanent.getCardType().contains(CardType.ARTIFACT)) {
return true;
}
}
}
}
}
}
@Override
public void reset() {
super.reset();
condition = false;
}
}
class BalothCageTrapAlternativeCost extends AlternativeCostImpl {
public BalothCageTrapAlternativeCost() {
super("you may pay {1}{G} rather than pay Baloth Cage Trap's mana cost");
this.add(new ManaCostsImpl("{1}{G}"));
}
public BalothCageTrapAlternativeCost(final BalothCageTrapAlternativeCost cost) {
super(cost);
}
@Override
public BalothCageTrapAlternativeCost copy() {
return new BalothCageTrapAlternativeCost(this);
}
@Override
public boolean isAvailable(Game game, Ability source) {
BalothCageTrapWatcher watcher = (BalothCageTrapWatcher) game.getState().getWatchers().get("BalothCageTrapWatcher");
if (watcher != null && watcher.conditionMet()) {
return true;
}
return false;
}
@Override
public String getText() {
return "If an opponent had an artifact enter the battlefield under his or her control this turn, you may pay {1}{G} rather than pay Baloth Cage Trap's mana cost";
public String toString() {
return "If an opponent had an artifact enter the battlefield under his or her control this turn";
}
}

View file

@ -27,25 +27,24 @@
*/
package mage.sets.zendikar;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.ColoredManaSymbol;
import mage.constants.Rarity;
import mage.constants.WatcherScope;
import mage.constants.Zone;
import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostImpl;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.ColoredManaCost;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.SnakeToken;
import mage.game.stack.StackObject;
import mage.players.Player;
import mage.watchers.Watcher;
/**
@ -59,11 +58,9 @@ public class CobraTrap extends CardImpl {
this.expansionSetCode = "ZEN";
this.subtype.add("Trap");
// If a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled, you may pay {G} rather than pay Cobra Trap's mana cost.
this.getSpellAbility().addAlternativeCost(
new CobraTrapAlternativeCost());
this.getSpellAbility().addWatcher(new CobraTrapWatcher());
this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{G}"), CobraTrapCondition.getInstance()), new CobraTrapWatcher());
// Put four 1/1 green Snake creature tokens onto the battlefield.
this.getSpellAbility().addEffect(new CreateTokenEffect(new SnakeToken(), 4));
}
@ -78,10 +75,33 @@ public class CobraTrap extends CardImpl {
}
}
class CobraTrapCondition implements Condition {
private static final CobraTrapCondition fInstance = new CobraTrapCondition();
public static Condition getInstance() {
return fInstance;
}
@Override
public boolean apply(Game game, Ability source) {
CobraTrapWatcher watcher = (CobraTrapWatcher) game.getState().getWatchers().get(CobraTrapWatcher.class.getName());
return watcher != null && watcher.conditionMet(source.getControllerId());
}
@Override
public String toString() {
return "If a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled";
}
}
class CobraTrapWatcher extends Watcher {
Set<UUID> players = new HashSet<>();
public CobraTrapWatcher() {
super("noncreature permanent destroyed", WatcherScope.PLAYER);
super(CobraTrapWatcher.class.getName(), WatcherScope.GAME);
}
public CobraTrapWatcher(final CobraTrapWatcher watcher) {
@ -95,51 +115,26 @@ class CobraTrapWatcher extends Watcher {
@Override
public void watch(GameEvent event, Game game) {
if (condition == true) { // no need to check - condition has already occured
return;
}
Player player = game.getPlayer(controllerId);
if (player != null && event.getType() == EventType.DESTROYED_PERMANENT) {
Permanent perm = (Permanent) game.getLastKnownInformation(event.getTargetId(), Zone.BATTLEFIELD);
if (event.getType() == EventType.DESTROYED_PERMANENT) {
Permanent perm = (Permanent) game.getPermanentOrLKIBattlefield(event.getTargetId()); // can regenerate or be indestructible
if (perm != null && !perm.getCardType().contains(CardType.CREATURE)) {
if (game.getStack().size() > 0) {
StackObject spell = game.getStack().getStackObject(event.getSourceId());
if (spell != null && game.getOpponents(controllerId).contains(spell.getControllerId())) {
condition = true;
if (spell != null && game.getOpponents(perm.getControllerId()).contains(spell.getControllerId())) {
players.add(perm.getControllerId());
}
}
}
}
}
}
class CobraTrapAlternativeCost extends AlternativeCostImpl<Cost> {
public CobraTrapAlternativeCost() {
super("you may pay {G} rather than pay Cobra Trap's mana cost");
this.add(new ColoredManaCost(ColoredManaSymbol.G));
}
public CobraTrapAlternativeCost(final CobraTrapAlternativeCost cost) {
super(cost);
}
@Override
public CobraTrapAlternativeCost copy() {
return new CobraTrapAlternativeCost(this);
public void reset() {
super.reset();
players.clear();
}
@Override
public boolean isAvailable(Game game, Ability source) {
Watcher watcher = game.getState().getWatchers().get("noncreature permanent destroyed", source.getControllerId());
if (watcher != null && watcher.conditionMet()) {
return true;
}
return false;
}
@Override
public String getText() {
return "If a noncreature permanent under your control was destroyed this turn by a spell or ability an opponent controlled, you may pay {G} rather than pay Cobra Trap's mana cost";
public boolean conditionMet(UUID playerId) {
return players.contains(playerId);
}
}

View file

@ -28,15 +28,14 @@
package mage.sets.zendikar;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostImpl;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.WatcherScope;
import mage.game.Game;
import mage.game.events.GameEvent;
@ -54,11 +53,10 @@ public class InfernoTrap extends CardImpl {
this.expansionSetCode = "ZEN";
this.subtype.add("Trap");
// If you've been dealt damage by two or more creatures this turn, you may pay {R} rather than pay Inferno Trap's mana cost.
this.getSpellAbility().addAlternativeCost(new InfernoTrapAlternativeCost());
this.getSpellAbility().addWatcher(new ControllerDamagedByCreatureWatcher());
// Inferno Trap deals 4 damage to target creature.
this.getSpellAbility().addEffect(new DamageTargetEffect(4));
this.getSpellAbility().addTarget(new TargetCreaturePermanent());

View file

@ -27,26 +27,23 @@
*/
package mage.sets.zendikar;
import java.util.HashMap;
import java.util.Map;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.AlternativeCostImpl;
import mage.abilities.costs.Cost;
import mage.abilities.condition.Condition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.DamageAllEffect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.WatcherScope;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.common.FilterLandPermanent;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.common.TargetLandPermanent;
import mage.watchers.Watcher;
import mage.watchers.common.PermanentsEnteredBattlefieldWatcher;
/**
*
@ -60,8 +57,7 @@ public class LavaballTrap extends CardImpl {
this.subtype.add("Trap");
// If an opponent had two or more lands enter the battlefield under his or her control this turn, you may pay {3}{R}{R} rather than pay Lavaball Trap's mana cost.
this.getSpellAbility().addAlternativeCost(new LavaballTrapAlternativeCost());
this.getSpellAbility().addWatcher(new LavaballTrapWatcher());
this.addAbility(new AlternativeCostSourceAbility(new ManaCostsImpl("{3}{R}{R}"), LavaballTrapCondition.getInstance()), new PermanentsEnteredBattlefieldWatcher());
// Destroy two target lands. Lavaball Trap deals 4 damage to each creature.
this.getSpellAbility().addEffect(new DestroyTargetEffect());
@ -80,87 +76,38 @@ public class LavaballTrap extends CardImpl {
}
}
class LavaballTrapWatcher extends Watcher {
class LavaballTrapCondition implements Condition {
private Map<UUID, Integer> amountOfLandsPlayedThisTurn = new HashMap<>();
private static final LavaballTrapCondition fInstance = new LavaballTrapCondition();
public LavaballTrapWatcher() {
super("LavaballTrapWatcher", WatcherScope.GAME);
}
public LavaballTrapWatcher(final LavaballTrapWatcher watcher) {
super(watcher);
for (Map.Entry<UUID, Integer> entry : watcher.amountOfLandsPlayedThisTurn.entrySet()) {
amountOfLandsPlayedThisTurn.put(entry.getKey(), entry.getValue());
}
public static Condition getInstance() {
return fInstance;
}
@Override
public LavaballTrapWatcher copy() {
return new LavaballTrapWatcher(this);
}
@Override
public void watch(GameEvent event, Game game) {
if (event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD) {
Permanent perm = game.getPermanent(event.getTargetId());
if (perm.getCardType().contains(CardType.LAND)) {
Integer amount = amountOfLandsPlayedThisTurn.get(perm.getControllerId());
if (amount == null) {
amount = 1;
} else {
++amount;
public boolean apply(Game game, Ability source) {
PermanentsEnteredBattlefieldWatcher watcher = (PermanentsEnteredBattlefieldWatcher) game.getState().getWatchers().get(PermanentsEnteredBattlefieldWatcher.class.getName());
if (watcher != null) {
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
List<Permanent> permanents = watcher.getThisTurnEnteringPermanents(opponentId);
if (permanents != null) {
int count = 0;
for (Permanent permanent : permanents) {
if (permanent.getCardType().contains(CardType.LAND)) {
count++;
if (count == 2) {
return true;
}
}
}
}
amountOfLandsPlayedThisTurn.put(perm.getControllerId(), amount);
}
}
}
public int maxLandsAnOpponentPlayedThisTurn(UUID playerId, Game game) {
int maxLands = 0;
for (UUID opponentId : game.getOpponents(playerId)) {
Integer amount = amountOfLandsPlayedThisTurn.get(opponentId);
if (amount != null && amount > maxLands) {
maxLands = amount;
}
}
return maxLands;
}
@Override
public void reset() {
super.reset();
amountOfLandsPlayedThisTurn.clear();
}
}
class LavaballTrapAlternativeCost extends AlternativeCostImpl<Cost> {
public LavaballTrapAlternativeCost() {
super("you may pay {3}{R}{R} rather than pay Lavaball Trap's mana cost");
this.add(new ManaCostsImpl("{3}{R}{R}"));
}
public LavaballTrapAlternativeCost(final LavaballTrapAlternativeCost cost) {
super(cost);
}
@Override
public LavaballTrapAlternativeCost copy() {
return new LavaballTrapAlternativeCost(this);
}
@Override
public boolean isAvailable(Game game, Ability source) {
LavaballTrapWatcher watcher = (LavaballTrapWatcher) game.getState().getWatchers().get("LavaballTrapWatcher");
if (watcher != null && watcher.maxLandsAnOpponentPlayedThisTurn(source.getControllerId(), game) > 1) {
return true;
}
return false;
}
@Override
public String getText() {
return "If an opponent had two or more lands enter the battlefield under his or her control this turn, you may pay {3}{R}{R} rather than pay Lavaball Trap's mana cost";
public String toString() {
return "If an opponent had two or more lands enter the battlefield under his or her control this turn";
}
}

Some files were not shown because too many files have changed in this diff Show more