Merge remote-tracking branch 'fork/master'

This commit is contained in:
vraskulin 2017-02-27 16:33:47 +03:00
commit f309717616
131 changed files with 1655 additions and 755 deletions

View file

@ -1057,6 +1057,12 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
this.tablesPane.hideTables();
}
public void setTableFilter() {
if (this.tablesPane != null) {
this.tablesPane.setTableFilter();
}
}
public void showGames(boolean setActive) {
MagePane topPanebefore = getTopMost(tablesPane);
if (!tablesPane.isVisible()) {

View file

@ -5,6 +5,7 @@ import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.cards.decks.DeckCardLists;
import mage.client.chat.LocalCommands;
import mage.constants.ManaType;
import mage.constants.PlayerAction;
import mage.game.match.MatchOptions;
@ -306,7 +307,11 @@ public class SessionHandler {
}
public static boolean sendChatMessage(UUID chatId, String text) {
if (!LocalCommands.handleLocalCommands(chatId, text)) {
return session.sendChatMessage(chatId, text);
} else {
return false;
}
}
public static boolean sendPlayerManaType(UUID gameId, UUID playerId, ManaType data) {

View file

@ -271,8 +271,8 @@ public class ChatPanelBasic extends javax.swing.JPanel {
if (color.equals(MessageColor.YELLOW)) {
textColor = "Yellow";
}
if (messageType == MessageType.WHISPER) {
if (username.equalsIgnoreCase("Whisper from " + SessionHandler.getUserName())) {
if (messageType == MessageType.WHISPER_FROM) {
if (username.equalsIgnoreCase(SessionHandler.getUserName())) {
if (message.toLowerCase().startsWith("profanity 0")) {
PreferencesDialog.saveValue(PreferencesDialog.KEY_GAME_USE_PROFANITY_FILTER, "0");
} else if (message.toLowerCase().startsWith("profanity 1")) {
@ -281,6 +281,10 @@ public class ChatPanelBasic extends javax.swing.JPanel {
PreferencesDialog.saveValue(PreferencesDialog.KEY_GAME_USE_PROFANITY_FILTER, "2");
}
}
username = "Whisper from " + username;
}
if (messageType == MessageType.WHISPER_TO) {
username = "Whisper to " + username;
}
Matcher matchPattern = cardNamePattern.matcher(message);

View file

@ -57,7 +57,8 @@ public class ChatPanelSeparated extends ChatPanelBasic {
public void receiveMessage(String username, String message, String time, ChatMessage.MessageType messageType, ChatMessage.MessageColor color) {
switch (messageType) {
case TALK:
case WHISPER:
case WHISPER_TO:
case WHISPER_FROM:
case USER_INFO:
super.receiveMessage(username, message, time, messageType, color);
return;

View file

@ -0,0 +1,65 @@
package mage.client.chat;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.util.IgnoreList;
import mage.interfaces.callback.ClientCallback;
import mage.view.ChatMessage;
import java.text.DateFormat;
import java.util.Date;
import java.util.Optional;
import java.util.StringTokenizer;
import java.util.UUID;
public class LocalCommands {
private static final DateFormat timeFormatter = DateFormat.getTimeInstance(DateFormat.SHORT);
/**
* Handler for commands that do not require server interaction, i.e settings etc
* @param chatId
* @param text
* @return true if the command was handled locally, else false
*/
public static boolean handleLocalCommands(UUID chatId, String text) {
final StringTokenizer st = new StringTokenizer(text.trim());
final int tokens = st.countTokens();
if (tokens == 0) {
return false;
}
final String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> "");
Optional<String> response = Optional.empty();
switch (st.nextToken()) {
case "/ignore":
case "\\ignore":
final String ignoreTarget = tokens > 1 ? st.nextToken() : "";
response = Optional.of(IgnoreList.ignore(serverAddress, ignoreTarget));
break;
case "/unignore":
case "\\unignore":
final String unignoreTarget = tokens > 1 ? st.nextToken() : "";
response = Optional.of(IgnoreList.unignore(serverAddress, unignoreTarget));
break;
// TODO: move profanity settings to here
default:
break;
}
if (response.isPresent()) {
displayLocalCommandResponse(chatId, response.get());
return true;
}
return false;
}
private static void displayLocalCommandResponse(UUID chatId, String response) {
final String text = new StringBuilder().append("<font color=yellow>").append(response).append("</font>").toString();
ClientCallback chatMessage = new ClientCallback("chatMessage", chatId,
new ChatMessage("", text, timeFormatter.format(new Date()), ChatMessage.MessageColor.BLUE));
MageFrame.getInstance().processCallback(chatMessage);
}
}

View file

@ -41,6 +41,7 @@ import mage.client.SessionHandler;
import mage.client.components.MageComponents;
import mage.client.table.TablePlayerPanel;
import mage.client.util.Event;
import mage.client.util.IgnoreList;
import mage.client.util.Listener;
import mage.constants.MatchTimeLimit;
import mage.constants.MultiplayerAttackOption;
@ -386,6 +387,8 @@ public class NewTableDialog extends MageDialog {
options.setPassword(this.txtPassword.getText());
options.setQuitRatio((Integer) this.spnQuitRatio.getValue());
options.setEdhPowerLevel((Integer) this.spnEdhPowerLevel.getValue());
String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> "");
options.setBannedUsers(IgnoreList.ignoreList(serverAddress));
if (!checkMatchOptions(options)) {
return;
}

View file

@ -1,8 +1,11 @@
package mage.client.preference;
import com.google.common.collect.Sets;
import mage.client.MageFrame;
import java.util.Set;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import mage.client.MageFrame;
// TODO: Move all preference related logic from MageFrame and PreferencesDialog to this class.
public class MagePreferences {
@ -14,6 +17,8 @@ public class MagePreferences {
private static final String KEY_EMAIL = "email";
private static final String KEY_AUTO_CONNECT = "autoConnect";
private static final String NODE_KEY_IGNORE_LIST = "ignoreListString";
private static Preferences prefs() {
// TODO: Move MageFrame.prefs to this class.
return MageFrame.getPreferences();
@ -98,4 +103,36 @@ public class MagePreferences {
public static void setAutoConnect(boolean autoConnect) {
prefs().putBoolean(KEY_AUTO_CONNECT, autoConnect);
}
public static void addIgnoredUser(String serverAddress, String username) {
ignoreListNode(serverAddress).putBoolean(username, true);
}
public static boolean removeIgnoredUser(String serverAddress, String username) {
Preferences ignoreList = ignoreListNode(serverAddress);
boolean exists = ignoreList.getBoolean(username, false);
if (exists) {
ignoreList.remove(username);
}
return exists;
}
public static Set<String> ignoreList(String serverAddress) {
try {
return Sets.newHashSet(ignoreListNode(serverAddress).keys());
} catch (BackingStoreException e) {
e.printStackTrace();
}
return Sets.newHashSet();
}
public static void clearIgnoreList(String serverAddress) throws BackingStoreException {
ignoreListNode(serverAddress).clear();
}
private static Preferences ignoreListNode(String serverAddress) {
return prefs().node(NODE_KEY_IGNORE_LIST).node(serverAddress);
}
}

View file

@ -34,6 +34,7 @@ import javax.swing.JOptionPane;
import javax.swing.SwingUtilities;
import mage.cards.decks.Deck;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.chat.ChatPanelBasic;
import mage.client.constants.Constants.DeckEditorMode;
import mage.client.dialog.PreferencesDialog;
@ -42,6 +43,7 @@ import mage.client.game.GamePanel;
import mage.client.plugins.impl.Plugins;
import mage.client.util.DeckUtil;
import mage.client.util.GameManager;
import mage.client.util.IgnoreList;
import mage.client.util.audio.AudioManager;
import mage.client.util.object.SaveObjectUtil;
import mage.interfaces.callback.CallbackClient;
@ -110,6 +112,15 @@ public class CallbackClientImpl implements CallbackClient {
break;
case "chatMessage": {
ChatMessage message = (ChatMessage) callback.getData();
// Drop messages from ignored users
if (message.getUsername() != null && IgnoreList.IGNORED_MESSAGE_TYPES.contains(message.getMessageType())) {
final String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> "");
if (IgnoreList.userIsIgnored(serverAddress, message.getUsername())) {
break;
}
}
ChatPanelBasic panel = MageFrame.getChat(callback.getObjectId());
if (panel != null) {
// play the sound related to the message
@ -422,6 +433,7 @@ public class CallbackClientImpl implements CallbackClient {
usedPanel.receiveMessage("", new StringBuilder("Download card images by using the \"Images\" menu to the top right .")
.append("<br/>Download icons and symbols by using the \"Symbols\" menu to the top right.")
.append("<br/>\\list - Show a list of available chat commands.")
.append("<br/>").append(IgnoreList.usage())
.append("<br/>Type <font color=green>\\w yourUserName profanity 0 (or 1 or 2)</font> to turn off/on the profanity filter").toString(),
null, MessageType.USER_INFO, ChatMessage.MessageColor.BLUE);
break;

View file

@ -140,4 +140,10 @@ public class TablesPane extends MagePane {
public void deactivated() {
tablesPanel.stopTasks();
}
public void setTableFilter() {
if (tablesPanel != null) {
tablesPanel.setTableFilter();
}
}
}

View file

@ -51,6 +51,7 @@ import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
@ -85,6 +86,7 @@ import mage.client.dialog.TableWaitingDialog;
import static mage.client.table.TablesPanel.PASSWORDED;
import mage.client.util.ButtonColumn;
import mage.client.util.GUISizeHelper;
import mage.client.util.IgnoreList;
import mage.client.util.MageTableRowSorter;
import mage.client.util.gui.GuiDisplayUtil;
import mage.client.util.gui.TableUtil;
@ -550,7 +552,7 @@ public class TablesPanel extends javax.swing.JPanel {
return chatPanelMain.getUserChatPanel();
}
private void setTableFilter() {
public void setTableFilter() {
// state
List<RowFilter<Object, Object>> stateFilterList = new ArrayList<>();
if (btnStateWaiting.isSelected()) {
@ -630,6 +632,20 @@ public class TablesPanel extends javax.swing.JPanel {
passwordFilterList.add(RowFilter.regexFilter("^\\*\\*\\*$", TableTableModel.COLUMN_PASSWORD));
}
// Hide games of ignored players
List<RowFilter<Object, Object>> ignoreListFilterList = new ArrayList<>();
String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> "");
final Set<String> ignoreListCopy = IgnoreList.ignoreList(serverAddress);
if (ignoreListCopy.size() > 0) {
ignoreListFilterList.add(new RowFilter<Object, Object>() {
@Override
public boolean include(Entry<? extends Object, ? extends Object> entry) {
final String owner = entry.getStringValue(TableTableModel.COLUMN_OWNER);
return !ignoreListCopy.contains(owner);
}
});
}
if (stateFilterList.isEmpty() || typeFilterList.isEmpty() || formatFilterList.isEmpty()
|| skillFilterList.isEmpty() || ratingFilterList.isEmpty()
|| passwordFilterList.isEmpty()) { // no selection
@ -673,6 +689,12 @@ public class TablesPanel extends javax.swing.JPanel {
filterList.addAll(passwordFilterList);
}
if (ignoreListFilterList.size() > 1) {
filterList.add(RowFilter.orFilter(ignoreListFilterList));
} else if (ignoreListFilterList.size() == 1) {
filterList.addAll(ignoreListFilterList);
}
if (filterList.size() == 1) {
activeTablesSorter.setRowFilter(filterList.get(0));
} else {
@ -1177,6 +1199,8 @@ public class TablesPanel extends javax.swing.JPanel {
options.setSkillLevel(SkillLevel.CASUAL);
options.setRollbackTurnsAllowed(true);
options.setQuitRatio(100);
String serverAddress = SessionHandler.getSession().getServerHostname().orElseGet(() -> "");
options.setBannedUsers(IgnoreList.ignoreList(serverAddress));
table = SessionHandler.createTable(roomId, options);
SessionHandler.joinTable(roomId, table.getTableId(), "Human", "Human", 1, DeckImporterUtil.importDeck("test.dck"), "");

View file

@ -0,0 +1,98 @@
package mage.client.util;
import com.google.common.collect.ImmutableSet;
import mage.client.MageFrame;
import mage.client.preference.MagePreferences;
import mage.view.ChatMessage;
import java.util.Arrays;
import java.util.Set;
public class IgnoreList {
private static final String USAGE = "<br/><font color=yellow>\\ignore - shows current ignore list on this server."
+ "<br/>\\ignore [username] - add a username to your ignore list on this server."
+ "<br/>\\unignore [username] - remove a username from your ignore list on this server.</font>";
public static final int MAX_IGNORE_LIST_SIZE = 50;
public static Set<ChatMessage.MessageType> IGNORED_MESSAGE_TYPES =
ImmutableSet.of(ChatMessage.MessageType.TALK,
ChatMessage.MessageType.WHISPER_FROM);
public static String usage() {
return USAGE;
}
public static Set<String> ignoreList(String serverAddress) {
return MagePreferences.ignoreList(serverAddress);
}
public static String ignoreListString(String serverAddress) {
final String[] list = MagePreferences.ignoreList(serverAddress).toArray(new String[0]);
Arrays.sort(list);
return "<font color=yellow>Current ignore list on " + serverAddress + ": "
+ Arrays.toString(list)
+ "</font>";
}
public static String ignore(String serverAddress, String user) {
if (user == null || user.length() == 0) {
return ignoreListString(serverAddress);
}
if (MagePreferences.ignoreList(serverAddress).size() >= MAX_IGNORE_LIST_SIZE) {
return "Your ignore list is too big, remove a user to be able to add a new one.";
}
if (userIsIgnored(serverAddress, user)) {
return new StringBuilder()
.append(user)
.append(" is already on your ignore list on ")
.append(serverAddress)
.toString();
}
MagePreferences.addIgnoredUser(serverAddress, user);
updateTablesTable();
return new StringBuilder()
.append("Added ")
.append(user)
.append(" to your ignore list on ")
.append(serverAddress)
.toString();
}
private static void updateTablesTable() {
MageFrame mageFrame = MageFrame.getInstance();
if (mageFrame != null) {
mageFrame.setTableFilter();
}
}
public static String unignore(String serverAddress, String user) {
if (user == null || user.length() == 0) {
return usage();
}
if (MagePreferences.removeIgnoredUser(serverAddress, user)) {
updateTablesTable();
return new StringBuilder()
.append("Removed ")
.append(user)
.append(" from your ignore list on ")
.append(serverAddress)
.toString();
} else {
return new StringBuilder()
.append("No such user \"")
.append(user)
.append("\" on your ignore list on ")
.append(serverAddress)
.toString();
}
}
public static boolean userIsIgnored(String serverAddress, String username) {
return MagePreferences.ignoreList(serverAddress).contains(username);
}
}

View file

@ -68,7 +68,7 @@ public class TransformedImageCache {
}
}
static final Map<Key, Map<BufferedImage, BufferedImage>> IMAGE_CACHE;
private static final Map<Key, Map<BufferedImage, BufferedImage>> IMAGE_CACHE;
static {
// TODO: can we use a single map?
@ -85,17 +85,17 @@ public class TransformedImageCache {
private static BufferedImage rotateImage(BufferedImage image, double angle) {
double sin = Math.abs(Math.sin(angle)), cos = Math.abs(Math.cos(angle));
int w = image.getWidth(), h = image.getHeight();
int neww = (int) Math.floor(w * cos + h * sin), newh = (int) Math.floor(h * cos + w * sin);
int width = image.getWidth(), height = image.getHeight();
int newWidth = (int) Math.floor(width * cos + height * sin), newHeight = (int) Math.floor(height * cos + width * sin);
GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice gs = ge.getDefaultScreenDevice();
GraphicsConfiguration gc = gs.getDefaultConfiguration();
BufferedImage result = gc.createCompatibleImage(neww, newh, Transparency.TRANSLUCENT);
BufferedImage result = gc.createCompatibleImage(newWidth, newHeight, Transparency.TRANSLUCENT);
Graphics2D g = result.createGraphics();
g.translate((neww - w) / 2, (newh - h) / 2);
g.rotate(angle, w / 2, h / 2);
g.translate((newWidth - width) / 2, (newHeight - height) / 2);
g.rotate(angle, width / 2, height / 2);
g.drawRenderedImage(image, null);
g.dispose();
return result;

View file

@ -0,0 +1,47 @@
package mage.client.preference;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
public class MagePreferencesTest {
@Before
public void setUp() {
MagePreferences.ignoreList("test.com.xx").clear();
}
@After
public void tearDown() {
MagePreferences.ignoreList("test.com.xx").clear();
}
@Test
public void testIgnoreList() throws Exception {
assertThat(MagePreferences.ignoreList("test.com.xx").size(), is(0));
assertThat(MagePreferences.removeIgnoredUser("test.com.xx", "test"), is(false));
MagePreferences.addIgnoredUser("test.com.xx", "test");
assertThat(MagePreferences.ignoreList("test.com.xx").size(), is(1));
assertThat(MagePreferences.ignoreList("other.com.xx").size(), is(0));
MagePreferences.addIgnoredUser("test.com.xx", "lul");
assertThat(MagePreferences.ignoreList("test.com.xx").size(), is(2));
assertThat(MagePreferences.ignoreList("test.com.xx").contains("test"), is(true));
assertThat(MagePreferences.ignoreList("test.com.xx").contains("lul"), is(true));
assertThat(MagePreferences.removeIgnoredUser("test.com.xx", "test"), is(true));
assertThat(MagePreferences.removeIgnoredUser("test.com.xx", "test"), is(false));
assertThat(MagePreferences.ignoreList("test.com.xx").size(), is(1));
assertThat(MagePreferences.removeIgnoredUser("test.com.xx", "lul"), is(true));
assertThat(MagePreferences.removeIgnoredUser("test.com.xx", "lul"), is(false));
assertThat(MagePreferences.ignoreList("test.com.xx").size(), is(0));
assertThat(MagePreferences.ignoreList("test.com.xx").contains("test"), is(false));
assertThat(MagePreferences.ignoreList("test.com.xx").contains("lul"), is(false));
}
}

View file

@ -0,0 +1,85 @@
package mage.client.util;
import mage.client.preference.MagePreferences;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import static junit.framework.TestCase.assertEquals;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.Is.is;
public class IgnoreListTest {
@Before
public void setUp() throws Exception {
MagePreferences.clearIgnoreList("test.com.xx");
}
@After
public void tearDown() throws Exception {
MagePreferences.clearIgnoreList("test.com.xx");
}
@Test
public void ignoreListEmpty() throws Exception {
assertThat(IgnoreList.ignoreListString("test.com.xx"), is("<font color=yellow>Current ignore list on test.com.xx: []</font>"));
}
@Test
public void ignoreList() throws Exception {
final String test = IgnoreList.ignore("test.com.xx", "test");
final String kranken = IgnoreList.ignore("test.com.xx", "kranken");
assertThat(IgnoreList.ignoreListString("test.com.xx"), is("<font color=yellow>Current ignore list on test.com.xx: [kranken, test]</font>"));
assertThat(test, is("Added test to your ignore list on test.com.xx"));
assertThat(kranken, is("Added kranken to your ignore list on test.com.xx"));
}
@Test
public void ignore() throws Exception {
assertThat(IgnoreList.userIsIgnored("test.com.xx", "kranken"), is(false));
final String r = IgnoreList.ignore("test.com.xx", "kranken");
assertThat(IgnoreList.userIsIgnored("test.com.xx", "kranken"), is(true));
assertEquals(r, "Added kranken to your ignore list on test.com.xx");
}
@Test
public void ignoreAgain() throws Exception {
assertThat(IgnoreList.userIsIgnored("test.com.xx", "kranken"), is(false));
IgnoreList.ignore("test.com.xx", "kranken");
final String r = IgnoreList.ignore("test.com.xx", "kranken");
assertThat(IgnoreList.userIsIgnored("test.com.xx", "kranken"), is(true));
assertEquals(r, "kranken is already on your ignore list on test.com.xx");
}
@Test
public void ignoreDefaultResponse() throws Exception {
final String r1 = IgnoreList.ignore("test.com.xx", "");
final String r2 = IgnoreList.ignore("test.com.xx", null);
assertThat(IgnoreList.ignoreListString("test.com.xx"), is("<font color=yellow>Current ignore list on test.com.xx: []</font>"));
assertEquals(r1, r2);
assertEquals(r2, "<font color=yellow>Current ignore list on test.com.xx: []</font>");
}
@Test
public void ignoreMaxSize() throws Exception {
for (int i = 0; i < 50; i++) {
IgnoreList.ignore("test.com.xx", "" + i);
}
final String r = IgnoreList.ignore("test.com.xx", "lul");
assertEquals(r, "Your ignore list is too big, remove a user to be able to add a new one.");
assertThat(IgnoreList.ignoreListString("test.com.xx"), is("<font color=yellow>Current ignore list on test.com.xx: [0, 1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 2, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 3, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 4, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 5, 6, 7, 8, 9]</font>"));
}
@Test
public void unignore() throws Exception {
assertThat(IgnoreList.userIsIgnored("test.com.xx", "kranken"), is(false));
IgnoreList.ignore("test.com.xx", "kranken");
assertThat(IgnoreList.userIsIgnored("test.com.xx", "kranken"), is(true));
final String r = IgnoreList.unignore("test.com.xx", "kranken");
assertThat(IgnoreList.userIsIgnored("test.com.xx", "kranken"), is(false));
assertEquals(r, "Removed kranken from your ignore list on test.com.xx");
}
}

View file

@ -244,6 +244,10 @@ public class SessionImpl implements Session {
});
}
public Optional<String> getServerHostname() {
return isConnected() ? Optional.of(connection.getHost()) : Optional.<String>empty();
}
@Override
public boolean stopConnecting() {
canceled = true;
@ -960,6 +964,8 @@ public class SessionImpl implements Session {
return false;
}
@Override
public boolean joinGame(UUID gameId) {
try {

View file

@ -29,6 +29,8 @@ package mage.remote.interfaces;
import mage.remote.Connection;
import java.util.Optional;
/**
* @author noxx
*/
@ -52,6 +54,8 @@ public interface Connect {
boolean isConnected();
Optional<String> getServerHostname();
boolean disconnectUser(String userSessionId);
boolean endUserSession(String userSessionId);

View file

@ -49,7 +49,7 @@ public class ChatMessage implements Serializable {
}
public enum MessageType {
USER_INFO, STATUS, GAME, TALK, WHISPER
USER_INFO, STATUS, GAME, TALK, WHISPER_FROM, WHISPER_TO
}
public enum SoundToPlay {

View file

@ -133,6 +133,10 @@ public class ChatManager {
return;
}
if (message.length() > 500) {
message = message.replaceFirst("^(.{500}).*", "$1 (rest of message truncated)");
}
String messageToCheck = message;
Matcher matchPattern = cardNamePattern.matcher(message);
while (matchPattern.find()) {
@ -186,7 +190,10 @@ public class ChatManager {
+ "<br/>\\me - shows the history of the current player"
+ "<br/>\\list or \\l - Show a list of commands"
+ "<br/>\\whisper or \\w [player name] [text] - whisper to the player with the given name"
+ "<br/>[Card Name] - Show a highlighted card name";
+ "<br/>[Card Name] - Show a highlighted card name"
+ "<br/>\\ignore - shows current ignore list on this server."
+ "<br/>\\ignore [username] - add a username to your ignore list on this server."
+ "<br/>\\unignore [username] - remove a username from your ignore list on this server.";
private boolean performUserCommand(User user, String message, UUID chatId, boolean doError) {
String command = message.substring(1).trim().toUpperCase(Locale.ENGLISH);

View file

@ -127,10 +127,10 @@ public class ChatSession {
public boolean broadcastWhisperToUser(User fromUser, User toUser, String message) {
if (clients.containsKey(toUser.getId())) {
toUser.fireCallback(new ClientCallback("chatMessage", chatId,
new ChatMessage(new StringBuilder("Whisper from ").append(fromUser.getName()).toString(), message, timeFormatter.format(new Date()), MessageColor.YELLOW, MessageType.WHISPER, SoundToPlay.PlayerWhispered)));
new ChatMessage(fromUser.getName(), message, timeFormatter.format(new Date()), MessageColor.YELLOW, MessageType.WHISPER_FROM, SoundToPlay.PlayerWhispered)));
if (clients.containsKey(fromUser.getId())) {
fromUser.fireCallback(new ClientCallback("chatMessage", chatId,
new ChatMessage(new StringBuilder("Whisper to ").append(toUser.getName()).toString(), message, timeFormatter.format(new Date()), MessageColor.YELLOW, MessageType.WHISPER, null)));
new ChatMessage(toUser.getName(), message, timeFormatter.format(new Date()), MessageColor.YELLOW, MessageType.WHISPER_TO, null)));
return true;
}
}

View file

@ -27,14 +27,6 @@
*/
package mage.server;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import mage.MageException;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists;
@ -68,6 +60,15 @@ import mage.server.util.ThreadExecutor;
import mage.view.ChatMessage;
import org.apache.log4j.Logger;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* @author BetaSteward_at_googlemail.com
*/
@ -99,7 +100,7 @@ public class TableController {
} else {
controllerName = "System";
}
table = new Table(roomId, options.getGameType(), options.getName(), controllerName, DeckValidatorFactory.getInstance().createDeckValidator(options.getDeckType()), options.getPlayerTypes(), TableRecorderImpl.getInstance(), match);
table = new Table(roomId, options.getGameType(), options.getName(), controllerName, DeckValidatorFactory.getInstance().createDeckValidator(options.getDeckType()), options.getPlayerTypes(), TableRecorderImpl.getInstance(), match, options.getBannedUsers());
chatId = ChatManager.getInstance().createChatSession("Match Table " + table.getId());
init();
}
@ -118,7 +119,7 @@ public class TableController {
} else {
controllerName = "System";
}
table = new Table(roomId, options.getTournamentType(), options.getName(), controllerName, DeckValidatorFactory.getInstance().createDeckValidator(options.getMatchOptions().getDeckType()), options.getPlayerTypes(), TableRecorderImpl.getInstance(), tournament);
table = new Table(roomId, options.getTournamentType(), options.getName(), controllerName, DeckValidatorFactory.getInstance().createDeckValidator(options.getMatchOptions().getDeckType()), options.getPlayerTypes(), TableRecorderImpl.getInstance(), tournament, options.getMatchOptions().getBannedUsers());
chatId = ChatManager.getInstance().createChatSession("Tourn. table " + table.getId());
}
@ -195,8 +196,9 @@ public class TableController {
return false;
}
Player player = createPlayer(name, seat.getPlayerType(), skill);
if (player != null) {
Optional<Player> playerOptional = createPlayer(name, seat.getPlayerType(), skill);
if (playerOptional.isPresent()) {
Player player = playerOptional.get();
if (!player.canJoinTable(table)) {
user.showUserMessage("Join Table", new StringBuilder("A ").append(seat.getPlayerType()).append(" player can't join this table.").toString());
return false;
@ -227,10 +229,11 @@ public class TableController {
}
public synchronized boolean replaceDraftPlayer(Player oldPlayer, String name, String playerType, int skill) {
Player newPlayer = createPlayer(name, playerType, skill);
if (newPlayer == null || table.getState() != TableState.DRAFTING) {
Optional<Player> newPlayerOpt = createPlayer(name, playerType, skill);
if (!newPlayerOpt.isPresent() || table.getState() != TableState.DRAFTING) {
return false;
}
Player newPlayer = newPlayerOpt.get();
TournamentPlayer oldTournamentPlayer = tournament.getPlayer(oldPlayer.getId());
tournament.removePlayer(oldPlayer.getId());
tournament.addPlayer(newPlayer, playerType);
@ -306,13 +309,14 @@ public class TableController {
}
}
Player player = createPlayer(name, seat.getPlayerType(), skill);
if (player == null) {
Optional<Player> playerOpt = createPlayer(name, seat.getPlayerType(), skill);
if (!playerOpt.isPresent()) {
String message = new StringBuilder("Could not create player ").append(name).append(" of type ").append(seat.getPlayerType()).toString();
logger.warn(new StringBuilder("User: ").append(user.getName()).append(" => ").append(message).toString());
user.showUserMessage("Join Table", message);
return false;
}
Player player = playerOpt.get();
logger.debug("DECK validated: " + table.getValidator().getName() + ' ' + player.getName() + ' ' + deck.getName());
if (!player.canJoinTable(table)) {
user.showUserMessage("Join Table", new StringBuilder("A ").append(seat.getPlayerType()).append(" player can't join this table.").toString());
@ -443,17 +447,18 @@ public class TableController {
// ReplayManager.getInstance().replayGame(table.getId(), userId);
// return true;
// }
private Player createPlayer(String name, String playerType, int skill) {
Player player;
private Optional<Player> createPlayer(String name, String playerType, int skill) {
Optional<Player> playerOpt;
if (options == null) {
player = PlayerFactory.getInstance().createPlayer(playerType, name, RangeOfInfluence.ALL, skill);
playerOpt = PlayerFactory.getInstance().createPlayer(playerType, name, RangeOfInfluence.ALL, skill);
} else {
player = PlayerFactory.getInstance().createPlayer(playerType, name, options.getRange(), skill);
playerOpt = PlayerFactory.getInstance().createPlayer(playerType, name, options.getRange(), skill);
}
if (player != null) {
if (playerOpt.isPresent()) {
Player player = playerOpt.get();
logger.trace("Player " + player.getName() + " created id: " + player.getId());
}
return player;
return playerOpt;
}
public void leaveTableAll() {

View file

@ -28,14 +28,16 @@
package mage.server.game;
import java.lang.reflect.Constructor;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import mage.constants.RangeOfInfluence;
import mage.players.Player;
import org.apache.log4j.Logger;
import java.lang.reflect.Constructor;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
/**
*
* @author BetaSteward_at_googlemail.com
@ -53,14 +55,14 @@ public class PlayerFactory {
private PlayerFactory() {}
public Player createPlayer(String playerType, String name, RangeOfInfluence range, int skill) {
public Optional<Player> createPlayer(String playerType, String name, RangeOfInfluence range, int skill) {
try {
Class playerTypeClass = playerTypes.get(playerType);
if (playerTypeClass != null) {
Constructor<?> con = playerTypeClass.getConstructor(String.class, RangeOfInfluence.class, int.class);
Player player = (Player) con.newInstance(name, range, skill);
logger.trace("Player created: " + name + " - " + player.getId());
return player;
return Optional.of(player);
}
else {
logger.fatal("Unknown player type: " + playerType);
@ -68,7 +70,7 @@ public class PlayerFactory {
} catch (Exception ex) {
logger.fatal("PlayerFactory error ", ex);
}
return null;
return Optional.empty();
}
public Set<String> getPlayerTypes() {

View file

@ -27,7 +27,6 @@
*/
package mage.cards.a;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.AttacksTriggeredAbility;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
@ -41,6 +40,8 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.game.permanent.token.ServoToken;
import java.util.UUID;
/**
*
* @author fireshoes
@ -62,7 +63,7 @@ public class AetherChaser extends CardImpl {
this.addAbility(new EntersBattlefieldTriggeredAbility(new GetEnergyCountersControllerEffect(2)));
// Whenever Aether Chaser attacks, you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token.
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2)), false,
this.addAbility(new AttacksTriggeredAbility(new DoIfCostPaid(new CreateTokenEffect(new ServoToken()), new PayEnergyCost(2)), true,
"Whenever {this} attacks you may pay {E}{E}. If you do, create a 1/1 colorless Servo artifact creature token."));
}

View file

@ -87,6 +87,10 @@ class AshlingsPrerogativeIncorrectOddityEffect extends PermanentsEnterBattlefiel
staticText = "Each creature without converted mana cost of the chosen value enters the battlefield tapped.";
}
public AshlingsPrerogativeIncorrectOddityEffect(final AshlingsPrerogativeIncorrectOddityEffect effect) {
super(effect);
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
int incorrectModResult;
@ -101,6 +105,11 @@ class AshlingsPrerogativeIncorrectOddityEffect extends PermanentsEnterBattlefiel
return permanent != null && creaturefilter.match(permanent, game) && permanent.getConvertedManaCost() % 2 == incorrectModResult;
}
@Override
public AshlingsPrerogativeIncorrectOddityEffect copy() {
return new AshlingsPrerogativeIncorrectOddityEffect(this);
}
}
class AshlingsPrerogativeCorrectOddityEffect extends GainAbilityAllEffect {
@ -112,6 +121,9 @@ class AshlingsPrerogativeCorrectOddityEffect extends GainAbilityAllEffect {
super(HasteAbility.getInstance(), Duration.WhileOnBattlefield, creaturefilter);
staticText = "Each creature with converted mana cost of the chosen value has haste.";
}
public AshlingsPrerogativeCorrectOddityEffect(final AshlingsPrerogativeCorrectOddityEffect effect) {
super(effect);
}
@Override
protected boolean selectedByRuntimeData(Permanent permanent, Ability source, Game game) {
@ -124,4 +136,8 @@ class AshlingsPrerogativeCorrectOddityEffect extends GainAbilityAllEffect {
return permanent != null && creaturefilter.match(permanent, game) && permanent.getConvertedManaCost() % 2 == correctModResult;
}
@Override
public AshlingsPrerogativeCorrectOddityEffect copy() {
return new AshlingsPrerogativeCorrectOddityEffect(this);
}
}

View file

@ -59,7 +59,7 @@ public class CanopyCover extends CardImpl {
private static final FilterObject filter = new FilterStackObject("spells or abilities your opponents control");
public CanopyCover(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{1}{G}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{G}");
this.subtype.add("Aura");
// Enchant creature
@ -70,7 +70,7 @@ public class CanopyCover extends CardImpl {
this.addAbility(ability);
// Enchanted creature can't be blocked except by creatures with flying or reach. (!this is a static ability of the enchantment)
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new OrchardSpiritEffect()));
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CanopyCoverEffect()));
// Enchanted creature can't be the target of spells or abilities your opponents control.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantBeTargetedAttachedEffect(filter, Duration.WhileOnBattlefield, AttachmentType.AURA, TargetController.OPPONENT)));
@ -86,14 +86,14 @@ public class CanopyCover extends CardImpl {
}
}
class OrchardSpiritEffect extends RestrictionEffect {
class CanopyCoverEffect extends RestrictionEffect {
public OrchardSpiritEffect() {
public CanopyCoverEffect() {
super(Duration.WhileOnBattlefield);
staticText = "Enchanted creature can't be blocked except by creatures with flying or reach";
}
public OrchardSpiritEffect(final OrchardSpiritEffect effect) {
public CanopyCoverEffect(final CanopyCoverEffect effect) {
super(effect);
}
@ -102,7 +102,7 @@ class OrchardSpiritEffect extends RestrictionEffect {
Permanent equipment = game.getPermanent(source.getSourceId());
if (equipment != null && equipment.getAttachedTo() != null) {
Permanent equipped = game.getPermanent(equipment.getAttachedTo());
if (permanent.getId().equals(equipped.getId())) {
if (equipped != null && permanent.getId().equals(equipped.getId())) {
return true;
}
}
@ -115,7 +115,7 @@ class OrchardSpiritEffect extends RestrictionEffect {
}
@Override
public OrchardSpiritEffect copy() {
return new OrchardSpiritEffect(this);
public CanopyCoverEffect copy() {
return new CanopyCoverEffect(this);
}
}

View file

@ -30,6 +30,7 @@ package mage.cards.c;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.LoyaltyAbility;
@ -62,13 +63,12 @@ import mage.target.TargetPlayer;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author jeffwadsworth
*/
public class ChandraPyromaster extends CardImpl {
public ChandraPyromaster(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{2}{R}{R}");
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{R}{R}");
this.subtype.add("Chandra");
this.addAbility(new PlanswalkerEntersWithLoyalityCountersAbility(4));
@ -211,10 +211,12 @@ class ChandraPyromasterEffect2 extends OneShotEffect {
Card card = library.removeFromTop(game);
if (card != null) {
controller.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getIdName() + " <this card may be played the turn it was exiled>", source.getSourceId(), game, Zone.LIBRARY, true);
if (!card.getManaCost().isEmpty()) {
ContinuousEffect effect = new ChandraPyromasterCastFromExileEffect();
effect.setTargetPointer(new FixedTarget(card.getId()));
game.addEffect(effect, source);
}
}
return true;
}
return false;

View file

@ -28,6 +28,7 @@
package mage.cards.c;
import java.util.UUID;
import mage.MageObject;
import mage.Mana;
import mage.abilities.Ability;
@ -57,7 +58,6 @@ import mage.target.common.TargetCreatureOrPlayer;
import mage.target.common.TargetCreaturePermanent;
/**
*
* @author fireshoes
*/
public class ChandraTorchOfDefiance extends CardImpl {
@ -120,6 +120,7 @@ class ChandraTorchOfDefianceEffect extends OneShotEffect {
if (card != null) {
boolean exiledCardWasCast = false;
controller.moveCardToExileWithInfo(card, source.getSourceId(), sourceObject.getIdName(), source.getSourceId(), game, Zone.LIBRARY, true);
if (!card.getManaCost().isEmpty())
if (controller.chooseUse(Outcome.Benefit, "Cast the card? (You still pay the costs)", source, game) && !card.getCardType().contains(CardType.LAND)) {
// LinkedHashMap<UUID, ActivatedAbility> useableAbilities = controller.getUseableActivatedAbilities(card, Zone.EXILED, game);
// for (ActivatedAbility ability : useableAbilities.values()) {
@ -131,6 +132,7 @@ class ChandraTorchOfDefianceEffect extends OneShotEffect {
if (!exiledCardWasCast) {
new DamagePlayersEffect(Outcome.Damage, new StaticValue(2), TargetController.OPPONENT).apply(game, source);
}
}
return true;
}

View file

@ -0,0 +1,99 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.DealtDamageToSourceTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import java.util.UUID;
/**
*
* @author vereena42
*/
public class CoalhaulerSwine extends CardImpl {
public CoalhaulerSwine(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{R}{R}");
this.subtype.add("Boar");
this.subtype.add("Beast");
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Whenever Coalhauler Swine is dealt damage, it deals that much damage to each player.
this.addAbility(new DealtDamageToSourceTriggeredAbility(Zone.BATTLEFIELD, new CoalhaulerSwineEffect(), false));
}
public CoalhaulerSwine(final CoalhaulerSwine card) {
super(card);
}
@Override
public CoalhaulerSwine copy() {
return new CoalhaulerSwine(this);
}
class CoalhaulerSwineEffect extends OneShotEffect {
public CoalhaulerSwineEffect() {
super(Outcome.Damage);
staticText = "it deals that much damage to each player";
}
public CoalhaulerSwineEffect(final CoalhaulerSwineEffect effect) {
super(effect);
}
@Override
public CoalhaulerSwineEffect copy() {
return new CoalhaulerSwineEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
for (UUID playerId : game.getPlayers().keySet()) {
Player player = game.getPlayer(playerId);
if(player != null) {
player.damage((Integer) this.getValue("damage"), source.getSourceId(), game, false, true);
}
}
return true;
}
}
}

View file

@ -0,0 +1,112 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.d;
import mage.abilities.Ability;
import mage.abilities.common.DiesAttachedTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
import java.util.UUID;
/**
*
* @author vereena42
*/
public class DeathWatch extends CardImpl {
public DeathWatch(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{B}");
this.subtype.add("Aura");
// Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);
// When enchanted creature dies, its controller loses life equal to its power and you gain life equal to its toughness.
this.addAbility( new DiesAttachedTriggeredAbility(new DeathWatchEffect(), "enchanted creature"));
}
public DeathWatch(final DeathWatch card) {
super(card);
}
@Override
public DeathWatch copy() {
return new DeathWatch(this);
}
class DeathWatchEffect extends OneShotEffect {
public DeathWatchEffect() {
super(Outcome.LoseLife);
staticText = "that creature's controller loses life equal to its power and you gain life equal to its toughness.";
}
public DeathWatchEffect(DeathWatchEffect copy) {
super(copy);
}
@Override
public DeathWatchEffect copy() {
return new DeathWatchEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent creature = (Permanent) getValue("attachedTo");
if(creature != null){
Player opponent = game.getPlayer(creature.getOwnerId());
if (opponent != null) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
controller.gainLife(creature.getToughness().getValue(), game);
opponent.loseLife(creature.getPower().getValue(), game, false);
return true;
}
}
}
return false;
}
}
}

View file

@ -29,23 +29,28 @@ package mage.cards.d;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CopyEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SubLayer;
import mage.constants.Zone;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
import mage.players.Player;
import mage.target.common.TargetCardInGraveyard;
import mage.util.functions.ApplyToPermanent;
/**
*
@ -54,7 +59,7 @@ import mage.target.common.TargetCardInGraveyard;
public class DimirDoppelganger extends CardImpl {
public DimirDoppelganger(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("Shapeshifter");
this.power = new MageInt(0);
@ -77,14 +82,14 @@ public class DimirDoppelganger extends CardImpl {
}
}
class DimirDoppelgangerEffect extends ContinuousEffectImpl {
class DimirDoppelgangerEffect extends OneShotEffect {
public DimirDoppelgangerEffect() {
super(Duration.WhileOnBattlefield, Layer.CopyEffects_1, SubLayer.NA, Outcome.BecomeCreature);
DimirDoppelgangerEffect() {
super(Outcome.Copy);
staticText = "Exile target creature card from a graveyard. {this} becomes a copy of that card and gains this ability";
}
public DimirDoppelgangerEffect(final DimirDoppelgangerEffect effect) {
DimirDoppelgangerEffect(final DimirDoppelgangerEffect effect) {
super(effect);
}
@ -95,49 +100,48 @@ class DimirDoppelgangerEffect extends ContinuousEffectImpl {
@Override
public boolean apply(Game game, Ability source) {
Card card = game.getCard(source.getFirstTarget());
Permanent permanent = game.getPermanent(source.getSourceId());
if (card == null || permanent == null) {
return false;
}
card.moveToExile(null, "Dimir Doppelganger", source.getSourceId(), game);
Card cardToCopy = card.copy();
cardToCopy.assignNewId();
permanent.setName(cardToCopy.getName());
permanent.getPower().setValue(cardToCopy.getPower().getValue());
permanent.getToughness().setValue(cardToCopy.getToughness().getValue());
permanent.getColor(game).setColor(cardToCopy.getColor(game));
permanent.getManaCost().clear();
permanent.getManaCost().add(cardToCopy.getManaCost());
permanent.getCardType().clear();
for (CardType type : cardToCopy.getCardType()) {
if (!permanent.getCardType().contains(type)) {
permanent.getCardType().add(type);
}
}
permanent.getSubtype(game).clear();
for (String type : cardToCopy.getSubtype(game)) {
if (!permanent.getSubtype(game).contains(type)) {
permanent.getSubtype(game).add(type);
}
}
permanent.getSupertype().clear();
for (String type : cardToCopy.getSupertype()) {
if (!permanent.getSupertype().contains(type)) {
permanent.getSupertype().add(type);
}
}
permanent.removeAllAbilities(source.getSourceId(), game);
// gains ability of Dimir Doppelganger
Ability dimirDoppelgangerAbility = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DimirDoppelgangerEffect(), new ManaCostsImpl("{1}{U}{B}"));
dimirDoppelgangerAbility.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard")));
permanent.addAbility(dimirDoppelgangerAbility, source.getSourceId(), game);
for (Ability ability : cardToCopy.getAbilities()) {
if (!permanent.getAbilities().contains(ability)) {
permanent.addAbility(ability, source.getSourceId(), game);
}
Player controller = game.getPlayer(source.getControllerId());
Permanent dimirDoppelganger = game.getPermanent(source.getSourceId());
Permanent newBluePrint = null;
if (controller != null
&& dimirDoppelganger != null) {
Card copyFromCard = game.getCard(source.getFirstTarget());
if (copyFromCard != null) {
Cards cardsToExile = new CardsImpl();
cardsToExile.add(copyFromCard);
controller.moveCards(cardsToExile, Zone.EXILED, source, game);
newBluePrint = new PermanentCard((Card) copyFromCard, source.getControllerId(), game);
newBluePrint.assignNewId();
ApplyToPermanent applier = new DimirDoppelgangerApplier();
applier.apply(game, newBluePrint);
CopyEffect copyEffect = new CopyEffect(Duration.Custom, newBluePrint, dimirDoppelganger.getId());
copyEffect.newId();
copyEffect.setApplier(applier);
Ability newAbility = source.copy();
copyEffect.init(newAbility, game);
game.addEffect(copyEffect, newAbility);
}
return true;
}
return false;
}
}
class DimirDoppelgangerApplier extends ApplyToPermanent {
@Override
public Boolean apply(Game game, Permanent permanent) {
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DimirDoppelgangerEffect(), new ManaCostsImpl("{1}{U}{B}"));
ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard")));
permanent.getAbilities().add(ability);
return true;
}
@Override
public Boolean apply(Game game, MageObject mageObject) {
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DimirDoppelgangerEffect(), new ManaCostsImpl("{1}{U}{B}"));
ability.addTarget(new TargetCardInGraveyard(new FilterCreatureCard("creature card in a graveyard")));
mageObject.getAbilities().add(ability);
return true;
}
}

View file

@ -82,7 +82,7 @@ class EssenceFluxEffect extends OneShotEffect {
EssenceFluxEffect() {
super(Outcome.Benefit);
staticText = "return that card to the battlefield under its owner's control";
staticText = "return that card to the battlefield under its owner's control. If it's a Spirit, put a +1/+1 counter on it";
}
EssenceFluxEffect(final EssenceFluxEffect effect) {

View file

@ -29,24 +29,27 @@ package mage.cards.l;
import java.util.UUID;
import mage.MageInt;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.common.PutCardIntoGraveFromAnywhereAllTriggeredAbility;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CopyEffect;
import mage.abilities.keyword.HexproofAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Layer;
import mage.constants.Outcome;
import mage.constants.SetTargetPointer;
import mage.constants.SubLayer;
import mage.constants.TargetController;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.PermanentCard;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
import mage.util.functions.ApplyToPermanent;
/**
*
@ -67,7 +70,7 @@ public class LazavDimirMastermind extends CardImpl {
// Whenever a creature card is put into an opponent's graveyard from anywhere, you may have Lazav, Dimir Mastermind become a copy of that card except its name is still Lazav, Dimir Mastermind, it's legendary in addition to its other types, and it gains hexproof and this ability.
this.addAbility(new PutCardIntoGraveFromAnywhereAllTriggeredAbility(
new LazavDimirEffect(), true,
new LazavDimirMastermindEffect(), true,
new FilterCreatureCard("a creature card"),
TargetController.OPPONENT, SetTargetPointer.CARD));
}
@ -82,80 +85,73 @@ public class LazavDimirMastermind extends CardImpl {
}
}
class LazavDimirEffect extends ContinuousEffectImpl {
class LazavDimirMastermindEffect extends OneShotEffect {
protected Card cardToCopy;
public LazavDimirEffect() {
super(Duration.WhileOnBattlefield, Layer.CopyEffects_1, SubLayer.NA, Outcome.BecomeCreature);
staticText = "have {this} become a copy of that card except its name is still {this}, it's legendary in addition to its other types, and it gains hexproof and this ability";
LazavDimirMastermindEffect() {
super(Outcome.Copy);
staticText = "you may have {this} become a copy of that card except its name is still {this}, it's legendary in addition to its other types, and it gains hexproof and this ability";
}
public LazavDimirEffect(final LazavDimirEffect effect) {
LazavDimirMastermindEffect(final LazavDimirMastermindEffect effect) {
super(effect);
this.cardToCopy = effect.cardToCopy;
}
@Override
public LazavDimirEffect copy() {
return new LazavDimirEffect(this);
}
@Override
public void init(Ability source, Game game) {
super.init(source, game);
Card card = game.getCard(((FixedTarget) getTargetPointer()).getTarget());
if (card != null) {
cardToCopy = card.copy();
cardToCopy.assignNewId();
} else {
discard();
}
public LazavDimirMastermindEffect copy() {
return new LazavDimirMastermindEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
discard();
Player controller = game.getPlayer(source.getControllerId());
Permanent lazavDimirMastermind = game.getPermanent(source.getSourceId());
Permanent newBluePrint = null;
if (controller != null
&& lazavDimirMastermind != null) {
Card copyFromCard = game.getCard(((FixedTarget) getTargetPointer()).getTarget());
if (copyFromCard != null) {
newBluePrint = new PermanentCard((Card) copyFromCard, source.getControllerId(), game);
newBluePrint.assignNewId();
ApplyToPermanent applier = new LazavDimirMastermindApplier();
applier.apply(game, newBluePrint);
CopyEffect copyEffect = new CopyEffect(Duration.Custom, newBluePrint, lazavDimirMastermind.getId());
copyEffect.newId();
copyEffect.setApplier(applier);
Ability newAbility = source.copy();
copyEffect.init(newAbility, game);
game.addEffect(copyEffect, newAbility);
}
return true;
}
permanent.getPower().setValue(cardToCopy.getPower().getValue());
permanent.getToughness().setValue(cardToCopy.getToughness().getValue());
permanent.getColor(game).setColor(cardToCopy.getColor(game));
permanent.getManaCost().clear();
permanent.getManaCost().add(cardToCopy.getManaCost());
permanent.getCardType().clear();
for (CardType type : cardToCopy.getCardType()) {
if (!permanent.getCardType().contains(type)) {
permanent.getCardType().add(type);
return false;
}
}
permanent.getSubtype(game).clear();
for (String type : cardToCopy.getSubtype(game)) {
if (!permanent.getSubtype(game).contains(type)) {
permanent.getSubtype(game).add(type);
}
}
permanent.getSupertype().clear();
permanent.getSupertype().add("Legendary");
for (String type : cardToCopy.getSupertype()) {
if (!permanent.getSupertype().contains(type)) {
permanent.getSupertype().add(type);
}
}
permanent.removeAllAbilities(source.getSourceId(), game);
permanent.addAbility(HexproofAbility.getInstance(), source.getSourceId(), game);
permanent.addAbility(new PutCardIntoGraveFromAnywhereAllTriggeredAbility(
new LazavDimirEffect(), true,
new FilterCreatureCard("a creature card"),
TargetController.OPPONENT, SetTargetPointer.CARD), source.getSourceId(), game);
}
for (Ability ability : cardToCopy.getAbilities()) {
if (!permanent.getAbilities().contains(ability)) {
permanent.addAbility(ability, source.getSourceId(), game);
}
class LazavDimirMastermindApplier extends ApplyToPermanent {
@Override
public Boolean apply(Game game, Permanent permanent) {
Ability ability = new PutCardIntoGraveFromAnywhereAllTriggeredAbility(
new LazavDimirMastermindEffect(), true,
new FilterCreatureCard("a creature card"),
TargetController.OPPONENT, SetTargetPointer.CARD);
permanent.getAbilities().add(ability);
permanent.setName("Lazav, Dimir Mastermind");
permanent.getSupertype().add("Legendary");
permanent.getAbilities().add(HexproofAbility.getInstance());
return true;
}
@Override
public Boolean apply(Game game, MageObject mageObject) {
Ability ability = new PutCardIntoGraveFromAnywhereAllTriggeredAbility(
new LazavDimirMastermindEffect(), true,
new FilterCreatureCard("a creature card"),
TargetController.OPPONENT, SetTargetPointer.CARD);
mageObject.getAbilities().add(ability);
mageObject.setName("Lazav, Dimir Mastermind");
mageObject.getSupertype().add("Legendary");
mageObject.getAbilities().add(HexproofAbility.getInstance());
return true;
}
}

View file

@ -0,0 +1,133 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.m;
import java.util.HashMap;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.TrampleAbility;
import mage.abilities.keyword.HasteAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author jeffwadsworth
*/
public class MenacingOgre extends CardImpl {
public MenacingOgre(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
this.subtype.add("Ogre");
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Trample
this.addAbility(TrampleAbility.getInstance());
// Haste
this.addAbility(HasteAbility.getInstance());
// When Menacing Ogre enters the battlefield, each player secretly chooses a number. Then those numbers are revealed. Each player with the highest number loses that much life. If you are one of those players, put two +1/+1 counters on Menacing Ogre.
this.addAbility(new EntersBattlefieldTriggeredAbility(new MenacingOgreEffect(), false));
}
public MenacingOgre(final MenacingOgre card) {
super(card);
}
@Override
public MenacingOgre copy() {
return new MenacingOgre(this);
}
}
class MenacingOgreEffect extends OneShotEffect {
public MenacingOgreEffect() {
super(Outcome.Detriment);
this.staticText = "each player secretly chooses a number. Then those numbers are revealed. Each player with the highest number loses that much life. If you are one of those players, put two +1/+1 counters on {this}";
}
public MenacingOgreEffect(final MenacingOgreEffect effect) {
super(effect);
}
@Override
public MenacingOgreEffect copy() {
return new MenacingOgreEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
int highestNumber = 0;
int number = 0;
Permanent menacingOgre = game.getPermanent(source.getSourceId());
String message = "Choose a number.";
HashMap<Player, Integer> numberChosen = new HashMap<>();
//players choose numbers
for (Player player : game.getPlayers().values()) {
if (player != null) {
number = player.getAmount(0, 1000, message, game);
numberChosen.put(player, number);
}
}
//get highest number
for (Player player : numberChosen.keySet()) {
if (highestNumber < numberChosen.get(player)) {
highestNumber = numberChosen.get(player);
}
}
//reveal numbers to players and follow through with effect
for (Player player : game.getPlayers().values()) {
if (player != null) {
game.informPlayers(player.getLogName() + " chose number " + numberChosen.get(player));
if (numberChosen.get(player) >= highestNumber) {
player.loseLife(highestNumber, game, false);
if (player.getId() == source.getControllerId()
&& menacingOgre != null) {
menacingOgre.addCounters(CounterType.P1P1.createInstance(2), source, game);
}
}
}
}
return true;
}
}

View file

@ -0,0 +1,64 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.m;
import mage.MageInt;
import mage.abilities.keyword.ChangelingAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import java.util.UUID;
/**
*
* @author vereena42
*/
public class MistformUltimus extends CardImpl {
public MistformUltimus(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}");
this.supertype.add("Legendary");
this.subtype.add("Illusion");
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Mistform Ultimus is every creature type.
this.subtype.add(ChangelingAbility.ALL_CREATURE_TYPE); // "All Creature Type"
}
public MistformUltimus(final MistformUltimus card) {
super(card);
}
@Override
public MistformUltimus copy() {
return new MistformUltimus(this);
}
}

View file

@ -31,12 +31,15 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.ContinuousEffectImpl;
import mage.abilities.effects.Effect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
@ -119,17 +122,11 @@ public class NecroticOoze extends CardImpl {
@Override
public Set<UUID> isDependentTo(List<ContinuousEffect> allEffectsInLayer) {
// the dependent classes needs to be an enclosed class for dependent check of continuous effects
Set<UUID> dependentTo = null;
for (ContinuousEffect effect : allEffectsInLayer) {
// http://www.mtgsalvation.com/forums/magic-fundamentals/magic-rulings/magic-rulings-archives/285211-yixlid-jailer-vs-necrotic-ooze
if (YixlidJailer.class.equals(effect.getClass().getEnclosingClass())) {
if (dependentTo == null) {
dependentTo = new HashSet<>();
}
dependentTo.add(effect.getId());
}
}
return dependentTo;
return allEffectsInLayer.stream()
.filter(effect -> YixlidJailer.class.equals(effect.getClass().getEnclosingClass()))
.map(Effect::getId)
.collect(Collectors.toSet());
}
}

View file

@ -25,19 +25,17 @@
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.o;
import java.util.UUID;
import mage.constants.CardType;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.common.LandfallAbility;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.counters.CounterType;
import mage.target.TargetPlayer;
@ -47,21 +45,23 @@ import mage.target.TargetPlayer;
*/
public class ObNixilisTheFallen extends CardImpl {
public ObNixilisTheFallen (UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}{B}");
public ObNixilisTheFallen(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}{B}");
this.supertype.add("Legendary");
this.subtype.add("Demon");
this.power = new MageInt(3);
this.toughness = new MageInt(3);
// Landfall - Whenever a land enters the battlefield under your control, you may have target player lose 3 life.
// If you do, put three +1/+1 counters on Ob Nixilis, the Fallen.
Ability ability = new LandfallAbility(new LoseLifeTargetEffect(3), true);
ability.addEffect(new AddCountersSourceEffect(CounterType.P1P1.createInstance(3)));
ability.addTarget(new TargetPlayer());
this.addAbility(ability);
}
public ObNixilisTheFallen (final ObNixilisTheFallen card) {
public ObNixilisTheFallen(final ObNixilisTheFallen card) {
super(card);
}

View file

@ -52,8 +52,7 @@ import mage.target.targetpointer.FixedTarget;
public class PhyrexianTyranny extends CardImpl {
public PhyrexianTyranny(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{U}{B}{R}");
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{U}{B}{R}");
// Whenever a player draws a card, that player loses 2 life unless he or she pays {2}.
this.addAbility(new PhyrexianTyrannyTriggeredAbility());
@ -127,7 +126,7 @@ class PhyrexianTyrannyEffect extends OneShotEffect {
if (player != null) {
Cost cost = new GenericManaCost(2);
if (!cost.pay(source, game, player.getId(), player.getId(), false, null)) {
player.damage(2, source.getSourceId(), game, false, true);
player.loseLife(2, game, false);
}
return true;
}

View file

@ -96,7 +96,6 @@ public class Homelands extends ExpansionSet {
cards.add(new SetCardInfo("Clockwork Gnomes", 127, Rarity.COMMON, mage.cards.c.ClockworkGnomes.class));
cards.add(new SetCardInfo("Coral Reef", 29, Rarity.COMMON, mage.cards.c.CoralReef.class));
cards.add(new SetCardInfo("Dark Maze", 31, Rarity.COMMON, mage.cards.d.DarkMaze.class, new CardGraphicInfo(null, true)));
cards.add(new SetCardInfo("Dark Maze", 31, Rarity.COMMON, mage.cards.d.DarkMaze.class, new CardGraphicInfo(null, true)));
cards.add(new SetCardInfo("Death Speakers", 109, Rarity.UNCOMMON, mage.cards.d.DeathSpeakers.class));
cards.add(new SetCardInfo("Didgeridoo", 130, Rarity.RARE, mage.cards.d.Didgeridoo.class));
cards.add(new SetCardInfo("Drudge Spell", 6, Rarity.UNCOMMON, mage.cards.d.DrudgeSpell.class));

View file

@ -127,6 +127,7 @@ public class Legions extends ExpansionSet {
cards.add(new SetCardInfo("Magma Sliver", 107, Rarity.RARE, mage.cards.m.MagmaSliver.class));
cards.add(new SetCardInfo("Merchant of Secrets", 44, Rarity.COMMON, mage.cards.m.MerchantOfSecrets.class));
cards.add(new SetCardInfo("Mistform Sliver", 46, Rarity.COMMON, mage.cards.m.MistformSliver.class));
cards.add(new SetCardInfo("Mistform Ultimus", 47, Rarity.RARE, mage.cards.m.MistformUltimus.class));
cards.add(new SetCardInfo("Nantuko Vigilante", 132, Rarity.COMMON, mage.cards.n.NantukoVigilante.class));
cards.add(new SetCardInfo("Needleshot Gourna", 133, Rarity.COMMON, mage.cards.n.NeedleshotGourna.class));
cards.add(new SetCardInfo("Noxious Ghoul", 77, Rarity.UNCOMMON, mage.cards.n.NoxiousGhoul.class));

View file

@ -216,10 +216,9 @@ public class MastersEditionIV extends ExpansionSet {
cards.add(new SetCardInfo("Regrowth", 163, Rarity.RARE, mage.cards.r.Regrowth.class));
cards.add(new SetCardInfo("Righteous Charge", 23, Rarity.COMMON, mage.cards.r.RighteousCharge.class));
cards.add(new SetCardInfo("Ring of Renewal", 224, Rarity.RARE, mage.cards.r.RingOfRenewal.class));
cards.add(new SetCardInfo("Rock Hydra", 133, Rarity.RARE, mage.cards.r.RockHydra.class));
cards.add(new SetCardInfo("Rockslide Ambush", 134, Rarity.COMMON, mage.cards.r.RockslideAmbush.class));
cards.add(new SetCardInfo("Roc of Kher Ridges", 132, Rarity.UNCOMMON, mage.cards.r.RocOfKherRidges.class));
cards.add(new SetCardInfo("Rock Hydra", 133, Rarity.RARE, mage.cards.r.RockHydra.class));
cards.add(new SetCardInfo("Rockslide Ambush", 134, Rarity.COMMON, mage.cards.r.RockslideAmbush.class));
cards.add(new SetCardInfo("Sandstorm", 164, Rarity.COMMON, mage.cards.s.Sandstorm.class));
cards.add(new SetCardInfo("Savannah", 250, Rarity.RARE, mage.cards.s.Savannah.class));
cards.add(new SetCardInfo("Savannah Lions", 24, Rarity.UNCOMMON, mage.cards.s.SavannahLions.class));

View file

@ -55,7 +55,6 @@ public class Onslaught extends ExpansionSet {
cards.add(new SetCardInfo("Blistering Firecat", 189, Rarity.RARE, mage.cards.b.BlisteringFirecat.class));
cards.add(new SetCardInfo("Bloodline Shaman", 249, Rarity.UNCOMMON, mage.cards.b.BloodlineShaman.class));
cards.add(new SetCardInfo("Bloodstained Mire", 313, Rarity.RARE, mage.cards.b.BloodstainedMire.class, new CardGraphicInfo(new ObjectColor("RB"), null,false)));
cards.add(new SetCardInfo("Bloodline Shaman", 249, Rarity.UNCOMMON, mage.cards.b.BloodlineShaman.class));
cards.add(new SetCardInfo("Boneknitter", 128, Rarity.UNCOMMON, mage.cards.b.Boneknitter.class));
cards.add(new SetCardInfo("Brightstone Ritual", 191, Rarity.COMMON, mage.cards.b.BrightstoneRitual.class));
cards.add(new SetCardInfo("Broodhatch Nantuko", 250, Rarity.UNCOMMON, mage.cards.b.BroodhatchNantuko.class));
@ -182,6 +181,7 @@ public class Onslaught extends ExpansionSet {
cards.add(new SetCardInfo("Lonely Sandbar", 320, Rarity.COMMON, mage.cards.l.LonelySandbar.class));
cards.add(new SetCardInfo("Mage's Guile", 91, Rarity.COMMON, mage.cards.m.MagesGuile.class));
cards.add(new SetCardInfo("Mana Echoes", 218, Rarity.RARE, mage.cards.m.ManaEchoes.class));
cards.add(new SetCardInfo("Menacing Ogre", 219, Rarity.RARE, mage.cards.m.MenacingOgre.class));
cards.add(new SetCardInfo("Misery Charm", 158, Rarity.COMMON, mage.cards.m.MiseryCharm.class));
cards.add(new SetCardInfo("Mistform Mutant", 95, Rarity.UNCOMMON, mage.cards.m.MistformMutant.class));
cards.add(new SetCardInfo("Mobilization", 44, Rarity.RARE, mage.cards.m.Mobilization.class));
@ -303,18 +303,12 @@ public class Onslaught extends ExpansionSet {
cards.add(new SetCardInfo("Wheel and Deal", 121, Rarity.RARE, mage.cards.w.WheelAndDeal.class));
cards.add(new SetCardInfo("Whipcorder", 60, Rarity.UNCOMMON, mage.cards.w.Whipcorder.class));
cards.add(new SetCardInfo("Windswept Heath", 328, Rarity.RARE, mage.cards.w.WindsweptHeath.class, new CardGraphicInfo(new ObjectColor("GW"), null, false)));
cards.add(new SetCardInfo("Bloodline Shaman", 249, Rarity.UNCOMMON, mage.cards.b.BloodlineShaman.class));
cards.add(new SetCardInfo("Trade Secrets", 118, Rarity.RARE, mage.cards.t.TradeSecrets.class));
cards.add(new SetCardInfo("Venomspout Brackus", 295, Rarity.UNCOMMON, mage.cards.v.VenomspoutBrackus.class));
cards.add(new SetCardInfo("Wirewood Elf", 301, Rarity.COMMON, mage.cards.w.WirewoodElf.class));
cards.add(new SetCardInfo("Wirewood Herald", 302, Rarity.COMMON, mage.cards.w.WirewoodHerald.class));
cards.add(new SetCardInfo("Wirewood Lodge", 329, Rarity.RARE, mage.cards.w.WirewoodLodge.class));
cards.add(new SetCardInfo("Wirewood Pride", 303, Rarity.COMMON, mage.cards.w.WirewoodPride.class));
cards.add(new SetCardInfo("Wirewood Savage", 304, Rarity.COMMON, mage.cards.w.WirewoodSavage.class));
cards.add(new SetCardInfo("Wooded Foothills", 330, Rarity.RARE, mage.cards.w.WoodedFoothills.class, new CardGraphicInfo(new ObjectColor("RG"), null, false)));
cards.add(new SetCardInfo("Bloodline Shaman", 249, Rarity.UNCOMMON, mage.cards.b.BloodlineShaman.class));
cards.add(new SetCardInfo("Trade Secrets", 118, Rarity.RARE, mage.cards.t.TradeSecrets.class));
cards.add(new SetCardInfo("Venomspout Brackus", 295, Rarity.UNCOMMON, mage.cards.v.VenomspoutBrackus.class));
cards.add(new SetCardInfo("Words of War", 244, Rarity.RARE, mage.cards.w.WordsOfWar.class));
cards.add(new SetCardInfo("Words of Wind", 122, Rarity.RARE, mage.cards.w.WordsOfWind.class));
cards.add(new SetCardInfo("Words of Worship", 61, Rarity.RARE, mage.cards.w.WordsOfWorship.class));

View file

@ -139,6 +139,7 @@ public class Planechase extends ExpansionSet {
cards.add(new SetCardInfo("Mage Slayer", 91, Rarity.UNCOMMON, mage.cards.m.MageSlayer.class));
cards.add(new SetCardInfo("Mask of Memory", 119, Rarity.UNCOMMON, mage.cards.m.MaskOfMemory.class));
cards.add(new SetCardInfo("Master of Etherium", 11, Rarity.RARE, mage.cards.m.MasterOfEtherium.class));
cards.add(new SetCardInfo("Menacing Ogre", 59, Rarity.RARE, mage.cards.m.MenacingOgre.class));
cards.add(new SetCardInfo("Mountain", 156, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(null, true)));
cards.add(new SetCardInfo("Mountain", 157, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(null, true)));
cards.add(new SetCardInfo("Mountain", 158, Rarity.LAND, mage.cards.basiclands.Mountain.class, new CardGraphicInfo(null, true)));

View file

@ -89,6 +89,7 @@ public class RavnicaCityOfGuilds extends ExpansionSet {
cards.add(new SetCardInfo("Clinging Darkness", 80, Rarity.COMMON, mage.cards.c.ClingingDarkness.class));
cards.add(new SetCardInfo("Cloudstone Curio", 257, Rarity.RARE, mage.cards.c.CloudstoneCurio.class));
cards.add(new SetCardInfo("Clutch of the Undercity", 197, Rarity.UNCOMMON, mage.cards.c.ClutchOfTheUndercity.class));
cards.add(new SetCardInfo("Coalhauler Swine", 119, Rarity.COMMON, mage.cards.c.CoalhaulerSwine.class));
cards.add(new SetCardInfo("Compulsive Research", 40, Rarity.COMMON, mage.cards.c.CompulsiveResearch.class));
cards.add(new SetCardInfo("Concerted Effort", 8, Rarity.RARE, mage.cards.c.ConcertedEffort.class));
cards.add(new SetCardInfo("Conclave Equenaut", 9, Rarity.COMMON, mage.cards.c.ConclaveEquenaut.class));

View file

@ -231,10 +231,9 @@ public class RevisedEdition extends ExpansionSet {
cards.add(new SetCardInfo("Resurrection", 218, Rarity.UNCOMMON, mage.cards.r.Resurrection.class));
cards.add(new SetCardInfo("Reverse Damage", 219, Rarity.RARE, mage.cards.r.ReverseDamage.class));
cards.add(new SetCardInfo("Righteousness", 221, Rarity.RARE, mage.cards.r.Righteousness.class));
cards.add(new SetCardInfo("Rock Hydra", 172, Rarity.RARE, mage.cards.r.RockHydra.class));
cards.add(new SetCardInfo("Rocket Launcher", 272, Rarity.RARE, mage.cards.r.RocketLauncher.class));
cards.add(new SetCardInfo("Roc of Kher Ridges", 171, Rarity.RARE, mage.cards.r.RocOfKherRidges.class));
cards.add(new SetCardInfo("Rock Hydra", 172, Rarity.RARE, mage.cards.r.RockHydra.class));
cards.add(new SetCardInfo("Rocket Launcher", 272, Rarity.RARE, mage.cards.r.RocketLauncher.class));
cards.add(new SetCardInfo("Rod of Ruin", 273, Rarity.UNCOMMON, mage.cards.r.RodOfRuin.class));
cards.add(new SetCardInfo("Royal Assassin", 33, Rarity.RARE, mage.cards.r.RoyalAssassin.class));
cards.add(new SetCardInfo("Sacrifice", 34, Rarity.UNCOMMON, mage.cards.s.Sacrifice.class));

View file

@ -105,6 +105,7 @@ public class TimeSpiralTimeshifted extends ExpansionSet {
cards.add(new SetCardInfo("Merieke Ri Berit", 95, Rarity.SPECIAL, mage.cards.m.MeriekeRiBerit.class));
cards.add(new SetCardInfo("Mindless Automaton", 111, Rarity.SPECIAL, mage.cards.m.MindlessAutomaton.class));
cards.add(new SetCardInfo("Mirari", 112, Rarity.SPECIAL, mage.cards.m.Mirari.class));
cards.add(new SetCardInfo("Mistform Ultimus", 26, Rarity.SPECIAL, mage.cards.m.MistformUltimus.class));
cards.add(new SetCardInfo("Moorish Cavalry", 11, Rarity.COMMON, mage.cards.m.MoorishCavalry.class));
cards.add(new SetCardInfo("Mystic Enforcer", 96, Rarity.SPECIAL, mage.cards.m.MysticEnforcer.class));
cards.add(new SetCardInfo("Mystic Snake", 97, Rarity.COMMON, mage.cards.m.MysticSnake.class));

View file

@ -68,6 +68,7 @@ public class Visions extends ExpansionSet {
cards.add(new SetCardInfo("Creeping Mold", 53, Rarity.UNCOMMON, mage.cards.c.CreepingMold.class));
cards.add(new SetCardInfo("Crypt Rats", 5, Rarity.COMMON, mage.cards.c.CryptRats.class));
cards.add(new SetCardInfo("Daraja Griffin", 102, Rarity.UNCOMMON, mage.cards.d.DarajaGriffin.class));
cards.add(new SetCardInfo("Death Watch", 7, Rarity.COMMON, mage.cards.d.DeathWatch.class));
cards.add(new SetCardInfo("Desertion", 30, Rarity.RARE, mage.cards.d.Desertion.class));
cards.add(new SetCardInfo("Diamond Kaleidoscope", 143, Rarity.RARE, mage.cards.d.DiamondKaleidoscope.class));
cards.add(new SetCardInfo("Dormant Volcano", 161, Rarity.UNCOMMON, mage.cards.d.DormantVolcano.class));

View file

@ -92,12 +92,12 @@ public class CrewTest extends CardTestPlayerBase {
setChoice(playerA, "Speedway Fanatic");
// Return all creatures to there owners hands
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Evacuation");
castSpell(1, PhaseStep.BEGIN_COMBAT, playerA, "Evacuation");
// (Re)Cast Smugglers Copter
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Smuggler's Copter");
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Smuggler's Copter");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
setStopAt(1, PhaseStep.END_TURN);
execute();
// Only crewed vehicles have card type creature

View file

@ -0,0 +1,98 @@
/*
* 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.replacement;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author LevelX2
*/
public class DamageEffectsTest extends CardTestPlayerBase {
/**
* Just encountered another bug. With Wurmcoil Engine out and with a
* Gratuitous Violence on the field, I only gained 6 life on blocking rather
* than 12 life.
*/
@Test
public void testDamageIsDoubledWithLifelink() {
// Landfall - Whenever a land enters the battlefield under your control, you may have target player lose 3 life.
// If you do, put three +1/+1 counters on Ob Nixilis, the Fallen.
addCard(Zone.BATTLEFIELD, playerB, "Ob Nixilis, the Fallen");
addCard(Zone.HAND, playerB, "Mountain");
// Deathtouch, lifelink
// When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink.
addCard(Zone.BATTLEFIELD, playerA, "Wurmcoil Engine");
// If a creature you control would deal damage to a creature or player, it deals double that damage to that creature or player instead.
addCard(Zone.BATTLEFIELD, playerA, "Gratuitous Violence");
playLand(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Mountain");
setChoice(playerB, "Yes");
addTarget(playerB, playerA);
attack(2, playerB, "Ob Nixilis, the Fallen");
block(2, playerA, "Wurmcoil Engine", "Ob Nixilis, the Fallen");
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertGraveyardCount(playerB, "Ob Nixilis, the Fallen", 1);
assertGraveyardCount(playerA, "Wurmcoil Engine", 1);
assertPermanentCount(playerA, "Wurm", 2);
assertLife(playerB, 20);
assertLife(playerA, 29); // -2 from Ob Nixilis + 12 from double damage with lifelink from Wurmcoil Engine
}
@Test
public void testDamageToPlayer() {
// Deathtouch, lifelink
// When Wurmcoil Engine dies, create a 3/3 colorless Wurm artifact creature token with deathtouch and a 3/3 colorless Wurm artifact creature token with lifelink.
addCard(Zone.BATTLEFIELD, playerA, "Wurmcoil Engine");
// If a creature you control would deal damage to a creature or player, it deals double that damage to that creature or player instead.
addCard(Zone.BATTLEFIELD, playerA, "Gratuitous Violence");
attack(1, playerA, "Wurmcoil Engine");
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertPermanentCount(playerA, "Wurmcoil Engine", 1);
assertLife(playerB, 8);
assertLife(playerA, 32);
}
}

View file

@ -44,6 +44,29 @@ public class ChandraPyromasterTest extends CardTestPlayerBase {
}
@Test
public void testAbility2AncestralVision() {
addCard(Zone.BATTLEFIELD, playerA, "Chandra, Pyromaster");
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 2);
skipInitShuffling();
addCard(Zone.LIBRARY, playerA, "Ancestral Vision");
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion", 1);
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "+0: Exile the top card of your library. You may play it this turn.");
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertLife(playerA, 20);
assertLife(playerB, 20);
assertPermanentCount(playerA, "Chandra, Pyromaster", 1);
assertGraveyardCount(playerA, "Ancestral Vision", 0);
assertExileCount(playerA, "Ancestral Vision", 1);
}
@Test
public void testAbility2CastCardFromExileWithOverlaod() {

View file

@ -28,7 +28,6 @@ import org.mage.test.player.TestPlayer;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilenameFilter;
import java.lang.reflect.InvocationTargetException;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@ -297,7 +296,8 @@ public abstract class MageTestBase {
}
protected Player createPlayer(String name, String playerType) {
return PlayerFactory.getInstance().createPlayer(playerType, name, RangeOfInfluence.ALL, 5);
Optional<Player> playerOptional = PlayerFactory.getInstance().createPlayer(playerType, name, RangeOfInfluence.ALL, 5);
return playerOptional.orElseThrow(() -> new NullPointerException("PlayerFactory error - player is not created"));
}
protected Player createRandomPlayer(String name) {

View file

@ -860,6 +860,25 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
Assert.assertEquals("(Exile) Card counts for player " + owner.getName() + " is not equal.", count, actualCount);
}
/**
* Assert card count in player's exile.
*
* @param owner {@link Player} who's exile should be counted.
* @param cardName Name of the cards that should be counted.
* @param count Expected count.
*/
public void assertExileCount(Player owner, String cardName, int count) throws AssertionError {
int actualCount = 0;
for (ExileZone exile : currentGame.getExile().getExileZones()) {
for (Card card : exile.getCards(currentGame)) {
if (card.getOwnerId().equals(owner.getId()) && card.getName().equals(cardName)) {
actualCount++;
}
}
}
Assert.assertEquals("(Exile " + owner.getName() + ") Card counts are not equal (" + cardName + ')', count, actualCount);
}
/**
* Assert card count in player's graveyard.
*

View file

@ -88,11 +88,7 @@ public class AttacksAttachedTriggeredAbility extends TriggeredAbilityImpl {
@Override
public String getRule() {
StringBuilder sb = new StringBuilder("Whenever ");
if (attachmentType.equals(AttachmentType.EQUIPMENT)) {
sb.append("equipped");
} else {
sb.append("enchanted");
}
sb.append(attachmentType.verb().toLowerCase());
return sb.append(" creature attacks, ").append(super.getRule()).toString();
}
}

View file

@ -23,8 +23,7 @@ public class AfterUpkeepStepCondtion implements Condition {
@Override
public boolean apply(Game game, Ability source) {
return !(game.getStep().getType() == PhaseStep.UNTAP
|| game.getStep().getType() == PhaseStep.UPKEEP);
return game.getStep().getType().isAfter(PhaseStep.UPKEEP);
}
@Override

View file

@ -26,10 +26,7 @@ public class BeforeBlockersAreDeclaredCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
return !(game.getStep().getType().equals(PhaseStep.DECLARE_BLOCKERS)
|| game.getStep().getType().equals(PhaseStep.FIRST_COMBAT_DAMAGE)
|| game.getStep().getType().equals(PhaseStep.COMBAT_DAMAGE)
|| game.getStep().getType().equals(PhaseStep.END_COMBAT));
return game.getStep().getType().isBefore(PhaseStep.DECLARE_BLOCKERS);
}
@Override

View file

@ -27,11 +27,6 @@
*/
package mage.abilities.costs.mana;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.costs.Cost;
@ -45,13 +40,15 @@ import mage.players.Player;
import mage.target.Targets;
import mage.util.ManaUtil;
import java.util.*;
/**
* @author BetaSteward_at_googlemail.com
* @param <T>
*/
public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements ManaCosts<T> {
protected UUID id;
protected final UUID id;
protected String text = null;
private static Map<String, ManaCosts> costs = new HashMap<>();
@ -323,7 +320,7 @@ public class ManaCostsImpl<T extends ManaCost> extends ArrayList<T> implements M
if (mana == null || mana.isEmpty()) {
return;
}
String[] symbols = mana.split("^\\{|\\}\\{|\\}$");
String[] symbols = mana.split("^\\{|}\\{|}$");
int modifierForX = 0;
for (String symbol : symbols) {
if (!symbol.isEmpty()) {

View file

@ -28,6 +28,7 @@
package mage.abilities.decorator;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.condition.Condition;
import mage.abilities.effects.AsThoughEffect;
@ -36,7 +37,6 @@ import mage.constants.Duration;
import mage.game.Game;
/**
*
* @author LevelX2
*/
public class ConditionalAsThoughEffect extends AsThoughEffectImpl {
@ -59,9 +59,9 @@ public class ConditionalAsThoughEffect extends AsThoughEffectImpl {
public ConditionalAsThoughEffect(final ConditionalAsThoughEffect effect) {
super(effect);
this.effect = (AsThoughEffect) effect.effect.copy();
this.effect = effect.effect.copy();
if (effect.otherwiseEffect != null) {
this.otherwiseEffect = (AsThoughEffect) effect.otherwiseEffect.copy();
this.otherwiseEffect = effect.otherwiseEffect.copy();
}
this.condition = effect.condition;
this.conditionState = effect.conditionState;

View file

@ -28,6 +28,7 @@
package mage.abilities.effects;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.SpellAbility;
@ -55,10 +56,10 @@ import mage.target.common.TargetCardInGraveyard;
* was not cast (so from Zone != Hand), this effect gets the target to whitch to
* attach it and adds the Aura the the battlefield and attachs it to the target.
* The "attachTo:" value in game state has to be set therefore.
*
* <p>
* If no "attachTo:" value is defined, the controlling player has to chose the
* aura target.
*
* <p>
* This effect is automatically added to ContinuousEffects at the start of a
* game
*
@ -204,8 +205,8 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (((ZoneChangeEvent) event).getToZone().equals(Zone.BATTLEFIELD)
&& !(((ZoneChangeEvent) event).getFromZone().equals(Zone.STACK))) {
if (((ZoneChangeEvent) event).getToZone() == Zone.BATTLEFIELD
&& (((ZoneChangeEvent) event).getFromZone() != Zone.STACK)) {
Card card = game.getCard(event.getTargetId());
if (card != null && (card.getCardType().contains(CardType.ENCHANTMENT) && card.hasSubtype("Aura", game)
|| // in case of transformable enchantments

View file

@ -33,6 +33,8 @@ import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import mage.MageObjectReference;
import mage.abilities.Ability;
import mage.abilities.MageSingleton;
@ -51,7 +53,6 @@ import mage.game.Game;
import mage.players.Player;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public abstract class ContinuousEffectImpl extends EffectImpl implements ContinuousEffect {
@ -171,9 +172,9 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
public void init(Ability source, Game game) {
targetPointer.init(game, source);
//20100716 - 611.2c
if (AbilityType.ACTIVATED.equals(source.getAbilityType())
|| AbilityType.SPELL.equals(source.getAbilityType())
|| AbilityType.TRIGGERED.equals(source.getAbilityType())) {
if (AbilityType.ACTIVATED == source.getAbilityType()
|| AbilityType.SPELL == source.getAbilityType()
|| AbilityType.TRIGGERED == source.getAbilityType()) {
if (layer != null) {
switch (layer) {
case CopyEffects_1:
@ -197,7 +198,7 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
@Override
public boolean isInactive(Ability source, Game game) {
if (duration.equals(Duration.UntilYourNextTurn)) {
if (duration == Duration.UntilYourNextTurn) {
Player player = game.getPlayer(startingControllerId);
if (player != null) {
if (player.isInGame()) {
@ -271,19 +272,13 @@ public abstract class ContinuousEffectImpl extends EffectImpl implements Continu
@Override
public Set<UUID> isDependentTo(List<ContinuousEffect> allEffectsInLayer) {
if (dependendToType != null) {
// the dependent classes needs to be an enclosed class for dependent check of continuous effects
Set<UUID> dependentTo = null;
for (ContinuousEffect effect : allEffectsInLayer) {
if (effect.getDependencyTypes().contains(dependendToType)) {
if (dependentTo == null) {
dependentTo = new HashSet<>();
return allEffectsInLayer.stream()
.filter(effect -> effect.getDependencyTypes().contains(dependendToType))
.map(Effect::getId)
.collect(Collectors.toSet());
}
dependentTo.add(effect.getId());
}
}
return dependentTo;
}
return null;
return new HashSet<>();
}
@Override

View file

@ -27,40 +27,12 @@
*/
package mage.abilities.effects;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.MageSingleton;
import mage.abilities.SpellAbility;
import mage.abilities.StaticAbility;
import mage.abilities.*;
import mage.abilities.keyword.SpliceOntoArcaneAbility;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.AbilityType;
import mage.constants.AsThoughEffectType;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.EffectType;
import mage.constants.Layer;
import mage.constants.ManaType;
import mage.constants.Outcome;
import mage.constants.SpellAbilityType;
import mage.constants.SubLayer;
import mage.constants.Zone;
import mage.constants.*;
import mage.filter.FilterCard;
import mage.filter.predicate.Predicate;
import mage.filter.predicate.Predicates;
@ -77,8 +49,11 @@ import mage.players.Player;
import mage.target.common.TargetCardInHand;
import org.apache.log4j.Logger;
import java.io.Serializable;
import java.util.*;
import java.util.Map.Entry;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class ContinuousEffects implements Serializable {
@ -218,7 +193,7 @@ public class ContinuousEffects implements Serializable {
case WhileOnStack:
case WhileInGraveyard:
HashSet<Ability> abilities = layeredEffects.getAbility(effect.getId());
if (abilities != null) {
if (!abilities.isEmpty()) {
for (Ability ability : abilities) {
// If e.g. triggerd abilities (non static) created the effect, the ability must not be in usable zone (e.g. Unearth giving Haste effect)
if (!(ability instanceof StaticAbility) || ability.isInUseableZone(game, null, null)) {
@ -358,7 +333,6 @@ public class ContinuousEffects implements Serializable {
}
/**
*
* @param event
* @param game
* @return a list of all {@link ReplacementEffect} that apply to the current
@ -717,7 +691,8 @@ public class ContinuousEffects implements Serializable {
spliceAbilities.remove(selectedAbility);
}
}
} while (!spliceAbilities.isEmpty() && controller.chooseUse(Outcome.Benefit, "Splice another card?", abilityToModify, game));
}
while (!spliceAbilities.isEmpty() && controller.chooseUse(Outcome.Benefit, "Splice another card?", abilityToModify, game));
controller.revealCards("Spliced cards", cardsToReveal, game);
}
}
@ -777,7 +752,7 @@ public class ContinuousEffects implements Serializable {
do {
HashMap<ReplacementEffect, HashSet<Ability>> rEffects = getApplicableReplacementEffects(event, game);
// Remove all consumed effects (ability dependant)
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext();) {
for (Iterator<ReplacementEffect> it1 = rEffects.keySet().iterator(); it1.hasNext(); ) {
ReplacementEffect entry = it1.next();
if (consumed.containsKey(entry.getId())) {
HashSet<UUID> consumedAbilitiesIds = consumed.get(entry.getId());
@ -865,9 +840,8 @@ public class ContinuousEffects implements Serializable {
if (consumed.containsKey(rEffect.getId())) {
HashSet<UUID> set = consumed.get(rEffect.getId());
if (rAbility != null) {
if (!set.contains(rAbility.getId())) {
set.add(rAbility.getId());
}
}
} else {
HashSet<UUID> set = new HashSet<>();
@ -936,7 +910,7 @@ public class ContinuousEffects implements Serializable {
for (ContinuousEffect effect : layer) {
if (activeLayerEffects.contains(effect) && !appliedEffects.contains(effect.getId())) { // Effect does still exist and was not applied yet
Set<UUID> dependentTo = effect.isDependentTo(layer);
if (dependentTo != null && !appliedEffects.containsAll(dependentTo)) {
if (!appliedEffects.containsAll(dependentTo)) {
waitingEffects.put(effect, dependentTo);
continue;
}
@ -959,7 +933,7 @@ public class ContinuousEffects implements Serializable {
if (!waitingEffects.isEmpty()) {
// check if waiting effects can be applied now
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext();) {
for (Iterator<Map.Entry<ContinuousEffect, Set<UUID>>> iterator = waitingEffects.entrySet().iterator(); iterator.hasNext(); ) {
Map.Entry<ContinuousEffect, Set<UUID>> entry = iterator.next();
if (appliedEffects.containsAll(entry.getValue())) { // all dependent to effects are applied now so apply the effect itself
appliedAbilities = appliedEffectAbilities.get(entry.getKey());
@ -1039,7 +1013,7 @@ public class ContinuousEffects implements Serializable {
for (ContinuousEffect effect : layer) {
if (numberOfEffects > 1) { // If an effect is dependent to not applied effects yet of this layer, so wait to apply this effect
Set<UUID> dependentTo = effect.isDependentTo(layer);
if (dependentTo != null && !appliedEffects.containsAll(dependentTo)) {
if (!appliedEffects.containsAll(dependentTo)) {
waitingEffects.put(effect, dependentTo);
continue;
}
@ -1162,17 +1136,16 @@ public class ContinuousEffects implements Serializable {
private void setControllerForEffect(ContinuousEffectsList<?> effects, UUID sourceId, UUID controllerId) {
for (Effect effect : effects) {
HashSet<Ability> abilities = effects.getAbility(effect.getId());
if (abilities != null) {
for (Ability ability : abilities) {
if (ability.getSourceId() != null) {
if (ability.getSourceId().equals(sourceId)) {
ability.setControllerId(controllerId);
}
} else if (!ability.getZone().equals(Zone.COMMAND)) {
} else if (ability.getZone() != Zone.COMMAND) {
logger.fatal("Continuous effect for ability with no sourceId Ability: " + ability);
}
}
}
}
}

View file

@ -34,6 +34,7 @@ import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.MageSingleton;
import mage.constants.Duration;
@ -41,9 +42,8 @@ import mage.game.Game;
import org.apache.log4j.Logger;
/**
*
* @author BetaSteward_at_googlemail.com
* @param <T>
* @author BetaSteward_at_googlemail.com
*/
public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList<T> {
@ -74,7 +74,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
}
public void removeEndOfTurnEffects() {
for (Iterator<T> i = this.iterator(); i.hasNext();) {
for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
T entry = i.next();
if (entry.getDuration() == Duration.EndOfTurn) {
i.remove();
@ -84,7 +84,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
}
public void removeEndOfCombatEffects() {
for (Iterator<T> i = this.iterator(); i.hasNext();) {
for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
T entry = i.next();
if (entry.getDuration() == Duration.EndOfCombat) {
i.remove();
@ -94,7 +94,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
}
public void removeInactiveEffects(Game game) {
for (Iterator<T> i = this.iterator(); i.hasNext();) {
for (Iterator<T> i = this.iterator(); i.hasNext(); ) {
T entry = i.next();
if (isInactive(entry, game)) {
i.remove();
@ -169,7 +169,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
}
public HashSet<Ability> getAbility(UUID effectId) {
return effectAbilityMap.get(effectId);
return effectAbilityMap.getOrDefault(effectId, new HashSet<>());
}
public void removeEffects(UUID effectIdToRemove, Set<Ability> abilitiesToRemove) {
@ -178,7 +178,7 @@ public class ContinuousEffectsList<T extends ContinuousEffect> extends ArrayList
abilities.removeAll(abilitiesToRemove);
}
if (abilities == null || abilities.isEmpty()) {
for (Iterator<T> iterator = this.iterator(); iterator.hasNext();) {
for (Iterator<T> iterator = this.iterator(); iterator.hasNext(); ) {
ContinuousEffect effect = iterator.next();
if (effect.getId().equals(effectIdToRemove)) {
iterator.remove();

View file

@ -97,13 +97,13 @@ public class EntersBattlefieldEffect extends ReplacementEffectImpl {
public boolean checksEventType(GameEvent event, Game game) {
switch (enterEventType) {
case OTHER:
return EventType.ENTERS_THE_BATTLEFIELD.equals(event.getType());
return EventType.ENTERS_THE_BATTLEFIELD == event.getType();
case SELF:
return EventType.ENTERS_THE_BATTLEFIELD_SELF.equals(event.getType());
return EventType.ENTERS_THE_BATTLEFIELD_SELF == event.getType();
case CONTROL:
return EventType.ENTERS_THE_BATTLEFIELD_CONTROL.equals(event.getType());
return EventType.ENTERS_THE_BATTLEFIELD_CONTROL == event.getType();
case COPY:
return EventType.ENTERS_THE_BATTLEFIELD_COPY.equals(event.getType());
return EventType.ENTERS_THE_BATTLEFIELD_COPY == event.getType();
}
return false;
}

View file

@ -44,7 +44,7 @@ import mage.players.Player;
*/
public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectImpl implements PayCostToAttackBlockEffect {
public static enum RestrictType {
public enum RestrictType {
ATTACK("attack"),
ATTACK_AND_BLOCK("attack or block"),
@ -111,7 +111,7 @@ public abstract class PayCostToAttackBlockEffectImpl extends ReplacementEffectIm
case BLOCK:
return event.getType().equals(GameEvent.EventType.DECLARE_BLOCKER);
case ATTACK_AND_BLOCK:
return event.getType() == GameEvent.EventType.DECLARE_ATTACKER || event.getType().equals(GameEvent.EventType.DECLARE_BLOCKER);
return event.getType() == GameEvent.EventType.DECLARE_ATTACKER || event.getType() == EventType.DECLARE_BLOCKER;
}
return false;
}

View file

@ -61,8 +61,8 @@ public class AddCombatAndMainPhaseEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
// 15.07.2006 If it's somehow not a main phase when Fury of the Horde resolves, all it does is untap all creatures that attacked that turn. No new phases are created.
if (TurnPhase.PRECOMBAT_MAIN.equals(game.getTurn().getPhaseType())
|| TurnPhase.POSTCOMBAT_MAIN.equals(game.getTurn().getPhaseType())) {
if (game.getTurn().getPhaseType() == TurnPhase.PRECOMBAT_MAIN
|| game.getTurn().getPhaseType() == TurnPhase.POSTCOMBAT_MAIN) {
// we can't add two turn modes at once, will add additional post combat on delayed trigger resolution
TurnMod combat = new TurnMod(source.getControllerId(), TurnPhase.COMBAT, TurnPhase.POSTCOMBAT_MAIN, false);
game.getState().getTurnMods().add(combat);

View file

@ -109,16 +109,12 @@ public class CantBeTargetedAttachedEffect extends ContinuousRuleModifyingEffectI
return staticText;
}
StringBuilder sb = new StringBuilder();
if (attachmentType.equals(AttachmentType.AURA)) {
sb.append("Enchanted creature");
} else {
sb.append("Equipped creature");
}
sb.append(attachmentType.verb() + " creature");
sb.append(" can't be the target of ");
sb.append(filterSource.getMessage());
if (!duration.toString().isEmpty()) {
sb.append(' ');
if (duration.equals(Duration.EndOfTurn)) {
if (duration == Duration.EndOfTurn) {
sb.append("this turn");
} else {
sb.append(duration.toString());

View file

@ -114,7 +114,7 @@ public class DontUntapInControllersNextUntapStepTargetEffect extends ContinuousR
public boolean applies(GameEvent event, Ability source, Game game) {
// the check if a permanent untap pahse is already handled is needed if multiple effects are added to prevent untap in next untap step of controller
// if we don't check it for every untap step of a turn only one effect would be consumed instead of all be valid for the next untap step
if (GameEvent.EventType.UNTAP_STEP.equals(event.getType())) {
if (event.getType() == EventType.UNTAP_STEP) {
boolean allHandled = true;
for (UUID targetId : getTargetPointer().getTargets(game, source)) {
Permanent permanent = game.getPermanent(targetId);

View file

@ -56,7 +56,7 @@ public class DontUntapInControllersUntapStepEnchantedEffect extends ContinuousRu
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (PhaseStep.UNTAP.equals(game.getTurn().getStepType())) {
if (game.getTurn().getStepType() == PhaseStep.UNTAP) {
Permanent enchantment = game.getPermanent(source.getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null && event.getTargetId().equals(enchantment.getAttachedTo())) {
Permanent permanent = game.getPermanent(enchantment.getAttachedTo());

View file

@ -81,7 +81,7 @@ public class DontUntapInControllersUntapStepTargetEffect extends ContinuousRuleM
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (PhaseStep.UNTAP.equals(game.getTurn().getStepType())) {
if (game.getTurn().getStepType() == PhaseStep.UNTAP) {
for (UUID targetId : targetPointer.getTargets(game, source)) {
if (event.getTargetId().equals(targetId)) {
Permanent permanent = game.getPermanent(targetId);

View file

@ -107,7 +107,7 @@ public class DontUntapInOpponentsNextUntapStepAllEffect extends ContinuousRuleMo
return false;
}
// remember the turn of the untap step the effect has to be applied
if (GameEvent.EventType.UNTAP_STEP.equals(event.getType())) {
if (event.getType() == EventType.UNTAP_STEP) {
if (game.getActivePlayerId().equals(getTargetPointer().getFirst(game, source))) {
if (validForTurnNum == game.getTurnNum()) { // the turn has a second untap step but the effect is already related to the first untap step
discard();

View file

@ -190,7 +190,7 @@ public class LookLibraryAndPickControllerEffect extends LookLibraryControllerEff
if (player.choose(Outcome.DrawCard, cards, target, game)) {
Cards pickedCards = new CardsImpl(target.getTargets());
cards.removeAll(pickedCards);
if (targetPickedCards.equals(Zone.LIBRARY) && !putOnTopSelected) {
if (targetPickedCards == Zone.LIBRARY && !putOnTopSelected) {
player.putCardsOnBottomOfLibrary(pickedCards, game, source, true);
} else {
player.moveCards(pickedCards.getCards(game), targetPickedCards, source, game);

View file

@ -40,7 +40,6 @@ import mage.game.events.GameEvent;
import mage.game.permanent.Permanent;
/**
*
* @author jeff
*/
public class RegenerateAttachedEffect extends ReplacementEffectImpl {
@ -102,13 +101,8 @@ public class RegenerateAttachedEffect extends ReplacementEffectImpl {
}
return false;
}
private void setText() {
StringBuilder sb = new StringBuilder();
if (attachmentType == AttachmentType.AURA) {
sb.append("Regenerate enchanted creature");
} else if (attachmentType == AttachmentType.EQUIPMENT) {
sb.append("Regenerate equipped creature");
}
staticText = sb.toString();
staticText = "Regenerate " + attachmentType.verb().toLowerCase() + " creature";
}
}

View file

@ -44,11 +44,7 @@ public class AttacksIfAbleAttachedEffect extends RequirementEffect {
public AttacksIfAbleAttachedEffect(Duration duration, AttachmentType attachmentType) {
super(duration);
if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature attacks each turn if able";
} else {
this.staticText = "Equipped creature attacks each turn if able";
}
this.staticText = attachmentType.verb() + " creature attacks each turn if able";
}
public AttacksIfAbleAttachedEffect(final AttacksIfAbleAttachedEffect effect) {

View file

@ -51,7 +51,7 @@ public class BlocksIfAbleAllEffect extends RequirementEffect {
super(duration);
staticText = new StringBuilder(filter.getMessage())
.append(" block ")
.append(duration.equals(Duration.EndOfTurn) ? "this":"each")
.append(duration == Duration.EndOfTurn ? "this":"each")
.append(" turn if able").toString();
this.filter = filter;
}

View file

@ -36,7 +36,6 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
@ -44,11 +43,7 @@ public class BlocksIfAbleAttachedEffect extends RequirementEffect {
public BlocksIfAbleAttachedEffect(Duration duration, AttachmentType attachmentType) {
super(duration);
if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature blocks each turn if able";
} else {
this.staticText = "Equipped creature blocks each turn if able";
}
this.staticText = attachmentType.verb() + " creature blocks each turn if able";
}
public BlocksIfAbleAttachedEffect(final BlocksIfAbleAttachedEffect effect) {

View file

@ -27,7 +27,6 @@
*/
package mage.abilities.effects.common.combat;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.AsThoughEffectImpl;
import mage.constants.AsThoughEffectType;
@ -38,8 +37,9 @@ import mage.filter.common.FilterCreaturePermanent;
import mage.game.Game;
import mage.game.permanent.Permanent;
import java.util.UUID;
/**
*
* @author Quercitron
*/
public class CanAttackAsThoughItDidntHaveDefenderAllEffect extends AsThoughEffectImpl {
@ -81,7 +81,7 @@ public class CanAttackAsThoughItDidntHaveDefenderAllEffect extends AsThoughEffec
StringBuilder sb = new StringBuilder(filter.getMessage());
sb.append(" can attack ");
if (!duration.toString().isEmpty()) {
if(Duration.EndOfTurn.equals(duration)) {
if (Duration.EndOfTurn == duration) {
sb.append("this turn");
} else {
sb.append(duration.toString());

View file

@ -44,7 +44,7 @@ public class CanAttackAsThoughItDidntHaveDefenderSourceEffect extends AsThoughEf
public CanAttackAsThoughItDidntHaveDefenderSourceEffect(Duration duration) {
super(AsThoughEffectType.ATTACK, duration, Outcome.Benefit);
staticText = "{this} can attack "
+ (duration.equals(Duration.EndOfTurn) ? "this turn " : "")
+ (duration == Duration.EndOfTurn ? "this turn " : "")
+ "as though it didn't have defender";
}

View file

@ -37,7 +37,6 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
@ -45,11 +44,7 @@ public class CanBlockOnlyFlyingAttachedEffect extends RestrictionEffect {
public CanBlockOnlyFlyingAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield);
if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature can block only creatures with flying";
} else {
this.staticText = "Equipped creature can block only creatures with flying";
}
this.staticText = attachmentType.verb() + " creature can block only creatures with flying";
}
public CanBlockOnlyFlyingAttachedEffect(final CanBlockOnlyFlyingAttachedEffect effect) {

View file

@ -20,11 +20,7 @@ public class CantAttackAloneAttachedEffect extends RestrictionEffect {
public CantAttackAloneAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield);
if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature can't attack alone";
} else {
this.staticText = "Equipped creature can't attack alone";
}
this.staticText = attachmentType.verb() + " creature can't attack alone";
}
public CantAttackAloneAttachedEffect(final CantAttackAloneAttachedEffect effect) {

View file

@ -44,11 +44,7 @@ public class CantAttackAttachedEffect extends RestrictionEffect {
public CantAttackAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield);
if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature can't attack";
} else {
this.staticText = "Equipped creature can't attack";
}
this.staticText = attachmentType.verb() + " creature can't attack";
}
public CantAttackAttachedEffect(final CantAttackAttachedEffect effect) {

View file

@ -48,7 +48,7 @@ public class CantAttackBlockAllEffect extends RestrictionEffect {
StringBuilder sb = new StringBuilder(filter.getMessage()).append(" can't attack or block");
if (!duration.toString().isEmpty()) {
sb.append(' ');
if (duration.equals(Duration.EndOfTurn)) {
if (duration == Duration.EndOfTurn) {
sb.append(" this turn");
} else {
sb.append(' ').append(duration.toString());

View file

@ -35,18 +35,13 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class CantAttackBlockAttachedEffect extends RestrictionEffect {
public CantAttackBlockAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield);
if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature can't attack or block";
} else {
this.staticText = "Equipped creature can't attack or block";
}
this.staticText = attachmentType.verb() + " creature can't attack or block";
}
public CantAttackBlockAttachedEffect(final CantAttackBlockAttachedEffect effect) {

View file

@ -46,8 +46,8 @@ public class CantAttackBlockUnlessPaysAttachedEffect extends PayCostToAttackBloc
public CantAttackBlockUnlessPaysAttachedEffect(ManaCosts manaCosts, AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK_AND_BLOCK, manaCosts);
staticText = (attachmentType.equals(AttachmentType.AURA) ? "Enchanted " : "Equipped ")
+ "creature can't attack or block unless its controller pays "
staticText = attachmentType.verb()
+ " creature can't attack or block unless its controller pays "
+ (manaCosts == null ? "" : manaCosts.getText());
}
@ -59,10 +59,10 @@ public class CantAttackBlockUnlessPaysAttachedEffect extends PayCostToAttackBloc
public boolean applies(GameEvent event, Ability source, Game game) {
Permanent enchantment = game.getPermanent(source.getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null) {
if (event.getType().equals(EventType.DECLARE_ATTACKER)) {
if (event.getType() == EventType.DECLARE_ATTACKER) {
return event.getSourceId().equals(enchantment.getAttachedTo());
}
if (event.getType().equals(EventType.DECLARE_BLOCKER)) {
if (event.getType() == EventType.DECLARE_BLOCKER) {
return event.getSourceId().equals(enchantment.getAttachedTo());
}
}

View file

@ -47,7 +47,7 @@ public class CantAttackBlockUnlessPaysSourceEffect extends PayCostToAttackBlockE
super(Duration.WhileOnBattlefield, Outcome.Detriment, restrictType, cost);
staticText = "{this} can't " + restrictType.toString() + " unless you "
+ cost == null ? "" : cost.getText()
+ (restrictType.equals(RestrictType.ATTACK) ? " <i>(This cost is paid as attackers are declared.)</i>" : "");
+ (restrictType == RestrictType.ATTACK ? " <i>(This cost is paid as attackers are declared.)</i>" : "");
}
public CantAttackBlockUnlessPaysSourceEffect(ManaCosts manaCosts, RestrictType restrictType) {
@ -62,10 +62,10 @@ public class CantAttackBlockUnlessPaysSourceEffect extends PayCostToAttackBlockE
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
if (!restrictType.equals(RestrictType.BLOCK) && event.getType().equals(EventType.DECLARE_ATTACKER)) {
if (!(restrictType == RestrictType.BLOCK) && event.getType() == EventType.DECLARE_ATTACKER) {
return event.getSourceId().equals(source.getSourceId());
}
if (!restrictType.equals(RestrictType.ATTACK) && event.getType().equals(EventType.DECLARE_BLOCKER)) {
if (!(restrictType == RestrictType.ATTACK) && event.getType() == EventType.DECLARE_BLOCKER) {
return event.getSourceId().equals(source.getSourceId());
}
return false;

View file

@ -46,11 +46,7 @@ public class CantAttackControllerAttachedEffect extends RestrictionEffect {
public CantAttackControllerAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield);
if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature can't attack you or a planeswalker you control";
} else {
this.staticText = "Equipped creature can't attack you or a planeswalker you control";
}
this.staticText = attachmentType.verb() + " creature can't attack you or a planeswalker you control";
}
public CantAttackControllerAttachedEffect(final CantAttackControllerAttachedEffect effect) {

View file

@ -46,8 +46,8 @@ public class CantAttackUnlessPaysAttachedEffect extends PayCostToAttackBlockEffe
public CantAttackUnlessPaysAttachedEffect(ManaCosts manaCosts, AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield, Outcome.Detriment, RestrictType.ATTACK, manaCosts);
staticText = (attachmentType.equals(AttachmentType.AURA) ? "Enchanted " : "Equipped ")
+ "creature can't attack unless its controller pays "
staticText = attachmentType.verb()
+ " creature can't attack unless its controller pays "
+ (manaCosts == null ? "" : manaCosts.getText());
}
@ -59,7 +59,7 @@ public class CantAttackUnlessPaysAttachedEffect extends PayCostToAttackBlockEffe
public boolean applies(GameEvent event, Ability source, Game game) {
Permanent enchantment = game.getPermanent(source.getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null) {
if (event.getType().equals(EventType.DECLARE_ATTACKER)) {
if (event.getType() == EventType.DECLARE_ATTACKER) {
return event.getSourceId().equals(enchantment.getAttachedTo());
}
}

View file

@ -59,7 +59,7 @@ public class CantAttackYouAllEffect extends RestrictionEffect {
this.alsoPlaneswalker = alsoPlaneswalker;
staticText = filterAttacker.getMessage() + " can't attack you"
+ (alsoPlaneswalker ? " or a planeswalker you control" : "")
+ (duration.equals(Duration.UntilYourNextTurn) ? " until your next turn" : "");
+ (duration == Duration.UntilYourNextTurn ? " until your next turn" : "");
}
CantAttackYouAllEffect(final CantAttackYouAllEffect effect) {

View file

@ -47,7 +47,7 @@ public class CantBeBlockedAllEffect extends RestrictionEffect {
this.filter = filter;
this.staticText = filter.getMessage() + " can't be blocked";
if (duration.equals(Duration.EndOfTurn)) {
if (duration == Duration.EndOfTurn) {
this.staticText += " this turn";
}
}

View file

@ -42,11 +42,7 @@ public class CantBeBlockedAttachedEffect extends RestrictionEffect {
public CantBeBlockedAttachedEffect(AttachmentType attachmentType) {
super(Duration.WhileOnBattlefield);
if (attachmentType.equals(AttachmentType.AURA)) {
this.staticText = "Enchanted creature can't be blocked";
} else {
this.staticText = "Equipped creature can't be blocked";
}
this.staticText = attachmentType.verb() + " creature can't be blocked";
}
public CantBeBlockedAttachedEffect(CantBeBlockedAttachedEffect effect) {

View file

@ -36,7 +36,6 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class CantBeBlockedByCreaturesAttachedEffect extends RestrictionEffect {
@ -47,13 +46,9 @@ public class CantBeBlockedByCreaturesAttachedEffect extends RestrictionEffect {
super(duration);
this.filter = filter;
StringBuilder sb = new StringBuilder();
if (attachmentType.equals(AttachmentType.AURA)) {
sb.append("Enchanted ");
} else {
sb.append("Equipped ");
}
staticText = sb.append("creature can't be blocked ")
.append(filter.getMessage().startsWith("except by") ? "":"by ").append(filter.getMessage()).toString();
sb.append(attachmentType.verb());
staticText = sb.append(" creature can't be blocked ")
.append(filter.getMessage().startsWith("except by") ? "" : "by ").append(filter.getMessage()).toString();
}
public CantBeBlockedByCreaturesAttachedEffect(final CantBeBlockedByCreaturesAttachedEffect effect) {

View file

@ -56,7 +56,7 @@ public class CantBeBlockedByOneAllEffect extends ContinuousEffectImpl {
this.amount = amount;
this.filter = filter;
StringBuilder sb = new StringBuilder("each ").append(filter.getMessage()).append(" can't be blocked ");
if (duration.equals(Duration.EndOfTurn)) {
if (duration == Duration.EndOfTurn) {
sb.append("this turn ");
}
sb.append("except by ").append(CardUtil.numberToText(amount)).append(" or more creatures");

View file

@ -54,7 +54,7 @@ public class CantBeBlockedByOneAttachedEffect extends ContinuousEffectImpl {
super(duration, Outcome.Benefit);
this.amount = amount;
this.attachmentType = attachmentType;
staticText = (attachmentType.equals(AttachmentType.AURA) ? "Enchanted" : "Equipped") + " creature can't be blocked except by " + amount + " or more creatures";
staticText = attachmentType.verb() + " creature can't be blocked except by " + amount + " or more creatures";
}
public CantBeBlockedByOneAttachedEffect(final CantBeBlockedByOneAttachedEffect effect) {

View file

@ -93,7 +93,7 @@ public class CantBeBlockedByTargetSourceEffect extends RestrictionEffect {
sb.append("Target ");
}
sb.append(target.getTargetName()).append(" can't block {this}");
if (duration.equals(Duration.EndOfTurn)) {
if (duration == Duration.EndOfTurn) {
sb.append(" this turn");
}

View file

@ -45,7 +45,7 @@ public class CantBeBlockedSourceEffect extends RestrictionEffect {
public CantBeBlockedSourceEffect(Duration duration) {
super(duration);
this.staticText = "{this} can't be blocked";
if (Duration.EndOfTurn.equals(this.duration)) {
if (this.duration == Duration.EndOfTurn) {
this.staticText += " this turn";
}
}

View file

@ -72,7 +72,7 @@ public class CantBlockAllEffect extends RestrictionEffect {
public String getText(Mode mode) {
StringBuilder sb = new StringBuilder();
sb.append(filter.getMessage()).append(" can't block");
if (Duration.EndOfTurn.equals(this.duration)) {
if (this.duration == Duration.EndOfTurn) {
sb.append(" this turn");
}
return sb.toString();

View file

@ -56,11 +56,8 @@ public class CantBlockAttachedEffect extends RestrictionEffect {
super(duration);
this.filter = filter;
StringBuilder sb = new StringBuilder();
if (attachmentType.equals(AttachmentType.AURA)) {
sb.append("Enchanted creature can't block");
} else {
sb.append("Equipped creature can't block");
}
sb.append(attachmentType.verb());
sb.append(" creature can't block");
if (!filter.getMessage().equals("creature")) {
sb.append(' ').append(filter.getMessage());
}

View file

@ -43,7 +43,7 @@ public class CantBlockSourceEffect extends RestrictionEffect {
public CantBlockSourceEffect(Duration duration) {
super(duration);
this.staticText = "{this} can't block";
if (duration.equals(Duration.EndOfTurn)) {
if (duration == Duration.EndOfTurn) {
this.staticText += " this turn";
}
}

View file

@ -89,7 +89,7 @@ public class CantBlockTargetEffect extends RestrictionEffect {
}
sb.append(" can't block");
if (Duration.EndOfTurn.equals(this.duration)) {
if (this.duration == Duration.EndOfTurn) {
sb.append(" this turn");
}

View file

@ -29,6 +29,7 @@
package mage.abilities.effects.common.combat;
import java.util.UUID;
import mage.constants.AttachmentType;
import mage.constants.Duration;
import mage.abilities.Ability;
@ -37,7 +38,6 @@ import mage.game.Game;
import mage.game.permanent.Permanent;
/**
*
* @author LevelX2
*/
public class MustBeBlockedByAllAttachedEffect extends RequirementEffect {
@ -51,7 +51,7 @@ public class MustBeBlockedByAllAttachedEffect extends RequirementEffect {
public MustBeBlockedByAllAttachedEffect(Duration duration, AttachmentType attachmentType) {
super(duration);
this.attachmentType = attachmentType;
staticText = "All creatures able to block " + (attachmentType.equals(AttachmentType.AURA) ? "enchanted":"equipped") + " creature do so";
staticText = "All creatures able to block " + attachmentType.verb().toLowerCase() + " creature do so";
}
public MustBeBlockedByAllAttachedEffect(final MustBeBlockedByAllAttachedEffect effect) {
@ -84,7 +84,7 @@ public class MustBeBlockedByAllAttachedEffect extends RequirementEffect {
public UUID mustBlockAttacker(Ability source, Game game) {
Permanent attachment = game.getPermanent(source.getSourceId());
if (attachment != null && attachment.getAttachedTo() != null) {
return attachment.getAttachedTo() ;
return attachment.getAttachedTo();
}
return null;
}

View file

@ -46,7 +46,7 @@ public class MustBeBlockedByAllTargetEffect extends RequirementEffect {
public MustBeBlockedByAllTargetEffect(Duration duration) {
super(duration);
staticText = new StringBuilder("All creatures able to block target creature ")
.append(this.getDuration().equals(Duration.EndOfTurn) ? "this turn ":"")
.append(this.getDuration() == Duration.EndOfTurn ? "this turn ":"")
.append("do so").toString();
}
@ -58,7 +58,7 @@ public class MustBeBlockedByAllTargetEffect extends RequirementEffect {
public boolean applies(Permanent permanent, Ability source, Game game) {
Permanent attackingCreature = game.getPermanent(this.getTargetPointer().getFirst(game, source));
if (attackingCreature != null && attackingCreature.isAttacking()) {
if (!source.getAbilityType().equals(AbilityType.STATIC)) {
if (source.getAbilityType() != AbilityType.STATIC) {
BlockedAttackerWatcher blockedAttackerWatcher = (BlockedAttackerWatcher) game.getState().getWatchers().get("BlockedAttackerWatcher");
if (blockedAttackerWatcher != null && blockedAttackerWatcher.creatureHasBlockedAttacker(attackingCreature, permanent, game)) {
// has already blocked this turn, so no need to do again

View file

@ -84,11 +84,7 @@ public class AddCardColorAttachedEffect extends ContinuousEffectImpl {
private void setText() {
StringBuilder sb = new StringBuilder();
if (attachmentType == AttachmentType.AURA)
sb.append("Enchanted");
else if (attachmentType == AttachmentType.EQUIPMENT)
sb.append("Equipped");
sb.append(attachmentType.verb());
sb.append(" creature is a ").append(addedColor.getDescription()).append(" in addition to its colors");
staticText = sb.toString();
}

View file

@ -62,7 +62,7 @@ public class AddCardSubTypeTargetEffect extends ContinuousEffectImpl {
target.getSubtype(game).add(addedSubType);
}
} else {
if (Duration.Custom.equals(duration)) {
if (duration == Duration.Custom) {
discard();
}
}

View file

@ -72,11 +72,8 @@ public class AddCardSubtypeAttachedEffect extends ContinuousEffectImpl {
private void setText() {
StringBuilder sb = new StringBuilder();
if (attachmentType == AttachmentType.AURA)
sb.append("Enchanted");
else if (attachmentType == AttachmentType.EQUIPMENT)
sb.append("Equipped");
sb.append(attachmentType.verb());
sb.append(" creature becomes ").append(addedSubtype).append(" in addition to its other types"); //TODO add attacked card type detection
staticText = sb.toString();
}

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