Merge pull request #45 from magefree/master

Merge https://github.com/magefree/mage
This commit is contained in:
L_J 2018-03-10 00:02:59 +01:00 committed by GitHub
commit 4b1c631044
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
197 changed files with 6318 additions and 1133 deletions

1
.gitignore vendored
View file

@ -135,3 +135,4 @@ client_secrets.json
dependency-reduced-pom.xml
mage-bundle
/Mage.Client/game-*.json

View file

@ -15,6 +15,7 @@
<name>Mage Client</name>
<dependencies>
<dependency>
<groupId>org.mage</groupId>
<artifactId>mage</artifactId>
@ -68,6 +69,11 @@
<artifactId>jetlang</artifactId>
<version>0.2.9</version>
</dependency>
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.286</version>
</dependency>
<dependency>
<groupId>com.jgoodies</groupId>
<artifactId>forms</artifactId>
@ -147,14 +153,9 @@
https://stackoverflow.com/questions/714243/sax2-driver-class-org-apache-crimson-parser-xmlreaderimpl-not-found-when-using
-->
<dependency>
<groupId>batik</groupId>
<artifactId>batik-transcoder</artifactId>
<version>1.6-1</version>
</dependency>
<dependency>
<groupId>crimson</groupId>
<artifactId>crimson</artifactId>
<version>1.1.3</version>
<groupId>org.apache.xmlgraphics</groupId>
<artifactId>batik-transcoder</artifactId>
<version>1.7</version>
</dependency>
<!-- svg support end -->
<dependency>

View file

@ -645,6 +645,8 @@
<EmptySpace max="-2" attributes="0"/>
<Component id="chkRules" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Component id="chkUnique" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Component id="cardCountLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="cardCount" min="-2" pref="48" max="-2" attributes="0"/>
@ -659,6 +661,7 @@
<Group type="103" groupAlignment="0" attributes="0">
<Component id="chkTypes" max="32767" attributes="0"/>
<Component id="chkRules" alignment="1" max="32767" attributes="0"/>
<Component id="chkUnique" alignment="1" max="32767" attributes="0"/>
<Component id="chkNames" alignment="1" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
@ -846,6 +849,29 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="chkRulesActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="chkUnique">
<Properties>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" value="Unique"/>
<Property name="toolTipText" type="java.lang.String" value="Singleton rules."/>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="4"/>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[69, 16]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[69, 16]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[69, 16]"/>
</Property>
<Property name="verticalTextPosition" type="int" value="3"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="chkUniqueActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="jButtonSearch">
<Properties>
<Property name="text" type="java.lang.String" value="Search"/>

View file

@ -232,7 +232,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
FilterCard filter = new FilterCard();
String name = jTextFieldSearch.getText().trim();
filter.add(new CardTextPredicate(name, chkNames.isSelected(), chkTypes.isSelected(), chkRules.isSelected()));
filter.add(new CardTextPredicate(name, chkNames.isSelected(), chkTypes.isSelected(), chkRules.isSelected(), chkUnique.isSelected()));
if (limited) {
ArrayList<Predicate<MageObject>> predicates = new ArrayList<>();
@ -543,6 +543,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
chkNames = new javax.swing.JCheckBox();
chkTypes = new javax.swing.JCheckBox();
chkRules = new javax.swing.JCheckBox();
chkUnique = new javax.swing.JCheckBox();
jButtonSearch = new javax.swing.JButton();
jButtonClean = new javax.swing.JButton();
cardCountLabel = new javax.swing.JLabel();
@ -1062,6 +1063,22 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
chkRulesActionPerformed(evt);
}
});
chkUnique.setSelected(true);
chkUnique.setText("Unique");
chkUnique.setToolTipText("Singleton results only.");
chkUnique.setFocusable(false);
chkUnique.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
chkUnique.setMaximumSize(new java.awt.Dimension(69, 16));
chkUnique.setMinimumSize(new java.awt.Dimension(69, 16));
chkUnique.setPreferredSize(new java.awt.Dimension(69, 16));
chkUnique.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
chkUnique.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
chkUniqueActionPerformed(evt);
}
});
jButtonSearch.setText("Search");
jButtonSearch.setToolTipText("Performs the search.");
@ -1109,6 +1126,8 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
.addComponent(chkTypes, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkRules, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkUnique, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(5, 5, 5)
.addComponent(cardCountLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@ -1122,6 +1141,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
.addGroup(cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(chkTypes, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(chkRules, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(chkUnique, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(chkNames, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(cardSelectorBottomPanelLayout.createSequentialGroup()
.addGroup(cardSelectorBottomPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -1341,6 +1361,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
// TODO add your handling code here:
}//GEN-LAST:event_chkRulesActionPerformed
private void chkUniqueActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_chkRulesActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_chkRulesActionPerformed
private void btnExpansionSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExpansionSearchActionPerformed
FastSearchUtil.showFastSearchForStringComboBox(cbExpansionSet, "Select set or expansion");
}//GEN-LAST:event_btnExpansionSearchActionPerformed
@ -1418,6 +1442,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
private javax.swing.JCheckBox chkPennyDreadful;
private javax.swing.JCheckBox chkPiles;
private javax.swing.JCheckBox chkRules;
private javax.swing.JCheckBox chkUnique;
private javax.swing.JCheckBox chkTypes;
private javax.swing.JButton jButtonAddToMain;
private javax.swing.JButton jButtonAddToSideboard;

View file

@ -41,6 +41,9 @@
<Component id="lblFreeMulligans" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="spnFreeMulligans" min="-2" pref="50" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="chkSpectatorsAllowed" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="13" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="txtName" min="-2" pref="178" max="-2" attributes="0"/>
@ -152,6 +155,7 @@
<Component id="spnFreeMulligans" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblFreeMulligans" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="chkRollbackTurnsAllowed" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="chkSpectatorsAllowed" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="cbGameType" alignment="3" min="-2" max="-2" attributes="0"/>
@ -274,6 +278,13 @@
<Property name="toolTipText" type="java.lang.String" value="The number of mulligans a player can use without decreasing the number of drawn cards."/>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="chkSpectatorsAllowed">
<Properties>
<Property name="text" type="java.lang.String" value="Spectators allowed"/>
<Property name="toolTipText" type="java.lang.String" value="Allow spectators to view your game."/>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="spnFreeMulligans">
</Component>
<Component class="javax.swing.JLabel" name="lblNumPlayers">

View file

@ -102,6 +102,7 @@ public class NewTableDialog extends MageDialog {
lblGameType = new javax.swing.JLabel();
cbGameType = new javax.swing.JComboBox();
chkRollbackTurnsAllowed = new javax.swing.JCheckBox();
chkSpectatorsAllowed = new javax.swing.JCheckBox();
chkRated = new javax.swing.JCheckBox();
lblFreeMulligans = new javax.swing.JLabel();
spnFreeMulligans = new javax.swing.JSpinner();
@ -151,6 +152,9 @@ public class NewTableDialog extends MageDialog {
chkRollbackTurnsAllowed.setText("Allow rollbacks");
chkRollbackTurnsAllowed.setToolTipText("<HTML>Allow to rollback to the start of previous turns<br>\nif all players agree.\n");
chkSpectatorsAllowed.setText("Allow Spectators");
chkSpectatorsAllowed.setToolTipText("<HTML>Allow spectators to watch.\n");
chkRated.setText("Rated");
chkRated.setToolTipText("Indicates if matches will be rated.");
@ -231,7 +235,9 @@ public class NewTableDialog extends MageDialog {
.addGap(13, 13, 13)
.addComponent(lblFreeMulligans)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, 50, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(13, 13, 13)
.addComponent(chkSpectatorsAllowed))
.addGroup(layout.createSequentialGroup()
.addComponent(txtName, javax.swing.GroupLayout.PREFERRED_SIZE, 178, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
@ -325,7 +331,8 @@ public class NewTableDialog extends MageDialog {
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(spnFreeMulligans, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblFreeMulligans)
.addComponent(chkRollbackTurnsAllowed))
.addComponent(chkRollbackTurnsAllowed)
.addComponent(chkSpectatorsAllowed))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(cbGameType, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblGameType)))
@ -401,6 +408,7 @@ public class NewTableDialog extends MageDialog {
options.setRange((RangeOfInfluence) this.cbRange.getSelectedItem());
options.setWinsNeeded((Integer) this.spnNumWins.getValue());
options.setRollbackTurnsAllowed(chkRollbackTurnsAllowed.isSelected());
options.setSpectatorsAllowed(chkSpectatorsAllowed.isSelected());
options.setRated(chkRated.isSelected());
options.setFreeMulligans((Integer) this.spnFreeMulligans.getValue());
options.setPassword(this.txtPassword.getText());
@ -658,6 +666,7 @@ public class NewTableDialog extends MageDialog {
}
this.spnNumWins.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_WINS + versionStr, "2")));
this.chkRollbackTurnsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED + versionStr, "Yes").equals("Yes"));
this.chkSpectatorsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_SPECTATORS_ALLOWED + versionStr, "Yes").equals("Yes"));
this.chkRated.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_RATED + versionStr, "No").equals("Yes"));
this.spnFreeMulligans.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS + versionStr, "0")));
@ -739,6 +748,7 @@ public class NewTableDialog extends MageDialog {
private javax.swing.JComboBox cbSkillLevel;
private javax.swing.JComboBox cbTimeLimit;
private javax.swing.JCheckBox chkRollbackTurnsAllowed;
private javax.swing.JCheckBox chkSpectatorsAllowed;
private javax.swing.JCheckBox chkRated;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;

View file

@ -229,6 +229,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_NEW_TABLE_GAME_TYPE = "newTableGameType";
public static final String KEY_NEW_TABLE_NUMBER_OF_WINS = "newTableNumberOfWins";
public static final String KEY_NEW_TABLE_ROLLBACK_TURNS_ALLOWED = "newTableRollbackTurnsAllowed";
public static final String KEY_NEW_TABLE_SPECTATORS_ALLOWED = "newTableSpectatorsAllowed";
public static final String KEY_NEW_TABLE_NUMBER_OF_FREE_MULLIGANS = "newTableNumberOfFreeMulligans";
public static final String KEY_NEW_TABLE_DECK_FILE = "newTableDeckFile";
public static final String KEY_NEW_TABLE_RANGE = "newTableRange";

View file

@ -64,7 +64,6 @@ public class FeedbackPanel extends javax.swing.JPanel {
private static final Logger LOGGER = Logger.getLogger(FeedbackPanel.class);
public enum FeedbackMode {
INFORM, QUESTION, CONFIRM, CANCEL, SELECT, END
}

View file

@ -394,7 +394,7 @@ public class HelperPanel extends JPanel {
}
} else {
// inform about other players
this.setOpaque(false);
this.mainPanel.setOpaque(false);
}
if (buttons.size() == 0) {

View file

@ -47,6 +47,8 @@ import mage.client.util.audio.AudioManager;
import mage.client.util.object.SaveObjectUtil;
import mage.interfaces.callback.CallbackClient;
import mage.interfaces.callback.ClientCallback;
import mage.remote.ActionData;
import mage.remote.Session;
import mage.utils.CompressUtil;
import mage.view.*;
import mage.view.ChatMessage.MessageType;
@ -102,7 +104,6 @@ public class CallbackClientImpl implements CallbackClient {
break;
case CHATMESSAGE: {
ChatMessage message = (ChatMessage) callback.getData();
// Drop messages from ignored users
if (message.getUsername() != null && IgnoreList.IGNORED_MESSAGE_TYPES.contains(message.getMessageType())) {
final String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> "");
@ -183,13 +184,22 @@ public class CallbackClientImpl implements CallbackClient {
case GAME_INIT: {
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_INIT", callback.getObjectId(), (GameView) callback.getData());
panel.init((GameView) callback.getData());
}
break;
}
case GAME_OVER: {
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData());
ActionData actionData = appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData());
String logFileName = "game-" + actionData.gameId + ".json";
S3Uploader.upload(logFileName, actionData.gameId.toString());
panel.endMessage((String) callback.getData(), callback.getMessageId());
}
break;
@ -201,6 +211,7 @@ public class CallbackClientImpl implements CallbackClient {
GameClientMessage message = (GameClientMessage) callback.getData();
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_ASK", callback.getObjectId(), message);
panel.ask(message.getMessage(), message.getGameView(), callback.getMessageId(), message.getOptions());
}
break;
@ -208,8 +219,10 @@ public class CallbackClientImpl implements CallbackClient {
case GAME_TARGET: // e.g. Pick triggered ability
{
GameClientMessage message = (GameClientMessage) callback.getData();
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_TARGET", callback.getObjectId(), message);
panel.pickTarget(message.getMessage(), message.getCardsView(), message.getGameView(),
message.getTargets(), message.isFlag(), message.getOptions(), callback.getMessageId());
}
@ -217,8 +230,10 @@ public class CallbackClientImpl implements CallbackClient {
}
case GAME_SELECT: {
GameClientMessage message = (GameClientMessage) callback.getData();
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_SELECT", callback.getObjectId(), message);
panel.select(message.getMessage(), message.getGameView(), callback.getMessageId(), message.getOptions());
}
break;
@ -226,6 +241,7 @@ public class CallbackClientImpl implements CallbackClient {
case GAME_CHOOSE_ABILITY: {
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_CHOOSE_PILE", callback.getObjectId(), callback.getData());
panel.pickAbility((AbilityPickerView) callback.getData());
}
break;
@ -234,15 +250,18 @@ public class CallbackClientImpl implements CallbackClient {
GameClientMessage message = (GameClientMessage) callback.getData();
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_CHOOSE_PILE", callback.getObjectId(), message);
panel.pickPile(message.getMessage(), message.getPile1(), message.getPile2());
}
break;
}
case GAME_CHOOSE_CHOICE: {
GameClientMessage message = (GameClientMessage) callback.getData();
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_CHOOSE_CHOICE", callback.getObjectId(), message);
panel.getChoice(message.getChoice(), callback.getObjectId());
}
break;
@ -251,35 +270,45 @@ public class CallbackClientImpl implements CallbackClient {
GameClientMessage message = (GameClientMessage) callback.getData();
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_PLAY_MANA", callback.getObjectId(), message);
panel.playMana(message.getMessage(), message.getGameView(), message.getOptions(), callback.getMessageId());
}
break;
}
case GAME_PLAY_XMANA: {
GameClientMessage message = (GameClientMessage) callback.getData();
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_PLAY_XMANA", callback.getObjectId(), message);
panel.playXMana(message.getMessage(), message.getGameView(), callback.getMessageId());
}
break;
}
case GAME_GET_AMOUNT: {
GameClientMessage message = (GameClientMessage) callback.getData();
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_GET_AMOUNT", callback.getObjectId(), message);
panel.getAmount(message.getMin(), message.getMax(), message.getMessage());
}
break;
}
case GAME_UPDATE: {
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_UPDATE", callback.getObjectId(), callback.getData());
panel.updateGame((GameView) callback.getData());
}
break;
}
case END_GAME_INFO:
MageFrame.getInstance().showGameEndDialog((GameEndView) callback.getData());
break;
case SHOW_USERMESSAGE:
List<String> messageData = (List<String>) callback.getData();
@ -293,6 +322,7 @@ public class CallbackClientImpl implements CallbackClient {
GameClientMessage message = (GameClientMessage) callback.getData();
GamePanel panel = MageFrame.getGame(callback.getObjectId());
if (panel != null) {
appendJsonEvent("GAME_INFORM", callback.getObjectId(), message);
panel.inform(message.getMessage(), message.getGameView(), callback.getMessageId());
}
}
@ -376,6 +406,14 @@ public class CallbackClientImpl implements CallbackClient {
});
}
private ActionData appendJsonEvent(String name, UUID gameId, Object value) {
Session session = SessionHandler.getSession();
ActionData actionData = new ActionData(name, gameId);
actionData.value = value;
session.appendJsonLog(actionData);
return actionData;
}
private void createChatStartMessage(ChatPanelBasic chatPanel) {
chatPanel.setStartMessageDone(true);
ChatPanelBasic usedPanel = chatPanel;

View file

@ -0,0 +1,45 @@
package mage.client.remote;
import com.amazonaws.AmazonClientException;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.transfer.TransferManager;
import com.amazonaws.services.s3.transfer.Upload;
import java.io.File;
import org.apache.log4j.Logger;
public class S3Uploader {
private static final Logger logger = Logger.getLogger(S3Uploader.class);
public static Boolean upload(String filePath, String keyName) throws Exception {
String existingBucketName = System.getenv("S3_BUCKET") != null ? System.getenv("S3_BUCKET")
: "xmage-game-logs-dev";
String accessKeyId = System.getenv("AWS_ACCESS_ID");
String secretKeyId = System.getenv("AWS_SECRET_KEY");
if (accessKeyId == null || "".equals(accessKeyId)
|| secretKeyId == null || "".equals(secretKeyId)
|| existingBucketName == null || "".equals(existingBucketName)) {
logger.info("Aborting json log sync.");
return false;
}
String path = new File("./" + filePath).getCanonicalPath();
logger.info("Syncing " + path + " to bucket: " + existingBucketName + " with AWS Access Id: " + accessKeyId);
BasicAWSCredentials awsCreds = new BasicAWSCredentials(accessKeyId, secretKeyId);
TransferManager tm = new TransferManager(awsCreds);
Upload upload = tm.upload(existingBucketName, "/game/" + keyName + ".json", new File(path));
try {
upload.waitForUploadResult();
logger.info("Sync Complete For " + path + " to bucket: " + existingBucketName + " with AWS Access Id: " + accessKeyId);
new File(path);
return true;
} catch (AmazonClientException amazonClientException) {
logger.fatal("Unable to upload file, upload was aborted.", amazonClientException);
return false;
}
}
}

View file

@ -1444,11 +1444,14 @@ class TableTableModel extends AbstractTableModel {
if (tables[arg0].isTournament()) {
return "Show";
} else {
owner = tables[arg0].getControllerName();
owner = tables[arg0].getControllerName();
if (SessionHandler.getSession() != null && owner.equals(SessionHandler.getUserName())) {
return "";
}
return "Watch";
if (tables[arg0].getSpectatorsAllowed()) {
return "Watch";
}
return "";
}
default:
return "";

View file

@ -302,7 +302,7 @@ public class CardPanelRenderImpl extends CardPanel {
= new CardPanelAttributes(cardWidth, cardHeight, isChoosable(), isSelected());
// Draw card itself
cardRenderer.draw(g2d, attribs);
cardRenderer.draw(g2d, attribs, image);
// Done
g2d.dispose();

View file

@ -17,10 +17,10 @@ import mage.view.PermanentView;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.awt.image.RasterFormatException;
import java.util.ArrayList;
import java.util.List;
import java.awt.image.BufferedImage;
/**
* @author stravant@gmail.com
@ -201,7 +201,8 @@ public abstract class CardRenderer {
// The Draw Method
// The draw method takes the information caculated by the constructor
// and uses it to draw to a concrete size of card and graphics.
public void draw(Graphics2D g, CardPanelAttributes attribs) {
public void draw(Graphics2D g, CardPanelAttributes attribs, BufferedImage image) {
// Pre template method layout, to calculate shared layout info
layout(attribs.cardWidth, attribs.cardHeight);
isSelected = attribs.isSelected;
@ -211,7 +212,7 @@ public abstract class CardRenderer {
drawBorder(g);
drawBackground(g);
drawArt(g);
drawFrame(g);
drawFrame(g, image);
if (!cardView.isAbility()) {
drawOverlays(g);
drawCounters(g);
@ -226,7 +227,7 @@ public abstract class CardRenderer {
protected abstract void drawArt(Graphics2D g);
protected abstract void drawFrame(Graphics2D g);
protected abstract void drawFrame(Graphics2D g, BufferedImage image);
// Template methods that are possible to override, but unlikely to be
// overridden.
@ -462,22 +463,44 @@ public abstract class CardRenderer {
}
} else {
StringBuilder sbType = new StringBuilder();
for (SuperType superType : cardView.getSuperTypes()) {
sbType.append(superType).append(' ');
}
for (CardType cardType : cardView.getCardTypes()) {
sbType.append(cardType.toString()).append(' ');
}
if (!cardView.getSubTypes().isEmpty()) {
sbType.append("- ");
for (SubType subType : cardView.getSubTypes()) {
sbType.append(subType).append(' ');
String spType = getCardSuperTypeLine();
String subType = getCardSubTypeLine();
if (spType.equalsIgnoreCase("")) {
sbType.append(subType);
} else {
sbType.append(spType);
if (!subType.equalsIgnoreCase("")) {
sbType.append("- ");
sbType.append(subType);
}
}
return sbType.toString();
}
}
protected String getCardSuperTypeLine() {
StringBuilder spType = new StringBuilder();
for (SuperType superType : cardView.getSuperTypes()) {
spType.append(superType).append(' ');
}
for (CardType cardType : cardView.getCardTypes()) {
spType.append(cardType.toString()).append(' ');
}
return spType.toString();
}
protected String getCardSubTypeLine() {
StringBuilder subType = new StringBuilder();
if (!cardView.getSubTypes().isEmpty()) {
for (SubType sType : cardView.getSubTypes()) {
subType.append(sType).append(' ');
}
}
return subType.toString();
}
// Set the card art image (CardPanel will give it to us when it
// is loaded and ready)
public void setArtImage(Image image) {

View file

@ -51,8 +51,8 @@ public final class CardRendererUtils {
// Return the buffered image
return bimage;
}
private static Color abitbrighter(Color c) {
public static Color abitbrighter(Color c) {
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
@ -68,7 +68,7 @@ public final class CardRendererUtils {
alpha);
}
private static Color abitdarker(Color c) {
public static Color abitdarker(Color c) {
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
@ -108,6 +108,35 @@ public final class CardRendererUtils {
g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2);
}
public static void drawZendikarLandBox(Graphics2D g, int x, int y, int w, int h, int bevel, Paint border, Paint fill) {
g.setColor(new Color(0, 0, 0, 150));
g.drawOval(x - 1, y, bevel * 2, h);
g.setPaint(border);
g.drawOval(x, y, bevel * 2 - 1, h - 1);
g.drawOval(x + w - bevel * 2, y, bevel * 2 - 1, h - 1);
g.drawOval(x + 1, y + 1, bevel * 2 - 3, h - 3);
g.drawOval(x + 1 + w - bevel * 2, y + 1, bevel * 2 - 3, h - 3);
// The big circle in the middle.. (diameter=2+1/4 of height) - 3/4 above line, 1/2 below 0.75 + .5 + 1= 2.25 = 9/4
g.drawOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4);
g.drawRect(x + bevel, y, w - 2 * bevel, h - 1);
g.drawRect(x + 1 + bevel, y + 1, w - 2 * bevel - 2, h - 3);
g.setPaint(fill);
g.setColor(abitbrighter(g.getColor()));
g.drawLine(x + 1 + bevel, y + 1, x + 1 + bevel + w - 2 * bevel - 2, y + 1);
g.setPaint(fill);
g.setColor(abitdarker(g.getColor()));
g.drawLine(x + 1 + bevel, y + h - 2, x + 1 + bevel + w - 2 * bevel - 2, y + h - 2);
g.fillOval(x + 2, y + 2, bevel * 2 - 4, h - 4);
g.fillOval(x + 2 + w - bevel * 2, y + 2, bevel * 2 - 4, h - 4);
g.fillRect(x + bevel, y + 2, w - 2 * bevel, h - 4);
g.fillOval(x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4);
}
// Get the width of a mana cost rendered with ManaSymbols.draw
public static int getManaCostWidth(String manaCost, int symbolSize) {
int width = 0;

View file

@ -7,7 +7,11 @@ package org.mage.card.arcane;
import java.awt.*;
import java.awt.font.*;
import java.awt.geom.Arc2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import java.awt.geom.Rectangle2D;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
@ -68,6 +72,13 @@ public class ModernCardRenderer extends CardRenderer {
BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage());
return new TexturePaint(img, new Rectangle(0, 0, img.getWidth(), img.getHeight()));
}
private static BufferedImage loadBackgroundImage(String name) {
URL url = ModernCardRenderer.class.getResource("/cardrender/background_texture_" + name + ".png");
ImageIcon icon = new ImageIcon(url);
BufferedImage img = CardRendererUtils.toBufferedImage(icon.getImage());
return img;
}
private static BufferedImage loadFramePart(String name) {
URL url = ModernCardRenderer.class.getResource("/cardrender/" + name + ".png");
@ -97,7 +108,18 @@ public class ModernCardRenderer extends CardRenderer {
public static final Paint BG_TEXTURE_ARTIFACT = loadBackgroundTexture("artifact");
public static final Paint BG_TEXTURE_LAND = loadBackgroundTexture("land");
public static final Paint BG_TEXTURE_VEHICLE = loadBackgroundTexture("vehicle");
public static final BufferedImage BG_IMG_WHITE = loadBackgroundImage("white");
public static final BufferedImage BG_IMG_BLUE = loadBackgroundImage("blue");
public static final BufferedImage BG_IMG_BLACK = loadBackgroundImage("black");
public static final BufferedImage BG_IMG_RED = loadBackgroundImage("red");
public static final BufferedImage BG_IMG_GREEN = loadBackgroundImage("green");
public static final BufferedImage BG_IMG_GOLD = loadBackgroundImage("gold");
public static final BufferedImage BG_IMG_ARTIFACT = loadBackgroundImage("artifact");
public static final BufferedImage BG_IMG_LAND = loadBackgroundImage("land");
public static final BufferedImage BG_IMG_VEHICLE = loadBackgroundImage("vehicle");
public static final BufferedImage BG_IMG_COLORLESS = loadBackgroundImage("colorless");
public static final BufferedImage FRAME_INVENTION = loadFramePart("invention_frame");
public static final Color BORDER_WHITE = new Color(216, 203, 188);
@ -279,27 +301,30 @@ public class ModernCardRenderer extends CardRenderer {
// Just draw a brown rectangle
drawCardBack(g);
} else {
BufferedImage bufferedImage = new BufferedImage(300, 300, BufferedImage.TYPE_INT_RGB);
// Set texture to paint with
g.setPaint(getBackgroundPaint(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes()));
BufferedImage bg = getBackgroundImage(cardView.getColor(), cardView.getCardTypes(), cardView.getSubTypes());
if (bg == null) {
return;
}
int bgw = bg.getWidth();
int bgh = bg.getHeight();
// Draw main part (most of card)
g.fillRoundRect(
borderWidth, borderWidth,
RoundRectangle2D rr = new RoundRectangle2D.Double(borderWidth, borderWidth,
cardWidth - borderWidth * 2, cardHeight - borderWidth * 4 - cornerRadius * 2,
cornerRadius - 1, cornerRadius - 1);
Area a = new Area(rr);
// Draw the M15 rounded "swoosh" at the bottom
g.fillRoundRect(
borderWidth, cardHeight - borderWidth * 4 - cornerRadius * 4,
RoundRectangle2D rr2 = new RoundRectangle2D.Double(borderWidth, cardHeight - borderWidth * 4 - cornerRadius * 4,
cardWidth - borderWidth * 2, cornerRadius * 4,
cornerRadius * 2, cornerRadius * 2);
// Draw the cutout into the "swoosh" for the textbox to lie over
g.fillRect(
borderWidth + contentInset, cardHeight - borderWidth * 5,
cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2);
a.add(new Area(rr2));
// Draw the M15 rounded "swoosh" at the bottom
Rectangle r = new Rectangle(borderWidth + contentInset, cardHeight - borderWidth * 5, cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2);
a.add(new Area(r));
g.setClip(a);
g.drawImage(bg, 0, 0, cardWidth, cardHeight, 0, 0, bgw, bgh, BOX_BLUE, null);
g.setClip(null);
}
}
@ -312,6 +337,8 @@ public class ModernCardRenderer extends CardRenderer {
Rectangle2D rect;
if (useInventionFrame()) {
rect = new Rectangle2D.Float(0, 0, 1, 1);
} else if (isZendikarFullArtLand()) {
rect = new Rectangle2D.Float(.079f, .11f, .84f, .84f);
} else if (cardView.getFrameStyle().isFullArt() || (cardView.isToken())) {
rect = new Rectangle2D.Float(.079f, .11f, .84f, .63f);
} else {
@ -330,6 +357,10 @@ public class ModernCardRenderer extends CardRenderer {
}
}
private boolean isZendikarFullArtLand() {
return cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC || cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC;
}
protected boolean isSourceArtFullArt() {
int color = artImage.getRGB(0, artImage.getHeight() / 2);
return (((color & 0x00FF0000) > 0x00200000)
@ -352,7 +383,7 @@ public class ModernCardRenderer extends CardRenderer {
if (artImage != null && !cardView.isFaceDown()) {
boolean useFaceArt = false;
if (faceArtImage != null) {
if (faceArtImage != null && !isZendikarFullArtLand()) {
useFaceArt = true;
}
@ -395,7 +426,7 @@ public class ModernCardRenderer extends CardRenderer {
totalContentInset + 1, totalContentInset + boxHeight,
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
sourceRect, shouldPreserveAspect);
} else {
} else if (!isZendikarFullArtLand()) {
drawArtIntoRect(g,
totalContentInset + 1, totalContentInset + boxHeight,
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
@ -405,7 +436,7 @@ public class ModernCardRenderer extends CardRenderer {
}
@Override
protected void drawFrame(Graphics2D g) {
protected void drawFrame(Graphics2D g, BufferedImage image) {
// Get the card colors to base the frame on
ObjectColor frameColors = getFrameObjectColor();
@ -421,12 +452,13 @@ public class ModernCardRenderer extends CardRenderer {
// Draw the main card content border
g.setPaint(borderPaint);
if (cardView.getFrameStyle() == FrameStyle.KLD_INVENTION) {
g.drawImage(FRAME_INVENTION, 0, 0, cardWidth, cardHeight, null);
g.drawRect(
totalContentInset, typeLineY,
contentWidth - 1, cardHeight - borderWidth * 3 - typeLineY - 1);
} else {
} else if (!isZendikarFullArtLand()) {
g.drawRect(
totalContentInset, totalContentInset,
contentWidth - 1, cardHeight - borderWidth * 3 - totalContentInset - 1);
@ -437,11 +469,13 @@ public class ModernCardRenderer extends CardRenderer {
g.setPaint(new Color(255, 255, 255, 150));
} else {
g.setPaint(textboxPaint);
}
g.fillRect(
totalContentInset + 1, typeLineY,
contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1);
if (!isZendikarFullArtLand()) {
g.fillRect(
totalContentInset + 1, typeLineY,
contentWidth - 2, cardHeight - borderWidth * 3 - typeLineY - 1);
}
// If it's a planeswalker, extend the textbox left border by some
if (cardView.isPlanesWalker()) {
@ -451,7 +485,7 @@ public class ModernCardRenderer extends CardRenderer {
cardWidth / 16, cardHeight - typeLineY - boxHeight - borderWidth * 3);
}
if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION) {
if (cardView.getFrameStyle() != FrameStyle.KLD_INVENTION && !isZendikarFullArtLand()) {
// Draw a shadow highlight at the right edge of the content frame
g.setColor(new Color(0, 0, 0, 100));
g.fillRect(
@ -470,26 +504,31 @@ public class ModernCardRenderer extends CardRenderer {
cardWidth - 2 * borderWidth, boxHeight,
contentInset,
borderPaint, boxColor);
// Draw the type line box
CardRendererUtils.drawRoundedBox(g,
borderWidth, typeLineY,
cardWidth - 2 * borderWidth, boxHeight,
contentInset,
borderPaint, boxColor);
if (!isZendikarFullArtLand()) {
CardRendererUtils.drawRoundedBox(g,
borderWidth, typeLineY,
cardWidth - 2 * borderWidth, boxHeight,
contentInset,
borderPaint, boxColor);
// Draw a small separator between the type line and box, and shadow
// at the left of the texbox, and above the name line
g.setColor(new Color(0, 0, 0, 150));
g.fillRect(
totalContentInset - 1, totalContentInset - 1,
contentWidth + 1, 1);
g.fillRect(
totalContentInset + 1, typeLineY + boxHeight,
contentWidth - 2, 1);
g.fillRect(
cardWidth - totalContentInset - 1, typeLineY + boxHeight,
1, cardHeight - borderWidth * 3 - typeLineY - boxHeight);
// Draw a small separator between the type line and box, and shadow
// at the left of the texbox, and above the name line
g.setColor(new Color(0, 0, 0, 150));
g.fillRect(
totalContentInset - 1, totalContentInset - 1,
contentWidth + 1, 1);
g.fillRect(
totalContentInset + 1, typeLineY + boxHeight,
contentWidth - 2, 1);
g.fillRect(
cardWidth - totalContentInset - 1, typeLineY + boxHeight,
1, cardHeight - borderWidth * 3 - typeLineY - boxHeight);
// Draw the type line
drawTypeLine(g, getCardTypeLine(),
totalContentInset, typeLineY,
contentWidth, boxHeight, true);
}
// Draw the transform circle
int nameOffset = drawTransformationCircle(g, borderPaint);
@ -502,20 +541,163 @@ public class ModernCardRenderer extends CardRenderer {
totalContentInset + nameOffset, totalContentInset,
contentWidth - nameOffset, boxHeight);
// Draw the type line
drawTypeLine(g, getCardTypeLine(),
totalContentInset, typeLineY,
contentWidth, boxHeight);
// Draw the textbox rules
drawRulesText(g, textboxKeywords, textboxRules,
totalContentInset + 2, typeLineY + boxHeight + 2,
contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3);
if (!isZendikarFullArtLand()) {
drawRulesText(g, textboxKeywords, textboxRules,
totalContentInset + 2, typeLineY + boxHeight + 2,
contentWidth - 4, cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3);
} else {
int x = totalContentInset;
int y = typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset;
int w = contentWidth;
int h = boxHeight - 4;
CardRendererUtils.drawZendikarLandBox(g,
x, y, w, h,
contentInset,
borderPaint, boxColor);
drawTypeLine(g, getCardSuperTypeLine(),
totalContentInset + contentInset, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset,
contentWidth / 2 - boxHeight, boxHeight - 4, false);
drawTypeLine(g, getCardSubTypeLine(),
totalContentInset + 4 * contentWidth / 7 + boxHeight, typeLineY + boxHeight + (cardHeight - typeLineY - boxHeight - 4 - borderWidth * 3) / 2 - contentInset,
3 * contentWidth / 7 - boxHeight - contentInset, boxHeight - 4, true);
if (cardView.getFrameStyle() == FrameStyle.ZEN_FULL_ART_BASIC) {
// Draw curved lines (old Zendikar land style) - bigger (around 6%) inset on curve on bottom than inset (around 4.5%) on top...
int x2 = x + contentWidth;
int y2 = y;
int thisy = totalContentInset + boxHeight;
drawZendikarCurvedFace(g, image, x, thisy, x2, y2,
boxColor, borderPaint);
} else if (cardView.getFrameStyle() == FrameStyle.BFZ_FULL_ART_BASIC) {
// Draw curved lines (BFZ land style)
int y2 = y;
int yb = totalContentInset + boxHeight;
int topxdelta = 45 * contentWidth / 1000;
int endydelta = 60 * (totalContentInset + y2) / 265;
int x2 = x + contentWidth;
// Curve ends at 60 out of 265
drawBFZCurvedFace(g, image, x, yb, x2, y2,
topxdelta, endydelta,
boxColor, borderPaint);
}
drawRulesText(g, textboxKeywords, textboxRules,
x, y,
w, h);
}
// Draw the bottom right stuff
drawBottomRight(g, borderPaint, boxColor);
}
public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2,
Color boxColor, Paint paint) {
BufferedImage artToUse = faceArtImage;
boolean hadToUseFullArt = false;
if (faceArtImage == null) {
if (artImage == null) {
return;
}
hadToUseFullArt = true;
artToUse = artImage;
}
int srcW = artToUse.getWidth();
int srcH = artToUse.getHeight();
if (hadToUseFullArt) {
// Get a box based on the standard scan from gatherer.
// Width = 185/223 pixels (centered)
// Height = 220/310, 38 pixels from top
int subx = 19 * srcW / 223;
int suby = 38 * srcH / 310;
artToUse = artImage.getSubimage(subx, suby, 185 * srcW / 223, 220 * srcH / 310);
}
Path2D.Double curve = new Path2D.Double();
int ew = x2 - x;
int eh = 700 * (y2 - y) / 335;
Arc2D arc = new Arc2D.Double(x, y - 197 * eh / 700, ew, eh, 0, 360, Arc2D.OPEN);
Arc2D innerarc = new Arc2D.Double(x + 1, y - 197 * eh / 700 + 1, ew - 2, eh - 2, 0, 360, Arc2D.OPEN);
curve.append(new Rectangle2D.Double(x, y, x2 - x, y2 - y), false);
g2.setClip(new Rectangle2D.Double(x, y, x2 - x, y2 - y));
g2.setClip(arc);
Rectangle2D r = curve.getBounds2D();
g2.drawImage(artToUse, x, y, x2 - x, y2 - y, null);
g2.setClip(null);
g2.setClip(new Rectangle2D.Double(x, y, x2 - x, y2 - y));
g2.setColor(CardRendererUtils.abitdarker(boxColor));
g2.draw(arc);
g2.setColor(Color.black);
g2.draw(innerarc);
g2.setClip(null);
}
public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2,
int topxdelta, int endydelta,
Color boxColor, Paint paint) {
BufferedImage artToUse = faceArtImage;
boolean hadToUseFullArt = false;
if (faceArtImage == null) {
if (artImage == null) {
return;
}
hadToUseFullArt = true;
artToUse = artImage;
}
int srcW = artToUse.getWidth();
int srcH = artToUse.getHeight();
if (hadToUseFullArt) {
// Get a box based on the standard scan from gatherer.
// Width = 185/223 pixels (centered)
// Height = 220/310, 38 pixels from top
int subx = 19 * srcW / 223;
int suby = 38 * srcH / 310;
artToUse = artImage.getSubimage(subx, suby, 185 * srcW / 223, 220 * srcH / 310);
}
Path2D.Double curve = new Path2D.Double();
curve.moveTo(x + topxdelta, y);
curve.quadTo(x, y + endydelta / 2, x, y + endydelta);
curve.lineTo(x, y2);
curve.lineTo(x2, y2);
curve.lineTo(x2, y + endydelta);
curve.quadTo(x2, y + endydelta / 2, x2 - topxdelta, y);
curve.lineTo(x + topxdelta, y);
Path2D.Double innercurve = new Path2D.Double();
innercurve.moveTo(x + topxdelta, y + 1);
innercurve.quadTo(x + 1, y + endydelta / 2, x + 1, y + endydelta);
innercurve.lineTo(x + 1, y2 - 1);
innercurve.lineTo(x2 - 1, y2 - 1);
innercurve.lineTo(x2 - 1, y + endydelta);
innercurve.quadTo(x2 - 1, y + endydelta / 2, x2 - topxdelta, y + 1);
innercurve.lineTo(x + topxdelta, y + 1);
Rectangle2D r = curve.getBounds2D();
int minX = (int) r.getX();
g2.setClip(curve);
g2.drawImage(artToUse, minX, y, (x2 - x) + (x - minX) * 2, y2 - y, null);
g2.setClip(null);
g2.setColor(CardRendererUtils.abitdarker(boxColor));
g2.setPaint(paint);
g2.draw(curve);
g2.setColor(Color.black);
g2.draw(innercurve);
}
// Draw the name line
protected void drawNameLine(Graphics2D g, String baseName, String manaCost, int x, int y, int w, int h) {
// Width of the mana symbols
@ -566,13 +748,13 @@ public class ModernCardRenderer extends CardRenderer {
}
// Draw the type line (color indicator, types, and expansion symbol)
protected void drawTypeLine(Graphics2D g, String baseTypeLine, int x, int y, int w, int h) {
protected void drawTypeLine(Graphics2D g, String baseTypeLine, int x, int y, int w, int h, boolean withSymbol) {
// Draw expansion symbol
int expansionSymbolWidth;
int expansionSymbolWidth = 0;
if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_SET_SYMBOL, "false").equals("false")) {
if (cardView.isAbility()) {
expansionSymbolWidth = 0;
} else {
} else if (withSymbol) {
expansionSymbolWidth = drawExpansionSymbol(g, x, y, w, h);
}
} else {
@ -792,9 +974,21 @@ public class ModernCardRenderer extends CardRenderer {
}
// Basic mana draw mana symbol in textbox (for basic lands)
if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand()) {
drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol());
return;
if (allRules.size() == 1 && (allRules.get(0) instanceof TextboxBasicManaRule) && cardView.isLand() || isZendikarFullArtLand()) {
if (!isZendikarFullArtLand()) {
drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol());
return;
} else // Big circle in the middle for Zendikar lands
if (allRules.size() == 1) {
// Size of mana symbol = 9/4 * h, 3/4h above line
drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol());
return;
} else {
if (allRules.size() > 1) {
drawBasicManaSymbol(g, x + w / 2 - h - h / 8, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, cardView.getFrameColor().toString());
}
return;
}
}
// Go through possible font sizes in descending order to find the best fit
@ -847,6 +1041,11 @@ public class ModernCardRenderer extends CardRenderer {
ManaSymbols.draw(g, symbs, x + (w - manaCostWidth) / 2, y + (h - symbHeight) / 2, symbHeight, Color.black, 2);
}
private void drawBasicManaSymbol(Graphics2D g, int x, int y, int w, int h, String symbol) {
String symbs = symbol;
ManaSymbols.draw(g, symbs, x, y, w, Color.black, 2);
}
// Get the first line of the textbox, the keyword string
private static String getKeywordRulesString(ArrayList<TextboxRule> keywords) {
StringBuilder builder = new StringBuilder();
@ -1073,7 +1272,34 @@ public class ModernCardRenderer extends CardRenderer {
return new Color(71, 86, 101);
}
}
// Determine which background image to use from a set of colors
// and the current card.
protected static BufferedImage getBackgroundImage(ObjectColor colors, Collection<CardType> types, SubTypeList subTypes) {
if (subTypes.contains(SubType.VEHICLE)) {
return BG_IMG_VEHICLE;
} else if (types.contains(CardType.LAND)) {
return BG_IMG_LAND;
} else if (types.contains(CardType.ARTIFACT)) {
return BG_IMG_ARTIFACT;
} else if (colors.isMulticolored()) {
return BG_IMG_GOLD;
} else if (colors.isWhite()) {
return BG_IMG_WHITE;
} else if (colors.isBlue()) {
return BG_IMG_BLUE;
} else if (colors.isBlack()) {
return BG_IMG_BLACK;
} else if (colors.isRed()) {
return BG_IMG_RED;
} else if (colors.isGreen()) {
return BG_IMG_GREEN;
} else {
// Colorless
return BG_IMG_COLORLESS;
}
}
// Get the box color for the given colors
protected Color getBoxColor(ObjectColor colors, Collection<CardType> types, boolean isNightCard) {
if (cardView.isAbility()) {

View file

@ -8,6 +8,7 @@ import mage.view.CardView;
import java.awt.*;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
@ -17,6 +18,7 @@ import java.util.List;
public class ModernSplitCardRenderer extends ModernCardRenderer {
private class HalfCardProps {
int x, y, w, h, cw, ch;
String name;
@ -27,7 +29,11 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
ArrayList<TextboxRule> keywords = new ArrayList<>();
}
private static ArrayList<CardType> ONLY_LAND_TYPE = new ArrayList<CardType>() {{add(CardType.LAND);}};
private static ArrayList<CardType> ONLY_LAND_TYPE = new ArrayList<CardType>() {
{
add(CardType.LAND);
}
};
// Right and left halves of the card content
private HalfCardProps rightHalf = new HalfCardProps();
@ -88,20 +94,20 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
// Decide size of divider
if (isAftermath()) {
dividerSize = borderWidth;
dividerAt = (int)(cardHeight*0.54);
dividerAt = (int) (cardHeight * 0.54);
} else {
int availHeight = cardHeight - totalContentInset - 3*borderWidth;
dividerSize = borderWidth*2;
dividerAt = (int)(totalContentInset + availHeight * 0.5 - borderWidth);
int availHeight = cardHeight - totalContentInset - 3 * borderWidth;
dividerSize = borderWidth * 2;
dividerAt = (int) (totalContentInset + availHeight * 0.5 - borderWidth);
}
// Decide size of each halves box
rightHalf.x = leftHalf.x = totalContentInset;
rightHalf.w = leftHalf.w = cardWidth - 2*totalContentInset;
rightHalf.w = leftHalf.w = cardWidth - 2 * totalContentInset;
leftHalf.y = totalContentInset;
leftHalf.h = dividerAt - totalContentInset;
rightHalf.y = dividerAt + dividerSize;
rightHalf.h = cardHeight - rightHalf.y - borderWidth*3;
rightHalf.h = cardHeight - rightHalf.y - borderWidth * 3;
// Content width / height (Exchanged from width / height if the card part is rotated)
if (isAftermath()) {
@ -126,7 +132,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
private ObjectColor getColorFromManaCostHack(ManaCosts costs) {
ObjectColor c = new ObjectColor();
List<String> symbols = costs.getSymbols();
for (String symbol: symbols) {
for (String symbol : symbols) {
if (symbol.contains("W")) {
c.setWhite(true);
} else if (symbol.contains("U")) {
@ -154,18 +160,18 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
// Draw main part (most of card)
g.fillRoundRect(
borderWidth, borderWidth,
cardWidth - 2*borderWidth, leftHalf.h + contentInset - borderWidth - 2*cornerRadius + (cornerRadius - 1),
cardWidth - 2 * borderWidth, leftHalf.h + contentInset - borderWidth - 2 * cornerRadius + (cornerRadius - 1),
cornerRadius - 1, cornerRadius - 1);
// Draw the M15 rounded "swoosh" at the bottom
g.fillRoundRect(
borderWidth, dividerAt - borderWidth - 4*cornerRadius,
cardWidth - 2*borderWidth, cornerRadius * 4,
borderWidth, dividerAt - borderWidth - 4 * cornerRadius,
cardWidth - 2 * borderWidth, cornerRadius * 4,
cornerRadius * 2, cornerRadius * 2);
// Draw the cutout into the "swoosh" for the textbox to lie over
g.fillRect(
borderWidth + contentInset, dividerAt - 2*borderWidth,
borderWidth + contentInset, dividerAt - 2 * borderWidth,
cardWidth - borderWidth * 2 - contentInset * 2, borderWidth * 2);
}
@ -176,8 +182,8 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
// Draw the M15 rounded "swoosh"es at the top and bottom
g.fillRoundRect(
borderWidth, dividerAt + dividerSize + borderWidth,
cardWidth - 2*borderWidth, rightHalf.h - 2*borderWidth,
cornerRadius*2, cornerRadius*2);
cardWidth - 2 * borderWidth, rightHalf.h - 2 * borderWidth,
cornerRadius * 2, cornerRadius * 2);
// Draw the cutout into the "swoosh" for the textbox to lie over
g.fillRect(
@ -236,8 +242,8 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
// Background of textbox
g.setPaint(textboxPaint);
g.fillRect(
1, typeLineY,
half.cw - 2, half.ch - typeLineY - 1);
1, typeLineY,
half.cw - 2, half.ch - typeLineY - 1);
// Draw the name line box
CardRendererUtils.drawRoundedBox(g,
@ -261,7 +267,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
// Draw the type line
drawTypeLine(g, half.typeLineString,
0, typeLineY,
half.cw, boxHeight - 4);
half.cw, boxHeight - 4, true);
// Draw the textbox rules
drawRulesText(g, half.keywords, half.rules,
@ -270,13 +276,13 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
}
private Graphics2D getUnmodifiedHalfContext(Graphics2D g) {
Graphics2D g2 = (Graphics2D)g.create();
Graphics2D g2 = (Graphics2D) g.create();
g2.translate(leftHalf.x, leftHalf.y);
return g2;
}
private Graphics2D getAftermathHalfContext(Graphics2D g) {
Graphics2D g2 = (Graphics2D)g.create();
Graphics2D g2 = (Graphics2D) g.create();
g2.translate(rightHalf.x, rightHalf.y);
g2.rotate(Math.PI / 2);
g2.translate(0, -rightHalf.w);
@ -284,7 +290,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
}
private Graphics2D getLeftHalfContext(Graphics2D g) {
Graphics2D g2 = (Graphics2D)g.create();
Graphics2D g2 = (Graphics2D) g.create();
g2.translate(leftHalf.x, leftHalf.y);
g2.rotate(-Math.PI / 2);
g2.translate(-leftHalf.cw, 0);
@ -292,7 +298,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
}
private Graphics2D getRightHalfContext(Graphics2D g) {
Graphics2D g2 = (Graphics2D)g.create();
Graphics2D g2 = (Graphics2D) g.create();
g2.translate(rightHalf.x, rightHalf.y);
g2.rotate(-Math.PI / 2);
g2.translate(-rightHalf.cw, 0);
@ -300,13 +306,13 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
}
@Override
protected void drawFrame(Graphics2D g) {
protected void drawFrame(Graphics2D g, BufferedImage image) {
if (isAftermath()) {
drawSplitHalfFrame(getUnmodifiedHalfContext(g), leftHalf, (int)(leftHalf.ch * TYPE_LINE_Y_FRAC));
drawSplitHalfFrame(getUnmodifiedHalfContext(g), leftHalf, (int) (leftHalf.ch * TYPE_LINE_Y_FRAC));
drawSplitHalfFrame(getAftermathHalfContext(g), rightHalf, (rightHalf.ch - boxHeight) / 2);
} else {
drawSplitHalfFrame(getLeftHalfContext(g), leftHalf, (int)(leftHalf.ch * TYPE_LINE_Y_FRAC));
drawSplitHalfFrame(getRightHalfContext(g), rightHalf, (int)(rightHalf.ch * TYPE_LINE_Y_FRAC));
drawSplitHalfFrame(getLeftHalfContext(g), leftHalf, (int) (leftHalf.ch * TYPE_LINE_Y_FRAC));
drawSplitHalfFrame(getRightHalfContext(g), rightHalf, (int) (rightHalf.ch * TYPE_LINE_Y_FRAC));
if (isFuse()) {
Graphics2D g2 = getRightHalfContext(g);
int totalFuseBoxWidth = rightHalf.cw * 2 + 2 * borderWidth + dividerSize;
@ -319,7 +325,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
borderPaint, boxColor);
drawNameLine(g2, "Fuse (You may cast both halves from your hand)", "",
0, rightHalf.ch,
totalFuseBoxWidth - 2*borderWidth, boxHeight);
totalFuseBoxWidth - 2 * borderWidth, boxHeight);
}
}
}

View file

@ -66,7 +66,7 @@ public class GathererSets implements Iterable<DownloadJob> {
//"APAC" -- gatherer do not have that set, scrly have PALP
//"ARENA" -- is't many set with different codes, not one
"CLASH", "CP", "DD3GVL", "DPA", "EURO", "FNMP", "GPX", "GRC", "GUR", "H17", "JR", "MBP", "MGDC", "MLP", "MPRP", "MPS-AKH", "PTC", "S00", "S99", "SUS", "SWS", "UGIN", "UGL", "V10", "V17", "WMCQ", // need to fix
"H09", "PD2", "PD3", "UNH", "CM1", "E02", "V11", "M25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "DDT", "8EB", "9EB", "CHR" // ok
"H09", "PD2", "PD3", "UNH", "CM1", "E02", "V11", "A25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "DDT", "8EB", "9EB", "CHR" // ok
// current testing
};

View file

@ -208,7 +208,7 @@ public enum ScryfallImageSource implements CardImageSource {
supportedSets.add("RIX");
supportedSets.add("WMCQ");
supportedSets.add("PPRO");
// supportedSets.add("A25");
supportedSets.add("A25");
// supportedSets.add("DOM");
// supportedSets.add("M19");

View file

@ -268,7 +268,7 @@ public enum WizardCardsImageSource implements CardImageSource {
supportedSets.add("V17"); // From the Vault: Transform
supportedSets.add("UST"); // Unstable
supportedSets.add("RIX"); // Rivals of Ixalan
// supportedSets.add("A25"); // Masters 25
supportedSets.add("A25"); // Masters 25
// supportedSets.add("DOM"); // Dominaria
// supportedSets.add("M19"); // Core 2019

Binary file not shown.

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 685 B

After

Width:  |  Height:  |  Size: 19 KiB

View file

@ -73,6 +73,6 @@ dd3evg=ddaevg
dd3gvl=ddagvl
dd3jvc=ddajvc
# Remove setname as soon as the images can be downloaded
ignore.urls=TOK,M19,M25,DOM,H17
ignore.urls=TOK,M19,DOM,H17
# sets ordered by release time (newest goes first)
token.lookup.order=M19,M25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC
token.lookup.order=M19,A25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC

View file

@ -25,6 +25,7 @@
<artifactId>jspf-core</artifactId>
<version>0.9.1</version>
</dependency>
<dependency>
<groupId>org.jboss.remoting</groupId>
<artifactId>jboss-remoting</artifactId>
@ -50,7 +51,11 @@
<artifactId>trove</artifactId>
<version>1.0.2</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.2</version>
</dependency>
</dependencies>
<!-- to get the reference to local repository with com\googlecode\jspf\jspf-core\0.9.1\ -->
<repositories>

View file

@ -0,0 +1,138 @@
/*
* Copyright 2018 nanarpuss_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.remote;
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.Expose;
import java.util.UUID;
public class ActionData {
@Expose
public UUID gameId;
@Expose
public String sessionId;
@Expose
public String type;
@Expose
public Object value;
@Expose
public String message;
public String toJson() {
GsonBuilder gsonBuilder = new GsonBuilder();
Gson gson = gsonBuilder.setExclusionStrategies(new CustomExclusionStrategy()).create();
return gson.toJson(this);
}
public ActionData(String type, UUID gameId, String sessionId) {
this.type = type;
this.sessionId = sessionId;
this.gameId = gameId;
}
public ActionData(String type, UUID gameId) {
this.type = type;
this.gameId = gameId;
}
public class CustomExclusionStrategy implements ExclusionStrategy {
// FIXME: Very crude way of whitelisting, as it applies to all levels of the JSON tree.
private final java.util.Set<String> KEEP = new java.util.HashSet<>(
java.util.Arrays.asList(
new String[]{
"id",
"choice",
"damage",
"abilityType",
"ability",
"abilities",
"method",
"data",
"options",
"life",
"players",
"zone",
"step",
"phase",
"attackers",
"blockers",
"tapped",
"damage",
"combat",
"paid",
"hand",
"stack",
"convertedManaCost",
"gameId",
"canPlayInHand",
"gameView",
"sessionId",
"power",
"choices",
"targets",
"loyalty",
"toughness",
"power",
"type",
"priorityTime",
"manaCost",
"value",
"message",
"cardsView",
"name",
"count",
"counters",
"battlefield",
"parentId"
}));
public CustomExclusionStrategy() {
}
// This method is called for all fields. if the method returns true the
// field is excluded from serialization
@Override
public boolean shouldSkipField(FieldAttributes f) {
String name = f.getName();
return !KEEP.contains(name);
}
// This method is called for all classes. If the method returns true the
// class is excluded.
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
}

View file

@ -24,8 +24,7 @@
* 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.remote;
import mage.remote.interfaces.ChatSession;
@ -46,4 +45,5 @@ import mage.remote.interfaces.Testable;
*/
public interface Session extends ClientData, Connect, GamePlay, GameTypes, ServerState, ChatSession, Feedback, PlayerActions, Replays, Testable {
public void appendJsonLog(ActionData actionData);
}

View file

@ -27,12 +27,14 @@
*/
package mage.remote;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import javax.swing.*;
import mage.MageException;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
@ -799,6 +801,9 @@ public class SessionImpl implements Session {
public boolean sendPlayerUUID(UUID gameId, UUID data) {
try {
if (isConnected()) {
ActionData actionData = new ActionData("SEND_PLAYER_UUID", gameId, getSessionId());
actionData.value = data;
appendJsonLog(actionData);
server.sendPlayerUUID(gameId, sessionId, data);
return true;
}
@ -814,6 +819,10 @@ public class SessionImpl implements Session {
public boolean sendPlayerBoolean(UUID gameId, boolean data) {
try {
if (isConnected()) {
ActionData actionData = new ActionData("SEND_PLAYER_BOOLEAN", gameId, getSessionId());
actionData.value = data;
appendJsonLog(actionData);
server.sendPlayerBoolean(gameId, sessionId, data);
return true;
}
@ -829,6 +838,10 @@ public class SessionImpl implements Session {
public boolean sendPlayerInteger(UUID gameId, int data) {
try {
if (isConnected()) {
ActionData actionData = new ActionData("SEND_PLAYER_INTEGER", gameId, getSessionId());
actionData.value = data;
appendJsonLog(actionData);
server.sendPlayerInteger(gameId, sessionId, data);
return true;
}
@ -844,6 +857,10 @@ public class SessionImpl implements Session {
public boolean sendPlayerString(UUID gameId, String data) {
try {
if (isConnected()) {
ActionData actionData = new ActionData("SEND_PLAYER_STRING", gameId, getSessionId());
actionData.value = data;
appendJsonLog(actionData);
server.sendPlayerString(gameId, sessionId, data);
return true;
}
@ -859,6 +876,9 @@ public class SessionImpl implements Session {
public boolean sendPlayerManaType(UUID gameId, UUID playerId, ManaType data) {
try {
if (isConnected()) {
ActionData actionData = new ActionData("SEND_PLAYER_MANA_TYPE", gameId, getSessionId());
actionData.value = data;
appendJsonLog(actionData);
server.sendPlayerManaType(gameId, playerId, sessionId, data);
return true;
}
@ -870,6 +890,17 @@ public class SessionImpl implements Session {
return false;
}
@Override
public void appendJsonLog(ActionData actionData) {
actionData.sessionId = getSessionId();
String logFileName = "game-" + actionData.gameId + ".json";
try (PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(logFileName, true)))) {
out.println(actionData.toJson());
} catch (IOException e) {
System.err.println(e);
}
}
@Override
public DraftPickView sendCardPick(UUID draftId, UUID cardId, Set<UUID> hiddenCards) {
try {
@ -1275,6 +1306,11 @@ public class SessionImpl implements Session {
public boolean sendPlayerAction(PlayerAction passPriorityAction, UUID gameId, Object data) {
try {
if (isConnected()) {
ActionData actionData = new ActionData("SEND_PLAYER_ACTION", gameId, getSessionId());
actionData.value = data;
appendJsonLog(actionData);
server.sendPlayerAction(passPriorityAction, gameId, sessionId, data);
return true;
}
@ -1437,12 +1473,9 @@ public class SessionImpl implements Session {
@Override
public boolean endUserSession(String userSessionId) {
try {
if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to end userSessionId " + userSessionId + '?', "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
if (isConnected()) {
server.endUserSession(sessionId, userSessionId);
return true;
}
if (isConnected()) {
server.endUserSession(sessionId, userSessionId);
return true;
}
} catch (MageException ex) {
handleMageException(ex);
@ -1455,12 +1488,9 @@ public class SessionImpl implements Session {
@Override
public boolean muteUserChat(String userName, long durationMinutes) {
try {
if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to mute user " + userName + " for " + durationMinutes + " minutes?", "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
if (isConnected()) {
server.muteUser(sessionId, userName, durationMinutes);
return true;
}
if (isConnected()) {
server.muteUser(sessionId, userName, durationMinutes);
return true;
}
} catch (MageException ex) {
handleMageException(ex);
@ -1473,12 +1503,9 @@ public class SessionImpl implements Session {
@Override
public boolean setActivation(String userName, boolean active) {
try {
if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to set active to " + active + " for user: " + userName + '?', "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
if (isConnected()) {
server.setActivation(sessionId, userName, active);
return true;
}
if (isConnected()) {
server.setActivation(sessionId, userName, active);
return true;
}
} catch (MageException ex) {
handleMageException(ex);
@ -1491,20 +1518,9 @@ public class SessionImpl implements Session {
@Override
public boolean toggleActivation(String userName) {
try {
if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to active?", "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
return setActivation(userName, true);
}
if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to INactive?", "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
return setActivation(userName, false);
}
if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to toggle activation for user: " + userName + '?', "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
if (isConnected()) {
server.toggleActivation(sessionId, userName);
return true;
}
if (isConnected()) {
server.toggleActivation(sessionId, userName);
return true;
}
} catch (MageException ex) {
handleMageException(ex);
@ -1517,12 +1533,9 @@ public class SessionImpl implements Session {
@Override
public boolean lockUser(String userName, long durationMinute) {
try {
if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to lock user: " + userName + " for " + durationMinute + " minutes?", "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
if (isConnected()) {
server.lockUser(sessionId, userName, durationMinute);
return true;
}
if (isConnected()) {
server.lockUser(sessionId, userName, durationMinute);
return true;
}
} catch (MageException ex) {
handleMageException(ex);

View file

@ -53,6 +53,8 @@ import mage.target.Target;
import mage.target.Targets;
import mage.util.SubTypeList;
import com.google.gson.annotations.Expose;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -61,11 +63,17 @@ public class CardView extends SimpleCardView {
private static final long serialVersionUID = 1L;
protected UUID parentId;
@Expose
protected String name;
@Expose
protected String displayName;
@Expose
protected List<String> rules;
@Expose
protected String power;
@Expose
protected String toughness;
@Expose
protected String loyalty = "";
protected String startingLoyalty;
protected EnumSet<CardType> cardTypes;
@ -110,7 +118,6 @@ public class CardView extends SimpleCardView {
protected ArtRect artRect = ArtRect.NORMAL;
protected List<UUID> targets;
protected UUID pairedCard;
protected List<UUID> bandedCards;
protected boolean paid;

View file

@ -32,6 +32,11 @@ import java.io.Serializable;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.annotations.Expose;
import mage.choices.Choice;
/**
@ -39,18 +44,29 @@ import mage.choices.Choice;
* @author BetaSteward_at_googlemail.com
*/
public class GameClientMessage implements Serializable {
@Expose
private static final long serialVersionUID = 1L;
@Expose
private GameView gameView;
@Expose
private CardsView cardsView;
@Expose
private CardsView cardsView2;
@Expose
private String message;
@Expose
private boolean flag;
@Expose
private String[] strings;
@Expose
private Set<UUID> targets;
@Expose
private int min;
@Expose
private int max;
@Expose
private Map<String, Serializable> options;
@Expose
private Choice choice;
public GameClientMessage(GameView gameView) {
@ -155,4 +171,11 @@ public class GameClientMessage implements Serializable {
return choice;
}
public String toJson() {
Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.create();
return gson.toJson(this);
}
}

View file

@ -28,11 +28,17 @@
package mage.view;
import java.io.Serializable;
import java.io.BufferedWriter;
import java.io.PrintWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.costs.Cost;
import mage.cards.Card;
@ -54,6 +60,8 @@ import mage.game.stack.StackObject;
import mage.players.Player;
import mage.watchers.common.CastSpellLastTurnWatcher;
import org.apache.log4j.Logger;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
/**
*
@ -64,7 +72,6 @@ public class GameView implements Serializable {
private static final long serialVersionUID = 1L;
private static final Logger LOGGER = Logger.getLogger(GameView.class);
private final int priorityTime;
private final List<PlayerView> players = new ArrayList<>();
private CardsView hand;
@ -351,4 +358,8 @@ public class GameView implements Serializable {
return rollbackTurnsAllowed;
}
public String toJson() {
Gson gson = new GsonBuilder().create();
return gson.toJson(this);
}
}

View file

@ -28,6 +28,8 @@
package mage.view;
import com.google.gson.annotations.Expose;
import java.io.Serializable;
import java.util.UUID;
@ -36,6 +38,7 @@ import java.util.UUID;
* @author BetaSteward_at_googlemail.com
*/
public class SimpleCardView implements Serializable {
@Expose
protected UUID id;
protected String expansionSetCode;
protected String tokenSetCode;

View file

@ -65,6 +65,7 @@ public class TableView implements Serializable {
private final boolean limited;
private final boolean rated;
private final boolean passworded;
private final boolean spectatorsAllowed;
public TableView(Table table) {
this.tableId = table.getId();
@ -139,6 +140,7 @@ public class TableView implements Serializable {
this.limited = table.getMatch().getOptions().isLimited();
this.rated = table.getMatch().getOptions().isRated();
this.passworded = !table.getMatch().getOptions().getPassword().isEmpty();
this.spectatorsAllowed = table.getMatch().getOptions().isSpectatorsAllowed();
} else {
// TOURNAMENT
if (table.getTournament().getOptions().getNumberRounds() > 0) {
@ -186,6 +188,7 @@ public class TableView implements Serializable {
this.limited = table.getTournament().getOptions().getMatchOptions().isLimited();
this.rated = table.getTournament().getOptions().getMatchOptions().isRated();
this.passworded = !table.getTournament().getOptions().getPassword().isEmpty();
this.spectatorsAllowed = table.getTournament().getOptions().isWatchingAllowed();
}
}
@ -200,6 +203,11 @@ public class TableView implements Serializable {
public String getControllerName() {
return controllerName;
}
public boolean getSpectatorsAllowed() {
return spectatorsAllowed;
}
public String getGameType() {
return gameType;

View file

@ -345,22 +345,53 @@ public class ConsolePanel extends javax.swing.JPanel {
private void btnEndSessionActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnEndSessionActionPerformed
int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow());
ConsoleFrame.getSession().endUserSession((String) tableUserModel.getValueAt(row, TableUserModel.POS_GAME_INFO));
String userSessionId = (String) tableUserModel.getValueAt(row, TableUserModel.POS_GAME_INFO);
if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to end userSessionId " + userSessionId + '?', "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
ConsoleFrame.getSession().endUserSession(userSessionId);
}
}//GEN-LAST:event_btnEndSessionActionPerformed
private void btnMuteUserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnMuteUserActionPerformed
int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow());
ConsoleFrame.getSession().muteUserChat((String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME), ((Number) spinnerMuteDurationMinutes.getValue()).longValue());
int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow());
String userName = (String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME);
long durationMinute = ((Number) spinnerMuteDurationMinutes.getValue()).longValue();
if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to mute user: " + userName + " for " + durationMinute + " minutes?", "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
ConsoleFrame.getSession().muteUserChat(userName, durationMinute);
}
}//GEN-LAST:event_btnMuteUserActionPerformed
private void btnDeActivateActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnDeActivateActionPerformed
int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow());
ConsoleFrame.getSession().toggleActivation((String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME));
String userName = (String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME);
if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to active?", "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
ConsoleFrame.getSession().setActivation(userName, true);
return;
}
if (JOptionPane.showConfirmDialog(null, "Did you want to set user: " + userName + " to inactive?", "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
ConsoleFrame.getSession().setActivation(userName, false);
return;
}
if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to toggle activation for user: " + userName + '?', "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
ConsoleFrame.getSession().toggleActivation(userName);
return;
}
}//GEN-LAST:event_btnDeActivateActionPerformed
private void btnLockUserActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnLockUserActionPerformed
int row = this.tblUsers.convertRowIndexToModel(tblUsers.getSelectedRow());
ConsoleFrame.getSession().lockUser((String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME), ((Number) spinnerMuteDurationMinutes.getValue()).longValue());
String userName = (String) tableUserModel.getValueAt(row, TableUserModel.POS_USER_NAME);
long durationMinute = ((Number) spinnerMuteDurationMinutes.getValue()).longValue();
if (JOptionPane.showConfirmDialog(null, "Are you sure you mean to lock user: " + userName + " for " + durationMinute + " minutes?", "WARNING",
JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) {
ConsoleFrame.getSession().lockUser(userName, durationMinute);
}
}//GEN-LAST:event_btnLockUserActionPerformed
private void btnRemoveTableActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRemoveTableActionPerformed

View file

@ -76,6 +76,12 @@
<version>${project.version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-compress</artifactId>
<version>1.16.1</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mage-game-commanderfreeforall</artifactId>

View file

@ -990,7 +990,7 @@ public class TableController {
|| !match.isDoneSideboarding()
|| (!matchPlayer.hasQuit() && match.getGame() != null && matchPlayer.getPlayer().isInGame())) {
Optional<User> user = UserManager.instance.getUser(userPlayerEntry.getKey());
if (!user.isPresent() || user.get().isActive()) {
if (!user.isPresent() || !user.get().isActive()) {
logger.warn("- Active user of match is missing: " + matchPlayer.getName());
logger.warn("-- matchId:" + match.getId());
logger.warn("-- userId:" + userPlayerEntry.getKey());

View file

@ -94,9 +94,12 @@ class ActOfAuthorityEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Permanent targetPermanent = game.getPermanent(this.getTargetPointer().getFirst(game, source));
if (targetPermanent != null && new ExileTargetEffect().apply(game, source)) {
ContinuousEffect effect = new ActOfAuthorityGainControlEffect(Duration.Custom, targetPermanent.getControllerId());
effect.setTargetPointer(new FixedTarget(source.getSourceId()));
game.addEffect(effect, source);
Permanent sourcePermanent = source.getSourcePermanentIfItStillExists(game);
if (sourcePermanent != null) {
ContinuousEffect effect = new ActOfAuthorityGainControlEffect(Duration.Custom, targetPermanent.getControllerId());
effect.setTargetPointer(new FixedTarget(sourcePermanent, game));
game.addEffect(effect, source);
}
return true;
}
return false;

View file

@ -109,7 +109,7 @@ class AdarkarValkyrieEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
DelayedTriggeredAbility delayedAbility = new AdarkarValkyrieDelayedTriggeredAbility(new FixedTarget(this.getTargetPointer().getFirst(game, source)));
DelayedTriggeredAbility delayedAbility = new AdarkarValkyrieDelayedTriggeredAbility(getTargetPointer().getFixedTarget(game, source));
game.addDelayedTriggeredAbility(delayedAbility, source);
return false;
}

View file

@ -51,17 +51,17 @@ import mage.target.targetpointer.FixedTarget;
public class AmbuscadeShaman extends CardImpl {
public AmbuscadeShaman(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
this.subtype.add(SubType.ORC);
this.subtype.add(SubType.SHAMAN);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Whenever Ambuscade Shaman or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn.
Effect effect = new BoostTargetEffect(2,2, Duration.EndOfTurn);
Effect effect = new BoostTargetEffect(2, 2, Duration.EndOfTurn);
effect.setText("that creature gets +2/+2 until end of turn");
this.addAbility(new AmbuscadeShamanTriggeredAbility(effect));
// Dash {3}{B} <i>(You may cast this spell for its dash cost. If you do, it gains haste, and it's returned from the battlefield to its owner's hand at the beginning of the next end step.)</i>);
this.addAbility(new DashAbility(this, "{3}{B}"));
@ -103,9 +103,7 @@ class AmbuscadeShamanTriggeredAbility extends TriggeredAbilityImpl {
Permanent permanent = game.getPermanent(targetId);
if (permanent.getControllerId().equals(this.controllerId)
&& permanent.isCreature()) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
}
this.getEffects().setTargetPointer(new FixedTarget(permanent, game));
return true;
}
return false;
@ -115,4 +113,4 @@ class AmbuscadeShamanTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() {
return "Whenever {this} or another creature enters the battlefield under your control, that creature gets +2/+2 until end of turn.";
}
}
}

View file

@ -45,16 +45,16 @@ import mage.target.targetpointer.FixedTarget;
/**
*
* @author KholdFuzion
*
*/
public class AnkhOfMishra extends CardImpl {
public AnkhOfMishra(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
// Whenever a land enters the battlefield, Ankh of Mishra deals 2 damage to that land's controller.
this.addAbility(new AnkhOfMishraAbility());
}
public AnkhOfMishra(final AnkhOfMishra card) {
@ -70,16 +70,16 @@ public class AnkhOfMishra extends CardImpl {
class AnkhOfMishraAbility extends TriggeredAbilityImpl {
public AnkhOfMishraAbility() {
super(Zone.BATTLEFIELD, new DamageTargetEffect(2));
super(Zone.BATTLEFIELD, new DamageTargetEffect(2));
}
AnkhOfMishraAbility(final AnkhOfMishraAbility ability) {
super(ability);
super(ability);
}
@Override
public AnkhOfMishraAbility copy() {
return new AnkhOfMishraAbility(this);
return new AnkhOfMishraAbility(this);
}
@Override
@ -104,6 +104,6 @@ class AnkhOfMishraAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
return "Whenever a land enters the battlefield, Ankh of Mishra deals 2 damage to that land's controller.";
return "Whenever a land enters the battlefield, {this} deals 2 damage to that land's controller.";
}
}
}

View file

@ -29,6 +29,7 @@ package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.OneShotEffect;
@ -53,7 +54,7 @@ import mage.target.targetpointer.FixedTarget;
public class ArbiterOfTheIdeal extends CardImpl {
public ArbiterOfTheIdeal(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{U}{U}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{U}{U}");
this.subtype.add(SubType.SPHINX);
this.power = new MageInt(4);
@ -79,6 +80,7 @@ public class ArbiterOfTheIdeal extends CardImpl {
class ArbiterOfTheIdealEffect extends OneShotEffect {
private static final FilterCard filter = new FilterCard();
static {
filter.add(Predicates.or(
new CardTypePredicate(CardType.ARTIFACT),
@ -102,32 +104,25 @@ class ArbiterOfTheIdealEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller == null || sourceObject == null) {
return false;
}
if (player.getLibrary().hasCards()) {
Card card = player.getLibrary().getFromTop(game);
Cards cards = new CardsImpl();
cards.add(card);
player.revealCards("Arbiter of the Ideal", cards, game);
if (card != null) {
if (filter.match(card, game) && player.chooseUse(outcome, new StringBuilder("Put ").append(card.getName()).append("onto battlefield?").toString(), source, game)) {
card.putOntoBattlefield(game, Zone.LIBRARY, source.getSourceId(), source.getControllerId());
Permanent permanent = game.getPermanent(card.getId());
if (permanent != null) {
permanent.addCounters(new Counter("Manifestation"), source, game);
ContinuousEffect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ENCHANTMENT);
effect.setTargetPointer(new FixedTarget(permanent.getId()));
game.addEffect(effect, source);
}
Card card = controller.getLibrary().getFromTop(game);
if (card != null) {
controller.revealCards(sourceObject.getIdName(), new CardsImpl(card), game);
if (filter.match(card, game) && controller.chooseUse(outcome, "Put " + card.getName() + "onto battlefield?", source, game)) {
controller.moveCards(card, Zone.BATTLEFIELD, source, game);
Permanent permanent = game.getPermanent(card.getId());
if (permanent != null) {
permanent.addCounters(new Counter("Manifestation"), source, game);
ContinuousEffect effect = new AddCardTypeTargetEffect(Duration.Custom, CardType.ENCHANTMENT);
effect.setTargetPointer(new FixedTarget(permanent, game));
game.addEffect(effect, source);
}
}
return true;
}
return false;
return true;
}
}

View file

@ -29,23 +29,18 @@ package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.GainLifeEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
* @author Loki
@ -53,7 +48,7 @@ import mage.target.targetpointer.FixedTarget;
public class ArchonOfRedemption extends CardImpl {
public ArchonOfRedemption(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{W}{W}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}{W}");
this.subtype.add(SubType.ARCHON);
this.power = new MageInt(3);
@ -75,8 +70,9 @@ public class ArchonOfRedemption extends CardImpl {
}
class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl {
ArchonOfRedemptionTriggeredAbility() {
super(Zone.BATTLEFIELD, new ArchonOfRedemptionEffect(), true);
super(Zone.BATTLEFIELD, null, true);
}
ArchonOfRedemptionTriggeredAbility(final ArchonOfRedemptionTriggeredAbility ability) {
@ -95,15 +91,13 @@ class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
UUID targetId = event.getTargetId();
Permanent permanent = game.getPermanent(targetId);
if (permanent.getControllerId().equals(this.controllerId)
Permanent permanent = game.getPermanent(event.getTargetId());
if (permanent.getControllerId().equals(getControllerId())
&& permanent.isCreature()
&& (targetId.equals(this.getSourceId())
|| (permanent.getAbilities().contains(FlyingAbility.getInstance()) && !targetId.equals(this.getSourceId())))) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getTargetId()));
}
&& (permanent.getId().equals(getSourceId())
|| (permanent.getAbilities().contains(FlyingAbility.getInstance())))) {
this.getEffects().clear();
this.addEffect(new GainLifeEffect(permanent.getPower().getValue()));
return true;
}
return false;
@ -111,35 +105,6 @@ class ArchonOfRedemptionTriggeredAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
return "Whenever {this} or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power";
return "Whenever {this} or another creature with flying enters the battlefield under your control, you may gain life equal to that creature's power.";
}
}
class ArchonOfRedemptionEffect extends OneShotEffect {
ArchonOfRedemptionEffect() {
super(Outcome.GainLife);
}
ArchonOfRedemptionEffect(final ArchonOfRedemptionEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent p = game.getPermanent(targetPointer.getFirst(game, source));
Player player = game.getPlayer(source.getControllerId());
if (p == null) {
p = (Permanent) game.getLastKnownInformation(targetPointer.getFirst(game, source), Zone.BATTLEFIELD);
}
if (p != null && player != null) {
player.gainLife(p.getPower().getValue(), game);
return true;
}
return false;
}
@Override
public ArchonOfRedemptionEffect copy() {
return new ArchonOfRedemptionEffect(this);
}
}

View file

@ -29,7 +29,7 @@ package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.OnEventTriggeredAbility;
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
import mage.abilities.effects.common.ReturnToHandSourceEffect;
import mage.abilities.keyword.FlyingAbility;
import mage.abilities.keyword.HasteAbility;
@ -37,7 +37,7 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.game.events.GameEvent;
import mage.constants.TargetController;
/**
*
@ -46,7 +46,7 @@ import mage.game.events.GameEvent;
public class ArchwingDragon extends CardImpl {
public ArchwingDragon(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}{R}");
this.subtype.add(SubType.DRAGON);
this.power = new MageInt(4);
@ -54,8 +54,10 @@ public class ArchwingDragon extends CardImpl {
this.addAbility(FlyingAbility.getInstance());
this.addAbility(HasteAbility.getInstance());
// At the beginning of the end step, return Archwing Dragon to its owner's hand.
this.addAbility(new OnEventTriggeredAbility(GameEvent.EventType.END_TURN_STEP_PRE, "beginning of the end step", new ReturnToHandSourceEffect(true), false));
this.addAbility(new BeginningOfEndStepTriggeredAbility(new ReturnToHandSourceEffect(true), TargetController.ANY, false));
}
public ArchwingDragon(final ArchwingDragon card) {

View file

@ -53,7 +53,7 @@ import mage.target.targetpointer.FixedTarget;
public class ArrogantBloodlord extends CardImpl {
public ArrogantBloodlord(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{B}{B}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{B}");
this.subtype.add(SubType.VAMPIRE);
this.subtype.add(SubType.KNIGHT);
@ -104,11 +104,8 @@ class ArrogantBloodlordTriggeredAbility extends TriggeredAbilityImpl {
&& Objects.equals(blocked, arrogantBloodlord)) {
return true;
}
if (blocker != null && Objects.equals(blocker, arrogantBloodlord)
&& game.getPermanent(event.getTargetId()).getPower().getValue() < 2) {
return true;
}
return false;
return blocker != null && Objects.equals(blocker, arrogantBloodlord)
&& game.getPermanent(event.getTargetId()).getPower().getValue() < 2;
}
@Override
@ -133,7 +130,7 @@ class ArrogantBloodlordEffect extends OneShotEffect {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null) {
AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new DestroyTargetEffect());
delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(source.getSourceId()));
delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(permanent, game));
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}

View file

@ -37,16 +37,13 @@ import mage.abilities.effects.common.CounterUnlessPaysEffect;
import mage.abilities.keyword.BandingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AbilityType;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
import mage.constants.Zone;
import mage.filter.FilterStackObject;
import mage.filter.predicate.Predicate;
import mage.game.Game;
import mage.game.stack.StackAbility;
import mage.target.common.TargetActivatedOrTriggeredAbility;
import mage.filter.FilterAbility;
import mage.filter.predicate.ability.ArtifactSourcePredicate;
import mage.target.common.TargetActivatedAbility;
/**
*
@ -54,26 +51,26 @@ import mage.target.common.TargetActivatedOrTriggeredAbility;
*/
public class AyeshaTanaka extends CardImpl {
private final static FilterStackObject filter = new FilterStackObject("activated ability from an artifact source");
private final static FilterAbility filter = new FilterAbility("activated ability from an artifact source");
static {
filter.add(new ArtifactSourcePredicate());
}
public AyeshaTanaka(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}{W}{U}{U}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{W}{W}{U}{U}");
addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.ARTIFICER);
this.power = new MageInt(2);
this.toughness = new MageInt(2);
// Banding
this.addAbility(BandingAbility.getInstance());
// {T}: Counter target activated ability from an artifact source unless that ability's controller pays {W}.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterUnlessPaysEffect(new ManaCostsImpl("{W}")), new TapSourceCost());
ability.addTarget(new TargetActivatedOrTriggeredAbility(filter));
ability.addTarget(new TargetActivatedAbility(filter));
this.addAbility(ability);
}
@ -86,22 +83,3 @@ public class AyeshaTanaka extends CardImpl {
return new AyeshaTanaka(this);
}
}
class ArtifactSourcePredicate implements Predicate<Ability> {
public ArtifactSourcePredicate() {
}
@Override
public boolean apply(Ability input, Game game) {
if (input instanceof StackAbility) {
return input.getSourceObject(game).isArtifact() && input.getAbilityType() == AbilityType.ACTIVATED;
}
return false;
}
@Override
public String toString() {
return "Source(Artifact)";
}
}

View file

@ -0,0 +1,127 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
import mage.abilities.condition.common.BeforeBlockersAreDeclaredCondition;
import mage.abilities.effects.RequirementEffect;
import mage.abilities.effects.common.combat.CanBlockAdditionalCreatureTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.TurnPhase;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.ObjectPlayer;
import mage.filter.predicate.ObjectPlayerPredicate;
import mage.game.Controllable;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author L_J
*/
public class BlazeOfGlory extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature defending player controls");
static {
filter.add(new BlazeOfGloryDefendingPlayerControlsPredicate());
}
public BlazeOfGlory(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{W}");
// Cast Blaze of Glory only during combat before blockers are declared.
this.addAbility(new CastOnlyDuringPhaseStepSourceAbility(TurnPhase.COMBAT, BeforeBlockersAreDeclaredCondition.instance));
// Target creature defending player controls can block any number of creatures this turn. It blocks each attacking creature this turn if able.
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
this.getSpellAbility().addEffect(new CanBlockAdditionalCreatureTargetEffect(Duration.EndOfTurn, 0));
this.getSpellAbility().addEffect(new BlazeOfGloryRequirementEffect());
}
public BlazeOfGlory(final BlazeOfGlory card) {
super(card);
}
@Override
public BlazeOfGlory copy() {
return new BlazeOfGlory(this);
}
}
class BlazeOfGloryDefendingPlayerControlsPredicate implements ObjectPlayerPredicate<ObjectPlayer<Controllable>> {
@Override
public boolean apply(ObjectPlayer<Controllable> input, Game game) {
return game.getCombat().getPlayerDefenders(game).contains(input.getObject().getControllerId());
}
}
class BlazeOfGloryRequirementEffect extends RequirementEffect {
public BlazeOfGloryRequirementEffect() {
super(Duration.EndOfTurn);
this.staticText = "It blocks each attacking creature this turn if able";
}
public BlazeOfGloryRequirementEffect(final BlazeOfGloryRequirementEffect effect) {
super(effect);
}
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
return permanent.getId().equals(targetPointer.getFirst(game, source));
}
@Override
public boolean mustAttack(Game game) {
return false;
}
@Override
public boolean mustBlock(Game game) {
return true;
}
@Override
public boolean mustBlockAllAttackers(Game game) {
return true;
}
@Override
public BlazeOfGloryRequirementEffect copy() {
return new BlazeOfGloryRequirementEffect(this);
}
}

View file

@ -53,7 +53,7 @@ import java.util.UUID;
*/
public class BrassTalonChimera extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Chimera creature you control");
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Chimera creature");
static {
filter.add(new SubtypePredicate(SubType.CHIMERA));

View file

@ -27,6 +27,7 @@
*/
package mage.cards.b;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
@ -35,17 +36,12 @@ import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.CounterTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AbilityType;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterStackObject;
import mage.filter.predicate.Predicate;
import mage.game.Game;
import mage.game.stack.StackAbility;
import mage.target.common.TargetActivatedOrTriggeredAbility;
import java.util.UUID;
import mage.filter.FilterAbility;
import mage.filter.predicate.ability.ArtifactSourcePredicate;
import mage.target.common.TargetActivatedAbility;
/**
*
@ -53,7 +49,7 @@ import java.util.UUID;
*/
public class BrownOuphe extends CardImpl {
private final static FilterStackObject filter = new FilterStackObject("activated ability from an artifact source");
private final static FilterAbility filter = new FilterAbility("activated ability from an artifact source");
static {
filter.add(new ArtifactSourcePredicate());
@ -69,7 +65,7 @@ public class BrownOuphe extends CardImpl {
// {1}{G}, {tap}: Counter target activated ability from an artifact source.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CounterTargetEffect(), new ManaCostsImpl<>("{1}{G}"));
ability.addCost(new TapSourceCost());
ability.addTarget(new TargetActivatedOrTriggeredAbility(filter));
ability.addTarget(new TargetActivatedAbility(filter));
this.addAbility(ability);
}
@ -82,22 +78,3 @@ public class BrownOuphe extends CardImpl {
return new BrownOuphe(this);
}
}
class ArtifactSourcePredicate implements Predicate<Ability> {
public ArtifactSourcePredicate() {
}
@Override
public boolean apply(Ability input, Game game) {
if (input instanceof StackAbility) {
return input.getSourceObject(game).isArtifact() && input.getAbilityType() == AbilityType.ACTIVATED;
}
return false;
}
@Override
public String toString() {
return "Source(Artifact)";
}
}

View file

@ -0,0 +1,121 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.b;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetArtifactPermanent;
/**
*
* @author sinsedrix
*/
public class BuildersBane extends CardImpl {
public BuildersBane(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{X}{R}");
// Destroy X target artifacts. Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way.
this.getSpellAbility().addTarget(new TargetArtifactPermanent());
this.getSpellAbility().addEffect(new BuildersBaneEffect());
}
@Override
public void adjustTargets(Ability ability, Game game) {
if (ability instanceof SpellAbility) {
ability.getTargets().clear();
int xValue = ability.getManaCostsToPay().getX();
ability.addTarget(new TargetArtifactPermanent(xValue, xValue));
}
}
public BuildersBane(final BuildersBane card) {
super(card);
}
@Override
public BuildersBane copy() {
return new BuildersBane(this);
}
}
class BuildersBaneEffect extends OneShotEffect {
public BuildersBaneEffect() {
super(Outcome.DestroyPermanent);
this.staticText = "Destroy X target artifacts. {this} deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way";
}
public BuildersBaneEffect(final BuildersBaneEffect effect) {
super(effect);
}
@Override
public BuildersBaneEffect copy() {
return new BuildersBaneEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Map<UUID, Integer> destroyedArtifactPerPlayer = new HashMap<>();
// Destroy X target artifacts.
for (UUID targetID : this.targetPointer.getTargets(game, source)) {
Permanent permanent = game.getPermanent(targetID);
if (permanent != null) {
if (permanent.destroy(source.getSourceId(), game, false)) {
if (game.getState().getZone(permanent.getId()) == Zone.GRAVEYARD) {
destroyedArtifactPerPlayer.merge(permanent.getControllerId(), 1, Integer::sum);
}
}
}
}
// Builder's Bane deals damage to each player equal to the number of artifacts he or she controlled put into a graveyard this way.
for (Map.Entry<UUID, Integer> entry : destroyedArtifactPerPlayer.entrySet()) {
Player player = game.getPlayer(entry.getKey());
if(player != null) {
player.damage(entry.getValue(), source.getSourceId(), game, false, true);
}
}
return true;
}
}

View file

@ -28,21 +28,16 @@
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.UntapAllEffect;
import mage.abilities.effects.common.continuous.BoostControlledEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
* @author Loki
@ -57,8 +52,9 @@ public class CallToGlory extends CardImpl {
public CallToGlory(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{W}");
this.getSpellAbility().addEffect(new CalltoGloryFirstEffect());
//Untap all creatures you control. Samurai creatures you control get +1/+1 until end of turn.
this.getSpellAbility().addEffect(new UntapAllEffect(StaticFilters.FILTER_CONTROLLED_CREATURE));
this.getSpellAbility().addEffect(new BoostControlledEffect(1, 1, Duration.EndOfTurn, filter, false));
}
@ -70,35 +66,4 @@ public class CallToGlory extends CardImpl {
public CallToGlory copy() {
return new CallToGlory(this);
}
}
class CalltoGloryFirstEffect extends OneShotEffect {
public CalltoGloryFirstEffect() {
super(Outcome.Untap);
staticText = "Untap all creatures you control";
}
public CalltoGloryFirstEffect(final CalltoGloryFirstEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
for (Permanent creature : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, player.getId(), game)) {
creature.untap(game);
}
return true;
}
return false;
}
@Override
public CalltoGloryFirstEffect copy() {
return new CalltoGloryFirstEffect(this);
}
}

View file

@ -28,6 +28,7 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
@ -48,7 +49,7 @@ import mage.game.events.GameEvent.EventType;
public class CityOfSolitude extends CardImpl {
public CityOfSolitude(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{G}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{G}");
// Players can cast spells and activate abilities only during their own turns.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CityOfSolitudeEffect()));
@ -74,12 +75,22 @@ class CityOfSolitudeEffect extends ContinuousRuleModifyingEffectImpl {
CityOfSolitudeEffect(final CityOfSolitudeEffect effect) {
super(effect);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == EventType.CAST_SPELL || event.getType() == EventType.ACTIVATE_ABILITY;
}
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
MageObject sourceObject = game.getObject(source.getSourceId());
MageObject eventObject = game.getObject(event.getSourceId());
if (sourceObject != null && eventObject != null) {
return "You can cast or activate anability of " + eventObject.getIdName() + " only during your own turns (" + sourceObject.getIdName() + "). ";
}
return null;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return !game.getActivePlayerId().equals(event.getPlayerId());
@ -89,4 +100,4 @@ class CityOfSolitudeEffect extends ContinuousRuleModifyingEffectImpl {
public CityOfSolitudeEffect copy() {
return new CityOfSolitudeEffect(this);
}
}
}

View file

@ -41,9 +41,9 @@ import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffec
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
@ -60,7 +60,7 @@ import mage.target.targetpointer.FixedTarget;
public class CoffinQueen extends CardImpl {
public CoffinQueen(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{B}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{B}");
this.subtype.add(SubType.ZOMBIE);
this.subtype.add(SubType.WIZARD);
this.power = new MageInt(1);
@ -68,14 +68,14 @@ public class CoffinQueen extends CardImpl {
// You may choose not to untap Coffin Queen during your untap step.
this.addAbility(new SkipUntapOptionalAbility());
// {2}{B}, {tap}: Put target creature card from a graveyard onto the battlefield under your control. When Coffin Queen becomes untapped or you lose control of Coffin Queen, exile that creature.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ReturnFromGraveyardToBattlefieldTargetEffect(), new ManaCostsImpl("{2}{B}"));
ability.addCost(new TapSourceCost());
ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card from a graveyard")));
ability.addEffect(new CoffinQueenCreateDelayedTriggerEffect());
ability.addEffect(new CoffinQueenCreateDelayedTriggerEffect());
this.addAbility(ability);
}
public CoffinQueen(final CoffinQueen card) {
@ -87,33 +87,30 @@ public class CoffinQueen extends CardImpl {
return new CoffinQueen(this);
}
}
class CoffinQueenCreateDelayedTriggerEffect extends OneShotEffect {
public CoffinQueenCreateDelayedTriggerEffect() {
super(Outcome.Detriment);
this.staticText = "When Coffin Queen becomes untapped or you lose control of Coffin Queen, exile that creature";
this.staticText = "When {this} becomes untapped or you lose control of {this}, exile that creature.";
}
public CoffinQueenCreateDelayedTriggerEffect(final CoffinQueenCreateDelayedTriggerEffect effect) {
super(effect);
}
@Override
public CoffinQueenCreateDelayedTriggerEffect copy() {
return new CoffinQueenCreateDelayedTriggerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent controlledCreature = game.getPermanent(source.getFirstTarget());
if (controlledCreature != null) {
DelayedTriggeredAbility delayedAbility = new CoffinQueenDelayedTriggeredAbility();
delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(controlledCreature.getId()));
delayedAbility.setSourceId(source.getSourceId());
delayedAbility.setControllerId(source.getControllerId());
delayedAbility.setSourceObject(source.getSourceObject(game), game);
delayedAbility.init(game);
game.addDelayedTriggeredAbility(delayedAbility);
delayedAbility.getEffects().get(0).setTargetPointer(new FixedTarget(controlledCreature, game));
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}
return false;
@ -123,7 +120,7 @@ class CoffinQueenCreateDelayedTriggerEffect extends OneShotEffect {
class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility {
CoffinQueenDelayedTriggeredAbility() {
super(new ExileTargetEffect(), Duration.EndOfGame, true);
super(new ExileTargetEffect(), Duration.EndOfGame, true);
}
CoffinQueenDelayedTriggeredAbility(CoffinQueenDelayedTriggeredAbility ability) {
@ -136,7 +133,6 @@ class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility {
|| event.getType() == EventType.UNTAPPED;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (EventType.LOST_CONTROL == event.getType()
@ -146,14 +142,14 @@ class CoffinQueenDelayedTriggeredAbility extends DelayedTriggeredAbility {
return EventType.UNTAPPED == event.getType()
&& event.getTargetId() != null && event.getTargetId().equals(getSourceId());
}
@Override
public CoffinQueenDelayedTriggeredAbility copy() {
return new CoffinQueenDelayedTriggeredAbility(this);
}
@Override
public String getRule() {
return "When {this} becomes untapped or you lose control of {this}, exile that creature";
return "When {this} becomes untapped or you lose control of {this}, exile that creature.";
}
}

View file

@ -81,7 +81,7 @@ public class ConduitOfRuin extends CardImpl {
// The first creature spell you cast each turn costs {2} less to cast.
Effect effect = new SpellsCostReductionControllerEffect(filterCost, 2);
effect.setText("The first creature spell you cast each turn costs {2} less to cast");
effect.setText("The first creature spell you cast each turn costs {2} less to cast.");
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect), new ConduitOfRuinWatcher());
}

View file

@ -0,0 +1,142 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.players.Player;
import mage.target.TargetCard;
import mage.target.common.TargetOpponent;
/**
*
* @author TheElk801 & L_J
*/
public class CruelFate extends CardImpl {
public CruelFate(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{U}");
// Look at the top five cards of target opponent's library. Put one of those cards into that player's graveyard and the rest on top of his or her library in any order.
this.getSpellAbility().addEffect(new CruelFateEffect());
this.getSpellAbility().addTarget(new TargetOpponent());
}
public CruelFate(final CruelFate card) {
super(card);
}
@Override
public CruelFate copy() {
return new CruelFate(this);
}
}
class CruelFateEffect extends OneShotEffect {
public CruelFateEffect() {
super(Outcome.DrawCard);
this.staticText = "Look at the top five cards of target opponent's library. Put one of those cards into that player's graveyard and the rest on top of his or her library in any order";
}
public CruelFateEffect(final CruelFateEffect effect) {
super(effect);
}
@Override
public CruelFateEffect copy() {
return new CruelFateEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Card sourceCard = game.getCard(source.getSourceId());
if (sourceCard != null) {
Player you = game.getPlayer(source.getControllerId());
Player player = game.getPlayer(source.getFirstTarget());
if (player != null && you != null) {
Cards cards = new CardsImpl();
int count = Math.min(player.getLibrary().size(), 5);
for (int i = 0; i < count; i++) {
Card card = player.getLibrary().removeFromTop(game);
if (card != null) {
cards.add(card);
}
}
you.lookAtCards(sourceCard.getIdName(), cards, game);
// card to put into opponent's graveyard
TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to put into target opponent's graveyard"));
if (player.canRespond()) {
if (cards.size() > 1) {
you.choose(Outcome.Detriment, cards, target, game);
Card card = cards.get(target.getFirstTarget(), game);
if (card != null) {
cards.remove(card);
card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true);
}
}
else if (cards.size() == 1) {
Card card = cards.get(cards.iterator().next(), game);
card.moveToZone(Zone.GRAVEYARD, source.getSourceId(), game, true);
}
}
// cards to put on the top of opponent's library
TargetCard target2 = new TargetCard(Zone.LIBRARY, new FilterCard("card to put on the top of target opponent's library"));
while (player.canRespond() && cards.size() > 1) {
you.choose(Outcome.Neutral, cards, target2, game);
Card card = cards.get(target2.getFirstTarget(), game);
if (card != null) {
cards.remove(card);
card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
}
target2.clearChosen();
}
if (cards.size() == 1) {
Card card = cards.get(cards.iterator().next(), game);
card.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true);
}
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,110 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.d;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
import mage.abilities.condition.common.AttackedThisStepCondition;
import mage.abilities.effects.PreventionEffectImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
import mage.filter.common.FilterAttackingCreature;
import mage.game.Game;
import mage.game.events.DamagePlayerEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.watchers.common.PlayerAttackedStepWatcher;
/**
*
* @author L_J
*/
public class DeepWood extends CardImpl {
public DeepWood(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}");
// Cast Deep Wood only during the declare attackers step and only if you've been attacked this step.
Ability ability = new CastOnlyDuringPhaseStepSourceAbility(
TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance,
"Cast {this} only during the declare attackers step and only if you've been attacked this step."
);
ability.addWatcher(new PlayerAttackedStepWatcher());
this.addAbility(ability);
// Prevent all damage that would be dealt to you this turn by attacking creatures.
this.getSpellAbility().addEffect(new DeepWoodEffect());
}
public DeepWood(final DeepWood card) {
super(card);
}
@Override
public DeepWood copy() {
return new DeepWood(this);
}
}
class DeepWoodEffect extends PreventionEffectImpl {
private static final FilterAttackingCreature filter = new FilterAttackingCreature();
DeepWoodEffect() {
super(Duration.EndOfTurn, Integer.MAX_VALUE, false);
staticText = "Prevent all damage that would be dealt to you this turn by attacking creatures";
}
DeepWoodEffect(final DeepWoodEffect effect) {
super(effect);
}
@Override
public DeepWoodEffect copy() {
return new DeepWoodEffect(this);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (super.applies(event, source, game) && event instanceof DamagePlayerEvent && event.getAmount() > 0) {
DamagePlayerEvent damageEvent = (DamagePlayerEvent) event;
if (event.getTargetId().equals(source.getControllerId())) {
Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId());
if (permanent != null && filter.match(permanent, game)) {
return true;
}
}
}
return false;
}
}

View file

@ -124,7 +124,7 @@ class DefiantVanguardEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent thisCreature = game.getPermanentOrLKIBattlefield(source.getId());
Permanent thisCreature = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (controller != null && thisCreature != null) {
BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName());
if (watcher != null) {
@ -136,10 +136,10 @@ class DefiantVanguardEffect extends OneShotEffect {
}
}
}
thisCreature.destroy(source.getSourceId(), game, false);
for (Permanent creature : toDestroy) {
creature.destroy(source.getSourceId(), game, false);
}
thisCreature.destroy(source.getSourceId(), game, false);
return true;
}
}

View file

@ -28,17 +28,14 @@
package mage.cards.d;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.*;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.*;
import mage.filter.common.*;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.permanent.AnotherPredicate;
import mage.game.Game;
import mage.game.events.DamageEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
@ -48,12 +45,6 @@ import mage.target.targetpointer.FixedTarget;
*/
public class DinosaurHunter extends CardImpl {
private static final FilterControlledCreaturePermanent filterAnotherDino = new FilterControlledCreaturePermanent();
static {
filterAnotherDino.add(new AnotherPredicate());
filterAnotherDino.add(new SubtypePredicate(SubType.DINOSAUR));
}
public DinosaurHunter(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}");
@ -78,11 +69,6 @@ public class DinosaurHunter extends CardImpl {
class DinosaurHunterAbility extends TriggeredAbilityImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent();
static {
filter.add(new SubtypePredicate(SubType.DINOSAUR));
}
DinosaurHunterAbility() {
super(Zone.BATTLEFIELD, new DestroyTargetEffect());
}
@ -103,11 +89,13 @@ class DinosaurHunterAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent sourcePermanet = game.getPermanent(event.getSourceId());
Permanent targetPermanet = game.getPermanent(event.getTargetId());
if (sourcePermanet != null && targetPermanet != null && event.getSourceId().equals(sourceId) && filter.match(targetPermanet, game)) {
getEffects().get(0).setTargetPointer(new FixedTarget(event.getTargetId()));
return true;
if (((DamageEvent) event).isCombatDamage()
&& event.getSourceId().equals(getSourceId())) {
Permanent targetPermanet = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (targetPermanet.hasSubtype(SubType.DINOSAUR, game)) {
getEffects().get(0).setTargetPointer(new FixedTarget(targetPermanet, game));
return true;
}
}
return false;
}
@ -116,4 +104,4 @@ class DinosaurHunterAbility extends TriggeredAbilityImpl {
public String getRule() {
return "Whenever {this} deals combat damage to a Dinosaur, destroy that creature.";
}
}
}

View file

@ -108,7 +108,8 @@ class DjinnIlluminatusGainReplicateEffect extends ContinuousEffectImpl {
if ((stackObject instanceof Spell)
&& !stackObject.isCopy()
&& stackObject.getControllerId().equals(source.getControllerId())
&& djinn.getControllerId().equals(source.getControllerId())) { // verify that the controller of the djinn cast that spell
&& djinn.getControllerId().equals(source.getControllerId()) // verify that the controller of the djinn cast that spell
&& !stackObject.getManaCost().isEmpty()) { //handle cases like Ancestral Vision
Spell spell = (Spell) stackObject;
if (filter.match(stackObject, game)) {
ReplicateAbility replicateAbility = replicateAbilities.computeIfAbsent(spell.getId(), k -> new ReplicateAbility(spell.getCard(), spell.getSpellAbility().getManaCosts().getText()));

View file

@ -31,7 +31,6 @@ import java.util.UUID;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.common.PutIntoGraveFromAnywhereSourceTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.abilities.effects.common.ShuffleIntoLibrarySourceEffect;
import mage.abilities.keyword.FearAbility;
@ -105,11 +104,7 @@ class DreadTriggeredAbility extends TriggeredAbilityImpl {
if (event.getPlayerId().equals(this.getControllerId())) {
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent != null && permanent.isCreature()) {
for (Effect effect : this.getEffects()) {
if (effect instanceof DestroyTargetEffect) {
effect.setTargetPointer(new FixedTarget(event.getSourceId()));
}
}
this.getEffects().setTargetPointer(new FixedTarget(permanent, game));
return true;
}
}

View file

@ -0,0 +1,73 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.d;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.effects.common.combat.CantBeBlockedByCreaturesAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.TargetController;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.ColorPredicate;
import mage.filter.predicate.permanent.ControllerPredicate;
/**
*
* @author L_J
*/
public class DreadCharge extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Black creatures you control");
private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("except by black creatures");
static {
filter.add(new ColorPredicate(ObjectColor.BLACK));
filter2.add(Predicates.not(new ColorPredicate(ObjectColor.BLACK)));
filter.add(new ControllerPredicate(TargetController.YOU));
}
public DreadCharge(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{B}");
// Black creatures you control can't be blocked this turn except by black creatures.
this.getSpellAbility().addEffect(new CantBeBlockedByCreaturesAllEffect(filter, filter2, Duration.EndOfTurn));
}
public DreadCharge(final DreadCharge card) {
super(card);
}
@Override
public DreadCharge copy() {
return new DreadCharge(this);
}
}

View file

@ -0,0 +1,145 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.e;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.RestrictionEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponent;
/**
*
* @author TheElk801 & L_J
*/
public class EunuchsIntrigues extends CardImpl {
public EunuchsIntrigues(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}");
// Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn.
this.getSpellAbility().addEffect(new EunuchsIntriguesEffect());
this.getSpellAbility().addTarget(new TargetOpponent());
}
public EunuchsIntrigues(final EunuchsIntrigues card) {
super(card);
}
@Override
public EunuchsIntrigues copy() {
return new EunuchsIntrigues(this);
}
}
class EunuchsIntriguesEffect extends OneShotEffect {
EunuchsIntriguesEffect() {
super(Outcome.Benefit);
this.staticText = "Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn.";
}
EunuchsIntriguesEffect(final EunuchsIntriguesEffect effect) {
super(effect);
}
@Override
public EunuchsIntriguesEffect copy() {
return new EunuchsIntriguesEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getFirstTarget());
if (player == null) {
return false;
}
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
filter.add(new ControllerIdPredicate(player.getId()));
Target target = new TargetPermanent(1, 1, filter, true);
if (target.canChoose(source.getSourceId(), player.getId(), game)) {
while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) {
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent != null) {
game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as his only creature able to block this turn");
}
}
game.addEffect(new EunuchsIntriguesRestrictionEffect(target.getFirstTarget()), source);
return true;
}
}
class EunuchsIntriguesRestrictionEffect extends RestrictionEffect {
protected UUID targetId;
public EunuchsIntriguesRestrictionEffect(UUID targetId) {
super(Duration.EndOfTurn);
this.targetId = targetId;
}
public EunuchsIntriguesRestrictionEffect(final EunuchsIntriguesRestrictionEffect effect) {
super(effect);
targetId = effect.targetId;
}
@Override
public EunuchsIntriguesRestrictionEffect copy() {
return new EunuchsIntriguesRestrictionEffect(this);
}
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
if (permanent.getControllerId().equals(source.getFirstTarget())) {
return true;
}
return false;
}
@Override
public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) {
if (targetId != null && blocker.getId().equals(targetId)) {
return true;
}
return false;
}
}

View file

@ -80,7 +80,7 @@ class ExaltedDragonCostToAttackBlockEffect extends PayCostToAttackBlockEffectImp
ExaltedDragonCostToAttackBlockEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK,
new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledLandPermanent("a land"))));
staticText = "{this} can't attack unless you sacrifice a land <i>(This cost is paid as attackers are declared.)</i>";
staticText = "{this} can't attack unless you sacrifice a land. <i>(This cost is paid as attackers are declared.)</i>";
}
ExaltedDragonCostToAttackBlockEffect(ExaltedDragonCostToAttackBlockEffect effect) {

View file

@ -94,6 +94,7 @@ class ExhumeEffect extends OneShotEffect {
FilterCreatureCard filterCreatureCard = new FilterCreatureCard("creature card from your graveyard");
filterCreatureCard.add(new OwnerIdPredicate(playerId));
TargetCardInGraveyard target = new TargetCardInGraveyard(filterCreatureCard);
target.setNotTarget(true);
if (target.canChoose(playerId, game)
&& player.chooseTarget(outcome, target, source, game)) {
Card card = game.getCard(target.getFirstTarget());

View file

@ -28,24 +28,22 @@
package mage.cards.e;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.DamageEvent;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.target.TargetSource;
/**
*
* @author MarcoMarin
*
* @author L_J
*/
public class EyeForAnEye extends CardImpl {
@ -53,8 +51,7 @@ public class EyeForAnEye extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{W}{W}");
// The next time a source of your choice would deal damage to you this turn, instead that source deals that much damage to you and Eye for an Eye deals that much damage to that source's controller.
this.addAbility(new EyeForAnEyeTriggeredAbility(new EyeForAnEyeEffect()));
this.getSpellAbility().addEffect(new EyeForAnEyeEffect());
}
public EyeForAnEye(final EyeForAnEye card) {
@ -67,48 +64,19 @@ public class EyeForAnEye extends CardImpl {
}
}
class EyeForAnEyeTriggeredAbility extends TriggeredAbilityImpl {
class EyeForAnEyeEffect extends ReplacementEffectImpl {
public EyeForAnEyeTriggeredAbility(Effect effect) {
super(Zone.BATTLEFIELD, effect);
}
public EyeForAnEyeTriggeredAbility(final EyeForAnEyeTriggeredAbility ability) {
super(ability);
}
@Override
public EyeForAnEyeTriggeredAbility copy() {
return new EyeForAnEyeTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGED_PLAYER;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
MageObject sourceObject = game.getObject(event.getSourceId());
this.getEffects().get(0).setValue("damageAmount", event.getAmount());
this.getEffects().get(0).setTargetPointer(new FixedTarget(game.getControllerId(sourceObject.getId())));
return true;
}
@Override
public String getRule() {
return "The next time a source of your choice would deal damage to you this turn, instead that source deals that much damage to you and {this} deals that much damage to that source's controller.";
}
}
class EyeForAnEyeEffect extends OneShotEffect {
private final TargetSource damageSource;
public EyeForAnEyeEffect() {
super(Outcome.Damage);
super(Duration.EndOfTurn, Outcome.RedirectDamage);
staticText = "The next time a source of your choice would deal damage to you this turn, instead that source deals that much damage to you and {this} deals that much damage to that source's controller";
this.damageSource = new TargetSource();
}
public EyeForAnEyeEffect(final EyeForAnEyeEffect effect) {
super(effect);
this.damageSource = effect.damageSource.copy();
}
@Override
@ -116,15 +84,48 @@ class EyeForAnEyeEffect extends OneShotEffect {
return new EyeForAnEyeEffect(this);
}
@Override
public void init(Ability source, Game game) {
this.damageSource.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game);
super.init(source, game);
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DAMAGE_PLAYER;
}
@Override
public boolean apply(Game game, Ability source) {
Integer damageAmount = (Integer) this.getValue("damageAmount");
UUID targetId = this.targetPointer.getFirst(game, source);
if (damageAmount != null && targetId != null) {
Player player = game.getPlayer(targetId);
if (player != null) {
player.damage(damageAmount, targetId, game, false, true);
return true;
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
DamageEvent damageEvent = (DamageEvent) event;
if (controller != null) {
controller.damage(damageEvent.getAmount(), damageEvent.getSourceId(), game, damageEvent.isCombatDamage(), damageEvent.isPreventable(), damageEvent.getAppliedEffects());
UUID sourceControllerId = game.getControllerId(damageEvent.getSourceId());
if (sourceControllerId != null) {
Player sourceController = game.getPlayer(sourceControllerId);
if (sourceController != null) {
sourceController.damage(damageEvent.getAmount(), source.getSourceId(), game, false, true);
return true;
}
}
}
return false;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
DamageEvent damageEvent = (DamageEvent) event;
if (controller != null) {
if (controller.getId() == damageEvent.getTargetId() && damageEvent.getSourceId().equals(damageSource.getFirstTarget())) {
this.discard();
return true;
}
}
return false;

View file

@ -0,0 +1,110 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.f;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheBeginOfNextUpkeepDelayedTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
import mage.abilities.effects.common.NameACardEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.target.common.TargetOpponent;
/**
*
* @author Quercitron & L_J
*/
public class Foreshadow extends CardImpl {
public Foreshadow(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{U}");
// Choose a card name, then target opponent puts the top card of his or her library into his or her graveyard. If that card has the chosen name, you draw a card.
this.getSpellAbility().addEffect(new NameACardEffect(NameACardEffect.TypeOfName.ALL));
this.getSpellAbility().addEffect(new ForeshadowEffect());
this.getSpellAbility().addTarget(new TargetOpponent());
// Draw a card at the beginning of the next turn's upkeep.
this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(
new AtTheBeginOfNextUpkeepDelayedTriggeredAbility(new DrawCardSourceControllerEffect(1)), false));
}
public Foreshadow(final Foreshadow card) {
super(card);
}
@Override
public Foreshadow copy() {
return new Foreshadow(this);
}
}
class ForeshadowEffect extends OneShotEffect {
public ForeshadowEffect() {
super(Outcome.DrawCard);
this.staticText = ", then target opponent puts the top card of his or her library into his or her graveyard. If that card has the chosen name, you draw a card";
}
public ForeshadowEffect(final ForeshadowEffect effect) {
super(effect);
}
@Override
public ForeshadowEffect copy() {
return new ForeshadowEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player targetPlayer = game.getPlayer(source.getFirstTarget());
String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY);
if (controller != null && targetPlayer != null && cardName != null && !cardName.isEmpty()) {
Card card = targetPlayer.getLibrary().getFromTop(game);
if (card != null) {
controller.moveCards(card, Zone.GRAVEYARD, source, game);
if (card.getName().equals(cardName)) {
controller.drawCards(1, game);
}
}
return true;
}
return false;
}
}

View file

@ -0,0 +1,153 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.g;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheEndOfCombatDelayedTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.InfoEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.filter.StaticFilters;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanent;
import mage.watchers.common.BlockedAttackerWatcher;
/**
*
* @author LevelX2 & L_J
*/
public class GlyphOfDoom extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Wall creature");
static {
filter.add(new SubtypePredicate(SubType.WALL));
}
public GlyphOfDoom(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{B}");
// Choose target Wall creature. At this turn's next end of combat, destroy all creatures that were blocked by that creature this turn.
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter));
this.getSpellAbility().addEffect(new InfoEffect("Choose target Wall creature"));
this.getSpellAbility().addEffect(new GlyphOfDoomCreateDelayedTriggeredAbilityEffect());
this.getSpellAbility().addWatcher(new BlockedAttackerWatcher());
}
public GlyphOfDoom(final GlyphOfDoom card) {
super(card);
}
@Override
public GlyphOfDoom copy() {
return new GlyphOfDoom(this);
}
}
class GlyphOfDoomCreateDelayedTriggeredAbilityEffect extends OneShotEffect {
public GlyphOfDoomCreateDelayedTriggeredAbilityEffect() {
super(Outcome.Benefit);
this.staticText = "At this turn's next end of combat, destroy all creatures that were blocked by that creature this turn";
}
public GlyphOfDoomCreateDelayedTriggeredAbilityEffect(final GlyphOfDoomCreateDelayedTriggeredAbilityEffect effect) {
super(effect);
}
@Override
public GlyphOfDoomCreateDelayedTriggeredAbilityEffect copy() {
return new GlyphOfDoomCreateDelayedTriggeredAbilityEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
if (!source.getTargets().isEmpty() && source.getFirstTarget() != null) {
MageObjectReference mor = new MageObjectReference(source.getFirstTarget(), game);
AtTheEndOfCombatDelayedTriggeredAbility delayedAbility = new AtTheEndOfCombatDelayedTriggeredAbility(new GlyphOfDoomEffect(mor));
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}
return false;
}
}
class GlyphOfDoomEffect extends OneShotEffect {
MageObjectReference targetCreature;
public GlyphOfDoomEffect(MageObjectReference targetCreature) {
super(Outcome.DestroyPermanent);
this.targetCreature = targetCreature;
}
public GlyphOfDoomEffect(final GlyphOfDoomEffect effect) {
super(effect);
targetCreature = effect.targetCreature;
}
@Override
public GlyphOfDoomEffect copy() {
return new GlyphOfDoomEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null && targetCreature != null) {
BlockedAttackerWatcher watcher = (BlockedAttackerWatcher) game.getState().getWatchers().get(BlockedAttackerWatcher.class.getSimpleName());
if (watcher != null) {
List<Permanent> toDestroy = new ArrayList<>();
for (Permanent creature : game.getBattlefield().getActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, source.getControllerId(), source.getSourceId(), game)) {
if (!creature.getId().equals(targetCreature.getSourceId())) {
if (watcher.creatureHasBlockedAttacker(new MageObjectReference(creature, game), targetCreature, game)) {
toDestroy.add(creature);
}
}
}
for (Permanent creature : toDestroy) {
creature.destroy(source.getSourceId(), game, false);
}
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,145 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.g;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.RestrictionEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControllerIdPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.TargetPermanent;
import mage.target.common.TargetOpponent;
/**
*
* @author TheElk801 & L_J
*/
public class GoblinWarCry extends CardImpl {
public GoblinWarCry(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}");
// Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn.
this.getSpellAbility().addEffect(new GoblinWarCryEffect());
this.getSpellAbility().addTarget(new TargetOpponent());
}
public GoblinWarCry(final GoblinWarCry card) {
super(card);
}
@Override
public GoblinWarCry copy() {
return new GoblinWarCry(this);
}
}
class GoblinWarCryEffect extends OneShotEffect {
GoblinWarCryEffect() {
super(Outcome.Benefit);
this.staticText = "Target opponent chooses a creature he or she controls. Other creatures he or she controls can't block this turn.";
}
GoblinWarCryEffect(final GoblinWarCryEffect effect) {
super(effect);
}
@Override
public GoblinWarCryEffect copy() {
return new GoblinWarCryEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getFirstTarget());
if (player == null) {
return false;
}
FilterCreaturePermanent filter = new FilterCreaturePermanent("creature you control");
filter.add(new ControllerIdPredicate(player.getId()));
Target target = new TargetPermanent(1, 1, filter, true);
if (target.canChoose(source.getSourceId(), player.getId(), game)) {
while (!target.isChosen() && target.canChoose(player.getId(), game) && player.canRespond()) {
player.chooseTarget(Outcome.DestroyPermanent, target, source, game);
}
Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent != null) {
game.informPlayers(player.getLogName() + " has chosen " + permanent.getLogName() + " as his only creature able to block this turn");
}
}
game.addEffect(new GoblinWarCryRestrictionEffect(target.getFirstTarget()), source);
return true;
}
}
class GoblinWarCryRestrictionEffect extends RestrictionEffect {
protected UUID targetId;
public GoblinWarCryRestrictionEffect(UUID targetId) {
super(Duration.EndOfTurn);
this.targetId = targetId;
}
public GoblinWarCryRestrictionEffect(final GoblinWarCryRestrictionEffect effect) {
super(effect);
targetId = effect.targetId;
}
@Override
public GoblinWarCryRestrictionEffect copy() {
return new GoblinWarCryRestrictionEffect(this);
}
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
if (permanent.getControllerId().equals(source.getFirstTarget())) {
return true;
}
return false;
}
@Override
public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) {
if (targetId != null && blocker.getId().equals(targetId)) {
return true;
}
return false;
}
}

View file

@ -0,0 +1,170 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.h;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
import mage.abilities.condition.common.AttackedThisStepCondition;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.AttackingPredicate;
import mage.game.Game;
import mage.game.events.DamagedPlayerEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.watchers.common.PlayerAttackedStepWatcher;
/**
*
* @author LevelX2 & L_J
*/
public class HarshJustice extends CardImpl {
public HarshJustice(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}");
// Cast Harsh Justice only during the declare attackers step and only if you've been attacked this step.
Ability ability = new CastOnlyDuringPhaseStepSourceAbility(
TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance,
"Cast {this} only during the declare attackers step and only if you've been attacked this step."
);
ability.addWatcher(new PlayerAttackedStepWatcher());
this.addAbility(ability);
// This turn, whenever an attacking creature deals combat damage to you, it deals that much damage to its controller.
this.getSpellAbility().addEffect(new CreateDelayedTriggeredAbilityEffect(new HarshJusticeTriggeredAbility()));
}
public HarshJustice(final HarshJustice card) {
super(card);
}
@Override
public HarshJustice copy() {
return new HarshJustice(this);
}
}
class HarshJusticeTriggeredAbility extends DelayedTriggeredAbility {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("attacking creature");
static {
filter.add(new AttackingPredicate());
}
public HarshJusticeTriggeredAbility() {
super(new HarshJusticeEffect(), Duration.EndOfTurn, false);
}
public HarshJusticeTriggeredAbility(final HarshJusticeTriggeredAbility ability) {
super(ability);
}
@Override
public HarshJusticeTriggeredAbility copy() {
return new HarshJusticeTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.DAMAGED_PLAYER;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Player controller = game.getPlayer(this.getControllerId());
DamagedPlayerEvent damageEvent = (DamagedPlayerEvent) event;
Permanent damagePermanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId());
if (controller != null && damagePermanent != null) {
if (damageEvent.isCombatDamage() && controller.getId().equals(damageEvent.getTargetId()) && filter.match(damagePermanent, game)) {
for (Effect effect : this.getEffects()) {
effect.setValue("damage", damageEvent.getAmount());
effect.setValue("sourceId", damageEvent.getSourceId());
}
return true;
}
}
return false;
}
@Override
public String getRule() {
return "This turn, whenever an attacking creature deals combat damage to you, " + super.getRule();
}
}
class HarshJusticeEffect extends OneShotEffect {
public HarshJusticeEffect() {
super(Outcome.Benefit);
this.staticText = "it deals that much damage to its controller";
}
public HarshJusticeEffect(final HarshJusticeEffect effect) {
super(effect);
}
@Override
public HarshJusticeEffect copy() {
return new HarshJusticeEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
int damage = (Integer) this.getValue("damage");
UUID sourceId = (UUID) this.getValue("sourceId");
MageObject sourceObject = game.getObject(source.getSourceId());
if (sourceObject != null && damage > 0 && sourceId != null) {
Permanent targetObject = game.getPermanentOrLKIBattlefield(sourceId);
if (targetObject != null) {
Player controller = game.getPlayer(targetObject.getControllerId());
if (controller != null) {
game.informPlayers(sourceObject.getLogName() + ": " + targetObject.getLogName() + " deals " + damage + " damage to " + controller.getLogName());
controller.damage(damage, sourceId, game, false, true);
return true;
}
}
}
return false;
}
}

View file

@ -29,10 +29,9 @@ package mage.cards.h;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheEndOfTurnStepPostDelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.DelayedTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ExileTargetEffect;
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
import mage.cards.CardImpl;
@ -52,7 +51,7 @@ import mage.target.targetpointer.FixedTarget;
public class HeatShimmer extends CardImpl {
public HeatShimmer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{R}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{R}");
// Create a token that's a copy of target creature. That token has haste and "At the beginning of the end step, exile this permanent."
this.getSpellAbility().addEffect(new HeatShimmerEffect());
@ -89,15 +88,15 @@ class HeatShimmerEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanentOrLKIBattlefield(getTargetPointer().getFirst(game, source));
if (controller != null && permanent != null) {
if (controller != null
&& permanent != null) {
CreateTokenCopyTargetEffect effect = new CreateTokenCopyTargetEffect(source.getControllerId(), null, true);
effect.setTargetPointer(new FixedTarget(permanent, game));
effect.apply(game, source);
for (Permanent addedToken : effect.getAddedPermanent()) {
Effect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(addedToken, game));
new CreateDelayedTriggeredAbilityEffect(new AtTheEndOfTurnStepPostDelayedTriggeredAbility(exileEffect), false).apply(game, source);
}
ExileTargetEffect exileEffect = new ExileTargetEffect();
exileEffect.setTargetPointer(new FixedTarget(effect.getAddedPermanent().get(0), game));
DelayedTriggeredAbility delayedAbility = new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect);
game.addDelayedTriggeredAbility(delayedAbility, source);
return true;
}
return false;

View file

@ -0,0 +1,110 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.h;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
import mage.abilities.condition.common.AttackedThisStepCondition;
import mage.abilities.effects.PreventionEffectImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
import mage.filter.common.FilterAttackingCreature;
import mage.game.Game;
import mage.game.events.DamagePlayerEvent;
import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
import mage.watchers.common.PlayerAttackedStepWatcher;
/**
*
* @author L_J
*/
public class HeavyFog extends CardImpl {
public HeavyFog(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{1}{G}");
// Cast Deep Wood only during the declare attackers step and only if you've been attacked this step.
Ability ability = new CastOnlyDuringPhaseStepSourceAbility(
TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance,
"Cast {this} only during the declare attackers step and only if you've been attacked this step."
);
ability.addWatcher(new PlayerAttackedStepWatcher());
this.addAbility(ability);
// Prevent all damage that would be dealt to you this turn by attacking creatures.
this.getSpellAbility().addEffect(new HeavyFogEffect());
}
public HeavyFog(final HeavyFog card) {
super(card);
}
@Override
public HeavyFog copy() {
return new HeavyFog(this);
}
}
class HeavyFogEffect extends PreventionEffectImpl {
private static final FilterAttackingCreature filter = new FilterAttackingCreature();
HeavyFogEffect() {
super(Duration.EndOfTurn, Integer.MAX_VALUE, false);
staticText = "Prevent all damage that would be dealt to you this turn by attacking creatures";
}
HeavyFogEffect(final HeavyFogEffect effect) {
super(effect);
}
@Override
public HeavyFogEffect copy() {
return new HeavyFogEffect(this);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (super.applies(event, source, game) && event instanceof DamagePlayerEvent && event.getAmount() > 0) {
DamagePlayerEvent damageEvent = (DamagePlayerEvent) event;
if (event.getTargetId().equals(source.getControllerId())) {
Permanent permanent = game.getPermanentOrLKIBattlefield(damageEvent.getSourceId());
if (permanent != null && filter.match(permanent, game)) {
return true;
}
}
}
return false;
}
}

View file

@ -48,7 +48,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate;
*/
public class HeraldOfThePantheon extends CardImpl {
private static final FilterCard filter = new FilterCard("enchantment spells");
private static final FilterCard filter = new FilterCard("Enchantment spells");
private static final FilterSpell filter2 = new FilterSpell("an enchantment spell");
static {

View file

@ -0,0 +1,108 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.h;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.effects.PreventionEffectData;
import mage.abilities.effects.common.PreventNextDamageFromChosenSourceToTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.players.Player;
import mage.target.common.TargetCreatureOrPlayer;
/**
*
* @author L_J
*/
public class HonorablePassage extends CardImpl {
public HonorablePassage(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}");
// The next time a source of your choice would deal damage to target creature or player this turn, prevent that damage. If damage from a red source is prevented this way, Honorable Passage deals that much damage to the source's controller.
this.getSpellAbility().addEffect(new HonorablePassageEffect());
this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());
}
public HonorablePassage(final HonorablePassage card) {
super(card);
}
@Override
public HonorablePassage copy() {
return new HonorablePassage(this);
}
}
class HonorablePassageEffect extends PreventNextDamageFromChosenSourceToTargetEffect {
public HonorablePassageEffect() {
super(Duration.EndOfTurn);
}
public HonorablePassageEffect(final HonorablePassageEffect effect) {
super(effect);
}
@Override
public HonorablePassageEffect copy() {
return new HonorablePassageEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
int damage = event.getAmount();
PreventionEffectData preventEffectData = preventDamageAction(event, source, game);
if (preventEffectData.getPreventedDamage() > 0) {
MageObject sourceObject = game.getObject(event.getSourceId());
if (sourceObject != null && sourceObject.getColor(game).isRed()) {
UUID sourceControllerId = game.getControllerId(event.getSourceId());
if (sourceControllerId != null) {
Player sourceController = game.getPlayer(sourceControllerId);
if (sourceController != null) {
sourceController.damage(damage, source.getSourceId(), game, false, true);
}
}
}
this.used = true;
}
return false;
}
@Override
public String getText(Mode mode) {
return "The next time a source of your choice would deal damage to target creature or player this turn, prevent that damage. If damage from a red source is prevented this way, {this} deals that much damage to the source's controller";
}
}

View file

@ -41,10 +41,10 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AsThoughEffectType;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.filter.FilterPermanent;
import mage.filter.predicate.Predicates;
@ -118,8 +118,8 @@ class HostageTakerExileEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Permanent card = game.getPermanent(targetPointer.getFirst(game, source));
Permanent permanent = game.getPermanent(source.getSourceId());
Permanent card = game.getPermanent(getTargetPointer().getFirst(game, source));
Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (permanent != null && card != null) {
Player controller = game.getPlayer(card.getControllerId());
if (controller != null) {

View file

@ -104,10 +104,7 @@ class HotSoupTriggeredAbility extends TriggeredAbilityImpl {
Permanent equipment = game.getPermanent(this.getSourceId());
if (equipment != null && equipment.getAttachedTo() != null) {
if (Objects.equals(event.getTargetId(), equipment.getAttachedTo())) {
for(Effect effect : this.getEffects())
{
effect.setTargetPointer(new FixedTarget(equipment.getAttachedTo()));
}
this.getEffects().setTargetPointer(new FixedTarget(equipment.getAttachedTo(), game));
return true;
}
}
@ -118,4 +115,4 @@ class HotSoupTriggeredAbility extends TriggeredAbilityImpl {
public String getRule() {
return "Whenever equipped creature is dealt damage, " + super.getRule();
}
}
}

View file

@ -31,6 +31,7 @@ import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousRuleModifyingEffectImpl;
import mage.abilities.keyword.FlashAbility;
@ -39,9 +40,9 @@ import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.AbilityType;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
@ -55,7 +56,7 @@ import mage.game.permanent.Permanent;
public class HushwingGryff extends CardImpl {
public HushwingGryff(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
this.subtype.add(SubType.HIPPOGRIFF);
this.power = new MageInt(2);
@ -92,10 +93,15 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl {
@Override
public String getInfoMessage(Ability source, GameEvent event, Game game) {
MageObject mageObject = game.getObject(event.getSourceId());
MageObject enteringObject = game.getObject(event.getSourceId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (mageObject != null && sourceObject != null) {
return sourceObject.getLogName() + " prevented ability of " + mageObject.getLogName() + " to trigger";
Ability ability = (Ability) getValue("targetAbility");
if (enteringObject != null && sourceObject != null && ability != null) {
MageObject abilitObject = game.getObject(ability.getSourceId());
if (abilitObject != null) {
return sourceObject.getLogName() + " prevented ability of " + abilitObject.getLogName()
+ " to trigger for " + enteringObject.getLogName() + " entering the battlefield.";
}
}
return null;
}
@ -111,7 +117,7 @@ class HushwingGryffEffect extends ContinuousRuleModifyingEffectImpl {
if (ability != null && ability.getAbilityType() == AbilityType.TRIGGERED) {
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
if (permanent != null && permanent.isCreature()) {
return true;
return (((TriggeredAbility) ability).checkTrigger(event, game));
}
}
return false;

View file

@ -28,6 +28,7 @@
package mage.cards.i;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.RestrictionEffect;
@ -88,7 +89,7 @@ class InterdictPredicate implements Predicate<Ability> {
@Override
public boolean apply(Ability input, Game game) {
if (input instanceof StackAbility && input.getAbilityType() == AbilityType.ACTIVATED) {
Permanent sourceObject = game.getPermanentOrLKIBattlefield(input.getSourceId());
MageObject sourceObject = input.getSourceObject(game);
if (sourceObject != null) {
return (sourceObject.isArtifact()
|| sourceObject.isEnchantment()
@ -120,9 +121,12 @@ class InterdictCounterEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
StackObject stackObject = game.getStack().getStackObject(source.getFirstTarget());
if (stackObject != null && game.getStack().counter(source.getFirstTarget(), source.getSourceId(), game)) {
InterdictCantActivateEffect effect = new InterdictCantActivateEffect();
effect.setTargetPointer(new FixedTarget(stackObject.getSourceId()));
game.getContinuousEffects().addEffect(effect, source);
Permanent sourcePermanent = stackObject.getStackAbility().getSourcePermanentIfItStillExists(game);
if (sourcePermanent != null) {
InterdictCantActivateEffect effect = new InterdictCantActivateEffect();
effect.setTargetPointer(new FixedTarget(sourcePermanent, game));
game.getContinuousEffects().addEffect(effect, source);
}
return true;
}
return false;
@ -143,7 +147,7 @@ class InterdictCantActivateEffect extends RestrictionEffect {
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
return getTargetPointer().getFirst(game, source).equals(permanent.getId());
return permanent.getId().equals(getTargetPointer().getFirst(game, source));
}
@Override

View file

@ -48,7 +48,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate;
public class JacesSanctum extends CardImpl {
private static final FilterCard filter = new FilterCard("instant and sorcery spells");
private static final FilterCard filter = new FilterCard("Instant and sorcery spells");
private static final FilterSpell filter2 = new FilterSpell("an instant or sorcery spell");

View file

@ -0,0 +1,73 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.j;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.CastOnlyDuringPhaseStepSourceAbility;
import mage.abilities.condition.common.AttackedThisStepCondition;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
import mage.target.common.TargetAttackingCreature;
import mage.watchers.common.PlayerAttackedStepWatcher;
/**
*
* @author TheElk801
*/
public class JustFate extends CardImpl {
public JustFate(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}");
// Cast Just Fate only during the declare attackers step and only if you've been attacked this step.
Ability ability = new CastOnlyDuringPhaseStepSourceAbility(
TurnPhase.COMBAT, PhaseStep.DECLARE_ATTACKERS, AttackedThisStepCondition.instance,
"Cast {this} only during the declare attackers step and only if you've been attacked this step."
);
ability.addWatcher(new PlayerAttackedStepWatcher());
this.addAbility(ability);
// Destroy target attacking creature.
this.getSpellAbility().addEffect(new DestroyTargetEffect());
this.getSpellAbility().addTarget(new TargetAttackingCreature());
}
public JustFate(final JustFate card) {
super(card);
}
@Override
public JustFate copy() {
return new JustFate(this);
}
}

View file

@ -16,7 +16,7 @@ public class KaerveksSpite extends CardImpl {
super(ownerId, cardSetInfo, new CardType[]{CardType.INSTANT}, "{B}{B}{B}");
// As an additional cost to cast Kaervek's Spite, sacrifice all permanents you control and discard your hand.
this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("all permanents you control")));
this.getSpellAbility().addCost(new SacrificeAllCost(new FilterControlledPermanent("permanents you control")));
this.getSpellAbility().addCost(new DiscardHandCost());
// Target player loses 5 life.

View file

@ -0,0 +1,75 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.k;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.RedirectDamageFromSourceToTargetEffect;
import mage.abilities.keyword.MorphAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author L_J
*/
public class KaronasZealot extends CardImpl {
public KaronasZealot(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{W}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.CLERIC);
this.power = new MageInt(2);
this.toughness = new MageInt(5);
// Morph {3}{W}{W}
this.addAbility(new MorphAbility(this, new ManaCostsImpl("{3}{W}{W}")));
// When Karona's Zealot is turned face up, all damage that would be dealt to it this turn is dealt to target creature instead.
Ability ability = new TurnedFaceUpSourceTriggeredAbility(new RedirectDamageFromSourceToTargetEffect(Duration.EndOfTurn, Integer.MAX_VALUE, false)
.setText("all damage that would be dealt to it this turn is dealt to target creature instead"));
ability.addTarget(new TargetCreaturePermanent());
this.addAbility(ability);
}
public KaronasZealot(final KaronasZealot card) {
super(card);
}
@Override
public KaronasZealot copy() {
return new KaronasZealot(this);
}
}

View file

@ -0,0 +1,80 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.k;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.condition.CompoundCondition;
import mage.abilities.condition.common.AttackedThisStepCondition;
import mage.abilities.condition.common.IsStepCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.decorator.ConditionalActivatedAbility;
import mage.abilities.effects.common.DamageTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.filter.common.FilterAttackingCreature;
import mage.target.TargetPermanent;
import mage.watchers.common.PlayerAttackedStepWatcher;
/**
*
* @author L_J
*/
public class KongmingsContraptions extends CardImpl {
public KongmingsContraptions(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.SOLDIER);
this.power = new MageInt(2);
this.toughness = new MageInt(4);
// {T}: Kongming's Contraptions deals 2 damage to target attacking creature. Activate this ability only during the declare attackers step and only if you've been attacked this step.
Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new DamageTargetEffect(2), new TapSourceCost(),
new CompoundCondition("during the declare attackers step and only if you've been attacked this step",
new IsStepCondition(PhaseStep.DECLARE_ATTACKERS, false), AttackedThisStepCondition.instance)
);
ability.addTarget(new TargetPermanent(new FilterAttackingCreature()));
this.addAbility(ability, new PlayerAttackedStepWatcher());
}
public KongmingsContraptions(final KongmingsContraptions card) {
super(card);
}
@Override
public KongmingsContraptions copy() {
return new KongmingsContraptions(this);
}
}

View file

@ -30,6 +30,7 @@ package mage.cards.k;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.cost.SpellsCostReductionControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -61,7 +62,9 @@ public class KrosanDrover extends CardImpl {
this.toughness = new MageInt(2);
// Creature spells you cast with converted mana cost 6 or greater cost {2} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(filter, 2)));
Effect effect = new SpellsCostReductionControllerEffect(filter, 2);
effect.setText("Creature spells you cast with converted mana cost 6 or greater cost {2} less to cast.");
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
}
public KrosanDrover(final KrosanDrover card) {

View file

@ -35,7 +35,6 @@ import mage.abilities.common.LeavesBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.AdjustingSourceCosts;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.GainLifeTargetEffect;
import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.abilities.effects.common.RegenerateSourceEffect;
@ -58,7 +57,7 @@ import mage.util.CardUtil;
public class LaquatussChampion extends CardImpl {
public LaquatussChampion(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{4}{B}{B}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}");
this.subtype.add(SubType.NIGHTMARE);
this.subtype.add(SubType.HORROR);
@ -126,9 +125,7 @@ class LaquatussChampionLeavesBattlefieldTriggeredAbility extends LeavesBattlefie
String key = CardUtil.getCardZoneString("targetPlayer", this.getSourceId(), game, true);
Object object = game.getState().getValue(key);
if (object instanceof UUID) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget((UUID) object));
}
this.getEffects().setTargetPointer(new FixedTarget((UUID) object));
return true;
}
}

View file

@ -118,7 +118,7 @@ class LeviathanCostToAttackBlockEffect extends PayCostToAttackBlockEffectImpl {
LeviathanCostToAttackBlockEffect() {
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK,
new SacrificeTargetCost(new TargetControlledPermanent(2, 2, filter, false)));
staticText = "{this} can't attack unless you sacrifice two Islands <i>(This cost is paid as attackers are declared.)</i>";
staticText = "{this} can't attack unless you sacrifice two Islands. <i>(This cost is paid as attackers are declared.)</i>";
}
LeviathanCostToAttackBlockEffect(LeviathanCostToAttackBlockEffect effect) {

View file

@ -47,7 +47,7 @@ import mage.target.common.TargetCreaturePermanent;
public class LyevDecree extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures your opponent controls");
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creatures your opponents control");
static {
filter.add(new ControllerPredicate(TargetController.OPPONENT));
}
@ -56,7 +56,7 @@ public class LyevDecree extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}");
// Detain up to two target creatures your opponent controls.
// Detain up to two target creatures your opponents control.
this.getSpellAbility().addEffect(new DetainTargetEffect());
Target target = new TargetCreaturePermanent(0,2,filter,false);
this.getSpellAbility().addTarget(target);

View file

@ -44,7 +44,7 @@ import mage.filter.predicate.mageobject.CardTypePredicate;
*/
public class ManaMatrix extends CardImpl {
private static final FilterCard filter = new FilterCard("instant and enchantment spells");
private static final FilterCard filter = new FilterCard("Instant and enchantment spells");
static {
filter.add(Predicates.or(

View file

@ -0,0 +1,112 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.m;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.continuous.BoostAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.SuperType;
import mage.filter.common.FilterAttackingCreature;
import mage.filter.common.FilterLandPermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SupertypePredicate;
import mage.game.Game;
import mage.game.combat.CombatGroup;
import mage.game.permanent.Permanent;
/**
*
* @author jeffwadsworth
*/
public class MercadiasDownfall extends CardImpl {
private static String rule = "Each attacking creature gets +1/+0 until end of turn for each nonbasic land defending player controls";
public MercadiasDownfall(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{R}");
// Each attacking creature gets +1/+0 until end of turn for each nonbasic land defending player controls.
this.getSpellAbility().addEffect(new BoostAllEffect(new DefendersNonBasicLandCount(), new StaticValue(0), Duration.EndOfTurn, new FilterAttackingCreature(), true, rule));
}
public MercadiasDownfall(final MercadiasDownfall card) {
super(card);
}
@Override
public MercadiasDownfall copy() {
return new MercadiasDownfall(this);
}
class DefendersNonBasicLandCount implements DynamicValue {
UUID defenderId;
@Override
public int calculate(Game game, Ability sourceAbility, Effect effect) {
for (CombatGroup group : game.getCombat().getGroups()) {
defenderId = group.getDefenderId();
if (group.isDefenderIsPlaneswalker()) {
Permanent permanent = game.getPermanent(defenderId);
if (permanent != null) {
defenderId = permanent.getControllerId();
}
}
FilterLandPermanent filter = new FilterLandPermanent("nonbasic land");
filter.add(Predicates.not(new SupertypePredicate(SuperType.BASIC)));
System.out.println("The number of nonbasic lands is " + game.getBattlefield().countAll(filter, defenderId, game));
return game.getBattlefield().countAll(filter, defenderId, game);
}
return 0;
}
@Override
public DynamicValue copy() {
return new DefendersNonBasicLandCount();
}
@Override
public String toString() {
return "X";
}
@Override
public String getMessage() {
return "the number of nonbasic lands defending player controls";
}
}
}

View file

@ -50,10 +50,10 @@ public class MerfolkRaiders extends CardImpl {
this.power = new MageInt(2);
this.toughness = new MageInt(3);
// Phasing
this.addAbility(PhasingAbility.getInstance());
// Islandwalk
this.addAbility(new IslandwalkAbility());
// Phasing
this.addAbility(PhasingAbility.getInstance());
}
public MerfolkRaiders(final MerfolkRaiders card) {

View file

@ -49,7 +49,7 @@ import mage.filter.predicate.mageobject.SubtypePredicate;
*/
public class MilitantInquisitor extends CardImpl {
private static final FilterControlledPermanent filter = new FilterControlledPermanent("equipment you control");
private static final FilterControlledPermanent filter = new FilterControlledPermanent("Equipment you control");
static {
filter.add(new CardTypePredicate(CardType.ARTIFACT));

View file

@ -105,7 +105,7 @@ class MinionOfTheWastesEffect extends OneShotEffect {
controller.loseLife(payAmount, game, false);
game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName())
.append(" pays ").append(payAmount).append(" life").toString());
game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source);
game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source);
return true;
}
return false;

View file

@ -0,0 +1,99 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.m;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author jeffwadsworth
*/
public class MoralityShift extends CardImpl {
public MoralityShift(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{5}{B}{B}");
// Exchange your graveyard and library. Then shuffle your library.
this.getSpellAbility().addEffect(new MoralityShiftEffect());
}
public MoralityShift(final MoralityShift card) {
super(card);
}
@Override
public MoralityShift copy() {
return new MoralityShift(this);
}
}
class MoralityShiftEffect extends OneShotEffect {
MoralityShiftEffect() {
super(Outcome.AIDontUseIt);
staticText = "Exchange your graveyard and library. Then shuffle your library.";
}
MoralityShiftEffect(MoralityShiftEffect effect) {
super(effect);
}
@Override
public MoralityShiftEffect copy() {
return new MoralityShiftEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
List<Card> copyLibrary = controller.getLibrary().getCards(game);
Set<Card> copyGraveyard = controller.getGraveyard().getCards(game);
controller.getLibrary().clear();
controller.getGraveyard().clear();
controller.getGraveyard().addAll(copyLibrary);
controller.getLibrary().addAll(copyGraveyard, game);
controller.shuffleLibrary(source, game);
return true;
}
return false;
}
}

View file

@ -0,0 +1,70 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.m;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.AddManaOfAnyColorEffect;
import mage.abilities.mana.SimpleManaAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
/**
*
* @author spjspj/psjpsj
*/
public class MoxLotus extends CardImpl {
public MoxLotus(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{15}");
// {t}: Add infinity (or 1*10^9 to account for a potential mana reflection) to your mana pool.
this.addAbility(new SimpleManaAbility(Zone.BATTLEFIELD, Mana.ColorlessMana(1000000000), new TapSourceCost()));
// {100}: Add one mana of any color to your mana pool.
Ability ability = new SimpleManaAbility(Zone.BATTLEFIELD, new AddManaOfAnyColorEffect(1), new ManaCostsImpl("{100}"));
this.addAbility(ability);
// You don't lose life due to mana burn.
// Situation normal??
}
public MoxLotus(final MoxLotus card) {
super(card);
}
@Override
public MoxLotus copy() {
return new MoxLotus(this);
}
}

View file

@ -128,7 +128,7 @@ class NamelessRaceEffect extends OneShotEffect {
controller.loseLife(payAmount, game, false);
game.informPlayers(new StringBuilder(sourceCard.getLogName()).append(": ").append(controller.getLogName())
.append(" pays ").append(payAmount).append(" life").toString());
game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.WhileOnBattlefield, SubLayer.SetPT_7b), source);
game.addEffect(new SetPowerToughnessSourceEffect(payAmount, payAmount, Duration.Custom, SubLayer.SetPT_7b), source);
return true;
}
return false;

View file

@ -51,18 +51,23 @@ import mage.target.common.TargetCreatureOrPlayer;
public class NivMizzetTheFiremind extends CardImpl {
public NivMizzetTheFiremind(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{U}{U}{R}{R}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}{U}{R}{R}");
addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.DRAGON);
this.subtype.add(SubType.WIZARD);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Flying
this.addAbility(FlyingAbility.getInstance());
// Whenever you draw a card, Niv-Mizzet, the Firemind deals 1 damage to target creature or player.
Ability ability = new DrawCardControllerTriggeredAbility(new DamageTargetEffect(1), false);
ability.addTarget(new TargetCreatureOrPlayer());
this.addAbility(ability);
// {T}: Draw a card.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new DrawCardSourceControllerEffect(1), new TapSourceCost()));
}

View file

@ -29,7 +29,6 @@ package mage.cards.n;
import java.util.UUID;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.DestroyTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -48,7 +47,7 @@ import mage.target.targetpointer.FixedTarget;
public class NoMercy extends CardImpl {
public NoMercy(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{B}{B}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{B}{B}");
// Whenever a creature deals damage to you, destroy it.
this.addAbility(new NoMercyTriggeredAbility());
@ -88,9 +87,7 @@ public class NoMercy extends CardImpl {
if (event.getPlayerId().equals(this.getControllerId())) {
Permanent permanent = game.getPermanent(event.getSourceId());
if (permanent != null && permanent.isCreature()) {
for (Effect effect : this.getEffects()) {
effect.setTargetPointer(new FixedTarget(event.getSourceId()));
}
this.getEffects().setTargetPointer(new FixedTarget(permanent, game));
return true;
}
}

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.cards.o;
import java.util.UUID;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.dynamicvalue.common.AttackingCreatureCount;
import mage.abilities.effects.common.GainLifeEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
/**
*
* @author L_J
*/
public class OrimsPrayer extends CardImpl {
public OrimsPrayer(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{W}{W}");
// Whenever one or more creatures attack you, you gain 1 life for each attacking creature.
this.addAbility(new OrimsPrayerTriggeredAbility());
}
public OrimsPrayer(final OrimsPrayer card) {
super(card);
}
@Override
public OrimsPrayer copy() {
return new OrimsPrayer(this);
}
}
class OrimsPrayerTriggeredAbility extends TriggeredAbilityImpl {
public OrimsPrayerTriggeredAbility() {
super(Zone.BATTLEFIELD, new GainLifeEffect(new AttackingCreatureCount()));
}
public OrimsPrayerTriggeredAbility(final OrimsPrayerTriggeredAbility ability) {
super(ability);
}
@Override
public OrimsPrayerTriggeredAbility copy() {
return new OrimsPrayerTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.DECLARED_ATTACKERS;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
return game.getCombat().getDefenders().contains(getControllerId());
}
@Override
public String getRule() {
return "Whenever one or more creatures attack you, " + super.getRule();
}
}

View file

@ -107,11 +107,11 @@ class PandemoniumEffect extends OneShotEffect {
if (enteringCreature != null) {
Permanent targetPermanent = game.getPermanent(source.getTargets().getFirstTarget());
if (targetPermanent != null) {
targetPermanent.damage(enteringCreature.getPower().getValue(), source.getSourceId(), game, false, true);
targetPermanent.damage(enteringCreature.getPower().getValue(), enteringCreature.getId(), game, false, true);
} else {
Player targetPlayer = game.getPlayer(source.getTargets().getFirstTarget());
if (targetPlayer != null) {
targetPlayer.damage(enteringCreature.getPower().getValue(), source.getSourceId(), game, false, true);
targetPlayer.damage(enteringCreature.getPower().getValue(), enteringCreature.getId(), game, false, true);
}
}
return true;

View file

@ -0,0 +1,181 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.p;
import java.util.Arrays;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCardInLibrary;
import mage.util.CardUtil;
import mage.util.RandomUtil;
/**
*
* @author jeffwadsworth
*/
public class ParallelThoughts extends CardImpl {
public ParallelThoughts(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{3}{U}{U}");
// When Parallel Thoughts enters the battlefield, search your library for seven cards, exile them in a face-down pile, and shuffle that pile. Then shuffle your library.
this.addAbility(new EntersBattlefieldTriggeredAbility(new ParallelThoughtsSearchEffect()));
// If you would draw a card, you may instead put the top card of the pile you exiled into your hand.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ParallelThoughtsReplacementEffect()));
}
public ParallelThoughts(final ParallelThoughts card) {
super(card);
}
@Override
public ParallelThoughts copy() {
return new ParallelThoughts(this);
}
}
class ParallelThoughtsSearchEffect extends OneShotEffect {
ParallelThoughtsSearchEffect() {
super(Outcome.Neutral);
this.staticText = "search your library for seven cards, exile them in a face-down pile, and shuffle that pile. Then shuffle your library";
}
ParallelThoughtsSearchEffect(final ParallelThoughtsSearchEffect effect) {
super(effect);
}
@Override
public ParallelThoughtsSearchEffect copy() {
return new ParallelThoughtsSearchEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
Cards cardsInExilePile = new CardsImpl();
if (controller != null
&& permanent != null) {
TargetCardInLibrary target = new TargetCardInLibrary(7, new FilterCard());
if (controller.searchLibrary(target, game)) {
for (UUID targetId : target.getTargets()) {
Card card = controller.getLibrary().getCard(targetId, game);
if (card != null) {
cardsInExilePile.add(card);
}
}
// shuffle that exiled pile
UUID[] shuffled = cardsInExilePile.toArray(new UUID[0]);
for (int n = shuffled.length - 1; n > 0; n--) {
int r = RandomUtil.nextInt(n + 1);
UUID temp = shuffled[n];
shuffled[n] = shuffled[r];
shuffled[r] = temp;
}
cardsInExilePile.clear();
cardsInExilePile.addAll(Arrays.asList(shuffled));
// move to exile zone and turn face down
for (Card card : cardsInExilePile.getCards(game)) {
controller.moveCardsToExile(card, source, game, false, CardUtil.getCardExileZoneId(game, source), permanent.getLogName());
card.setFaceDown(true, game);
}
// shuffle controller library
controller.shuffleLibrary(source, game);
}
return true;
}
return false;
}
}
class ParallelThoughtsReplacementEffect extends ReplacementEffectImpl {
ParallelThoughtsReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.DrawCard);
staticText = "If you would draw a card, you may instead put the top card of the pile you exiled with Parallel Thoughts into your hand";
}
ParallelThoughtsReplacementEffect(final ParallelThoughtsReplacementEffect effect) {
super(effect);
}
@Override
public ParallelThoughtsReplacementEffect copy() {
return new ParallelThoughtsReplacementEffect(this);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null
&& !game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)).getCards(game).isEmpty()) {
Card card = game.getExile().getExileZone(CardUtil.getCardExileZoneId(game, source)).getCards(game).iterator().next();
if (card != null) {
controller.moveCards(card, Zone.HAND, source, game);
}
}
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == EventType.DRAW_CARD;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return source.getControllerId().equals(event.getPlayerId());
}
}

View file

@ -0,0 +1,115 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.p;
import java.util.UUID;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.PutCardFromHandOnTopOfLibraryCost;
import mage.abilities.effects.PreventionEffectImpl;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.target.TargetSource;
/**
*
* @author jeffwadsworth
*/
public class Penance extends CardImpl {
public Penance(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
// Put a card from your hand on top of your library: The next time a black or red source of your choice would deal damage this turn, prevent that damage.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PenanceEffect(), new PutCardFromHandOnTopOfLibraryCost()));
}
public Penance(final Penance card) {
super(card);
}
@Override
public Penance copy() {
return new Penance(this);
}
}
class PenanceEffect extends PreventionEffectImpl {
private final TargetSource target;
public PenanceEffect() {
super(Duration.EndOfTurn, Integer.MAX_VALUE, false, false);
this.staticText = "The next time a black or red source of your choice would deal damage to you this turn, prevent that damage.";
this.target = new TargetSource();
}
public PenanceEffect(final PenanceEffect effect) {
super(effect);
this.target = effect.target.copy();
}
@Override
public PenanceEffect copy() {
return new PenanceEffect(this);
}
@Override
public void init(Ability source, Game game) {
this.target.choose(Outcome.PreventDamage, source.getControllerId(), source.getSourceId(), game);
super.init(source, game);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
this.used = true;
this.discard(); // only one use
return true;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (!this.used
&& super.applies(event, source, game)) {
if (event.getTargetId().equals(source.getControllerId())
&& event.getSourceId().equals(target.getFirstTarget())) {
return (game.getObject(target.getFirstTarget()).getColor(game).contains(ObjectColor.BLACK)
|| game.getObject(target.getFirstTarget()).getColor(game).contains(ObjectColor.RED));
}
}
return false;
}
}

View file

@ -46,7 +46,7 @@ public class PlanarGate extends CardImpl {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{6}");
// Creature spells you cast cost up to {2} less to cast.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCreatureCard("creature spells"), 2, true)));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SpellsCostReductionControllerEffect(new FilterCreatureCard("Creature spells"), 2, true)));
}
public PlanarGate(final PlanarGate card) {

View file

@ -93,11 +93,6 @@ class ProgenitusProtectionAbility extends ProtectionAbility {
return new ProgenitusProtectionAbility(this);
}
@Override
public String getRule() {
return "Protection from everything";
}
@Override
public boolean canTarget(MageObject source, Game game) {
return false;

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