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/target
mage.updater.client/target mage.updater.client/target
# Mage.Verify
Mage.Verify/target
Mage.Verify/AllCards.json.zip
Mage.Verify/AllSets.json.zip
releases releases
Utils/author.txt Utils/author.txt
.DS_Store .DS_Store

View file

@ -1,6 +1,6 @@
XMage.de 1 (Europe/Germany) fast :xmage.de:17171 XMage.de 1 (Europe/Germany) fast :xmage.de:17171
woogerworks (North America/USA) :xmage.woogerworks.com: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 XMage.tahiti :xmage.tahiti.one:443
Seedds Server (Asia) :115.29.203.80:17171 Seedds Server (Asia) :115.29.203.80:17171
localhost -> connect to your local server (must be started):localhost: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); l.duplicateCards(toDuplicate);
} }
} }
private void showAll() { private void showAll() {
for (DragCardGridListener l : listeners) { for (DragCardGridListener l : listeners) {
l.showAll(); l.showAll();
@ -1381,7 +1382,6 @@ public class DragCardGrid extends JPanel implements DragCardSource, DragCardTarg
while (m.find()) { while (m.find()) {
System.out.println("0=" + m.group(0) + ",,,1=" + m.group(1) + ",,,2=" + m.group(2) + ",,,3=" + m.group(3)); 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"; 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; int num = 1;
if (manaCounts.get(m.group(2)) != null) { if (manaCounts.get(m.group(2)) != null) {
num = manaCounts.get(m.group(2)); 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"/> <Component id="checkBoxEndTurnOthers" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <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"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -4171,7 +4171,7 @@
<Component id="panelCardImages" min="-2" max="-2" attributes="0"/> <Component id="panelCardImages" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="panelBackgroundImages" min="-2" 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>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -4230,7 +4230,7 @@
</Group> </Group>
<Component id="cbUseDefaultImageFolder" min="-2" max="-2" attributes="0"/> <Component id="cbUseDefaultImageFolder" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace min="0" pref="231" max="32767" attributes="0"/> <EmptySpace min="0" pref="270" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
@ -4744,7 +4744,7 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="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"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -5655,7 +5655,7 @@
<Component id="jLabel17" min="-2" max="-2" attributes="0"/> <Component id="jLabel17" min="-2" max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
<EmptySpace pref="91" max="32767" attributes="0"/> <EmptySpace pref="198" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -5874,6 +5874,251 @@
</Container> </Container>
</SubComponents> </SubComponents>
</Container> </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> </SubComponents>
</Container> </Container>
<Component class="javax.swing.JButton" name="saveButton"> <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_MAJOR = 1;
public final static int MAGE_VERSION_MINOR = 4; public final static int MAGE_VERSION_MINOR = 4;
public final static int MAGE_VERSION_PATCH = 16; 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 = ""; public final static String MAGE_VERSION_INFO = "";
private final int major; private final int major;

View file

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

View file

@ -65,9 +65,14 @@ import mage.constants.ManaType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.PhaseStep; import mage.constants.PhaseStep;
import mage.constants.PlayerAction; 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.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 static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
import mage.constants.RangeOfInfluence; import mage.constants.RangeOfInfluence;
import static mage.constants.SpellAbilityType.SPLIT;
import static mage.constants.SpellAbilityType.SPLIT_FUSED;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.common.FilterAttackingCreature; import mage.filter.common.FilterAttackingCreature;
import mage.filter.common.FilterBlockingCreature; import mage.filter.common.FilterBlockingCreature;
@ -730,12 +735,14 @@ public class HumanPlayer extends PlayerImpl {
} }
private boolean checkPassStep(Game game) { private boolean checkPassStep(Game game) {
if (game.getStep() != null) { try {
if (playerId.equals(game.getActivePlayerId())) { if (playerId.equals(game.getActivePlayerId())) {
return !this.getUserData().getUserSkipPrioritySteps().getYourTurn().isPhaseStepSet(game.getStep().getType()); return !this.getUserData().getUserSkipPrioritySteps().getYourTurn().isPhaseStepSet(game.getStep().getType());
} else { } else {
return !this.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getStep().getType()); 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; return true;
} }
@ -1459,6 +1466,11 @@ public class HumanPlayer extends PlayerImpl {
protected void updateGameStatePriority(String methodName, Game game) { 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 (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 + "]"); logger.debug("Setting game priority to " + getId() + " [" + methodName + "]");
game.getState().setPriorityPlayerId(getId()); 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 ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new ArenaEffect(), new GenericManaCost(3));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());
ability.addTarget(new TargetControlledCreaturePermanent()); ability.addTarget(new TargetControlledCreaturePermanent());
ability.addTarget(new TargetOpponentsChoicePermanent(new FilterControlledCreaturePermanent())); ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, new FilterControlledCreaturePermanent(), false, true));
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -28,13 +28,14 @@
package mage.cards.c; package mage.cards.c;
import java.util.UUID; import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.Mode;
import mage.abilities.common.AttacksAttachedTriggeredAbility; import mage.abilities.common.AttacksAttachedTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility; 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.OneShotEffect;
import mage.abilities.effects.common.AttachEffect; import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.LoseLifeDefendingPlayerEffect;
import mage.abilities.effects.common.combat.CantBeBlockedAttachedEffect; import mage.abilities.effects.common.combat.CantBeBlockedAttachedEffect;
import mage.abilities.keyword.EnchantAbility; import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
@ -43,8 +44,6 @@ import mage.constants.AttachmentType;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.counters.Counter;
import mage.filter.FilterPermanent;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.players.Player; import mage.players.Player;
@ -58,7 +57,7 @@ import mage.target.common.TargetCreaturePermanent;
public class CloakingDevice extends CardImpl { public class CloakingDevice extends CardImpl {
public CloakingDevice(UUID ownerId, CardSetInfo setInfo) { 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"); this.subtype.add("Aura");
// Enchant creature // Enchant creature
@ -72,7 +71,7 @@ public class CloakingDevice extends CardImpl {
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeBlockedAttachedEffect(AttachmentType.AURA))); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeBlockedAttachedEffect(AttachmentType.AURA)));
// Whenever enchanted creature attacks, defending player loses 1 life. // 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); 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 class DarkDecision extends CardImpl {
public DarkDecision(UUID ownerId, CardSetInfo setInfo) { 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. // As an additional cost to cast Dark Decision, pay 1 life.
this.getSpellAbility().addCost(new PayLifeCost(1)); this.getSpellAbility().addCost(new PayLifeCost(1));
@ -103,7 +103,7 @@ class DarkDecisionEffect extends OneShotEffect {
if (card != null) { if (card != null) {
controller.moveCardsToExile(card, source, game, true, source.getSourceId(), sourceObject.getIdName()); controller.moveCardsToExile(card, source, game, true, source.getSourceId(), sourceObject.getIdName());
ContinuousEffect effect = new DarkDecisionMayPlayExiledEffect(); ContinuousEffect effect = new DarkDecisionMayPlayExiledEffect();
effect.setTargetPointer(new FixedTarget(card.getId())); effect.setTargetPointer(new FixedTarget(card.getId(), game));
game.addEffect(effect, source); game.addEffect(effect, source);
} }
controller.shuffleLibrary(source, game); controller.shuffleLibrary(source, game);
@ -137,7 +137,7 @@ class DarkDecisionMayPlayExiledEffect extends AsThoughEffectImpl {
@Override @Override
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) { 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()); ExileZone exileZone = game.getExile().getExileZone(source.getSourceId());
return exileZone != null && exileZone.contains(getTargetPointer().getFirst(game, source)); 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. // {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 ability = new ActivateIfConditionActivatedAbility(Zone.BATTLEFIELD, new DiaochanArtfulBeautyDestroyEffect(), new TapSourceCost(), MyTurnBeforeAttackersDeclaredCondition.getInstance());
ability.addTarget(new TargetCreaturePermanent()); ability.addTarget(new TargetCreaturePermanent());
ability.addTarget(new TargetOpponentsChoicePermanent(new FilterCreaturePermanent())); ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, new FilterCreaturePermanent(), false, true));
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -49,7 +49,7 @@ import mage.game.stack.StackObject;
public class DoublingSeason extends CardImpl { public class DoublingSeason extends CardImpl {
public DoublingSeason(UUID ownerId, CardSetInfo setInfo) { 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. // 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())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DoublingSeasonTokenEffect()));
@ -91,16 +91,8 @@ class DoublingSeasonTokenEffect extends ReplacementEffectImpl {
@Override @Override
public boolean applies(GameEvent event, Ability source, Game game) { public boolean applies(GameEvent event, Ability source, Game game) {
StackObject spell = game.getStack().getStackObject(event.getSourceId()); StackObject stackObject = game.getStack().getStackObject(event.getSourceId());
if (spell != null && spell.getControllerId().equals(source.getControllerId())) { return stackObject != null && event.getPlayerId().equals(source.getControllerId());
return true;
}
return false;
}
@Override
public boolean apply(Game game, Ability source) {
return true;
} }
@Override @Override
@ -139,10 +131,7 @@ class DoublingSeasonCounterEffect extends ReplacementEffectImpl {
if (permanent == null) { if (permanent == null) {
permanent = game.getPermanentEntering(event.getTargetId()); permanent = game.getPermanentEntering(event.getTargetId());
} }
if (permanent != null && permanent.getControllerId().equals(source.getControllerId())) { return permanent != null && permanent.getControllerId().equals(source.getControllerId());
return true;
}
return false;
} }
@Override @Override

View file

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

View file

@ -49,17 +49,17 @@ import mage.target.TargetSpell;
public class ErsatzGnomes extends CardImpl { public class ErsatzGnomes extends CardImpl {
public ErsatzGnomes(UUID ownerId, CardSetInfo setInfo) { 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.subtype.add("Gnome");
this.power = new MageInt(1); this.power = new MageInt(1);
this.toughness = 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 ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesColorTargetEffect(new ObjectColor(), Duration.Custom), new TapSourceCost());
ability.addTarget(new TargetSpell()); ability.addTarget(new TargetSpell());
this.addAbility(ability); 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 = new SimpleActivatedAbility(Zone.BATTLEFIELD, new BecomesColorTargetEffect(new ObjectColor(), Duration.EndOfTurn), new TapSourceCost());
ability.addTarget(new TargetPermanent()); ability.addTarget(new TargetPermanent());
this.addAbility(ability); 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 java.util.UUID;
import mage.MageInt; import mage.MageInt;
import mage.abilities.Ability; import mage.abilities.keyword.CanBlockSpaceflightAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.RestrictionEffect;
import mage.abilities.keyword.FlashAbility; import mage.abilities.keyword.FlashAbility;
import mage.abilities.keyword.SpaceflightAbility;
import mage.abilities.keyword.TrampleAbility; import mage.abilities.keyword.TrampleAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; 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 class Exogorth extends CardImpl {
public Exogorth(UUID ownerId, CardSetInfo setInfo) { 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.subtype.add("Slug");
this.power = new MageInt(7); this.power = new MageInt(7);
this.toughness = new MageInt(7); this.toughness = new MageInt(7);
@ -61,8 +54,8 @@ public class Exogorth extends CardImpl {
// Trample // Trample
this.addAbility(TrampleAbility.getInstance()); this.addAbility(TrampleAbility.getInstance());
// Exogorth can block only creatures with spaceflight. // Exogorth can block creatures with spaceflight.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CanBlockOnlySpaceflightEffect())); this.addAbility(CanBlockSpaceflightAbility.getInstance());
} }
public Exogorth(final Exogorth card) { public Exogorth(final Exogorth card) {
@ -74,31 +67,3 @@ public class Exogorth extends CardImpl {
return new Exogorth(this); 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) { 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("Mirialan");
this.subtype.add("Jedi"); this.subtype.add("Jedi");
this.power = new MageInt(2); 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. // 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()); Ability ability = new EntersBattlefieldTriggeredAbility(new ForceAdeptEffect());
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent(filter1)); ability.addTarget(new TargetControlledCreaturePermanent(filter1));
this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter2)); ability.addTarget(new TargetCreaturePermanent(filter2));
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -36,6 +36,7 @@ import mage.abilities.keyword.FlyingAbility;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
import mage.constants.Zone;
import mage.filter.FilterCard; import mage.filter.FilterCard;
import mage.filter.predicate.mageobject.CardTypePredicate; 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"); private static final FilterCard filter = new FilterCard("an artifact card");
static { static {
filter.add(new CardTypePredicate(CardType.ARTIFACT)); filter.add(new CardTypePredicate(CardType.ARTIFACT));
} }
public GlintNestCrane(UUID ownerId, CardSetInfo setInfo) { 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.subtype.add("Bird");
this.power = new MageInt(1); this.power = new MageInt(1);
this.toughness = new MageInt(3); 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 // 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. // 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) { public GlintNestCrane(final GlintNestCrane card) {

View file

@ -25,13 +25,9 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.cards.g; package mage.cards.g;
import java.util.UUID; import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Zone;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility; import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost; import mage.abilities.costs.common.TapSourceCost;
@ -39,9 +35,12 @@ import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect; import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.Zone;
import mage.counters.CounterType; import mage.counters.CounterType;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player; import mage.players.Player;
import mage.target.TargetPlayer; import mage.target.TargetPlayer;
@ -51,15 +50,16 @@ import mage.target.TargetPlayer;
*/ */
public class Grindclock extends CardImpl { public class Grindclock extends CardImpl {
public Grindclock (UUID ownerId, CardSetInfo setInfo) { public Grindclock(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{2}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), new TapSourceCost())); this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new AddCountersSourceEffect(CounterType.CHARGE.createInstance()), new TapSourceCost()));
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GrindclockEffect(), new TapSourceCost()); Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new GrindclockEffect(), new TapSourceCost());
ability.addTarget(new TargetPlayer()); ability.addTarget(new TargetPlayer());
this.addAbility(ability); this.addAbility(ability);
} }
public Grindclock (final Grindclock card) { public Grindclock(final Grindclock card) {
super(card); super(card);
} }
@ -71,6 +71,7 @@ public class Grindclock extends CardImpl {
} }
class GrindclockEffect extends OneShotEffect { class GrindclockEffect extends OneShotEffect {
public GrindclockEffect() { public GrindclockEffect() {
super(Outcome.Detriment); 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}"; 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 @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
int amount = game.getPermanent(source.getSourceId()).getCounters(game).getCount(CounterType.CHARGE); Permanent sourceObject = game.getPermanentOrLKIBattlefield(source.getSourceId());
Player targetPlayer = game.getPlayer(source.getFirstTarget()); if (sourceObject != null) {
if (targetPlayer != null) { int amount = sourceObject.getCounters(game).getCount(CounterType.CHARGE);
targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game); Player targetPlayer = game.getPlayer(source.getFirstTarget());
return true; if (targetPlayer != null) {
targetPlayer.moveCards(targetPlayer.getLibrary().getTopCards(game, amount), Zone.GRAVEYARD, source, game);
return true;
}
} }
return false; return false;
} }

View file

@ -44,7 +44,7 @@ import mage.constants.CardType;
public class GunganCaptain extends CardImpl { public class GunganCaptain extends CardImpl {
public GunganCaptain(UUID ownerId, CardSetInfo setInfo) { 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("Gungan");
this.subtype.add("Warrior"); this.subtype.add("Warrior");
this.power = new MageInt(1); 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 ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new MagusOfTheArenaEffect(), new GenericManaCost(3));
ability.addCost(new TapSourceCost()); ability.addCost(new TapSourceCost());
ability.addTarget(new TargetControlledCreaturePermanent()); ability.addTarget(new TargetControlledCreaturePermanent());
ability.addTarget(new TargetOpponentsChoicePermanent(new FilterControlledCreaturePermanent())); ability.addTarget(new TargetOpponentsChoicePermanent(1, 1, new FilterControlledCreaturePermanent(), false, true));
this.addAbility(ability); this.addAbility(ability);
} }

View file

@ -59,7 +59,7 @@ import mage.sets.Commander;
public class PaintersServant extends CardImpl { public class PaintersServant extends CardImpl {
public PaintersServant(UUID ownerId, CardSetInfo setInfo) { 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.subtype.add("Scarecrow");
this.power = new MageInt(1); this.power = new MageInt(1);
@ -97,24 +97,23 @@ class PaintersServantEffect extends ContinuousEffectImpl {
if (color == null) { if (color == null) {
return false; return false;
} }
String colorString = color.toString();
for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) { for (Permanent perm : game.getBattlefield().getActivePermanents(source.getControllerId(), game)) {
setObjectColor(perm, colorString, game); perm.getColor(game).addColor(color);
} }
// Stack // Stack
for (MageObject object : game.getStack()) { for (MageObject object : game.getStack()) {
if (object instanceof Spell) { if (object instanceof Spell) {
setObjectColor(object, colorString, game); object.getColor(game).addColor(color);
} }
} }
// Exile // Exile
for (Card card : game.getExile().getAllCards(game)) { for (Card card : game.getExile().getAllCards(game)) {
setCardColor(card, colorString, game); game.getState().getCreateCardAttribute(card).getColor().addColor(color);
} }
// Command // Command
for (CommandObject commandObject : game.getState().getCommand()) { for (CommandObject commandObject : game.getState().getCommand()) {
if (commandObject instanceof Commander) { if (commandObject instanceof Commander) {
setObjectColor(commandObject, colorString, game); commandObject.getColor(game).addColor(color);
} }
} }
@ -123,15 +122,15 @@ class PaintersServantEffect extends ContinuousEffectImpl {
if (player != null) { if (player != null) {
// Hand // Hand
for (Card card : player.getHand().getCards(game)) { for (Card card : player.getHand().getCards(game)) {
setCardColor(card, colorString, game); game.getState().getCreateCardAttribute(card).getColor().addColor(color);
} }
// Library // Library
for (Card card : player.getLibrary().getCards(game)) { for (Card card : player.getLibrary().getCards(game)) {
setCardColor(card, colorString, game); game.getState().getCreateCardAttribute(card).getColor().addColor(color);
} }
// Graveyard // Graveyard
for (Card card : player.getGraveyard().getCards(game)) { 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; 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 @Override
public PaintersServantEffect copy() { public PaintersServantEffect copy() {
return new PaintersServantEffect(this); 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. // {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 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); this.addAbility(ability);

View file

@ -33,7 +33,7 @@ import mage.abilities.common.EntersBattlefieldAllTriggeredAbility;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility; import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.effects.Effect; import mage.abilities.effects.Effect;
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect; 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.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType; import mage.constants.CardType;
@ -45,6 +45,7 @@ import mage.filter.predicate.other.OwnerPredicate;
import mage.game.Game; import mage.game.Game;
import mage.game.events.EntersTheBattlefieldEvent; import mage.game.events.EntersTheBattlefieldEvent;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.target.targetpointer.FixedTarget;
import mage.watchers.common.CastFromGraveyardWatcher; import mage.watchers.common.CastFromGraveyardWatcher;
/** /**
@ -60,14 +61,16 @@ public class PrizedAmalgam extends CardImpl {
} }
public PrizedAmalgam(UUID ownerId, CardSetInfo setInfo) { 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.subtype.add("Zombie");
this.power = new MageInt(3); this.power = new MageInt(3);
this.toughness = 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. // 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( this.addAbility(new PrizedAmalgamTriggerdAbility(new CreateDelayedTriggeredAbilityEffect(
new AtTheBeginOfNextEndStepDelayedTriggeredAbility(new ReturnSourceFromGraveyardToBattlefieldEffect(true))), filter), new AtTheBeginOfNextEndStepDelayedTriggeredAbility(effect)), filter),
new CastFromGraveyardWatcher()); new CastFromGraveyardWatcher());
} }
@ -98,20 +101,32 @@ class PrizedAmalgamTriggerdAbility extends EntersBattlefieldAllTriggeredAbility
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { 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)) { if (super.checkTrigger(event, game)) {
EntersTheBattlefieldEvent entersEvent = (EntersTheBattlefieldEvent) event; EntersTheBattlefieldEvent entersEvent = (EntersTheBattlefieldEvent) event;
if (entersEvent.getFromZone().equals(Zone.GRAVEYARD)) { if (entersEvent.getFromZone().equals(Zone.GRAVEYARD)) {
return true; result = true;
} } else if (entersEvent.getFromZone().equals(Zone.STACK) && entersEvent.getTarget().getControllerId().equals(getControllerId())) {
if (entersEvent.getFromZone().equals(Zone.STACK) && entersEvent.getTarget().getControllerId().equals(getControllerId())) {
CastFromGraveyardWatcher watcher = (CastFromGraveyardWatcher) game.getState().getWatchers().get(CastFromGraveyardWatcher.class.getName()); CastFromGraveyardWatcher watcher = (CastFromGraveyardWatcher) game.getState().getWatchers().get(CastFromGraveyardWatcher.class.getName());
if (watcher != null) { if (watcher != null) {
int zcc = game.getState().getZoneChangeCounter(event.getSourceId()); 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 @Override

View file

@ -28,9 +28,6 @@
package mage.cards.s; package mage.cards.s;
import java.util.UUID; import java.util.UUID;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Zone;
import mage.MageInt; import mage.MageInt;
import mage.abilities.common.SimpleStaticAbility; import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.dynamicvalue.common.CardsInControllerGraveyardCount; 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.abilities.effects.common.continuous.BoostSourceEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Zone;
import mage.filter.common.FilterArtifactCard; import mage.filter.common.FilterArtifactCard;
/** /**
@ -47,13 +47,15 @@ import mage.filter.common.FilterArtifactCard;
public class SalvageSlasher extends CardImpl { public class SalvageSlasher extends CardImpl {
public SalvageSlasher(UUID ownerId, CardSetInfo setInfo) { 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("Human");
this.subtype.add("Rogue"); this.subtype.add("Rogue");
this.power = new MageInt(1); this.power = new MageInt(1);
this.toughness = 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), new StaticValue(0),
Duration.WhileOnBattlefield); Duration.WhileOnBattlefield);
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));

View file

@ -47,13 +47,13 @@ import mage.watchers.common.LifeLossOtherFromCombatWatcher;
public class SithMarauder extends CardImpl { public class SithMarauder extends CardImpl {
public SithMarauder(UUID ownerId, CardSetInfo setInfo) { 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("Human");
this.subtype.add("Sith"); this.subtype.add("Sith");
this.power = new MageInt(5); this.power = new MageInt(5);
this.toughness = new MageInt(4); 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( Ability ability = new ConditionalTriggeredAbility(
new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(3)), new EntersBattlefieldTriggeredAbility(new DamageTargetEffect(3)),
HateCondition.getInstance(), HateCondition.getInstance(),

View file

@ -63,7 +63,7 @@ import mage.target.common.TargetCardInLibrary;
public class TezzeretTheSeeker extends CardImpl { public class TezzeretTheSeeker extends CardImpl {
public TezzeretTheSeeker(UUID ownerId, CardSetInfo setInfo) { 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.subtype.add("Tezzeret");
this.addAbility(new PlanswalkerEntersWithLoyalityCountersAbility(4)); 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)); filter.add(new ConvertedManaCostPredicate(ComparisonType.LessThan, cmc + 1));
TargetCardInLibrary target = new TargetCardInLibrary(filter); TargetCardInLibrary target = new TargetCardInLibrary(filter);

View file

@ -62,10 +62,10 @@ public class TradeFederationBattleship extends CardImpl {
} }
public TradeFederationBattleship(UUID ownerId, CardSetInfo setInfo) { 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.subtype.add("Starship");
this.power = new MageInt(2); this.power = new MageInt(0);
this.toughness = new MageInt(2); this.toughness = new MageInt(6);
// Spaceflight // Spaceflight
this.addAbility(SpaceflightAbility.getInstance()); this.addAbility(SpaceflightAbility.getInstance());

View file

@ -25,16 +25,14 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.cards.v; package mage.cards.v;
import java.util.UUID; import java.util.UUID;
import mage.constants.CardType;
import mage.abilities.effects.common.LoseLifeTargetControllerEffect; import mage.abilities.effects.common.LoseLifeTargetControllerEffect;
import mage.abilities.effects.common.ReturnToHandTargetEffect; import mage.abilities.effects.common.ReturnToHandTargetEffect;
import mage.cards.CardImpl; import mage.cards.CardImpl;
import mage.cards.CardSetInfo; import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.target.common.TargetCreaturePermanent; import mage.target.common.TargetCreaturePermanent;
/** /**
@ -43,9 +41,8 @@ import mage.target.common.TargetCreaturePermanent;
*/ */
public class VaporSnag extends CardImpl { public class VaporSnag extends CardImpl {
public VaporSnag (UUID ownerId, CardSetInfo setInfo) { public VaporSnag(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}"); super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
// Return target creature to its owner's hand. Its controller loses 1 life. // Return target creature to its owner's hand. Its controller loses 1 life.
this.getSpellAbility().addEffect(new ReturnToHandTargetEffect()); this.getSpellAbility().addEffect(new ReturnToHandTargetEffect());
@ -53,7 +50,7 @@ public class VaporSnag extends CardImpl {
this.getSpellAbility().addTarget(new TargetCreaturePermanent()); this.getSpellAbility().addTarget(new TargetCreaturePermanent());
} }
public VaporSnag (final VaporSnag card) { public VaporSnag(final VaporSnag card) {
super(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()); FilterLandPermanent filterLandForOpponent = new FilterLandPermanent("nonbasic land not controlled by " + controller.getLogName());
filterLandForOpponent.add(Predicates.not(new SupertypePredicate("Basic"))); filterLandForOpponent.add(Predicates.not(new SupertypePredicate("Basic")));
filterLandForOpponent.add(Predicates.not(new ControllerIdPredicate(controller.getId()))); 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)); ability.addTarget(new TargetPermanent(filterCreature));
FilterCreaturePermanent filterCreatureForOpponent = new FilterCreaturePermanent("creature not controlled by " + controller.getLogName()); FilterCreaturePermanent filterCreatureForOpponent = new FilterCreaturePermanent("creature not controlled by " + controller.getLogName());
filterCreatureForOpponent.add(Predicates.not(new ControllerIdPredicate(controller.getId()))); 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("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("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("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("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", 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))); 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; package mage.sets;
import mage.cards.ExpansionSet; import mage.cards.ExpansionSet;
import mage.constants.SetType;
import mage.constants.Rarity; 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("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("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("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)); 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 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("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("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("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("Faceless Devourer", 108, Rarity.UNCOMMON, mage.cards.f.FacelessDevourer.class));
cards.add(new SetCardInfo("Fallen Ideal", 109, Rarity.UNCOMMON, mage.cards.f.FallenIdeal.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("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("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("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("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("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)); 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() { public void testBloodlord() {
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8); addCard(Zone.BATTLEFIELD, playerA, "Swamp", 8);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1); 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 // 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, "Bloodlord of Vaasgoth"); // {3}{B}{B}
addCard(Zone.HAND, playerA, "Barony Vampire"); // 3/2 {2}{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 * Returns a new color which contains all of the colors of this ObjectColor
* in addition to all of the colors of the other ObjectColor. * in addition to all of the colors of the other ObjectColor.
*
* @param other The other ObjectColor to union with * @param other The other ObjectColor to union with
* @return A new color which is the union of this and other * @return A new color which is the union of this and other
*/ */
public ObjectColor union(ObjectColor other) { public ObjectColor union(ObjectColor other) {
ObjectColor newColor = new ObjectColor(); ObjectColor newColor = new ObjectColor();
newColor.white = white | other.white; newColor.white = white | other.white;
newColor.blue = blue | other.blue; newColor.blue = blue | other.blue;
newColor.black = black | other.black; newColor.black = black | other.black;
newColor.red = red | other.red; newColor.red = red | other.red;
newColor.green = green | other.green; newColor.green = green | other.green;
return newColor; return newColor;
} }
@ -146,6 +147,24 @@ public class ObjectColor implements Serializable, Copyable<ObjectColor>, Compara
this.setWhite(color.isWhite()); 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() { public boolean isColorless() {
return !(hasColor()); 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 if (sourceObject != null && !this.getAbilityType().equals(AbilityType.TRIGGERED)) { // triggered abilities check this already in playerImpl.triggerAbility
sourceObject.adjustTargets(this, game); sourceObject.adjustTargets(this, game);
} }
// Flashback abilities haven't made the choices the underlying spell might need for targetting. // Flashback abilities haven't made the choices the underlying spell might need for targeting.
if (!(this instanceof FlashbackAbility) && getTargets().size() > 0 && getTargets().chooseTargets( if (!(this instanceof FlashbackAbility)
getEffects().get(0).getOutcome(), this.controllerId, this, noMana, game) == false) { && getTargets().size() > 0) {
if ((variableManaCost != null || announceString != null) && !game.isSimulation()) { Outcome outcome = getEffects().isEmpty() ? Outcome.Detriment : getEffects().get(0).getOutcome();
game.informPlayer(controller, (sourceObject != null ? sourceObject.getIdName() : "") + ": no valid targets with this value of X"); 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 } // end modes

View file

@ -85,10 +85,12 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect {
int value = amount.calculate(game, source, this); int value = amount.calculate(game, source, this);
boolean result = false; boolean result = false;
ChoiceColor choice = new ChoiceColor(false); ChoiceColor choice = new ChoiceColor(true);
for (int i = 0; i < value; i++) { for (int i = 0; i < value; i++) {
if (!choice.isChosen()) { while (!choice.isChosen()) {
if (!controller.choose(outcome, choice, game)) { controller.choose(outcome, choice, game);
if (!controller.isInGame()) {
return false; 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); 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, public LookLibraryAndPickControllerEffect(int numberOfCards, int numberToPick, FilterCard pickFilter, boolean reveal, boolean upTo,
Zone targetZonePickedCards, boolean optional) { Zone targetZonePickedCards, boolean optional) {
this(new StaticValue(numberOfCards), false, new StaticValue(numberToPick), pickFilter, Zone.LIBRARY, false, reveal, upTo, targetZonePickedCards, 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, public LookLibraryAndPickControllerEffect(DynamicValue numberOfCards, boolean mayShuffleAfter, DynamicValue numberToPick,
FilterCard pickFilter, Zone targetZoneLookedCards, boolean putOnTop, boolean reveal, boolean upTo) { FilterCard pickFilter, Zone targetZoneLookedCards, boolean putOnTop, boolean reveal, boolean upTo) {
this(numberOfCards, mayShuffleAfter, numberToPick, pickFilter, targetZoneLookedCards, putOnTop, reveal, upTo, Zone.HAND, false); this(numberOfCards, mayShuffleAfter, numberToPick, pickFilter, targetZoneLookedCards, putOnTop, reveal, upTo, Zone.HAND, false);

View file

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

View file

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

View file

@ -193,8 +193,11 @@ public abstract class ExpansionSet implements Serializable {
if (15 > theBooster.size()) { if (15 > theBooster.size()) {
List<CardInfo> commons = getCardsByRarity(Rarity.COMMON); List<CardInfo> commons = getCardsByRarity(Rarity.COMMON);
while (15 > theBooster.size()) { while (15 > theBooster.size() && !commons.isEmpty()) {
addToBooster(theBooster, commons); 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 // raise this if db structure was changed
private static final long CARD_DB_VERSION = 47; private static final long CARD_DB_VERSION = 47;
// raise this if new cards were added to the server // 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 Dao<CardInfo, Object> cardDao;
private Set<String> classNames; private Set<String> classNames;

View file

@ -1499,6 +1499,17 @@ public abstract class GameImpl implements Game, Serializable {
@Override @Override
public void addTriggeredAbility(TriggeredAbility ability) { 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) { if (ability instanceof TriggeredManaAbility || ability instanceof DelayedTriggeredManaAbility) {
// 20110715 - 605.4 // 20110715 - 605.4
Ability manaAbiltiy = ability.copy(); 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. // If we can't find the card we can't remove it.
return false; return false;
} }
// If needed take attributes from the spell (e.g. color of spell was changed)
card = takeAttributesFromSpell(card, event, game);
boolean success = false; boolean success = false;
if (info.faceDown) { if (info.faceDown) {
card.setFaceDown(true, game); card.setFaceDown(true, game);
@ -282,4 +284,23 @@ public class ZonesHandler {
order.add(cards.getCards(game).iterator().next()); order.add(cards.getCards(game).iterator().next());
return order; 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(); transformable = card.isTransformable();
if (transformable) { if (transformable) {
this.nightCard = card.isNightCard(); this.nightCard = card.isNightCard();
if (! this.nightCard) { if (!this.nightCard) {
this.secondSideCard = card.getSecondCardFace(); this.secondSideCard = card.getSecondCardFace();
this.secondSideCardClazz = this.secondSideCard.getClass(); this.secondSideCardClazz = this.secondSideCard.getClass();
} }

View file

@ -27,15 +27,9 @@
*/ */
package mage.game.permanent; package mage.game.permanent;
import java.util.ArrayList;
import java.util.UUID; import java.util.UUID;
import mage.cards.Card; import mage.cards.Card;
import mage.cards.CardsImpl;
import mage.cards.MeldCard;
import mage.constants.Zone;
import mage.game.Game; 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 @Override
public void checkForCountersToAdd(Permanent permanent, Game game) { public void checkForCountersToAdd(Permanent permanent, Game game) {
throw new UnsupportedOperationException("Not supported for Spell"); card.checkForCountersToAdd(permanent, game);
} }
@Override @Override

View file

@ -25,15 +25,13 @@
* authors and should not be interpreted as representing official policies, either expressed * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.game.turn; package mage.game.turn;
import mage.constants.PhaseStep;
import mage.constants.TurnPhase;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.UUID; 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 class TurnMods extends ArrayList<TurnMod> {
public TurnMods() {} public TurnMods() {
}
public TurnMods(final TurnMods mods) { public TurnMods(final TurnMods mods) {
for (TurnMod mod: mods) { for (TurnMod mod : mods) {
this.add(mod.copy()); this.add(mod.copy());
} }
} }
@ -95,7 +94,7 @@ public class TurnMods extends ArrayList<TurnMod> {
it.remove(); 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()); it = this.listIterator(this.size());
while (it.hasPrevious()) { while (it.hasPrevious()) {
TurnMod turnMod = it.previous(); TurnMod turnMod = it.previous();

View file

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

View file

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

View file

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