Merge origin/master

This commit is contained in:
fireshoes 2016-10-22 08:27:01 -05:00
commit 856802d861
74 changed files with 6070 additions and 4262 deletions

5
.gitignore vendored
View file

@ -77,6 +77,11 @@ Mage/target
Mage.Updater/target
mage.updater.client/target
# Mage.Verify
Mage.Verify/target
Mage.Verify/AllCards.json.zip
Mage.Verify/AllSets.json.zip
releases
Utils/author.txt
.DS_Store

View file

@ -1,6 +1,6 @@
XMage.de 1 (Europe/Germany) fast :xmage.de:17171
woogerworks (North America/USA) :xmage.woogerworks.com:17171
woogerworks replacement (North America/USA) :158.69.192.238:17171
woogerworks replacment (North America/USA) :158.69.192.238:17171
XMage.tahiti :xmage.tahiti.one:443
Seedds Server (Asia) :115.29.203.80:17171
localhost -> connect to your local server (must be started):localhost:17171

View file

@ -1094,6 +1094,7 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
l.duplicateCards(toDuplicate);
}
}
private void showAll() {
for (DragCardGridListener l : listeners) {
l.showAll();
@ -1381,7 +1382,6 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
while (m.find()) {
System.out.println("0=" + m.group(0) + ",,,1=" + m.group(1) + ",,,2=" + m.group(2) + ",,,3=" + m.group(3));
str = "Add" + m.group(1) + m.group(3) + "to your mana pool";
System.out.println("Found " + m.group(2) + " in " + card.getName());
int num = 1;
if (manaCounts.get(m.group(2)) != null) {
num = manaCounts.get(m.group(2));

View file

@ -0,0 +1,116 @@
package mage.client.components;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPopupMenu;
import mage.client.dialog.PreferencesDialog;
/**
*
* @author Campbell Suter <znix@znix.xyz>
*/
public class KeyBindButton extends JButton implements ActionListener {
private final PreferencesDialog preferences;
private final String key;
private PopupItem item;
private JPopupMenu menu;
private int keyCode;
private String text;
/**
* For the IDE only, do not use!
*/
public KeyBindButton() {
this(null, null);
}
public KeyBindButton(PreferencesDialog preferences, String key) {
this.preferences = preferences;
this.key = key;
addActionListener(this);
fixText();
}
private JPopupMenu getMenu() {
menu = new JPopupMenu();
menu.add(item = new PopupItem());
return menu;
}
private void applyNewKeycode(int code) {
preferences.getKeybindButtons().stream()
.filter(b -> b != KeyBindButton.this)
.filter(b -> b.getKeyCode() == code)
.forEach(b -> b.setKeyCode(0));
setKeyCode(code);
menu.setVisible(false);
}
private void fixText() {
if (keyCode == 0) {
text = "<None>";
} else {
text = KeyEvent.getKeyText(keyCode);
}
repaint();
}
public void setKeyCode(int keyCode) {
this.keyCode = keyCode;
switch (keyCode) {
case KeyEvent.VK_ESCAPE:
case KeyEvent.VK_SPACE:
keyCode = 0;
}
fixText();
setSize(getPreferredSize());
}
public int getKeyCode() {
return keyCode;
}
@Override
public String getText() {
return text;
}
public String getKey() {
return key;
}
@Override
public void actionPerformed(ActionEvent e) {
getMenu().show(this, 0, 0);
item.requestFocusInWindow();
}
private class PopupItem extends JLabel implements KeyListener {
public PopupItem() {
super("Press a key");
addKeyListener(this);
setFocusable(true);
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
applyNewKeycode(e.getKeyCode());
}
@Override
public void keyReleased(KeyEvent e) {
}
}
}

View file

@ -0,0 +1,40 @@
package mage.client.components;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import javax.swing.JButton;
import mage.client.dialog.PreferencesDialog;
/**
*
* @author Campbell Suter <znix@znix.xyz>
*/
public class KeyboundButton extends JButton {
private final String text;
private static final Font keyFont = new Font(Font.SANS_SERIF, Font.BOLD, 13);
public KeyboundButton(String key) {
text = PreferencesDialog.getCachedKeyText(key);
}
@Override
protected void paintComponent(Graphics g) {
if (ui != null && g != null) {
Graphics sg = g.create();
try {
ui.update(sg, this);
sg.setColor(Color.white);
sg.setFont(keyFont);
int textWidth = sg.getFontMetrics(keyFont).stringWidth(text);
int centerX = (getWidth() - textWidth) / 2;
sg.drawString(text, centerX, 28);
} finally {
sg.dispose();
}
}
}
}

View file

@ -3949,7 +3949,7 @@
<Component id="checkBoxEndTurnOthers" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="phases_stopSettings" pref="266" max="32767" attributes="0"/>
<Component id="phases_stopSettings" pref="356" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
@ -4171,7 +4171,7 @@
<Component id="panelCardImages" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="panelBackgroundImages" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="53" max="32767" attributes="0"/>
<EmptySpace pref="125" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -4230,7 +4230,7 @@
</Group>
<Component id="cbUseDefaultImageFolder" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="0" pref="231" max="32767" attributes="0"/>
<EmptySpace min="0" pref="270" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
@ -4744,7 +4744,7 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="avatarPane" pref="484" max="32767" attributes="0"/>
<Component id="avatarPane" pref="584" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
@ -5655,7 +5655,7 @@
<Component id="jLabel17" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace pref="91" max="32767" attributes="0"/>
<EmptySpace pref="198" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -5874,6 +5874,251 @@
</Container>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="tabControls">
<Constraints>
<Constraint layoutClass="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout" value="org.netbeans.modules.form.compat2.layouts.support.JTabbedPaneSupportLayout$JTabbedPaneConstraintsDescription">
<JTabbedPaneConstraints tabName="Controls">
<Property name="tabTitle" type="java.lang.String" value="Controls"/>
</JTabbedPaneConstraints>
</Constraint>
</Constraints>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="bttnResetControls" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="labelCancel" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="labelNextTurn" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="labelEndStep" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="labelMainStep" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="labelYourTurn" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="lebelSkip" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="labelPriorEnd" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="labelSkipStep" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="labelConfirm" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="keyConfirm" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="keyCancelSkip" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="keyNextTurn" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="keySkipStack" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="keyYourTurn" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="keyMainStep" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="keyPriorEnd" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="keySkipStep" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="keyEndStep" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="controlsDescriptionLabel" pref="498" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Component id="controlsDescriptionLabel" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="labelConfirm" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="keyConfirm" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="labelCancel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="keyCancelSkip" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="labelNextTurn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="keyNextTurn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="labelEndStep" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="keyEndStep" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="labelSkipStep" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="keySkipStep" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="labelMainStep" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="keyMainStep" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="labelYourTurn" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="keyYourTurn" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lebelSkip" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="keySkipStack" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="labelPriorEnd" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="keyPriorEnd" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="bttnResetControls" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="labelNextTurn">
<Properties>
<Property name="text" type="java.lang.String" value="Next Turn"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="labelEndStep">
<Properties>
<Property name="text" type="java.lang.String" value="End Step"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="labelMainStep">
<Properties>
<Property name="text" type="java.lang.String" value="Main Step"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="labelYourTurn">
<Properties>
<Property name="text" type="java.lang.String" value="Your Turn"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="lebelSkip">
<Properties>
<Property name="text" type="java.lang.String" value="Skip Stack"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="labelPriorEnd">
<Properties>
<Property name="text" type="java.lang.String" value="Prior End"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="labelCancel">
<Properties>
<Property name="text" type="java.lang.String" value="Cancel Skip"/>
</Properties>
</Component>
<Component class="mage.client.components.KeyBindButton" name="keyCancelSkip">
<Properties>
<Property name="text" type="java.lang.String" value="keyBindButton1"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new KeyBindButton(this, KEY_CONTROL_CANCEL_SKIP)"/>
</AuxValues>
</Component>
<Component class="mage.client.components.KeyBindButton" name="keyNextTurn">
<Properties>
<Property name="text" type="java.lang.String" value="keyBindButton1"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new KeyBindButton(this, KEY_CONTROL_NEXT_TURN)"/>
</AuxValues>
</Component>
<Component class="mage.client.components.KeyBindButton" name="keyMainStep">
<Properties>
<Property name="text" type="java.lang.String" value="keyBindButton1"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new KeyBindButton(this, KEY_CONTROL_MAIN_STEP)"/>
</AuxValues>
</Component>
<Component class="mage.client.components.KeyBindButton" name="keyEndStep">
<Properties>
<Property name="text" type="java.lang.String" value="keyBindButton1"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new KeyBindButton(this, KEY_CONTROL_END_STEP)"/>
</AuxValues>
</Component>
<Component class="mage.client.components.KeyBindButton" name="keyYourTurn">
<Properties>
<Property name="text" type="java.lang.String" value="keyBindButton1"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new KeyBindButton(this, KEY_CONTROL_YOUR_TURN)"/>
</AuxValues>
</Component>
<Component class="mage.client.components.KeyBindButton" name="keySkipStack">
<Properties>
<Property name="text" type="java.lang.String" value="keyBindButton1"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new KeyBindButton(this, KEY_CONTROL_SKIP_STACK)"/>
</AuxValues>
</Component>
<Component class="mage.client.components.KeyBindButton" name="keyPriorEnd">
<Properties>
<Property name="text" type="java.lang.String" value="keyBindButton1"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new KeyBindButton(this, KEY_CONTROL_PRIOR_END)"/>
</AuxValues>
</Component>
<Component class="mage.client.components.KeyBindButton" name="keySkipStep">
<Properties>
<Property name="text" type="java.lang.String" value="keyBindButton1"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new KeyBindButton(this, KEY_CONTROL_SKIP_STEP)"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="labelSkipStep">
<Properties>
<Property name="text" type="java.lang.String" value="Skip Step"/>
</Properties>
</Component>
<Component class="mage.client.components.KeyBindButton" name="keyConfirm">
<Properties>
<Property name="text" type="java.lang.String" value="keyBindButton1"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new KeyBindButton(this, KEY_CONTROL_CONFIRM)"/>
</AuxValues>
</Component>
<Component class="javax.swing.JLabel" name="labelConfirm">
<Properties>
<Property name="text" type="java.lang.String" value="Confirm"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="controlsDescriptionLabel">
<Properties>
<Property name="text" type="java.lang.String" value="&lt;html&gt;Click on a button and press a key to change a keybind.&lt;br&gt;Space and ESC are not available, and will set the keybind to nothing.&lt;br&gt;If you are currently playing a game, the changes will not take effect until you start a new game."/>
<Property name="verticalAlignment" type="int" value="1"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="bttnResetControls">
<Properties>
<Property name="text" type="java.lang.String" value="Reset to default"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="bttnResetControlsActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="saveButton">

File diff suppressed because it is too large Load diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View file

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

View file

@ -25,7 +25,6 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.player.ai;
import java.util.LinkedList;
@ -53,9 +52,6 @@ import mage.game.turn.PostCombatMainStep;
import mage.game.turn.Step;
import org.apache.log4j.Logger;
/**
*
* @author ayratn
@ -91,10 +87,10 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
private boolean priorityPlay(Game game) {
if (lastLoggedTurn != game.getTurnNum()) {
lastLoggedTurn = game.getTurnNum();
logger.info("======================= Turn: "+ game.getTurnNum() + " ["+ game.getPlayer(game.getActivePlayerId()).getName() +"] =========================================");
logger.info("======================= Turn: " + game.getTurnNum() + " [" + game.getPlayer(game.getActivePlayerId()).getName() + "] =========================================");
}
logState(game);
logger.debug("Priority -- Step: " + (game.getTurn().getStepType() + " ").substring(0,25) + " ActivePlayer-" + game.getPlayer(game.getActivePlayerId()).getName() + " PriorityPlayer-" + name);
logger.debug("Priority -- Step: " + (game.getTurn().getStepType() + " ").substring(0, 25) + " ActivePlayer-" + game.getPlayer(game.getActivePlayerId()).getName() + " PriorityPlayer-" + name);
game.getState().setPriorityPlayerId(playerId);
game.firePriorityEvent(playerId);
switch (game.getTurn().getStepType()) {
@ -111,8 +107,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
}
act(game);
return true;
}
else {
} else {
pass(game);
}
return false;
@ -128,8 +123,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
}
act(game);
return true;
}
else {
} else {
pass(game);
}
return false;
@ -141,12 +135,12 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
return false;
case POSTCOMBAT_MAIN:
// if (game.getActivePlayerId().equals(playerId)) {
printOutState(game);
if (actions.isEmpty()) {
calculatePostCombatActions(game);
}
act(game);
return true;
printOutState(game);
if (actions.isEmpty()) {
calculatePostCombatActions(game);
}
act(game);
return true;
// }
// else {
// pass(game);
@ -178,7 +172,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
// prevent repeating always the same action with no cost
boolean doThis = true;
if (root.abilities.size() == 1) {
for (Ability ability:root.abilities) {
for (Ability ability : root.abilities) {
if (ability.getManaCosts().convertedManaCost() == 0 && ability.getCosts().isEmpty()) {
if (actionCache.contains(ability.getRule() + "_" + ability.getSourceId())) {
doThis = false; // don't do it again
@ -217,7 +211,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
actions = new LinkedList<>(root.abilities);
combat = root.combat;
} else {
logger.debug("[" + game.getPlayer(playerId).getName() + "] no better score current: " + currentScore + " bestScore: " + bestScore );
logger.debug("[" + game.getPlayer(playerId).getName() + "] no better score current: " + currentScore + " bestScore: " + bestScore);
}
} else {
logger.debug("[" + game.getPlayer(playerId).getName() + "][post] Action: skip");
@ -229,7 +223,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
protected int addActions(SimulationNode2 node, int depth, int alpha, int beta) {
boolean stepFinished = false;
int val;
if (logger.isTraceEnabled() && node !=null && node.getAbilities() != null && !node.getAbilities().toString().equals("[Pass]")){
if (logger.isTraceEnabled() && node != null && node.getAbilities() != null && !node.getAbilities().toString().equals("[Pass]")) {
logger.trace("Add Action [" + depth + "] " + node.getAbilities().toString() + " a: " + alpha + " b: " + beta);
}
Game game = node.getGame();
@ -243,42 +237,39 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
val = GameStateEvaluator2.evaluate(playerId, game);
if (logger.isTraceEnabled()) {
StringBuilder sb = new StringBuilder("Add Actions -- reached end state <").append(val).append(">");
SimulationNode2 logNode = node;
SimulationNode2 logNode = node;
do {
sb.append(new StringBuilder(" <- ["+logNode.getDepth()+"]" + (logNode.getAbilities() != null ? logNode.getAbilities().toString():"[empty]")));
sb.append(new StringBuilder(" <- [" + logNode.getDepth() + "]" + (logNode.getAbilities() != null ? logNode.getAbilities().toString() : "[empty]")));
logNode = logNode.getParent();
} while((logNode.getParent() != null));
} while ((logNode.getParent() != null));
logger.trace(sb);
}
} else if (node.getChildren().size() > 0) {
if (logger.isDebugEnabled()) {
StringBuilder sb = new StringBuilder("Add Action [").append(depth)
.append("] -- something added children ")
.append(node.getAbilities() != null ? node.getAbilities().toString():"null")
.append(node.getAbilities() != null ? node.getAbilities().toString() : "null")
.append(" added children: ").append(node.getChildren().size()).append(" (");
for (SimulationNode2 logNode: node.getChildren()) {
sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString():"null").append(", ");
for (SimulationNode2 logNode : node.getChildren()) {
sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString() : "null").append(", ");
}
sb.append(")");
logger.debug(sb);
}
val = minimaxAB(node, depth-1, alpha, beta);
}
else {
val = minimaxAB(node, depth - 1, alpha, beta);
} else {
logger.trace("Add Action -- alpha: " + alpha + " beta: " + beta + " depth:" + depth + " step:" + game.getTurn().getStepType() + " for player:" + game.getPlayer(game.getPlayerList().get()).getName());
if (allPassed(game)) {
if (!game.getStack().isEmpty()) {
resolve(node, depth, game);
}
else {
} else {
stepFinished = true;
}
}
if (game.gameOver(null)) {
val = GameStateEvaluator2.evaluate(playerId, game);
}
else if (stepFinished) {
} else if (stepFinished) {
logger.debug("Step finished");
int testScore = GameStateEvaluator2.evaluate(playerId, game);
if (game.getActivePlayerId().equals(playerId)) {
@ -286,8 +277,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
// if score at end of step is worse than original score don't check further
//logger.debug("Add Action -- abandoning check, no immediate benefit");
val = testScore;
}
else {
} else {
/*switch (game.getTurn().getStepType()) {
case PRECOMBAT_MAIN:
val = simulateCombat(game, node, depth-1, alpha, beta, false);
@ -301,32 +291,29 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
}*/
val = GameStateEvaluator2.evaluate(playerId, game);
}
}
else {
} else {
val = GameStateEvaluator2.evaluate(playerId, game);
/*if (game.getTurn().getStepType() == PhaseStep.DECLARE_ATTACKERS)
val = simulateBlockers(game, node, playerId, depth-1, alpha, beta, true);
else
val = GameStateEvaluator2.evaluate(playerId, game);
*/
*/
}
}
else if (node.getChildren().size() > 0) {
} else if (node.getChildren().size() > 0) {
if (logger.isDebugEnabled()) {
StringBuilder sb = new StringBuilder("Add Action [").append(depth)
.append("] -- trigger ")
.append(node.getAbilities() != null ? node.getAbilities().toString():"null")
.append(node.getAbilities() != null ? node.getAbilities().toString() : "null")
.append(" added children: ").append(node.getChildren().size()).append(" (");
for (SimulationNode2 logNode: node.getChildren()) {
sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString():"null").append(", ");
for (SimulationNode2 logNode : node.getChildren()) {
sb.append(logNode.getAbilities() != null ? logNode.getAbilities().toString() : "null").append(", ");
}
sb.append(")");
logger.debug(sb);
}
val = minimaxAB(node, depth, alpha, beta);
}
else {
} else {
val = simulatePriority(node, game, depth, alpha, beta);
}
}
@ -353,25 +340,20 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_ATTACKERS, game.getActivePlayerId(), game.getActivePlayerId()))) {
val = simulateAttackers(game, node, game.getActivePlayerId(), depth, alpha, beta, counter);
}
}
else if (!counter) {
} else if (!counter) {
simulateToEnd(game);
val = simulatePostCombatMain(game, node, depth, alpha, beta);
}
}
}
else {
if (!game.getStep().skipStep(game, game.getActivePlayerId())) {
game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, game.getActivePlayerId()));
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, game.getActivePlayerId(), game.getActivePlayerId()))) {
//only suitable for two player games - only simulates blocks for 1st defender
val = simulateBlockers(game, node, game.getCombat().getDefenders().iterator().next(), depth, alpha, beta, counter);
}
}
else if (!counter) {
finishCombat(game);
///val = simulateCounterAttack(game, node, depth, alpha, beta);
} else if (!game.getStep().skipStep(game, game.getActivePlayerId())) {
game.fireEvent(new GameEvent(GameEvent.EventType.DECLARE_BLOCKERS_STEP_PRE, null, null, game.getActivePlayerId()));
if (!game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DECLARING_BLOCKERS, game.getActivePlayerId(), game.getActivePlayerId()))) {
//only suitable for two player games - only simulates blocks for 1st defender
val = simulateBlockers(game, node, game.getCombat().getDefenders().iterator().next(), depth, alpha, beta, counter);
}
} else if (!counter) {
finishCombat(game);
///val = simulateCounterAttack(game, node, depth, alpha, beta);
}
if (val == null) {
val = GameStateEvaluator2.evaluate(playerId, game);
@ -382,7 +364,6 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
return val;
}
protected int simulateAttackers(Game game, SimulationNode2 node, UUID attackerId, int depth, int alpha, int beta, boolean counter) {
if (ALLOW_INTERRUPT && Thread.interrupted()) {
Thread.currentThread().interrupt();
@ -396,7 +377,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
if (logger.isDebugEnabled()) {
logger.debug(attacker.getName() + "'s possible attackers: " + attacker.getAvailableAttackers(defenderId, game));
}
for (Combat engagement: attacker.addAttackers(game)) {
for (Combat engagement : attacker.addAttackers(game)) {
if (logger.isDebugEnabled()) {
logger.debug("Sim Attackers: " + engagement.getAttackers() + ", blockers: " + engagement.getBlockers());
}
@ -405,8 +386,8 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
break;
}
Game sim = game.copy();
for (CombatGroup group: engagement.getGroups()) {
for (UUID attackId: group.getAttackers()) {
for (CombatGroup group : engagement.getGroups()) {
for (UUID attackId : group.getAttackers()) {
sim.getPlayer(attackerId).declareAttacker(attackId, defenderId, sim, false);
}
}
@ -424,7 +405,7 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
sim.fireEvent(GameEvent.getEvent(GameEvent.EventType.DECLARE_ATTACKERS_STEP_POST, sim.getActivePlayerId(), sim.getActivePlayerId()));
Combat simCombat = sim.getCombat().copy();
sim.getPhase().setStep(new DeclareBlockersStep());
val = simulateCombat(sim, newNode, depth-1, alpha, beta, counter);
val = simulateCombat(sim, newNode, depth - 1, alpha, beta, counter);
if (!attackerId.equals(playerId)) {
if (val < beta) {
beta = val;
@ -434,15 +415,12 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
bestNode.setCombat(newNode.getChildren().get(0).getCombat());
}
}
}
else {
if (val > alpha) {
alpha = val;
bestNode = newNode;
bestNode.setScore(val);
if (newNode.getChildren().size() > 0) {
bestNode.setCombat(newNode.getChildren().get(0).getCombat());
}
} else if (val > alpha) {
alpha = val;
bestNode = newNode;
bestNode.setScore(val);
if (newNode.getChildren().size() > 0) {
bestNode.setCombat(newNode.getChildren().get(0).getCombat());
}
}
}
@ -475,16 +453,16 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
logger.debug(defender.getName() + "'s possible blockers: " + defender.getAvailableBlockers(game));
}
List<Combat> combats = defender.addBlockers(game);
for (Combat engagement: combats) {
for (Combat engagement : combats) {
if (alpha >= beta) {
logger.debug("Sim blockers -- pruning blockers");
break;
}
Game sim = game.copy();
for (CombatGroup group: engagement.getGroups()) {
for (CombatGroup group : engagement.getGroups()) {
if (group.getAttackers().size() > 0) {
UUID attackerId = group.getAttackers().get(0);
for (UUID blockerId: group.getBlockers()) {
for (UUID blockerId : group.getBlockers()) {
sim.getPlayer(defenderId).declareBlocker(defenderId, blockerId, attackerId, sim);
}
}
@ -505,11 +483,9 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
finishCombat(sim);
if (sim.gameOver(null)) {
val = GameStateEvaluator2.evaluate(playerId, sim);
}
else if (!counter) {
val = simulatePostCombatMain(sim, newNode, depth-1, alpha, beta);
}
else {
} else if (!counter) {
val = simulatePostCombatMain(sim, newNode, depth - 1, alpha, beta);
} else {
val = GameStateEvaluator2.evaluate(playerId, sim);
}
if (!defenderId.equals(playerId)) {
@ -519,14 +495,11 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
bestNode.setScore(val);
bestNode.setCombat(simCombat);
}
}
else {
if (val > alpha) {
alpha = val;
bestNode = newNode;
bestNode.setScore(val);
bestNode.setCombat(simCombat);
}
} else if (val > alpha) {
alpha = val;
bestNode = newNode;
bestNode.setScore(val);
bestNode.setCombat(simCombat);
}
}
}
@ -570,7 +543,6 @@ public class ComputerPlayer7 extends ComputerPlayer6 {
val = GameStateEvaluator2.evaluate(playerId, game);
return val;
}*/
protected void simulateStep(Game game, Step step) {
if (ALLOW_INTERRUPT && Thread.interrupted()) {
Thread.currentThread().interrupt();

View file

@ -65,9 +65,14 @@ import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.constants.PhaseStep;
import mage.constants.PlayerAction;
import static mage.constants.PlayerAction.HOLD_PRIORITY;
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_ID_NO;
import static mage.constants.PlayerAction.REQUEST_AUTO_ANSWER_RESET_ALL;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_NAME_LAST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
import mage.constants.RangeOfInfluence;
import static mage.constants.SpellAbilityType.SPLIT;
import static mage.constants.SpellAbilityType.SPLIT_FUSED;
import mage.constants.Zone;
import mage.filter.common.FilterAttackingCreature;
import mage.filter.common.FilterBlockingCreature;
@ -730,12 +735,14 @@ public class HumanPlayer extends PlayerImpl {
}
private boolean checkPassStep(Game game) {
if (game.getStep() != null) {
try {
if (playerId.equals(game.getActivePlayerId())) {
return !this.getUserData().getUserSkipPrioritySteps().getYourTurn().isPhaseStepSet(game.getStep().getType());
} else {
return !this.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getStep().getType());
}
} catch (NullPointerException ex) {
logger.error("null pointer exception UserData = " + userData == null ? "null" : "not null");
}
return true;
}
@ -1459,6 +1466,11 @@ public class HumanPlayer extends PlayerImpl {
protected void updateGameStatePriority(String methodName, Game game) {
if (game.getState().getPriorityPlayerId() != null) { // don't do it if priority was set to null before (e.g. discard in cleanaup)
if (getId() == null) {
logger.fatal("Player with no ID: " + name);
this.quit(game);
return;
}
logger.debug("Setting game priority to " + getId() + " [" + methodName + "]");
game.getState().setPriorityPlayerId(getId());
}

View file

@ -58,7 +58,7 @@ public class Arena extends CardImpl {
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ArenaEffect(), new GenericManaCost(3));
ability.addCost(new TapSourceCost());
ability.addTarget(new TargetControlledCreaturePermanent());
ability.addTarget(new TargetOpponentsChoicePermanent(new FilterControlledCreaturePermanent()));
ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, new FilterControlledCreaturePermanent(), false, true));
this.addAbility(ability);
}

View file

@ -28,13 +28,14 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.AttacksAttachedTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.LoseLifeDefendingPlayerEffect;
import mage.abilities.effects.common.combat.CantBeBlockedAttachedEffect;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
@ -43,8 +44,6 @@ import mage.constants.AttachmentType;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.Counter;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -58,7 +57,7 @@ import mage.target.common.TargetCreaturePermanent;
public class CloakingDevice extends CardImpl {
public CloakingDevice(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{U}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{U}");
this.subtype.add("Aura");
// Enchant creature
@ -72,7 +71,7 @@ public class CloakingDevice extends CardImpl {
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeBlockedAttachedEffect(AttachmentType.AURA)));
// Whenever enchanted creature attacks, defending player loses 1 life.
this.addAbility(new AttacksAttachedTriggeredAbility(new LoseLifeDefendingPlayerEffect(1, true), AttachmentType.AURA, false));
this.addAbility(new AttacksAttachedTriggeredAbility(new CloakingDeviceLoseLifeDefendingPlayerEffect(1, true), AttachmentType.AURA, false));
}
@ -85,3 +84,54 @@ public class CloakingDevice extends CardImpl {
return new CloakingDevice(this);
}
}
class CloakingDeviceLoseLifeDefendingPlayerEffect extends OneShotEffect {
private DynamicValue amount;
private boolean attackerIsSource;
/**
*
* @param amount
* @param attackerIsSource true if the source.getSourceId() contains the
* attacker false if attacker has to be taken from targetPointer
*/
public CloakingDeviceLoseLifeDefendingPlayerEffect(int amount, boolean attackerIsSource) {
this(new StaticValue(amount), attackerIsSource);
}
public CloakingDeviceLoseLifeDefendingPlayerEffect(DynamicValue amount, boolean attackerIsSource) {
super(Outcome.Damage);
this.amount = amount;
this.attackerIsSource = attackerIsSource;
}
public CloakingDeviceLoseLifeDefendingPlayerEffect(final CloakingDeviceLoseLifeDefendingPlayerEffect effect) {
super(effect);
this.amount = effect.amount.copy();
this.attackerIsSource = effect.attackerIsSource;
}
@Override
public CloakingDeviceLoseLifeDefendingPlayerEffect copy() {
return new CloakingDeviceLoseLifeDefendingPlayerEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent enchantment = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null) {
Player defender = game.getPlayer(game.getCombat().getDefendingPlayerId(enchantment.getAttachedTo(), game));
if (defender != null) {
defender.loseLife(amount.calculate(game, source, this), game, false);
}
}
return true;
}
@Override
public String getText(Mode mode) {
return "defending player loses " + amount + " life";
}
}

View file

@ -55,7 +55,7 @@ import mage.target.targetpointer.FixedTarget;
public class DarkDecision extends CardImpl {
public DarkDecision(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{B}{R}");
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{B}{R}");
// As an additional cost to cast Dark Decision, pay 1 life.
this.getSpellAbility().addCost(new PayLifeCost(1));
@ -103,7 +103,7 @@ class DarkDecisionEffect extends OneShotEffect {
if (card != null) {
controller.moveCardsToExile(card, source, game, true, source.getSourceId(), sourceObject.getIdName());
ContinuousEffect effect = new DarkDecisionMayPlayExiledEffect();
effect.setTargetPointer(new FixedTarget(card.getId()));
effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source);
}
controller.shuffleLibrary(source, game);
@ -137,7 +137,7 @@ class DarkDecisionMayPlayExiledEffect extends AsThoughEffectImpl {
@Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
if (objectId.equals(getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getSourceId())) {
if (objectId.equals(getTargetPointer().getFirst(game, source)) && affectedControllerId.equals(source.getControllerId())) {
ExileZone exileZone = game.getExile().getExileZone(source.getSourceId());
return exileZone != null && exileZone.contains(getTargetPointer().getFirst(game, source));
}

View file

@ -64,7 +64,7 @@ public class DiaochanArtfulBeauty extends CardImpl {
// {tap}: Destroy target creature of your choice, then destroy target creature of an opponent's choice. Activate this ability only during your turn, before attackers are declared.
Ability ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new DiaochanArtfulBeautyDestroyEffect(), new TapSourceCost(), MyTurnBeforeAttackersDeclaredCondition.getInstance());
ability.addTarget(new TargetCreaturePermanent());
ability.addTarget(new TargetOpponentsChoicePermanent(new FilterCreaturePermanent()));
ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, new FilterCreaturePermanent(), false, true));
this.addAbility(ability);
}

View file

@ -49,7 +49,7 @@ import mage.game.stack.StackObject;
public class DoublingSeason extends CardImpl {
public DoublingSeason(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{4}{G}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{G}");
// If an effect would put one or more tokens onto the battlefield under your control, it puts twice that many of those tokens onto the battlefield instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DoublingSeasonTokenEffect()));
@ -91,16 +91,8 @@ class DoublingSeasonTokenEffect extends ReplacementEffectImpl {
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
StackObject spell = game.getStack().getStackObject(event.getSourceId());
if (spell != null && spell.getControllerId().equals(source.getControllerId())) {
return true;
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
StackObject stackObject = game.getStack().getStackObject(event.getSourceId());
return stackObject != null && event.getPlayerId().equals(source.getControllerId());
}
@Override
@ -139,10 +131,7 @@ class DoublingSeasonCounterEffect extends ReplacementEffectImpl {
if (permanent == null) {
permanent = game.getPermanentEntering(event.getTargetId());
}
if (permanent != null && permanent.getControllerId().equals(source.getControllerId())) {
return true;
}
return false;
return permanent != null && permanent.getControllerId().equals(source.getControllerId());
}
@Override

View file

@ -34,7 +34,6 @@ import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CastSourceTriggeredAbility;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.effects.common.turn.ControlTargetPlayerNextTurnEffect;
@ -64,12 +63,13 @@ import mage.util.CardUtil;
public class EmrakulThePromisedEnd extends CardImpl {
private static final FilterCard filter = new FilterCard("instants");
static {
filter.add(new CardTypePredicate(CardType.INSTANT));
}
public EmrakulThePromisedEnd(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{13}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{13}");
this.supertype.add("Legendary");
this.subtype.add("Eldrazi");
this.power = new MageInt(13);

View file

@ -49,17 +49,17 @@ import mage.target.TargetSpell;
public class ErsatzGnomes extends CardImpl {
public ErsatzGnomes(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}");
this.subtype.add("Gnome");
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// {tap}: Target spell becomes colorless.
// {T}: Target spell becomes colorless.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesColorTargetEffect(new ObjectColor(), Duration.Custom), new TapSourceCost());
ability.addTarget(new TargetSpell());
this.addAbility(ability);
// {tap}: Target permanent becomes colorless until end of turn.
// {T}: Target permanent becomes colorless until end of turn.
ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesColorTargetEffect(new ObjectColor(), Duration.EndOfTurn), new TapSourceCost());
ability.addTarget(new TargetPermanent());
this.addAbility(ability);

View file

@ -0,0 +1,69 @@
/*
* 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.effects.common.continuous.GainControlTargetEffect;
import mage.abilities.keyword.BuybackAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.target.common.TargetOpponentsChoicePermanent;
/**
*
* @author spjspj
*/
public class Evangelize extends CardImpl {
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent();
public Evangelize(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}");
// Buyback {2}{W}{W}
this.addAbility(new BuybackAbility("{2}{W}{W}"));
// Gain control of target creature of an opponent's choice that he or she controls.
GainControlTargetEffect effect = new GainControlTargetEffect(Duration.EndOfGame);
effect.setText("Gain control of target creature of an opponent's choice that he or she controls");
this.getSpellAbility().addEffect(effect);
this.getSpellAbility().addTarget(new TargetOpponentsChoicePermanent(1, 1, filter, false, true));
}
public Evangelize(final Evangelize card) {
super(card);
}
@Override
public Evangelize copy() {
return new Evangelize(this);
}
}

View file

@ -29,19 +29,12 @@ package mage.cards.e;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.RestrictionEffect;
import mage.abilities.keyword.CanBlockSpaceflightAbility;
import mage.abilities.keyword.FlashAbility;
import mage.abilities.keyword.SpaceflightAbility;
import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
@ -50,7 +43,7 @@ import mage.game.permanent.Permanent;
public class Exogorth extends CardImpl {
public Exogorth(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{5}{G}{G}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{G}{G}");
this.subtype.add("Slug");
this.power = new MageInt(7);
this.toughness = new MageInt(7);
@ -61,8 +54,8 @@ public class Exogorth extends CardImpl {
// Trample
this.addAbility(TrampleAbility.getInstance());
// Exogorth can block only creatures with spaceflight.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CanBlockOnlySpaceflightEffect()));
// Exogorth can block creatures with spaceflight.
this.addAbility(CanBlockSpaceflightAbility.getInstance());
}
public Exogorth(final Exogorth card) {
@ -74,31 +67,3 @@ public class Exogorth extends CardImpl {
return new Exogorth(this);
}
}
class CanBlockOnlySpaceflightEffect extends RestrictionEffect {
public CanBlockOnlySpaceflightEffect() {
super(Duration.EndOfTurn);
this.staticText = "{this} can block only creatures with spaceflight";
}
public CanBlockOnlySpaceflightEffect(final CanBlockOnlySpaceflightEffect effect) {
super(effect);
}
@Override
public boolean applies(Permanent permanent, Ability source, Game game) {
return permanent.getId().equals(source.getSourceId());
}
@Override
public boolean canBlock(Permanent attacker, Permanent blocker, Ability source, Game game) {
return attacker.getAbilities().contains(SpaceflightAbility.getInstance());
}
@Override
public CanBlockOnlySpaceflightEffect copy() {
return new CanBlockOnlySpaceflightEffect(this);
}
}

View file

@ -0,0 +1,95 @@
/*
* 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.effects.OneShotEffect;
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
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 emerald000
*/
public class Flux extends CardImpl {
public Flux(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{U}");
// Each player discards any number of cards, then draws that many cards.
this.getSpellAbility().addEffect(new FluxEffect());
// Draw a card.
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
}
public Flux(final Flux card) {
super(card);
}
@Override
public Flux copy() {
return new Flux(this);
}
}
class FluxEffect extends OneShotEffect {
FluxEffect() {
super(Outcome.DrawCard);
this.staticText = "Each player discards any number of cards, then draws that many cards";
}
FluxEffect(final FluxEffect effect) {
super(effect);
}
@Override
public FluxEffect copy() {
return new FluxEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null) {
int numToDiscard = player.getAmount(0, player.getHand().size(), "Discard how many cards?", game);
player.discard(numToDiscard, false, source, game);
player.drawCards(numToDiscard, game);
}
}
return true;
}
}

View file

@ -63,7 +63,7 @@ public class ForceAdept extends CardImpl {
}
public ForceAdept(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{G}{U}{W}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{G}{U}{W}");
this.subtype.add("Mirialan");
this.subtype.add("Jedi");
this.power = new MageInt(2);
@ -74,8 +74,8 @@ public class ForceAdept extends CardImpl {
// When Force Adept enters the battlefield, return another target creature you control and target creature you don't control to their owner's hands.
Ability ability = new EntersBattlefieldTriggeredAbility(new ForceAdeptEffect());
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(filter1));
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter2));
ability.addTarget(new TargetControlledCreaturePermanent(filter1));
ability.addTarget(new TargetCreaturePermanent(filter2));
this.addAbility(ability);
}

View file

@ -36,6 +36,7 @@ import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.CardTypePredicate;
@ -48,11 +49,11 @@ public class GlintNestCrane extends CardImpl {
private static final FilterCard filter = new FilterCard("an artifact card");
static {
filter.add(new CardTypePredicate(CardType.ARTIFACT));
filter.add(new CardTypePredicate(CardType.ARTIFACT));
}
public GlintNestCrane(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}");
this.subtype.add("Bird");
this.power = new MageInt(1);
this.toughness = new MageInt(3);
@ -62,7 +63,8 @@ public class GlintNestCrane extends CardImpl {
// When Glint-Nest Crane enters the battlefield, look at the top four cards of your library. You may reveal an artifact card from among them and
// put it into your hand. Put the rest on the bottom of your library in any order.
this.addAbility(new EntersBattlefieldTriggeredAbility(new LookLibraryAndPickControllerEffect(new StaticValue(4), false, new StaticValue(1), filter, false)));
this.addAbility(new EntersBattlefieldTriggeredAbility(
new LookLibraryAndPickControllerEffect(new StaticValue(4), false, new StaticValue(1), filter, Zone.LIBRARY, false, true, false, Zone.HAND, true)));
}
public GlintNestCrane(final GlintNestCrane card) {

View file

@ -25,13 +25,9 @@
* 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.constants.CardType;
import mage.constants.Zone;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
@ -39,9 +35,12 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPlayer;
@ -51,15 +50,16 @@ import mage.target.TargetPlayer;
*/
public class Grindclock extends CardImpl {
public Grindclock (UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}");
public Grindclock(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), new TapSourceCost()));
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GrindclockEffect(), new TapSourceCost());
ability.addTarget(new TargetPlayer());
this.addAbility(ability);
}
public Grindclock (final Grindclock card) {
public Grindclock(final Grindclock card) {
super(card);
}
@ -71,6 +71,7 @@ public class Grindclock extends CardImpl {
}
class GrindclockEffect extends OneShotEffect {
public GrindclockEffect() {
super(Outcome.Detriment);
staticText = "Target player puts the top X cards of his or her library into his or her graveyard, where X is the number of charge counters on {this}";
@ -82,11 +83,14 @@ class GrindclockEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
int amount = game.getPermanent(source.getSourceId()).getCounters(game).getCount(CounterType.CHARGE);
Player targetPlayer = game.getPlayer(source.getFirstTarget());
if (targetPlayer != null) {
targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game);
return true;
Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId());
if (sourceObject != null) {
int amount = sourceObject.getCounters(game).getCount(CounterType.CHARGE);
Player targetPlayer = game.getPlayer(source.getFirstTarget());
if (targetPlayer != null) {
targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game);
return true;
}
}
return false;
}

View file

@ -44,7 +44,7 @@ import mage.constants.CardType;
public class GunganCaptain extends CardImpl {
public GunganCaptain(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{U}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{U}");
this.subtype.add("Gungan");
this.subtype.add("Warrior");
this.power = new MageInt(1);

View file

@ -64,7 +64,7 @@ public class MagusOfTheArena extends CardImpl {
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MagusOfTheArenaEffect(), new GenericManaCost(3));
ability.addCost(new TapSourceCost());
ability.addTarget(new TargetControlledCreaturePermanent());
ability.addTarget(new TargetOpponentsChoicePermanent(new FilterControlledCreaturePermanent()));
ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, new FilterControlledCreaturePermanent(), false, true));
this.addAbility(ability);
}

View file

@ -59,7 +59,7 @@ import mage.sets.Commander;
public class PaintersServant extends CardImpl {
public PaintersServant(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{2}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{2}");
this.subtype.add("Scarecrow");
this.power = new MageInt(1);
@ -97,24 +97,23 @@ class PaintersServantEffect extends ContinuousEffectImpl {
if (color == null) {
return false;
}
String colorString = color.toString();
for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
setObjectColor(perm, colorString, game);
perm.getColor(game).addColor(color);
}
// Stack
for (MageObject object : game.getStack()) {
if (object instanceof Spell) {
setObjectColor(object, colorString, game);
object.getColor(game).addColor(color);
}
}
// Exile
for (Card card : game.getExile().getAllCards(game)) {
setCardColor(card, colorString, game);
game.getState().getCreateCardAttribute(card).getColor().addColor(color);
}
// Command
for (CommandObject commandObject : game.getState().getCommand()) {
if (commandObject instanceof Commander) {
setObjectColor(commandObject, colorString, game);
commandObject.getColor(game).addColor(color);
}
}
@ -123,15 +122,15 @@ class PaintersServantEffect extends ContinuousEffectImpl {
if (player != null) {
// Hand
for (Card card : player.getHand().getCards(game)) {
setCardColor(card, colorString, game);
game.getState().getCreateCardAttribute(card).getColor().addColor(color);
}
// Library
for (Card card : player.getLibrary().getCards(game)) {
setCardColor(card, colorString, game);
game.getState().getCreateCardAttribute(card).getColor().addColor(color);
}
// Graveyard
for (Card card : player.getGraveyard().getCards(game)) {
setCardColor(card, colorString, game);
game.getState().getCreateCardAttribute(card).getColor().addColor(color);
}
}
}
@ -140,47 +139,6 @@ class PaintersServantEffect extends ContinuousEffectImpl {
return false;
}
protected static void setCardColor(Card card, String colorString, Game game) {
ObjectColor color = game.getState().getCreateCardAttribute(card).getColor();
switch (colorString) {
case "W":
color.setWhite(true);
break;
case "B":
color.setBlack(true);
break;
case "U":
color.setBlue(true);
break;
case "G":
color.setGreen(true);
break;
case "R":
color.setRed(true);
break;
}
}
protected static void setObjectColor(MageObject obj, String colorString, Game game) {
switch (colorString) {
case "W":
obj.getColor(game).setWhite(true);
break;
case "B":
obj.getColor(game).setBlack(true);
break;
case "U":
obj.getColor(game).setBlue(true);
break;
case "G":
obj.getColor(game).setGreen(true);
break;
case "R":
obj.getColor(game).setRed(true);
break;
}
}
@Override
public PaintersServantEffect copy() {
return new PaintersServantEffect(this);

View file

@ -69,7 +69,7 @@ public class Preacher extends CardImpl {
// {tap}: Gain control of target creature of an opponent's choice that he or she controls for as long as Preacher remains tapped.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreacherEffect(), new TapSourceCost());
ability.addTarget(new TargetOpponentsChoicePermanent(new FilterControlledCreaturePermanent()));
ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, new FilterControlledCreaturePermanent(), false, true));
this.addAbility(ability);

View file

@ -33,7 +33,7 @@ import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
import mage.abilities.effects.common.ReturnSourceFromGraveyardToBattlefieldEffect;
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -45,6 +45,7 @@ import mage.filter.predicate.other.OwnerPredicate;
import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent;
import mage.target.targetpointer.FixedTarget;
import mage.watchers.common.CastFromGraveyardWatcher;
/**
@ -60,14 +61,16 @@ public class PrizedAmalgam extends CardImpl {
}
public PrizedAmalgam(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{U}{B}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{U}{B}");
this.subtype.add("Zombie");
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Whenever a creature enters the battlefield, if it entered from your graveyard or you cast it from your graveyard, return Prized Amalgam from your graveyard to the battlefield tapped at the beginning of the next end step.
Effect effect = new ReturnFromGraveyardToBattlefieldTargetEffect(true);
effect.setText("return {this} from your graveyard to the battlefield tapped at the beginning of the next end step");
this.addAbility(new PrizedAmalgamTriggerdAbility(new CreateDelayedTriggeredAbilityEffect(
new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true))), filter),
new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect)), filter),
new CastFromGraveyardWatcher());
}
@ -98,20 +101,32 @@ class PrizedAmalgamTriggerdAbility extends EntersBattlefieldAllTriggeredAbility
@Override
public boolean checkTrigger(GameEvent event, Game game) {
/**
* 4/8/2016 Prized Amalgams ability triggers only if its in your
* graveyard immediately after a creature enters the battlefield from
* your graveyard or you cast a creature from your graveyard. A Prized
* Amalgam thats already on the battlefield wont be returned at the
* beginning of the next end step if its put into your graveyard later.
*/
boolean result = false;
if (super.checkTrigger(event, game)) {
EntersTheBattlefieldEvent entersEvent = (EntersTheBattlefieldEvent) event;
if (entersEvent.getFromZone().equals(Zone.GRAVEYARD)) {
return true;
}
if (entersEvent.getFromZone().equals(Zone.STACK) && entersEvent.getTarget().getControllerId().equals(getControllerId())) {
result = true;
} else if (entersEvent.getFromZone().equals(Zone.STACK) && entersEvent.getTarget().getControllerId().equals(getControllerId())) {
CastFromGraveyardWatcher watcher = (CastFromGraveyardWatcher) game.getState().getWatchers().get(CastFromGraveyardWatcher.class.getName());
if (watcher != null) {
int zcc = game.getState().getZoneChangeCounter(event.getSourceId());
return watcher.spellWasCastFromGraveyard(event.getSourceId(), zcc - 1);
result = watcher.spellWasCastFromGraveyard(event.getSourceId(), zcc - 1);
}
}
}
return false;
if (result) {
for (Effect effect : getEffects()) {
effect.setTargetPointer(new FixedTarget(getSourceId(), game.getState().getZoneChangeCounter(getSourceId())));
}
}
return result;
}
@Override

View file

@ -28,9 +28,6 @@
package mage.cards.s;
import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Zone;
import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount;
@ -38,6 +35,9 @@ import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.common.continuous.BoostSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Zone;
import mage.filter.common.FilterArtifactCard;
/**
@ -47,13 +47,15 @@ import mage.filter.common.FilterArtifactCard;
public class SalvageSlasher extends CardImpl {
public SalvageSlasher(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{1}{B}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{1}{B}");
this.subtype.add("Human");
this.subtype.add("Rogue");
this.power = new MageInt(1);
this.toughness = new MageInt(1);
BoostSourceEffect effect = new BoostSourceEffect(new CardsInControllerGraveyardCount(new FilterArtifactCard()),
// Salvage Slasher gets +1/+0 for each artifact card in your graveyard.
BoostSourceEffect effect = new BoostSourceEffect(new CardsInControllerGraveyardCount(new FilterArtifactCard("artifact card")),
new StaticValue(0),
Duration.WhileOnBattlefield);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));

View file

@ -47,13 +47,13 @@ import mage.watchers.common.LifeLossOtherFromCombatWatcher;
public class SithMarauder extends CardImpl {
public SithMarauder(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{R}{R}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
this.subtype.add("Human");
this.subtype.add("Sith");
this.power = new MageInt(5);
this.toughness = new MageInt(4);
// <i>Hate</i> &mdash; When Sith Marauder enters the battlefield, if opponent lost life from source other than combat damage this turn, Sith Marauder deals 3 damage to target creature or player.
// <i>Hate</i> &mdash; When Sith Marauder enters the battlefield, if an opponent lost life from a source other than combat damage this turn, Sith Marauder deals 3 damage to target creature or player.
Ability ability = new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(3)),
HateCondition.getInstance(),

View file

@ -63,7 +63,7 @@ import mage.target.common.TargetCardInLibrary;
public class TezzeretTheSeeker extends CardImpl {
public TezzeretTheSeeker(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{3}{U}{U}");
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{3}{U}{U}");
this.subtype.add("Tezzeret");
this.addAbility(new PlanswalkerEntersWithLoyalityCountersAbility(4));
@ -118,7 +118,7 @@ class TezzeretTheSeekerEffect2 extends OneShotEffect {
}
}
FilterArtifactCard filter = new FilterArtifactCard(new StringBuilder("artifact card with converted mana cost ").append(cmc).append(" or less").toString());
FilterArtifactCard filter = new FilterArtifactCard("artifact card with converted mana cost " + cmc + " or less");
filter.add(new ConvertedManaCostPredicate(ComparisonType.LessThan, cmc + 1));
TargetCardInLibrary target = new TargetCardInLibrary(filter);

View file

@ -62,10 +62,10 @@ public class TradeFederationBattleship extends CardImpl {
}
public TradeFederationBattleship(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"{3}{W}{U}{B}");
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT, CardType.CREATURE}, "{3}{W}{U}{B}");
this.subtype.add("Starship");
this.power = new MageInt(2);
this.toughness = new MageInt(2);
this.power = new MageInt(0);
this.toughness = new MageInt(6);
// Spaceflight
this.addAbility(SpaceflightAbility.getInstance());

View file

@ -25,16 +25,14 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.v;
import java.util.UUID;
import mage.constants.CardType;
import mage.abilities.effects.common.LoseLifeTargetControllerEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.target.common.TargetCreaturePermanent;
/**
@ -43,9 +41,8 @@ import mage.target.common.TargetCreaturePermanent;
*/
public class VaporSnag extends CardImpl {
public VaporSnag (UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}");
public VaporSnag(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
// Return target creature to its owner's hand. Its controller loses 1 life.
this.getSpellAbility().addEffect(new ReturnToHandTargetEffect());
@ -53,7 +50,7 @@ public class VaporSnag extends CardImpl {
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
}
public VaporSnag (final VaporSnag card) {
public VaporSnag(final VaporSnag card) {
super(card);
}

View file

@ -86,12 +86,12 @@ public class VolcanicOffering extends CardImpl {
FilterLandPermanent filterLandForOpponent = new FilterLandPermanent("nonbasic land not controlled by " + controller.getLogName());
filterLandForOpponent.add(Predicates.not(new SupertypePredicate("Basic")));
filterLandForOpponent.add(Predicates.not(new ControllerIdPredicate(controller.getId())));
ability.addTarget(new TargetOpponentsChoicePermanent(filterLandForOpponent));
ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, filterLandForOpponent, false, true));
ability.addTarget(new TargetPermanent(filterCreature));
FilterCreaturePermanent filterCreatureForOpponent = new FilterCreaturePermanent("creature not controlled by " + controller.getLogName());
filterCreatureForOpponent.add(Predicates.not(new ControllerIdPredicate(controller.getId())));
ability.addTarget(new TargetOpponentsChoicePermanent(filterCreatureForOpponent));
ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, filterCreatureForOpponent, false, true));
}
}

View file

@ -0,0 +1,119 @@
/*
* 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.w;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
import mage.abilities.costs.mana.GenericManaCost;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.mageobject.AnotherTargetPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.Target;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author emerald000
*/
public class Withdraw extends CardImpl {
public Withdraw(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}{U}");
// Return target creature to its owner's hand. Then return another target creature to its owner's hand unless its controller pays {1}.
this.getSpellAbility().addEffect(new WithdrawEffect());
Target target = new TargetCreaturePermanent(new FilterCreaturePermanent("creature to return unconditionally"));
target.setTargetTag(1);
this.getSpellAbility().addTarget(target);
FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature to return unless {1} is paid");
filter.add(new AnotherTargetPredicate(2));
target = new TargetCreaturePermanent(filter);
target.setTargetTag(2);
this.getSpellAbility().addTarget(target);
}
public Withdraw(final Withdraw card) {
super(card);
}
@Override
public Withdraw copy() {
return new Withdraw(this);
}
}
class WithdrawEffect extends OneShotEffect {
WithdrawEffect() {
super(Outcome.ReturnToHand);
this.staticText = "Return target creature to its owner's hand. Then return another target creature to its owner's hand unless its controller pays {1}";
}
WithdrawEffect(final WithdrawEffect effect) {
super(effect);
}
@Override
public WithdrawEffect copy() {
return new WithdrawEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Effect effect = new ReturnToHandTargetEffect();
effect.setTargetPointer(new FixedTarget(source.getFirstTarget()));
effect.apply(game, source);
Permanent secondCreature = game.getPermanent(source.getTargets().get(1).getFirstTarget());
if (secondCreature != null) {
Player creatureController = game.getPlayer(secondCreature.getControllerId());
if (creatureController != null) {
Cost cost = new GenericManaCost(1);
if (creatureController.chooseUse(Outcome.Benefit, "Pay {1}? (Otherwise " + secondCreature.getName() + " will be returned to its owner's hand)", source, game)) {
cost.pay(source, game, source.getSourceId(), creatureController.getId(), false);
}
if (!cost.isPaid()) {
creatureController.moveCards(secondCreature, Zone.HAND, source, game);
}
}
}
return true;
}
}

View file

@ -124,6 +124,7 @@ public class Portal extends ExpansionSet {
cards.add(new SetCardInfo("Fire Tempest", 133, Rarity.RARE, mage.cards.f.FireTempest.class));
cards.add(new SetCardInfo("Flashfires", 134, Rarity.UNCOMMON, mage.cards.f.Flashfires.class));
cards.add(new SetCardInfo("Fleet-Footed Monk", 177, Rarity.COMMON, mage.cards.f.FleetFootedMonk.class));
cards.add(new SetCardInfo("Flux", 56, Rarity.UNCOMMON, mage.cards.f.Flux.class));
cards.add(new SetCardInfo("Foot Soldiers", 178, Rarity.COMMON, mage.cards.f.FootSoldiers.class));
cards.add(new SetCardInfo("Forest", 203, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(null, true)));
cards.add(new SetCardInfo("Forest", 204, Rarity.LAND, mage.cards.basiclands.Forest.class, new CardGraphicInfo(null, true)));

View file

@ -28,8 +28,8 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.constants.SetType;
import mage.constants.Rarity;
import mage.constants.SetType;
/**
*
@ -148,6 +148,7 @@ public class Prophecy extends ExpansionSet {
cards.add(new SetCardInfo("Whipstitched Zombie", 81, Rarity.COMMON, mage.cards.w.WhipstitchedZombie.class));
cards.add(new SetCardInfo("Wild Might", 134, Rarity.COMMON, mage.cards.w.WildMight.class));
cards.add(new SetCardInfo("Wintermoon Mesa", 143, Rarity.RARE, mage.cards.w.WintermoonMesa.class));
cards.add(new SetCardInfo("Withdraw", 54, Rarity.COMMON, mage.cards.w.Withdraw.class));
cards.add(new SetCardInfo("Zerapa Minotaur", 108, Rarity.COMMON, mage.cards.z.ZerapaMinotaur.class));
}
}

View file

@ -86,6 +86,7 @@ public class TimeSpiral extends ExpansionSet {
cards.add(new SetCardInfo("Errant Doomsayers", 15, Rarity.COMMON, mage.cards.e.ErrantDoomsayers.class));
cards.add(new SetCardInfo("Errant Ephemeron", 60, Rarity.COMMON, mage.cards.e.ErrantEphemeron.class));
cards.add(new SetCardInfo("Eternity Snare", 61, Rarity.COMMON, mage.cards.e.EternitySnare.class));
cards.add(new SetCardInfo("Evangelize", 16, Rarity.RARE, mage.cards.e.Evangelize.class));
cards.add(new SetCardInfo("Evil Eye of Urborg", 107, Rarity.UNCOMMON, mage.cards.e.EvilEyeOfUrborg.class));
cards.add(new SetCardInfo("Faceless Devourer", 108, Rarity.UNCOMMON, mage.cards.f.FacelessDevourer.class));
cards.add(new SetCardInfo("Fallen Ideal", 109, Rarity.UNCOMMON, mage.cards.f.FallenIdeal.class));

View file

@ -106,6 +106,7 @@ public class Weatherlight extends ExpansionSet {
cards.add(new SetCardInfo("Fire Whip", 100, Rarity.COMMON, mage.cards.f.FireWhip.class));
cards.add(new SetCardInfo("Fit of Rage", 102, Rarity.COMMON, mage.cards.f.FitOfRage.class));
cards.add(new SetCardInfo("Fledgling Djinn", 11, Rarity.COMMON, mage.cards.f.FledglingDjinn.class));
cards.add(new SetCardInfo("Flux", 27, Rarity.COMMON, mage.cards.f.Flux.class));
cards.add(new SetCardInfo("Fog Elemental", 40, Rarity.COMMON, mage.cards.f.FogElemental.class));
cards.add(new SetCardInfo("Foriysian Brigade", 130, Rarity.UNCOMMON, mage.cards.f.ForiysianBrigade.class));
cards.add(new SetCardInfo("Gaea's Blessing", 71, Rarity.UNCOMMON, mage.cards.g.GaeasBlessing.class));

View file

@ -67,7 +67,7 @@ public class BloodthirstTest extends CardTestPlayerBase {
public void testBloodlord() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
// Bloodthirst 3
// Bloodthirst 3 (If an opponent was dealt damage this turn, this creature enters the battlefield with 3 +1/+1 counters)
// Whenever you cast a Vampire creature spell, it gains bloodthirst 3
addCard(Zone.HAND, playerA, "Bloodlord of Vaasgoth"); // {3}{B}{B}
addCard(Zone.HAND, playerA, "Barony Vampire"); // 3/2 {2}{B}

View file

@ -0,0 +1,162 @@
/*
* 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 org.mage.test.cards.continuous;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.permanent.Permanent;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class ErsatzGnomesTest extends CardTestPlayerBase {
/**
* Ersatz Gnomes is incorrectly used I targeted a spell which is a permanent
* to colorless. When it enters the field its suppose to be colorless not go
* back to normal. It's colorless until it leaves the battlefield when you
* make a permanent spell colorless when you cast it.
*/
@Test
public void testColorlessSpellCreatesColorlessPermanent() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.HAND, playerA, "Silvercoat Lion", 1);
// {T}: Target spell becomes colorless.
// {T}: Target permanent becomes colorless until end of turn.
addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Silvercoat Lion");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, "Silvercoat Lion", 1);
assertTapped("Ersatz Gnomes", true);
Permanent lion = getPermanent("Silvercoat Lion", playerA);
Assert.assertTrue("Silvercoat lion has to be colorless", lion.getColor(currentGame).isColorless());
}
@Test
public void testColorlessSpellCreatesColorlessPermanentUntilItBattlefieldLeft() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.HAND, playerA, "Silvercoat Lion", 1);
// {T}: Target spell becomes colorless.
// {T}: Target permanent becomes colorless until end of turn.
addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes");
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
addCard(Zone.HAND, playerB, "Unsummon");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Silvercoat Lion");
castSpell(1, PhaseStep.END_COMBAT, playerB, "Unsummon", "Silvercoat Lion");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Silvercoat Lion");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerB, "Unsummon", 1);
assertHandCount(playerA, "Silvercoat Lion", 0);
assertPermanentCount(playerA, "Silvercoat Lion", 1);
assertTapped("Ersatz Gnomes", true);
Permanent lion = getPermanent("Silvercoat Lion", playerA);
Assert.assertTrue("Silvercoat lion has to be white", lion.getColor(currentGame).isWhite());
}
@Test
public void testChangeColorOfBestowSpell() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 4);
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion");
// Bestow {3}{W}
// Lifelink
// Echanted creature gets +1/+1 and has lifelink.
addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W}
// {T}: Target spell becomes colorless.
// {T}: Target permanent becomes colorless until end of turn.
addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes");
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
addCard(Zone.HAND, playerB, "Unsummon");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon using bestow", "Silvercoat Lion");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertHandCount(playerA, "Hopeful Eidolon", 0);
assertPermanentCount(playerA, "Hopeful Eidolon", 1);
assertPowerToughness(playerA, "Silvercoat Lion", 3, 3);
assertTapped("Ersatz Gnomes", true);
Permanent eidolon = getPermanent("Hopeful Eidolon", playerA);
Assert.assertTrue("Hopeful Eidolon Enchantment has to be colorless", eidolon.getColor(currentGame).isColorless());
}
@Test
public void testChangeColorOfBestowSpellUnsummon() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
// Bestow {3}{W}
// Lifelink
// Echanted creature gets +1/+1 and has lifelink.
addCard(Zone.HAND, playerA, "Hopeful Eidolon");// Creature {W}
// {T}: Target spell becomes colorless.
// {T}: Target permanent becomes colorless until end of turn.
addCard(Zone.BATTLEFIELD, playerA, "Ersatz Gnomes");
addCard(Zone.BATTLEFIELD, playerB, "Island", 1);
addCard(Zone.HAND, playerB, "Unsummon");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Hopeful Eidolon");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Target spell", "Hopeful Eidolon");
castSpell(1, PhaseStep.END_COMBAT, playerB, "Unsummon", "Hopeful Eidolon");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Hopeful Eidolon");
setStopAt(1, PhaseStep.END_TURN);
execute();
assertGraveyardCount(playerB, "Unsummon", 1);
assertHandCount(playerA, "Hopeful Eidolon", 0);
assertPermanentCount(playerA, "Hopeful Eidolon", 1);
assertTapped("Ersatz Gnomes", true);
Permanent lion = getPermanent("Hopeful Eidolon", playerA);
Assert.assertTrue("Hopeful Eidolon has to be white", lion.getColor(currentGame).isWhite());
}
}

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 org.mage.test.turnmod;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class ExtraTurnsTest extends CardTestPlayerBase {
/**
* Emrakul, the Promised End not giving an extra turn when cast in the
* opponent's turn
*/
@Test
public void testEmrakulCastOnOpponentsTurn() {
addCard(Zone.BATTLEFIELD, playerA, "Island", 12);
addCard(Zone.GRAVEYARD, playerA, "Island", 1);
// Emrakul, the Promised End costs {1} less to cast for each card type among cards in your graveyard.
// When you cast Emrakul, you gain control of target opponent during that player's next turn. After that turn, that player takes an extra turn.
// Flying
// Trample
// Protection from instants
addCard(Zone.HAND, playerA, "Emrakul, the Promised End", 1); // {13}
// Flash (You may cast this spell any time you could cast an instant.)
// Creature cards you own that aren't on the battlefield have flash.
// Each opponent can cast spells only any time he or she could cast a sorcery.
addCard(Zone.BATTLEFIELD, playerA, "Teferi, Mage of Zhalfir", 1);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerA, "Emrakul, the Promised End");
setStopAt(3, PhaseStep.UPKEEP);
execute();
assertPermanentCount(playerA, "Emrakul, the Promised End", 1);
Assert.assertTrue("For extra turn, playerB has to be the active player ", currentGame.getActivePlayerId().equals(playerB.getId()));
}
}

76
Mage.Verify/pom.xml Normal file
View file

@ -0,0 +1,76 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.mage</groupId>
<artifactId>mage-root</artifactId>
<version>1.4.16</version>
</parent>
<artifactId>mage-verify</artifactId>
<packaging>jar</packaging>
<name>Mage Verify</name>
<dependencies>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mage</artifactId>
<version>${mage-version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>mage-sets</artifactId>
<version>${mage-version}</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.6.3</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<type>jar</type>
</dependency>
</dependencies>
<build>
<pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>-Dfile.encoding=UTF-8</argLine>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<!-- <configuration>
<compilerArgument>-Xlint:unchecked</compilerArgument>
</configuration> -->
</plugin>
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</pluginManagement>
<finalName>mage-verify</finalName>
</build>
<properties/>
</project>

View file

@ -0,0 +1,40 @@
package mage.verify;
import java.util.List;
class JsonCard {
public String layout;
public String name;
public List<String> names; // flip cards
public String manaCost;
public int cmc;
public List<String> colors;
public List<String> colorIdentity;
public String type;
public List<String> supertypes;
public List<String> types;
public List<String> subtypes;
public String text;
public String power;
public String toughness;
public int loyalty;
public String imageName;
public boolean starter; // only available in boxed sets and not in boosters
public int hand; // vanguard
public int life; // vanguard
public String mciNumber;
// only available in AllSets.json
public String artist;
public String flavor;
public String id;
public int multiverseid;
public String rarity;
public boolean reserved;
public int[] variations;
public String number;
public String releaseDate; // promos
public String border;
public String watermark;
public boolean timeshifted;
}

View file

@ -0,0 +1,23 @@
package mage.verify;
import java.util.List;
import java.util.Map;
class JsonSet {
public String name;
public String code;
public String oldCode;
public String gathererCode;
public String magicCardsInfoCode;
public String[] magicRaritiesCodes;
public String releaseDate;
public String border;
public String type;
public List<Object> booster; // [String|[String]]
public List<JsonCard> cards;
public String block;
public boolean onlineOnly;
public String mkm_id;
public String mkm_name;
public Map<String, String> translations;
}

View file

@ -0,0 +1,109 @@
package mage.verify;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.StandardCopyOption;
import java.text.Normalizer;
import java.util.HashMap;
import java.util.Map;
import java.util.zip.ZipInputStream;
public class MtgJson {
private MtgJson() {}
private static class CardHolder {
private static final Map<String, JsonCard> cards;
static {
try {
cards = loadAllCards();
addAliases(cards);
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private static class SetHolder {
private static final Map<String, JsonSet> sets;
static {
try {
sets = loadAllSets();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
private static Map<String, JsonCard> loadAllCards() throws IOException {
return readFromZip("AllCards.json.zip", new TypeReference<Map<String, JsonCard>>() {});
}
private static Map<String, JsonSet> loadAllSets() throws IOException {
return readFromZip("AllSets.json.zip", new TypeReference<Map<String, JsonSet>>() {});
}
private static <T> T readFromZip(String filename, TypeReference<T> ref) throws IOException {
InputStream stream = MtgJson.class.getResourceAsStream(filename);
if (stream == null) {
File file = new File(filename);
if (!file.exists()) {
InputStream download = new URL("http://mtgjson.com/json/" + filename).openStream();
Files.copy(download, file.toPath(), StandardCopyOption.REPLACE_EXISTING);
System.out.println("Downloaded " + filename + " to " + file.getAbsolutePath());
} else {
System.out.println("Using " + filename + " from " + file.getAbsolutePath());
}
stream = new FileInputStream(file);
}
ZipInputStream zipInputStream = new ZipInputStream(stream);
zipInputStream.getNextEntry();
return new ObjectMapper().readValue(zipInputStream, ref);
}
public static Map<String, JsonSet> sets() {
return SetHolder.sets;
}
public static JsonCard card(String name) {
return findReference(CardHolder.cards, name);
}
private static <T> T findReference(Map<String, T> reference, String name) {
T ref = reference.get(name);
if (ref == null) {
name = name.replaceFirst("\\bA[Ee]", "Æ");
ref = reference.get(name);
}
if (ref == null) {
name = name.replace("'", "\""); // for Kongming, "Sleeping Dragon" & Pang Tong, "Young Phoenix"
ref = reference.get(name);
}
return ref;
}
private static <T> void addAliases(Map<String, T> reference) {
Map<String, String> aliases = new HashMap<>();
for (String name : reference.keySet()) {
String unaccented = stripAccents(name);
if (!name.equals(unaccented)) {
aliases.put(name, unaccented);
}
}
for (Map.Entry<String, String> mapping : aliases.entrySet()) {
reference.put(mapping.getValue(), reference.get(mapping.getKey()));
}
}
private static String stripAccents(String str) {
String decomposed = Normalizer.normalize(str, Normalizer.Form.NFKD);
return decomposed.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "");
}
}

View file

@ -0,0 +1,232 @@
package mage.verify;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Paths;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mage.ObjectColor;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.SplitCard;
import mage.cards.basiclands.BasicLand;
import mage.constants.CardType;
import org.junit.Assert;
import org.junit.Test;
public class VerifyCardDataTest {
// right now this is very noisy, and not useful enough to make any assertions on
private static final boolean CHECK_SOURCE_TOKENS = false;
public static List<Card> allCards() {
Collection<ExpansionSet> sets = Sets.getInstance().values();
List<Card> cards = new ArrayList<>();
for (ExpansionSet set : sets) {
if (set.isCustomSet()) {
continue;
}
for (ExpansionSet.SetCardInfo setInfo : set.getSetCardInfo()) {
cards.add(CardImpl.createCard(setInfo.getCardClass(), new CardSetInfo(setInfo.getName(), set.getCode(),
setInfo.getCardNumber(), setInfo.getRarity(), setInfo.getGraphicInfo())));
}
}
return cards;
}
private void warn(Card card, String message) {
System.out.println("Warning: " + message + " for " + card.getName());
}
private void fail(Card card, String category, String message) {
failed++;
System.out.println("Error: (" + category + ") " + message + " for " + card.getName());
}
private int failed = 0;
@Test
public void verifyCards() throws IOException {
for (Card card : allCards()) {
Set<String> tokens = findSourceTokens(card.getClass());
if (card.isSplitCard()) {
check(((SplitCard) card).getLeftHalfCard(), null);
check(((SplitCard) card).getRightHalfCard(), null);
} else {
check(card, tokens);
}
}
if (failed > 0) {
Assert.fail(failed + " Errors");
}
}
private static final Pattern SHORT_JAVA_STRING = Pattern.compile("(?<=\")[A-Z][a-z]+(?=\")");
private Set<String> findSourceTokens(Class c) throws IOException {
if (!CHECK_SOURCE_TOKENS || BasicLand.class.isAssignableFrom(c)) {
return null;
}
String path = "../Mage.Sets/src/" + c.getName().replace(".", "/") + ".java";
try {
String source = new String(Files.readAllBytes(Paths.get(path)), StandardCharsets.UTF_8);
Matcher matcher = SHORT_JAVA_STRING.matcher(source);
Set<String> tokens = new HashSet<>();
while (matcher.find()) {
tokens.add(matcher.group());
}
return tokens;
} catch (NoSuchFileException e) {
System.out.println("failed to read " + path);
return null;
}
}
private void check(Card card, Set<String> tokens) {
JsonCard ref = MtgJson.card(card.getName());
if (ref == null) {
warn(card, "Missing card reference");
return;
}
checkAll(card, ref);
if (tokens != null) {
JsonCard ref2 = null;
if (card.isFlipCard()) {
ref2 = MtgJson.card(card.getFlipCardName());
}
for (String token : tokens) {
if (!(token.equals(card.getName())
|| containsInTypesOrText(ref, token)
|| containsInTypesOrText(ref, token.toLowerCase())
|| (ref2 != null && (containsInTypesOrText(ref2, token) || containsInTypesOrText(ref2, token.toLowerCase()))))) {
System.out.println("unexpected token " + token + " in " + card);
}
}
}
}
private boolean containsInTypesOrText(JsonCard ref, String token) {
return contains(ref.types, token)
|| contains(ref.subtypes, token)
|| contains(ref.supertypes, token)
|| ref.text.contains(token);
}
private boolean contains(Collection<String> options, String value) {
return options != null && options.contains(value);
}
private void checkAll(Card card, JsonCard ref) {
checkCost(card, ref);
checkPT(card, ref);
checkSubtypes(card, ref);
checkSupertypes(card, ref);
checkTypes(card, ref);
checkColors(card, ref);
}
private void checkColors(Card card, JsonCard ref) {
// gatherer is missing the color indicator on one card:
if ("Ulrich, Uncontested Alpha".equals(ref.name)) {
return;
}
Collection<String> expected = ref.colors;
ObjectColor color = card.getColor(null);
if (expected == null) {
expected = Collections.emptyList();
}
if (expected.size() != color.getColorCount()
|| (color.isBlack() && !expected.contains("Black"))
|| (color.isBlue() && !expected.contains("Blue"))
|| (color.isGreen() && !expected.contains("Green"))
|| (color.isRed() && !expected.contains("Red"))
|| (color.isWhite() && !expected.contains("White"))) {
fail(card, "colors", color + " != " + expected);
}
}
private void checkSubtypes(Card card, JsonCard ref) {
Collection<String> expected = ref.subtypes;
if (expected != null && expected.contains("Urzas")) {
expected = new ArrayList<>(expected);
for (ListIterator<String> it = ((List<String>) expected).listIterator(); it.hasNext();) {
if (it.next().equals("Urzas")) {
it.set("Urza's");
}
}
}
if (!eqSet(card.getSubtype(null), expected)) {
fail(card, "subtypes", card.getSubtype(null) + " != " + expected);
}
}
private void checkSupertypes(Card card, JsonCard ref) {
Collection<String> expected = ref.supertypes;
if (!eqSet(card.getSupertype(), expected)) {
fail(card, "supertypes", card.getSupertype() + " != " + expected);
}
}
private void checkTypes(Card card, JsonCard ref) {
Collection<String> expected = ref.types;
List<String> type = new ArrayList<>();
for (CardType cardType : card.getCardType()) {
type.add(cardType.toString());
}
if (!eqSet(type, expected)) {
fail(card, "types", type + " != " + expected);
}
}
private static <T> boolean eqSet(Collection<T> a, Collection<T> b) {
if (a == null || a.isEmpty()) {
return b == null || b.isEmpty();
}
return b != null && a.size() == b.size() && a.containsAll(b);
}
private void checkPT(Card card, JsonCard ref) {
if (!eqPT(card.getPower().toString(), ref.power) || !eqPT(card.getToughness().toString(), ref.toughness)) {
String pt = card.getPower() + "/" + card.getToughness();
String expected = ref.power + "/" + ref.toughness;
fail(card, "pt", pt + " != " + expected);
}
}
private boolean eqPT(String found, String expected) {
if (expected == null) {
return "0".equals(found);
} else {
return found.equals(expected) || expected.contains("*");
}
}
private void checkCost(Card card, JsonCard ref) {
String expected = ref.manaCost;
String cost = join(card.getManaCost().getSymbols());
if ("".equals(cost)) {
cost = null;
}
if (cost != null) {
cost = cost.replaceAll("P\\}", "/P}");
}
if (!Objects.equals(cost, expected)) {
fail(card, "cost", cost + " != " + expected);
}
}
private String join(Iterable<?> items) {
StringBuilder result = new StringBuilder();
for (Object item : items) {
result.append(item);
}
return result.toString();
}
}

View file

@ -85,15 +85,16 @@ public class ObjectColor implements Serializable, Copyable<ObjectColor>, Compara
/**
* Returns a new color which contains all of the colors of this ObjectColor
* in addition to all of the colors of the other ObjectColor.
*
* @param other The other ObjectColor to union with
* @return A new color which is the union of this and other
*/
public ObjectColor union(ObjectColor other) {
ObjectColor newColor = new ObjectColor();
newColor.white = white | other.white;
newColor.blue = blue | other.blue;
newColor.blue = blue | other.blue;
newColor.black = black | other.black;
newColor.red = red | other.red;
newColor.red = red | other.red;
newColor.green = green | other.green;
return newColor;
}
@ -146,6 +147,24 @@ public class ObjectColor implements Serializable, Copyable<ObjectColor>, Compara
this.setWhite(color.isWhite());
}
public void addColor(ObjectColor color) {
if (color.isWhite()) {
setWhite(true);
}
if (color.isBlue()) {
setBlue(true);
}
if (color.isBlack()) {
setBlack(true);
}
if (color.isRed()) {
setRed(true);
}
if (color.isGreen()) {
setGreen(true);
}
}
public boolean isColorless() {
return !(hasColor());
}

View file

@ -328,13 +328,16 @@ public abstract class AbilityImpl implements Ability {
if (sourceObject != null && !this.getAbilityType().equals(AbilityType.TRIGGERED)) { // triggered abilities check this already in playerImpl.triggerAbility
sourceObject.adjustTargets(this, game);
}
// Flashback abilities haven't made the choices the underlying spell might need for targetting.
if (!(this instanceof FlashbackAbility) && getTargets().size() > 0 && getTargets().chooseTargets(
getEffects().get(0).getOutcome(), this.controllerId, this, noMana, game) == false) {
if ((variableManaCost != null || announceString != null) && !game.isSimulation()) {
game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets with this value of X");
// Flashback abilities haven't made the choices the underlying spell might need for targeting.
if (!(this instanceof FlashbackAbility)
&& getTargets().size() > 0) {
Outcome outcome = getEffects().isEmpty() ? Outcome.Detriment : getEffects().get(0).getOutcome();
if (getTargets().chooseTargets(outcome, this.controllerId, this, noMana, game) == false) {
if ((variableManaCost != null || announceString != null) && !game.isSimulation()) {
game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets with this value of X");
}
return false; // when activation of ability is canceled during target selection
}
return false; // when activation of ability is canceled during target selection
}
} // end modes

View file

@ -85,10 +85,12 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect {
int value = amount.calculate(game, source, this);
boolean result = false;
ChoiceColor choice = new ChoiceColor(false);
ChoiceColor choice = new ChoiceColor(true);
for (int i = 0; i < value; i++) {
if (!choice.isChosen()) {
if (!controller.choose(outcome, choice, game)) {
while (!choice.isChosen()) {
controller.choose(outcome, choice, game);
if (!controller.isInGame()) {
return false;
}
}

View file

@ -75,12 +75,33 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
this(new StaticValue(numberOfCards), false, new StaticValue(numberToPick), pickFilter, Zone.LIBRARY, false, true, upTo);
}
/**
*
* @param numberOfCards
* @param numberToPick
* @param pickFilter
* @param reveal
* @param upTo
* @param targetZonePickedCards
* @param optional
*/
public LookLibraryAndPickControllerEffect(int numberOfCards, int numberToPick, FilterCard pickFilter, boolean reveal, boolean upTo,
Zone targetZonePickedCards, boolean optional) {
this(new StaticValue(numberOfCards), false, new StaticValue(numberToPick), pickFilter, Zone.LIBRARY, false, reveal, upTo, targetZonePickedCards, optional);
}
/**
*
* @param numberOfCards
* @param mayShuffleAfter
* @param numberToPick
* @param pickFilter
* @param targetZoneLookedCards
* @param putOnTop
* @param reveal
* @param upTo
*/
public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards, boolean mayShuffleAfter, DynamicValue numberToPick,
FilterCard pickFilter, Zone targetZoneLookedCards, boolean putOnTop, boolean reveal, boolean upTo) {
this(numberOfCards, mayShuffleAfter, numberToPick, pickFilter, targetZoneLookedCards, putOnTop, reveal, upTo, Zone.HAND, false);

View file

@ -27,6 +27,8 @@
*/
package mage.abilities.effects.common;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.Mode;
@ -70,12 +72,14 @@ public class ReturnFromGraveyardToBattlefieldTargetEffect extends OneShotEffect
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Set<Card> cardsToMove = new HashSet<>();
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
Card card = game.getCard(targetId);
if (card != null) {
controller.moveCards(card, Zone.BATTLEFIELD, source, game, tapped, false, false, null);
cardsToMove.add(card);
}
}
controller.moveCards(cardsToMove, Zone.BATTLEFIELD, source, game, tapped, false, false, null);
return true;
}
return false;

View file

@ -57,20 +57,26 @@ public class RemoveCounterSourceEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent != null && permanent.getCounters(game).getCount(counter.getName()) >= counter.getCount()) {
permanent.removeCounters(counter.getName(), counter.getCount(), game);
if (!game.isSimulation()) {
game.informPlayers("Removed " + counter.getCount() + " " + counter.getName() + " counter from " + permanent.getLogName());
if (permanent != null) {
int toRemove = Math.min(counter.getCount(), permanent.getCounters(game).getCount(counter.getName()));
if (toRemove > 0) {
permanent.removeCounters(counter.getName(), toRemove, game);
if (!game.isSimulation()) {
game.informPlayers("Removed " + toRemove + " " + counter.getName() + " counter from " + permanent.getLogName());
}
}
return true;
}
Card card = game.getCard(source.getSourceId());
if (card != null && card.getCounters(game).getCount(counter.getName()) >= counter.getCount()) {
card.removeCounters(counter.getName(), counter.getCount(), game);
if (!game.isSimulation()) {
game.informPlayers("Removed " + counter.getCount() + " " + counter.getName()
+ " counter from " + card.getLogName()
+ " (" + card.getCounters(game).getCount(counter.getName()) + " left)");
if (card != null) {
int toRemove = Math.min(counter.getCount(), card.getCounters(game).getCount(counter.getName()));
if (toRemove > 0) {
card.removeCounters(counter.getName(), toRemove, game);
if (!game.isSimulation()) {
game.informPlayers("Removed " + toRemove + " " + counter.getName()
+ " counter from " + card.getLogName()
+ " (" + card.getCounters(game).getCount(counter.getName()) + " left)");
}
}
return true;
}

View file

@ -0,0 +1,43 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.abilities.keyword;
import java.io.ObjectStreamException;
import mage.abilities.MageSingleton;
import mage.abilities.StaticAbility;
import mage.constants.Zone;
/**
*
* @author LevelX2
*/
public class CanBlockSpaceflightAbility extends StaticAbility implements MageSingleton {
private static final CanBlockSpaceflightAbility fINSTANCE = new CanBlockSpaceflightAbility();
private Object readResolve() throws ObjectStreamException {
return fINSTANCE;
}
public static CanBlockSpaceflightAbility getInstance() {
return fINSTANCE;
}
private CanBlockSpaceflightAbility() {
super(Zone.ALL, null);
}
@Override
public String getRule() {
return "{this} can block creatures with spaceflight.";
}
@Override
public CanBlockSpaceflightAbility copy() {
return fINSTANCE;
}
}

View file

@ -90,7 +90,8 @@ class SpaceFlightEffect extends RestrictionEffect implements MageSingleton {
@Override
public boolean canBeBlocked(Permanent attacker, Permanent blocker, Ability source, Game game) {
return blocker.getAbilities().containsKey(SpaceflightAbility.getInstance().getId());
return blocker.getAbilities().containsKey(SpaceflightAbility.getInstance().getId())
|| blocker.getAbilities().containsKey(CanBlockSpaceflightAbility.getInstance().getId());
}
@Override

View file

@ -193,8 +193,11 @@ public abstract class ExpansionSet implements Serializable {
if (15 > theBooster.size()) {
List<CardInfo> commons = getCardsByRarity(Rarity.COMMON);
while (15 > theBooster.size()) {
while (15 > theBooster.size() && !commons.isEmpty()) {
addToBooster(theBooster, commons);
if (commons.isEmpty()) {
commons = getCardsByRarity(Rarity.COMMON);
}
}
}

View file

@ -64,7 +64,7 @@ public enum CardRepository {
// raise this if db structure was changed
private static final long CARD_DB_VERSION = 47;
// raise this if new cards were added to the server
private static final long CARD_CONTENT_VERSION = 63;
private static final long CARD_CONTENT_VERSION = 64;
private Dao<CardInfo, Object> cardDao;
private Set<String> classNames;

View file

@ -1499,6 +1499,17 @@ public abstract class GameImpl implements Game, Serializable {
@Override
public void addTriggeredAbility(TriggeredAbility ability) {
if (ability.getControllerId() == null) {
String sourceName = "no sourceId";
if (ability.getSourceId() != null) {
MageObject mageObject = getObject(ability.getSourceId());
if (mageObject != null) {
sourceName = mageObject.getName();
}
}
logger.fatal("Added triggered ability without controller: " + sourceName + " rule: " + ability.getRule());
return;
}
if (ability instanceof TriggeredManaAbility || ability instanceof DelayedTriggeredManaAbility) {
// 20110715 - 605.4
Ability manaAbiltiy = ability.copy();

View file

@ -208,6 +208,8 @@ public class ZonesHandler {
// If we can't find the card we can't remove it.
return false;
}
// If needed take attributes from the spell (e.g. color of spell was changed)
card = takeAttributesFromSpell(card, event, game);
boolean success = false;
if (info.faceDown) {
card.setFaceDown(true, game);
@ -282,4 +284,23 @@ public class ZonesHandler {
order.add(cards.getCards(game).iterator().next());
return order;
}
private static Card takeAttributesFromSpell(Card card, ZoneChangeEvent event, Game game) {
if (Zone.STACK.equals(event.getFromZone())) {
Spell spell = game.getStack().getSpell(event.getTargetId());
if (spell != null) {
boolean doCopy = false;
if (!card.getColor(game).equals(spell.getColor(game))) {
doCopy = true;
}
if (doCopy) {
// the card that is referenced to in the permanent is copied and the spell attributes are set to this copied card
card = card.copy();
card.getColor(game).setColor(spell.getColor(game));
}
}
}
return card;
}
}

View file

@ -132,7 +132,7 @@ public class PermanentCard extends PermanentImpl {
transformable = card.isTransformable();
if (transformable) {
this.nightCard = card.isNightCard();
if (! this.nightCard) {
if (!this.nightCard) {
this.secondSideCard = card.getSecondCardFace();
this.secondSideCardClazz = this.secondSideCard.getClass();
}

View file

@ -27,15 +27,9 @@
*/
package mage.game.permanent;
import java.util.ArrayList;
import java.util.UUID;
import mage.cards.Card;
import mage.cards.CardsImpl;
import mage.cards.MeldCard;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.ZoneChangeEvent;
import mage.players.Player;
/**
*

View file

@ -899,7 +899,7 @@ public class Spell extends StackObjImpl implements Card {
@Override
public void checkForCountersToAdd(Permanent permanent, Game game) {
throw new UnsupportedOperationException("Not supported for Spell");
card.checkForCountersToAdd(permanent, game);
}
@Override

View file

@ -25,15 +25,13 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.game.turn;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.UUID;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
/**
*
@ -41,10 +39,11 @@ import java.util.UUID;
*/
public class TurnMods extends ArrayList<TurnMod> {
public TurnMods() {}
public TurnMods() {
}
public TurnMods(final TurnMods mods) {
for (TurnMod mod: mods) {
for (TurnMod mod : mods) {
this.add(mod.copy());
}
}
@ -95,7 +94,7 @@ public class TurnMods extends ArrayList<TurnMod> {
it.remove();
}
}
// now delete all other - control next turn effect is not cumulative
// now delete all other effects that control current active player - control next turn of player effects are not cumulative
it = this.listIterator(this.size());
while (it.hasPrevious()) {
TurnMod turnMod = it.previous();

View file

@ -3,7 +3,6 @@
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.target.common;
import java.util.UUID;
@ -11,6 +10,7 @@ import mage.abilities.Ability;
import mage.constants.Outcome;
import mage.filter.FilterPermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
@ -21,20 +21,23 @@ import mage.target.TargetPermanent;
public class TargetOpponentsChoicePermanent extends TargetPermanent {
protected UUID opponentId = null;
private boolean dontTargetPlayer = false;
public TargetOpponentsChoicePermanent(FilterPermanent filter) {
super(1, 1, filter, false);
this.targetName = filter.getMessage();
}
public TargetOpponentsChoicePermanent(int minNumTargets, int maxNumTargets, FilterPermanent filter, boolean notTarget) {
public TargetOpponentsChoicePermanent(int minNumTargets, int maxNumTargets, FilterPermanent filter, boolean notTarget, boolean dontTargetPlayer) {
super(minNumTargets, maxNumTargets, filter, notTarget);
this.targetName = filter.getMessage();
this.dontTargetPlayer = dontTargetPlayer;
}
public TargetOpponentsChoicePermanent(final TargetOpponentsChoicePermanent target) {
super(target);
this.opponentId = target.opponentId;
this.dontTargetPlayer = target.dontTargetPlayer;
}
@Override
@ -47,8 +50,22 @@ public class TargetOpponentsChoicePermanent extends TargetPermanent {
@Override
public boolean canTarget(UUID controllerId, UUID id, Ability source, Game game) {
Permanent permanent = game.getPermanent(id);
if (opponentId != null) {
return super.canTarget(opponentId, id, source, game);
if (permanent != null) {
if (source != null) {
boolean canSourceControllerTarget = true;
if (!isNotTarget()) {
if (!permanent.canBeTargetedBy(game.getObject(source.getId()), controllerId, game)
|| !permanent.canBeTargetedBy(game.getObject(source.getSourceId()), controllerId, game)) {
canSourceControllerTarget = false;
}
}
canSourceControllerTarget &= super.canTarget(opponentId, id, source, game);
canSourceControllerTarget &= filter.match(permanent, source.getSourceId(), opponentId, game);
return canSourceControllerTarget;
}
}
}
return false;
}
@ -65,7 +82,7 @@ public class TargetOpponentsChoicePermanent extends TargetPermanent {
private UUID getOpponentId(UUID playerId, Ability source, Game game) {
if (opponentId == null) {
TargetOpponent target = new TargetOpponent();
TargetOpponent target = new TargetOpponent(dontTargetPlayer);
Player player = game.getPlayer(playerId);
if (player != null) {
if (player.chooseTarget(Outcome.Detriment, target, source, game)) {

View file

@ -50,6 +50,7 @@ public class LifeLossOtherFromCombatWatcher extends Watcher {
public LifeLossOtherFromCombatWatcher(final LifeLossOtherFromCombatWatcher watcher) {
super(watcher);
this.players.addAll(watcher.players);
}
@Override

View file

@ -61,6 +61,7 @@
<module>Mage.Tests</module>
<module>Mage.Updater</module>
<module>Mage.Stats</module>
<module>Mage.Verify</module>
</modules>
<repositories>