mirror of
https://github.com/magefree/mage.git
synced 2026-01-26 21:29:17 -08:00
commit
27a4ef8322
36 changed files with 2415 additions and 229 deletions
|
|
@ -1022,8 +1022,9 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
|
||||
// Show the tables pane if there wasn't already an active pane
|
||||
MagePane topPanebefore = getTopMost(tablesPane);
|
||||
if (topPanebefore == null) {
|
||||
setActive(tablesPane);
|
||||
setActive(tablesPane);
|
||||
if (topPanebefore != null && topPanebefore != tablesPane) {
|
||||
setActive(topPanebefore);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1189,7 +1190,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
|
||||
private static final long serialVersionUID = -9104885239063142218L;
|
||||
private ImagePanel backgroundPane;
|
||||
private TablesPane tablesPane;
|
||||
private final TablesPane tablesPane;
|
||||
// private CollectionViewerPane collectionViewerPane;
|
||||
|
||||
public void setStatusText(String status) {
|
||||
|
|
@ -1319,7 +1320,10 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
DownloadPictures.startDownload(null, missingCards);
|
||||
break;
|
||||
case CLIENT_DISCONNECT:
|
||||
SessionHandler.disconnect(false);
|
||||
if (SessionHandler.isConnected()) {
|
||||
endTables();
|
||||
SessionHandler.disconnect(false);
|
||||
}
|
||||
tablesPane.clearChat();
|
||||
showMessage("You have disconnected");
|
||||
setWindowTitle();
|
||||
|
|
@ -1347,6 +1351,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
break;
|
||||
case CLIENT_EXIT:
|
||||
if (SessionHandler.isConnected()) {
|
||||
endTables();
|
||||
SessionHandler.disconnect(false);
|
||||
}
|
||||
CardRepository.instance.closeDB();
|
||||
|
|
@ -1374,6 +1379,15 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
}
|
||||
}
|
||||
|
||||
private void endTables() {
|
||||
for (UUID gameId : GAMES.keySet()) {
|
||||
SessionHandler.quitMatch(gameId);
|
||||
}
|
||||
for (UUID draftId : DRAFTS.keySet()) {
|
||||
SessionHandler.quitDraft(draftId);
|
||||
}
|
||||
}
|
||||
|
||||
public void changeGUISize() {
|
||||
ImageCaches.flush();
|
||||
setGUISize();
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ public class UserView implements Serializable {
|
|||
private final String host;
|
||||
private final String sessionId;
|
||||
private final Date timeConnected;
|
||||
private final Date lastActivity;
|
||||
private final String gameInfo;
|
||||
private final String userState;
|
||||
private final Date muteChatUntil;
|
||||
|
|
@ -48,11 +49,12 @@ public class UserView implements Serializable {
|
|||
private final String email;
|
||||
private final String userIdStr;
|
||||
|
||||
public UserView(String userName, String host, String sessionId, Date timeConnected, String gameInfo, String userState, Date muteChatUntil, String clientVersion, String email, String userIdStr) {
|
||||
public UserView(String userName, String host, String sessionId, Date timeConnected, Date lastActivity, String gameInfo, String userState, Date muteChatUntil, String clientVersion, String email, String userIdStr) {
|
||||
this.userName = userName;
|
||||
this.host = host;
|
||||
this.sessionId = sessionId;
|
||||
this.timeConnected = timeConnected;
|
||||
this.lastActivity = lastActivity;
|
||||
this.gameInfo = gameInfo;
|
||||
this.userState = userState;
|
||||
this.muteChatUntil = muteChatUntil;
|
||||
|
|
@ -93,6 +95,10 @@ public class UserView implements Serializable {
|
|||
return timeConnected;
|
||||
}
|
||||
|
||||
public Date getLastActivity() {
|
||||
return lastActivity;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,22 +33,20 @@
|
|||
*/
|
||||
package mage.server.console;
|
||||
|
||||
import mage.remote.Session;
|
||||
import mage.view.TableView;
|
||||
import mage.view.UserView;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.swing.*;
|
||||
import static javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN;
|
||||
import static javax.swing.JTable.AUTO_RESIZE_OFF;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableRowSorter;
|
||||
import mage.remote.Session;
|
||||
import mage.view.TableView;
|
||||
import mage.view.UserView;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -359,13 +357,14 @@ class TableUserModel extends AbstractTableModel {
|
|||
public static final int POS_USER_NAME = 0;
|
||||
public static final int POS_HOST = 1;
|
||||
public static final int POS_TIME_CONNECTED = 2;
|
||||
public static final int POS_SESSION_ID = 3;
|
||||
public static final int POS_GAME_INFO = 4;
|
||||
public static final int POS_USER_STATE = 5;
|
||||
public static final int POS_CHAT_MUTE = 6;
|
||||
public static final int POS_CLIENT_VERSION = 7;
|
||||
public static final int POS_LAST_ACTIVITY = 3;
|
||||
public static final int POS_SESSION_ID = 4;
|
||||
public static final int POS_GAME_INFO = 5;
|
||||
public static final int POS_USER_STATE = 6;
|
||||
public static final int POS_CHAT_MUTE = 7;
|
||||
public static final int POS_CLIENT_VERSION = 8;
|
||||
|
||||
private final String[] columnNames = new String[]{"User Name", "Host", "Time Connected", "SessionId", "Gameinfo", "User state", "Chat mute", "Client Version"};
|
||||
private final String[] columnNames = new String[]{"User Name", "Host", "Time Connected", "Last activity", "SessionId", "Gameinfo", "User state", "Chat mute", "Client Version"};
|
||||
private UserView[] users = new UserView[0];
|
||||
private static final DateFormat formatterTime = new SimpleDateFormat("HH:mm:ss");
|
||||
private static final DateFormat formatterTimeStamp = new SimpleDateFormat("yy-M-dd HH:mm:ss");
|
||||
|
|
@ -394,6 +393,8 @@ class TableUserModel extends AbstractTableModel {
|
|||
return users[arg0].getHost();
|
||||
case POS_TIME_CONNECTED:
|
||||
return formatterTime.format(users[arg0].getTimeConnected());
|
||||
case POS_LAST_ACTIVITY:
|
||||
return formatterTime.format(users[arg0].getLastActivity());
|
||||
case POS_SESSION_ID:
|
||||
return users[arg0].getSessionId();
|
||||
case POS_GAME_INFO:
|
||||
|
|
@ -544,10 +545,8 @@ class UpdateUsersTask extends SwingWorker<Void, List<UserView>> {
|
|||
return true;
|
||||
}
|
||||
for (UserView u1 : previousUsers) {
|
||||
boolean found = false;
|
||||
for (UserView u2 : usersToCheck) {
|
||||
if (u1.getUserName().equals(u2.getUserName())) {
|
||||
found = true;
|
||||
String s = u1.getUserName() + ',' + u1.getHost();
|
||||
if (peopleIps.get(s) == null) {
|
||||
logger.warn("Found new user: " + u1.getUserName() + ',' + u1.getHost());
|
||||
|
|
@ -561,13 +560,9 @@ class UpdateUsersTask extends SwingWorker<Void, List<UserView>> {
|
|||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// some new user replaced old one
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// seems nothing has been changed
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@
|
|||
leasePeriod="5000"
|
||||
socketWriteTimeout="10000"
|
||||
maxGameThreads="10"
|
||||
maxSecondsIdle="600"
|
||||
maxSecondsIdle="300"
|
||||
minUserNameLength="3"
|
||||
maxUserNameLength="14"
|
||||
invalidUserNamePattern="[^a-z0-9_]"
|
||||
|
|
|
|||
|
|
@ -1271,22 +1271,7 @@ public class MageServerImpl implements MageServer {
|
|||
|
||||
@Override
|
||||
public List<UserView> execute() throws MageException {
|
||||
List<UserView> users = new ArrayList<>();
|
||||
for (User user : UserManager.instance.getUsers()) {
|
||||
users.add(new UserView(
|
||||
user.getName(),
|
||||
user.getHost(),
|
||||
user.getSessionId(),
|
||||
user.getConnectionTime(),
|
||||
user.getGameInfo(),
|
||||
user.getUserState().toString(),
|
||||
user.getChatLockedUntil(),
|
||||
user.getClientVersion(),
|
||||
user.getEmail(),
|
||||
user.getUserIdStr()
|
||||
));
|
||||
}
|
||||
return users;
|
||||
return UserManager.instance.getUserInfoList();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ import mage.interfaces.callback.ClientCallback;
|
|||
import mage.interfaces.callback.ClientCallbackMethod;
|
||||
import mage.players.net.UserData;
|
||||
import mage.players.net.UserGroup;
|
||||
import static mage.server.DisconnectReason.LostConnection;
|
||||
import mage.server.game.GamesRoom;
|
||||
import mage.server.game.GamesRoomManager;
|
||||
import mage.server.util.ConfigSettings;
|
||||
|
|
@ -270,12 +271,13 @@ public class Session {
|
|||
this.isAdmin = true;
|
||||
User user = UserManager.instance.createUser("Admin", host, null).orElse(
|
||||
UserManager.instance.getUserByName("Admin").get());
|
||||
|
||||
UserData adminUserData = UserData.getDefaultUserDataView();
|
||||
adminUserData.setGroupId(UserGroup.ADMIN.getGroupId());
|
||||
user.setUserData(adminUserData);
|
||||
if (!UserManager.instance.connectToSession(sessionId, user.getId())) {
|
||||
logger.info("Error connecting Admin!");
|
||||
} else {
|
||||
user.setUserState(User.UserState.Connected);
|
||||
}
|
||||
this.userId = user.getId();
|
||||
}
|
||||
|
|
@ -329,39 +331,20 @@ public class Session {
|
|||
|
||||
// because different threads can activate this
|
||||
public void userLostConnection() {
|
||||
boolean lockSet = false;
|
||||
try {
|
||||
if (lock.tryLock(5000, TimeUnit.MILLISECONDS)) {
|
||||
lockSet = true;
|
||||
logger.debug("SESSION LOCK SET sessionId: " + sessionId);
|
||||
} else {
|
||||
logger.warn("CAN'T GET LOCK - userId: " + userId + " hold count: " + lock.getHoldCount());
|
||||
}
|
||||
Optional<User> _user = UserManager.instance.getUser(userId);
|
||||
if (!_user.isPresent()) {
|
||||
return; //user was already disconnected by other thread
|
||||
}
|
||||
User user = _user.get();
|
||||
if (!user.isConnected()) {
|
||||
return;
|
||||
}
|
||||
if (!user.getSessionId().equals(sessionId)) {
|
||||
// user already reconnected with another instance
|
||||
logger.info("OLD SESSION IGNORED - " + user.getName());
|
||||
return;
|
||||
}
|
||||
// logger.info("LOST CONNECTION - " + user.getName() + " id: " + userId);
|
||||
UserManager.instance.disconnect(userId, DisconnectReason.LostConnection);
|
||||
|
||||
} catch (InterruptedException ex) {
|
||||
logger.error("SESSION LOCK lost connection - userId: " + userId, ex);
|
||||
} finally {
|
||||
if (lockSet) {
|
||||
lock.unlock();
|
||||
logger.trace("SESSION LOCK UNLOCK sessionId: " + sessionId);
|
||||
}
|
||||
Optional<User> _user = UserManager.instance.getUser(userId);
|
||||
if (!_user.isPresent()) {
|
||||
return; //user was already disconnected by other thread
|
||||
}
|
||||
User user = _user.get();
|
||||
if (!user.isConnected()) {
|
||||
return;
|
||||
}
|
||||
if (!user.getSessionId().equals(sessionId)) {
|
||||
// user already reconnected with another instance
|
||||
logger.info("OLD SESSION IGNORED - " + user.getName());
|
||||
} else {
|
||||
// logger.info("LOST CONNECTION - " + user.getName() + " id: " + userId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void kill(DisconnectReason reason) {
|
||||
|
|
@ -397,7 +380,7 @@ public class Session {
|
|||
logger.warn(" - method: " + call.getMethod());
|
||||
logger.warn(" - cause: " + getBasicCause(ex).toString());
|
||||
logger.trace("Stack trace:", ex);
|
||||
userLostConnection();
|
||||
SessionManager.instance.disconnect(sessionId, LostConnection);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,34 +125,30 @@ public enum SessionManager {
|
|||
public void disconnect(String sessionId, DisconnectReason reason) {
|
||||
Session session = sessions.get(sessionId);
|
||||
if (session != null) {
|
||||
if (reason != DisconnectReason.AdminDisconnect) {
|
||||
if (!sessions.containsKey(sessionId)) {
|
||||
// session was removed meanwhile by another thread so we can return
|
||||
return;
|
||||
}
|
||||
logger.debug("DISCONNECT " + reason.toString() + " - sessionId: " + sessionId);
|
||||
sessions.remove(sessionId);
|
||||
switch (reason) {
|
||||
case Disconnected: // regular session end or wrong client version
|
||||
if (session.getUserId() != null) { // if wrong client version no userId is set
|
||||
session.kill(reason);
|
||||
}
|
||||
break;
|
||||
case SessionExpired: // session ends after no reconnect happens in the defined time span
|
||||
session.kill(reason);
|
||||
break;
|
||||
case LostConnection: // user lost connection - session expires countdaoun starts
|
||||
session.userLostConnection();
|
||||
break;
|
||||
case ConnectingOtherInstance:
|
||||
break;
|
||||
default:
|
||||
logger.trace("endSession: unexpected reason " + reason.toString() + " - sessionId: " + sessionId);
|
||||
}
|
||||
} else {
|
||||
sessions.remove(sessionId);
|
||||
session.kill(reason);
|
||||
if (!sessions.containsKey(sessionId)) {
|
||||
// session was removed meanwhile by another thread so we can return
|
||||
return;
|
||||
}
|
||||
logger.debug("DISCONNECT " + reason.toString() + " - sessionId: " + sessionId);
|
||||
sessions.remove(sessionId);
|
||||
switch (reason) {
|
||||
case AdminDisconnect:
|
||||
session.kill(reason);
|
||||
break;
|
||||
case ConnectingOtherInstance:
|
||||
case Disconnected: // regular session end or wrong client version
|
||||
UserManager.instance.disconnect(session.getUserId(), reason);
|
||||
break;
|
||||
case SessionExpired: // session ends after no reconnect happens in the defined time span
|
||||
break;
|
||||
case LostConnection: // user lost connection - session expires countdown starts
|
||||
session.userLostConnection();
|
||||
UserManager.instance.disconnect(session.getUserId(), reason);
|
||||
break;
|
||||
default:
|
||||
logger.trace("endSession: unexpected reason " + reason.toString() + " - sessionId: " + sessionId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -64,7 +64,10 @@ public class User {
|
|||
|
||||
public enum UserState {
|
||||
|
||||
Created, Connected, Disconnected, Reconnected, Expired
|
||||
Created, // Used if user is created an not connected to the session
|
||||
Connected, // Used if user is correctly connected
|
||||
Disconnected, // Used if the user lost connection
|
||||
Offline // set if the user was disconnected and expired or regularly left XMage. Removed is the user later after some time
|
||||
}
|
||||
|
||||
private final UUID userId;
|
||||
|
|
@ -164,7 +167,7 @@ public class User {
|
|||
userState = UserState.Connected;
|
||||
logger.trace("USER - created: " + userName + " id: " + userId);
|
||||
} else {
|
||||
userState = UserState.Reconnected;
|
||||
userState = UserState.Connected;
|
||||
reconnect();
|
||||
logger.trace("USER - reconnected: " + userName + " id: " + userId);
|
||||
}
|
||||
|
|
@ -212,23 +215,14 @@ public class User {
|
|||
}
|
||||
|
||||
public boolean isConnected() {
|
||||
return userState == UserState.Connected || userState == UserState.Reconnected;
|
||||
return userState == UserState.Connected;
|
||||
}
|
||||
|
||||
public String getDisconnectDuration() {
|
||||
long secondsDisconnected = getSecondsDisconnected();
|
||||
long secondsLeft;
|
||||
String sign = "";
|
||||
if (secondsDisconnected > (3 * 60)) {
|
||||
sign = "-";
|
||||
secondsLeft = secondsDisconnected - (3 * 60);
|
||||
} else {
|
||||
secondsLeft = (3 * 60) - secondsDisconnected;
|
||||
}
|
||||
|
||||
int minutes = (int) secondsLeft / 60;
|
||||
int seconds = (int) secondsLeft % 60;
|
||||
return new StringBuilder(sign).append(Integer.toString(minutes)).append(':').append(seconds > 9 ? seconds : '0' + Integer.toString(seconds)).toString();
|
||||
int minutes = (int) secondsDisconnected / 60;
|
||||
int seconds = (int) secondsDisconnected % 60;
|
||||
return Integer.toString(minutes) + ':' + (seconds > 9 ? seconds : '0' + Integer.toString(seconds));
|
||||
}
|
||||
|
||||
public long getSecondsDisconnected() {
|
||||
|
|
@ -239,6 +233,20 @@ public class User {
|
|||
return connectionTime;
|
||||
}
|
||||
|
||||
public Date getLastActivity() {
|
||||
return lastActivity;
|
||||
}
|
||||
|
||||
public String getConnectionDuration() {
|
||||
int minutes = (int) SystemUtil.getDateDiff(connectionTime, new Date(), TimeUnit.SECONDS) / 60;
|
||||
int hours = 0;
|
||||
if (minutes > 59) {
|
||||
hours = (int) minutes / 60;
|
||||
minutes = minutes - (hours * 60);
|
||||
}
|
||||
return Integer.toString(hours) + ":" + (minutes > 9 ? Integer.toString(minutes) : '0' + Integer.toString(minutes));
|
||||
}
|
||||
|
||||
public void fireCallback(final ClientCallback call) {
|
||||
if (isConnected()) {
|
||||
SessionManager.instance.getSession(sessionId).ifPresent(session
|
||||
|
|
@ -331,19 +339,17 @@ public class User {
|
|||
}
|
||||
lastActivity = new Date();
|
||||
if (userState == UserState.Disconnected) { // this can happen if user reconnects very fast after disconnect
|
||||
userState = UserState.Reconnected;
|
||||
userState = UserState.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isExpired(Date expired) {
|
||||
if (lastActivity.before(expired)) {
|
||||
logger.trace(userName + " is expired!");
|
||||
userState = UserState.Expired;
|
||||
return true;
|
||||
}
|
||||
logger.trace("isExpired: User " + userName + " lastActivity: " + lastActivity + " expired: " + expired);
|
||||
return false;
|
||||
/*userState == UserState.Disconnected && */
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -511,11 +517,15 @@ public class User {
|
|||
tournament++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!isConnected()) {
|
||||
tournamentPlayer.setDisconnectInfo(" (discon. " + getDisconnectDuration() + ')');
|
||||
} else {
|
||||
tournamentPlayer.setDisconnectInfo("");
|
||||
switch (getUserState()) {
|
||||
case Disconnected:
|
||||
tournamentPlayer.setDisconnectInfo(" (discon. " + getDisconnectDuration() + ')');
|
||||
break;
|
||||
case Offline:
|
||||
tournamentPlayer.setDisconnectInfo(" Offline");
|
||||
break;
|
||||
default:
|
||||
tournamentPlayer.setDisconnectInfo("");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
@ -586,11 +596,18 @@ public class User {
|
|||
return userState;
|
||||
}
|
||||
|
||||
public void setUserState(UserState userState) {
|
||||
this.userState = userState;
|
||||
}
|
||||
|
||||
public String getPingInfo() {
|
||||
if (isConnected()) {
|
||||
return pingInfo;
|
||||
} else {
|
||||
return " (discon. " + getDisconnectDuration() + ')';
|
||||
switch (getUserState()) {
|
||||
case Disconnected:
|
||||
return " (discon. " + getDisconnectDuration() + ')';
|
||||
case Offline:
|
||||
return " Offline";
|
||||
default:
|
||||
return pingInfo + " " + getConnectionDuration();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ import mage.server.User.UserState;
|
|||
import mage.server.record.UserStats;
|
||||
import mage.server.record.UserStatsRepository;
|
||||
import mage.server.util.ThreadExecutor;
|
||||
import mage.view.UserView;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
/**
|
||||
|
|
@ -44,7 +45,12 @@ import org.apache.log4j.Logger;
|
|||
public enum UserManager {
|
||||
instance;
|
||||
|
||||
private static final Logger logger = Logger.getLogger(UserManager.class);
|
||||
|
||||
protected final ScheduledExecutorService expireExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
protected final ScheduledExecutorService userListExecutor = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
private List<UserView> userInfoList = new ArrayList<>();
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(UserManager.class);
|
||||
|
||||
|
|
@ -54,6 +60,8 @@ public enum UserManager {
|
|||
|
||||
UserManager() {
|
||||
expireExecutor.scheduleAtFixedRate(this::checkExpired, 60, 60, TimeUnit.SECONDS);
|
||||
|
||||
userListExecutor.scheduleAtFixedRate(this::updateUserInfoList, 4, 4, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public Optional<User> createUser(String userName, String host, AuthorizedUser authorizedUser) {
|
||||
|
|
@ -100,10 +108,16 @@ public enum UserManager {
|
|||
}
|
||||
|
||||
public void disconnect(UUID userId, DisconnectReason reason) {
|
||||
if (userId != null) {
|
||||
getUser(userId).ifPresent(user -> user.setSessionId(""));// Session will be set again with new id if user reconnects
|
||||
Optional<User> user = UserManager.instance.getUser(userId);
|
||||
if (user.isPresent()) {
|
||||
user.get().setSessionId("");
|
||||
if (reason == DisconnectReason.Disconnected) {
|
||||
user.get().setUserState(UserState.Offline);
|
||||
}
|
||||
}
|
||||
if (userId != null) {
|
||||
ChatManager.instance.removeUser(userId, reason);
|
||||
}
|
||||
ChatManager.instance.removeUser(userId, reason);
|
||||
}
|
||||
|
||||
public boolean isAdmin(UUID userId) {
|
||||
|
|
@ -148,18 +162,57 @@ public enum UserManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Is the connection lost for more than 3 minutes, the user will be removed
|
||||
* (within 3 minutes the user can reconnect)
|
||||
* Is the connection lost for more than 3 minutes, the user will be set to
|
||||
* offline status. The user will be removed in validity check after 15
|
||||
* minutes of no activities
|
||||
*
|
||||
*/
|
||||
private void checkExpired() {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.add(Calendar.MINUTE, -3);
|
||||
List<User> usersToCheck = new ArrayList<>(users.values());
|
||||
for (User user : usersToCheck) {
|
||||
if (user.getUserState() != UserState.Expired && user.isExpired(calendar.getTime())) {
|
||||
removeUser(user.getId(), DisconnectReason.SessionExpired);
|
||||
Calendar calendarExp = Calendar.getInstance();
|
||||
calendarExp.add(Calendar.MINUTE, -3);
|
||||
Calendar calendarRemove = Calendar.getInstance();
|
||||
calendarRemove.add(Calendar.MINUTE, -8);
|
||||
List<User> toRemove = new ArrayList<>();
|
||||
for (User user : users.values()) {
|
||||
if (user.getUserState() == UserState.Disconnected || user.getUserState() == UserState.Offline
|
||||
&& user.isExpired(calendarExp.getTime())) {
|
||||
user.setUserState(UserState.Offline);
|
||||
}
|
||||
if (user.getUserState() == UserState.Offline && user.isExpired(calendarRemove.getTime())) {
|
||||
toRemove.add(user);
|
||||
}
|
||||
}
|
||||
for (User user : toRemove) {
|
||||
removeUser(user.getId(), DisconnectReason.SessionExpired);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method recreated the user list that will be send to all clients
|
||||
*
|
||||
*/
|
||||
private void updateUserInfoList() {
|
||||
List<UserView> newUserInfoList = new ArrayList<>();
|
||||
for (User user : UserManager.instance.getUsers()) {
|
||||
newUserInfoList.add(new UserView(
|
||||
user.getName(),
|
||||
user.getHost(),
|
||||
user.getSessionId(),
|
||||
user.getConnectionTime(),
|
||||
user.getLastActivity(),
|
||||
user.getGameInfo(),
|
||||
user.getUserState().toString(),
|
||||
user.getChatLockedUntil(),
|
||||
user.getClientVersion(),
|
||||
user.getEmail(),
|
||||
user.getUserIdStr()
|
||||
));
|
||||
}
|
||||
userInfoList = newUserInfoList;
|
||||
}
|
||||
|
||||
public List<UserView> getUserInfoList() {
|
||||
return userInfoList;
|
||||
}
|
||||
|
||||
public void handleException(Exception ex) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,12 @@
|
|||
*/
|
||||
package mage.server.game;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import mage.MageException;
|
||||
import mage.cards.decks.DeckCardLists;
|
||||
import mage.constants.TableState;
|
||||
|
|
@ -48,13 +54,6 @@ import mage.view.TableView;
|
|||
import mage.view.UsersView;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
|
|
@ -107,26 +106,28 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
|
|||
matchView = matchList;
|
||||
List<UsersView> users = new ArrayList<>();
|
||||
for (User user : UserManager.instance.getUsers()) {
|
||||
try {
|
||||
users.add(new UsersView(user.getUserData().getFlagName(), user.getName(),
|
||||
user.getMatchHistory(), user.getMatchQuitRatio(), user.getTourneyHistory(),
|
||||
user.getTourneyQuitRatio(), user.getGameInfo(), user.getPingInfo(),
|
||||
user.getUserData().getGeneralRating(), user.getUserData().getConstructedRating(),
|
||||
user.getUserData().getLimitedRating()));
|
||||
} catch (Exception ex) {
|
||||
LOGGER.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex);
|
||||
users.add(new UsersView(
|
||||
(user.getUserData() != null && user.getUserData().getFlagName() != null) ? user.getUserData().getFlagName() : "world",
|
||||
user.getName() != null ? user.getName() : "<no name>",
|
||||
user.getMatchHistory() != null ? user.getMatchHistory() : "<no match history>",
|
||||
user.getMatchQuitRatio(),
|
||||
user.getTourneyHistory() != null ? user.getTourneyHistory() : "<no tourney history>",
|
||||
user.getTourneyQuitRatio(),
|
||||
"[exception]",
|
||||
user.getPingInfo() != null ? user.getPingInfo() : "<no ping>",
|
||||
user.getUserData() != null ? user.getUserData().getGeneralRating() : 0,
|
||||
user.getUserData() != null ? user.getUserData().getConstructedRating() : 0,
|
||||
user.getUserData() != null ? user.getUserData().getLimitedRating() : 0));
|
||||
if (user.getUserState() != User.UserState.Offline && !user.getName().equals("Admin")) {
|
||||
try {
|
||||
users.add(new UsersView(user.getUserData().getFlagName(), user.getName(),
|
||||
user.getMatchHistory(), user.getMatchQuitRatio(), user.getTourneyHistory(),
|
||||
user.getTourneyQuitRatio(), user.getGameInfo(), user.getPingInfo(),
|
||||
user.getUserData().getGeneralRating(), user.getUserData().getConstructedRating(),
|
||||
user.getUserData().getLimitedRating()));
|
||||
} catch (Exception ex) {
|
||||
LOGGER.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex);
|
||||
users.add(new UsersView(
|
||||
(user.getUserData() != null && user.getUserData().getFlagName() != null) ? user.getUserData().getFlagName() : "world",
|
||||
user.getName() != null ? user.getName() : "<no name>",
|
||||
user.getMatchHistory() != null ? user.getMatchHistory() : "<no match history>",
|
||||
user.getMatchQuitRatio(),
|
||||
user.getTourneyHistory() != null ? user.getTourneyHistory() : "<no tourney history>",
|
||||
user.getTourneyQuitRatio(),
|
||||
"[exception]",
|
||||
user.getPingInfo() != null ? user.getPingInfo() : "<no ping>",
|
||||
user.getUserData() != null ? user.getUserData().getGeneralRating() : 0,
|
||||
user.getUserData() != null ? user.getUserData().getConstructedRating() : 0,
|
||||
user.getUserData() != null ? user.getUserData().getLimitedRating() : 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
124
Mage.Sets/src/mage/cards/b/BalanWanderingKnight.java
Normal file
124
Mage.Sets/src/mage/cards/b/BalanWanderingKnight.java
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.condition.common.EquippedMultipleSourceCondition;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilitySourceEffect;
|
||||
import mage.abilities.keyword.DoubleStrikeAbility;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Saga
|
||||
*/
|
||||
public class BalanWanderingKnight extends CardImpl {
|
||||
|
||||
private static final String rule = "{this} has double strike as long as two or more Equipment are attached to it.";
|
||||
|
||||
public BalanWanderingKnight(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}{W}");
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.CAT, SubType.KNIGHT);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// First Strike
|
||||
this.addAbility(FirstStrikeAbility.getInstance());
|
||||
|
||||
// Balan, Wandering Knight has double strike as long as two or more Equipment are attached to it.
|
||||
ConditionalContinuousEffect effect = new ConditionalContinuousEffect(new GainAbilitySourceEffect(DoubleStrikeAbility.getInstance()), EquippedMultipleSourceCondition.instance, rule);
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
|
||||
|
||||
// {1}{W}: Attach all Equipment you control to Balan.
|
||||
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new BalanWanderingKnightEffect(), new ManaCostsImpl("{1}{W}")));
|
||||
}
|
||||
|
||||
public BalanWanderingKnight(final BalanWanderingKnight card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BalanWanderingKnight copy() {
|
||||
return new BalanWanderingKnight(this);
|
||||
}
|
||||
|
||||
static class BalanWanderingKnightEffect extends OneShotEffect {
|
||||
|
||||
public BalanWanderingKnightEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Attach all Equipment you control to {this}.";
|
||||
}
|
||||
|
||||
public BalanWanderingKnightEffect(final BalanWanderingKnightEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BalanWanderingKnightEffect copy() {
|
||||
return new BalanWanderingKnightEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent balan = game.getPermanent(source.getSourceId());
|
||||
if (balan != null) {
|
||||
FilterPermanent filter = new FilterPermanent();
|
||||
filter.add(new SubtypePredicate(SubType.EQUIPMENT));
|
||||
for (Permanent equipment : game.getBattlefield().getAllActivePermanents(filter, source.getControllerId(),game)) {
|
||||
if (equipment != null) {
|
||||
//If an Equipment can’t equip, it isn’t attached, and it doesn’t become unattached (if it’s attached to a creature).
|
||||
if (!balan.cantBeAttachedBy(equipment, game)) {
|
||||
balan.addAttachment(equipment.getId(), game);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
84
Mage.Sets/src/mage/cards/b/BloodswornSteward.java
Normal file
84
Mage.Sets/src/mage/cards/b/BloodswornSteward.java
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.CommanderPredicate;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Saga
|
||||
*/
|
||||
public class BloodswornSteward extends CardImpl {
|
||||
|
||||
private final static FilterCreaturePermanent filter = new FilterCreaturePermanent("Commander creatures");
|
||||
static {
|
||||
filter.add(new CommanderPredicate());
|
||||
}
|
||||
|
||||
public BloodswornSteward(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}{R}");
|
||||
this.subtype.add(SubType.VAMPIRE, SubType.KNIGHT);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Commander creatures you control get +2/+2 and have haste.
|
||||
Ability ability = new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(2, 2, Duration.WhileOnBattlefield, filter));
|
||||
Effect effect = new GainAbilityControlledEffect(HasteAbility.getInstance(), Duration.WhileOnBattlefield, filter);
|
||||
effect.setText("and have haste");
|
||||
ability.addEffect(effect);
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public BloodswornSteward(final BloodswornSteward card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BloodswornSteward copy() {
|
||||
return new BloodswornSteward(this);
|
||||
}
|
||||
}
|
||||
113
Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java
Normal file
113
Mage.Sets/src/mage/cards/c/CrimsonHonorGuard.java
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.c;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.permanent.CommanderPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public class CrimsonHonorGuard extends CardImpl {
|
||||
|
||||
public CrimsonHonorGuard(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
|
||||
|
||||
this.subtype.add("Vampire");
|
||||
this.subtype.add("Knight");
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(5);
|
||||
|
||||
// Trample
|
||||
this.addAbility(TrampleAbility.getInstance());
|
||||
|
||||
// At the beginning of each player's end step, Crimson Honor Guard deals 4 damage to that player unless he or she controls a commander.
|
||||
this.addAbility(new BeginningOfEndStepTriggeredAbility(new CrimsonHonorGuardEffect(), TargetController.ANY, false));
|
||||
}
|
||||
|
||||
public CrimsonHonorGuard(final CrimsonHonorGuard card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CrimsonHonorGuard copy() {
|
||||
return new CrimsonHonorGuard(this);
|
||||
}
|
||||
}
|
||||
|
||||
class CrimsonHonorGuardEffect extends OneShotEffect {
|
||||
|
||||
private final static FilterPermanent filter = new FilterPermanent("Commander");
|
||||
|
||||
static {
|
||||
filter.add(new CommanderPredicate());
|
||||
}
|
||||
|
||||
public CrimsonHonorGuardEffect() {
|
||||
super(Outcome.Damage);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Effect copy() {
|
||||
return new CrimsonHonorGuardEffect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(game.getActivePlayerId());
|
||||
if (player != null) {
|
||||
int numCommanders = game.getBattlefield().getActivePermanents(filter, player.getId(), game).size();
|
||||
if (numCommanders == 0) {
|
||||
player.damage(4, source.getSourceId(), game, false, true);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getText(Mode mode) {
|
||||
return "{this} deals 4 damage to that player unless he or she controls a commander";
|
||||
}
|
||||
}
|
||||
98
Mage.Sets/src/mage/cards/g/GrimlockDinobotLeader.java
Normal file
98
Mage.Sets/src/mage/cards/g/GrimlockDinobotLeader.java
Normal 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 mage.cards.g;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.filter.predicate.permanent.TransformedPredicate;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Saga
|
||||
*/
|
||||
public class GrimlockDinobotLeader extends CardImpl{
|
||||
|
||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("Dinosaurs and Vehicles");
|
||||
static {
|
||||
filter.add(Predicates.or(new SubtypePredicate(SubType.DINOSAUR), new SubtypePredicate(SubType.VEHICLE)));
|
||||
}
|
||||
|
||||
private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("Transformers creatures");
|
||||
static {
|
||||
filter2.add(Predicates.not(new SubtypePredicate(SubType.DINOSAUR)));
|
||||
filter2.add(Predicates.not(new SubtypePredicate(SubType.VEHICLE)));
|
||||
filter2.add(Predicates.or(new AbilityPredicate(TransformAbility.class), new TransformedPredicate()));
|
||||
}
|
||||
|
||||
public GrimlockDinobotLeader(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT,CardType.CREATURE}, "{1}{R}{G}{W}");
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.AUTOBOT);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
this.transformable = true;
|
||||
this.secondSideCardClazz = GrimlockFerociousKing.class;
|
||||
|
||||
// Dinosaurs, Vehicles and other Transformers creatures you control get +2/+0.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(2, 0, Duration.WhileOnBattlefield, filter, false)));
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new BoostControlledEffect(2, 0, Duration.WhileOnBattlefield, filter2, true)));
|
||||
|
||||
// {2}: Grimlock, Dinobot Leader becomes Grimlock, Ferocious King.
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(true), new ManaCostsImpl("{2}")));
|
||||
}
|
||||
|
||||
public GrimlockDinobotLeader(final GrimlockDinobotLeader card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrimlockDinobotLeader copy() {
|
||||
return new GrimlockDinobotLeader(this);
|
||||
}
|
||||
|
||||
}
|
||||
80
Mage.Sets/src/mage/cards/g/GrimlockFerociousKing.java
Normal file
80
Mage.Sets/src/mage/cards/g/GrimlockFerociousKing.java
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.g;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.Zone;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Saga
|
||||
*/
|
||||
public class GrimlockFerociousKing extends CardImpl{
|
||||
|
||||
public GrimlockFerociousKing(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT,CardType.CREATURE},"");
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.DINOSAUR);
|
||||
this.power = new MageInt(8);
|
||||
this.toughness = new MageInt(8);
|
||||
this.color.setRed(true);
|
||||
this.color.setGreen(true);
|
||||
this.color.setWhite(true);
|
||||
|
||||
this.transformable = true;
|
||||
this.nightCard = true;
|
||||
|
||||
// Trample
|
||||
this.addAbility(TrampleAbility.getInstance());
|
||||
|
||||
// {2}: Grimlock, Ferocious King becomes Grimlock, Dinobot Leader.
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new TransformSourceEffect(false), new ManaCostsImpl("{2}")));
|
||||
}
|
||||
|
||||
public GrimlockFerociousKing(final GrimlockFerociousKing card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GrimlockFerociousKing copy() {
|
||||
return new GrimlockFerociousKing(this);
|
||||
}
|
||||
|
||||
}
|
||||
123
Mage.Sets/src/mage/cards/h/HeraldsHorn.java
Normal file
123
Mage.Sets/src/mage/cards/h/HeraldsHorn.java
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.h;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AsEntersBattlefieldAbility;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ChooseCreatureTypeEffect;
|
||||
import mage.abilities.effects.common.cost.SpellsCostReductionAllOfChosenSubtypeEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.filter.predicate.mageobject.ChosenSubtypePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Saga
|
||||
*/
|
||||
public class HeraldsHorn extends CardImpl {
|
||||
|
||||
public HeraldsHorn(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
|
||||
|
||||
// As Herald's Horn enters the battlefield, choose a creature type.
|
||||
this.addAbility(new AsEntersBattlefieldAbility(new ChooseCreatureTypeEffect(Outcome.BoostCreature)));
|
||||
|
||||
// Creature spells you cast of the chosen type cost {1} less to cast.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD,
|
||||
new SpellsCostReductionAllOfChosenSubtypeEffect(new FilterCreatureCard("Creature spells you cast of the chosen type"), 1)));
|
||||
|
||||
// At the beginning of your upkeep, look at the top card of your library. If it's a creature card of the chosen type, you may reveal it and put it into your hand.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.BATTLEFIELD, new HeraldsHornEffect(), TargetController.YOU, false));
|
||||
}
|
||||
|
||||
public HeraldsHorn(final HeraldsHorn card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeraldsHorn copy() {
|
||||
return new HeraldsHorn(this);
|
||||
}
|
||||
}
|
||||
|
||||
class HeraldsHornEffect extends OneShotEffect {
|
||||
|
||||
public HeraldsHornEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "look at the top card of your library. If it's a creature card of the chosen type, you may reveal it and put it into your hand";
|
||||
}
|
||||
|
||||
public HeraldsHornEffect(final HeraldsHornEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HeraldsHornEffect copy() {
|
||||
return new HeraldsHornEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = game.getObject(source.getSourceId());
|
||||
|
||||
// Look at the top card of your library.
|
||||
if (controller.getLibrary().hasCards()) {
|
||||
Card card = controller.getLibrary().getFromTop(game);
|
||||
Cards cards = new CardsImpl(card);
|
||||
controller.lookAtCards(sourceObject.getIdName(), cards, game);
|
||||
|
||||
// If it's a creature card of the chosen type, you may reveal it and put it into your hand.
|
||||
FilterCreatureCard filter = new FilterCreatureCard("creature card of the chosen type");
|
||||
filter.add(new ChosenSubtypePredicate(source.getSourceId()));
|
||||
String message = "Reveal the top card of your library and put that card into your hand?";
|
||||
if (card != null) {
|
||||
if (filter.match(card, game) && controller.chooseUse(Outcome.Benefit, message, source, game)) {
|
||||
controller.moveCards(card, Zone.HAND, source, game);
|
||||
controller.revealCards(sourceObject.getIdName() + " put into hand", cards, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
108
Mage.Sets/src/mage/cards/h/HungryLynx.java
Normal file
108
Mage.Sets/src/mage/cards/h/HungryLynx.java
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.h;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.abilities.common.DiesCreatureTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.CreateTokenTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAllEffect;
|
||||
import mage.abilities.effects.common.counter.AddCountersAllEffect;
|
||||
import mage.abilities.keyword.ProtectionAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.game.permanent.token.DeathtouchRatToken;
|
||||
import mage.target.Target;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Saga
|
||||
*/
|
||||
public class HungryLynx extends CardImpl {
|
||||
|
||||
private final static FilterControlledCreaturePermanent filterCat = new FilterControlledCreaturePermanent("Cats");
|
||||
static {
|
||||
filterCat.add(new SubtypePredicate(SubType.CAT));
|
||||
}
|
||||
|
||||
private static final FilterCard filterProRat = new FilterCard("Rats");
|
||||
static {
|
||||
filterProRat.add(new SubtypePredicate(SubType.RAT));
|
||||
}
|
||||
|
||||
private final static FilterCreaturePermanent filterRat = new FilterCreaturePermanent("a Rat");
|
||||
static {
|
||||
filterRat.add(new SubtypePredicate(SubType.RAT));
|
||||
}
|
||||
|
||||
public HungryLynx(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{1}{G}");
|
||||
this.subtype.add(SubType.CAT);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Cats you control have protection from Rats.
|
||||
Effect effect = new GainAbilityAllEffect(new ProtectionAbility(filterProRat), Duration.WhileOnBattlefield, filterCat);
|
||||
effect.setText("Cats you control have protection from Rats. <i>(They can't be blocked, targeted, or dealt damage by Rats.)</i>");
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect));
|
||||
|
||||
// At the beginning of your end step, target opponent creates a 1/1 black Rat creature token with deathtouch.
|
||||
Ability ability = new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, new CreateTokenTargetEffect(new DeathtouchRatToken()), TargetController.YOU, null, false);
|
||||
Target target = new TargetOpponent();
|
||||
ability.addTarget(target);
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever a Rat dies, put a +1/+1 counter on each Cat you control.
|
||||
Effect effect2 = new AddCountersAllEffect(CounterType.P1P1.createInstance(), filterCat);
|
||||
effect2.setText("put a +1/+1 counter on each Cat you control");
|
||||
this.addAbility(new DiesCreatureTriggeredAbility(effect2, false, filterRat));
|
||||
}
|
||||
|
||||
public HungryLynx(final HungryLynx card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HungryLynx copy() {
|
||||
return new HungryLynx(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -157,6 +157,7 @@ class KarnLiberatedEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
game.addDelayedTriggeredAbility(new KarnLiberatedDelayedTriggeredAbility(exileId), source);
|
||||
game.setStartingPlayerId(source.getControllerId());
|
||||
game.start(null);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
146
Mage.Sets/src/mage/cards/n/NazahnReveredBladesmith.java
Normal file
146
Mage.Sets/src/mage/cards/n/NazahnReveredBladesmith.java
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* 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.n;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksCreatureYouControlTriggeredAbility;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.TapTargetEffect;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInHandOrOnBattlefieldEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
import mage.filter.predicate.permanent.EquippedPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public class NazahnReveredBladesmith extends CardImpl {
|
||||
|
||||
private static final FilterControlledCreaturePermanent equippedFilter = new FilterControlledCreaturePermanent("equipped creatures you control");
|
||||
|
||||
static {
|
||||
equippedFilter.add(new EquippedPredicate());
|
||||
equippedFilter.add(new ControllerPredicate(TargetController.YOU));
|
||||
}
|
||||
|
||||
private static final FilterCard filter = new FilterCard("Equipment card");
|
||||
|
||||
static {
|
||||
filter.add(new CardTypePredicate(CardType.ARTIFACT));
|
||||
filter.add(new SubtypePredicate(SubType.EQUIPMENT));
|
||||
}
|
||||
|
||||
public NazahnReveredBladesmith(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}{W}");
|
||||
|
||||
addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add("Cat");
|
||||
this.subtype.add("Artificer");
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// When Nazahn, Revered Bladesmith enters the battlefield, search your library for an Equipment card and reveal it. If you reveal a card named Hammer of Nazahn this way, put it onto the battlefield. Otherwise, put that card into your hand. Then shuffle your library.
|
||||
TargetCardInLibrary target = new TargetCardInLibrary(1, 1, filter);
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new SearchLibraryPutInHandOrOnBattlefieldEffect(target, true, true, "Hammer of Nazahn"), true));
|
||||
|
||||
// Whenever an equipped creature you control attacks, you may tap target creature defending player controls.
|
||||
Ability ability = new AttacksCreatureYouControlTriggeredAbility(new NazahnTapEffect(), false, equippedFilter, true);
|
||||
ability.addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature defending player controls")));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void adjustTargets(Ability ability, Game game) {
|
||||
if (ability instanceof AttacksCreatureYouControlTriggeredAbility) {
|
||||
FilterCreaturePermanent filterDefender = new FilterCreaturePermanent("creature defending player controls");
|
||||
for (Effect effect : ability.getEffects()) {
|
||||
if (effect instanceof NazahnTapEffect) {
|
||||
filterDefender.add(new ControllerIdPredicate(game.getCombat().getDefendingPlayerId(effect.getTargetPointer().getFirst(game, ability), game)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
ability.getTargets().clear();
|
||||
TargetCreaturePermanent target = new TargetCreaturePermanent(filterDefender);
|
||||
ability.addTarget(target);
|
||||
}
|
||||
}
|
||||
|
||||
public NazahnReveredBladesmith(final NazahnReveredBladesmith card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NazahnReveredBladesmith copy() {
|
||||
return new NazahnReveredBladesmith(this);
|
||||
}
|
||||
}
|
||||
|
||||
class NazahnTapEffect extends TapTargetEffect {
|
||||
|
||||
NazahnTapEffect() {
|
||||
super();
|
||||
}
|
||||
|
||||
NazahnTapEffect(final NazahnTapEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NazahnTapEffect copy() {
|
||||
return new NazahnTapEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getFirstTarget());
|
||||
if (permanent != null) {
|
||||
permanent.tap(game);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
189
Mage.Sets/src/mage/cards/p/PatronOfTheVein.java
Normal file
189
Mage.Sets/src/mage/cards/p/PatronOfTheVein.java
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
import mage.abilities.effects.common.ExileTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public class PatronOfTheVein extends CardImpl {
|
||||
|
||||
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("creature an opponent controls");
|
||||
|
||||
static {
|
||||
filter.add(new ControllerPredicate(TargetController.OPPONENT));
|
||||
}
|
||||
|
||||
public PatronOfTheVein(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{B}{B}");
|
||||
|
||||
this.subtype.add("Vampire");
|
||||
this.subtype.add("Shaman");
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// When Patron of the Vein enters the battlefield, destroy target creature an opponent controls.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new DestroyTargetEffect(), false);
|
||||
ability.addTarget(new TargetCreaturePermanent(filter));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Whenever a creature an opponent controls dies, exile it and put a +1/+1 counter on each Vampire you control.
|
||||
Ability ability2 = new PatronOfTheVeinCreatureDiesTriggeredAbility();
|
||||
this.addAbility(ability2);
|
||||
}
|
||||
|
||||
public PatronOfTheVein(final PatronOfTheVein card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatronOfTheVein copy() {
|
||||
return new PatronOfTheVein(this);
|
||||
}
|
||||
}
|
||||
|
||||
class PatronOfTheVeinCreatureDiesTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public PatronOfTheVeinCreatureDiesTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new PatronOfTheVeinExileCreatureEffect(), false);
|
||||
}
|
||||
|
||||
public PatronOfTheVeinCreatureDiesTriggeredAbility(final PatronOfTheVeinCreatureDiesTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatronOfTheVeinCreatureDiesTriggeredAbility copy() {
|
||||
return new PatronOfTheVeinCreatureDiesTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (((ZoneChangeEvent) event).isDiesEvent()) {
|
||||
if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) {
|
||||
Card creature = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||
if (creature != null && creature.isCreature()) {
|
||||
for (Effect effect : this.getEffects()) {
|
||||
effect.setTargetPointer(new FixedTarget(creature.getId()));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever a creature an opponent controls dies, exile it and put a +1/+1 counter on each Vampire you control";
|
||||
}
|
||||
}
|
||||
|
||||
class PatronOfTheVeinExileCreatureEffect extends OneShotEffect {
|
||||
|
||||
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent();
|
||||
|
||||
static {
|
||||
filter.add(new SubtypePredicate(SubType.VAMPIRE));
|
||||
}
|
||||
|
||||
public PatronOfTheVeinExileCreatureEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "exile it and put a +1/+1 counter on each Vampire you control";
|
||||
}
|
||||
|
||||
public PatronOfTheVeinExileCreatureEffect(final PatronOfTheVeinExileCreatureEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PatronOfTheVeinExileCreatureEffect copy() {
|
||||
return new PatronOfTheVeinExileCreatureEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
MageObject sourceObject = source.getSourceObject(game);
|
||||
Card card = game.getCard(this.getTargetPointer().getFirst(game, source));
|
||||
|
||||
if (card != null) {
|
||||
Effect effect = new ExileTargetEffect();
|
||||
effect.setTargetPointer(new FixedTarget(card.getId()));
|
||||
effect.apply(game, source);
|
||||
}
|
||||
|
||||
for (Permanent permanent : game.getState().getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) {
|
||||
permanent.addCounters(CounterType.P1P1.createInstance(), source, game);
|
||||
game.informPlayers(sourceObject.getName() + ": Put a +1/+1 counter on " + permanent.getLogName());
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,6 @@
|
|||
|
||||
package mage.cards.p;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
|
|
@ -42,9 +41,12 @@ import mage.constants.CardType;
|
|||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.mageobject.CardTypePredicate;
|
||||
import mage.target.common.TargetControlledCreaturePermanent;
|
||||
import mage.target.common.TargetControlledPermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Viserion, North
|
||||
|
|
@ -53,6 +55,10 @@ public class PistonSledge extends CardImpl {
|
|||
|
||||
private static FilterControlledPermanent filter = new FilterControlledPermanent("an artifact");
|
||||
|
||||
static {
|
||||
filter.add(new CardTypePredicate(CardType.ARTIFACT));
|
||||
}
|
||||
|
||||
public PistonSledge (UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}");
|
||||
this.subtype.add("Equipment");
|
||||
|
|
|
|||
116
Mage.Sets/src/mage/cards/q/QasaliSlingers.java
Normal file
116
Mage.Sets/src/mage/cards/q/QasaliSlingers.java
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/*
|
||||
* 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.q;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
import mage.abilities.keyword.ReachAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public class QasaliSlingers extends CardImpl {
|
||||
|
||||
public QasaliSlingers(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{4}{G}");
|
||||
|
||||
this.subtype.add("Cat");
|
||||
this.subtype.add("Warrior");
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(5);
|
||||
|
||||
// Reach
|
||||
this.addAbility(ReachAbility.getInstance());
|
||||
|
||||
// Whenever Qasali Slingers or another Cat enters the battlefield under your control, you may destroy target artifact or enchantment.
|
||||
this.addAbility(new QasaliSlingersTriggeredAbility());
|
||||
}
|
||||
|
||||
public QasaliSlingers(final QasaliSlingers card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QasaliSlingers copy() {
|
||||
return new QasaliSlingers(this);
|
||||
}
|
||||
}
|
||||
|
||||
class QasaliSlingersTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public QasaliSlingersTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new DestroyTargetEffect(), true);
|
||||
this.addTarget(new TargetPermanent(StaticFilters.ARTIFACT_OR_ENCHANTMENT_PERMANENT));
|
||||
}
|
||||
|
||||
public QasaliSlingersTriggeredAbility(final QasaliSlingersTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public QasaliSlingersTriggeredAbility copy() {
|
||||
return new QasaliSlingersTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
if (permanent != null) {
|
||||
if (permanent.getId().equals(this.getSourceId())) {
|
||||
return true;
|
||||
}
|
||||
if (permanent.hasSubtype(SubType.CAT, game) && permanent.getControllerId().equals(this.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever {this} or another Cat enters the battlefield under your control, you may destroy target artifact or enchantment.";
|
||||
}
|
||||
}
|
||||
127
Mage.Sets/src/mage/cards/s/ScalelordReckoner.java
Normal file
127
Mage.Sets/src/mage/cards/s/ScalelordReckoner.java
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.DestroyTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterNonlandPermanent;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.filter.predicate.permanent.ControllerIdPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public class ScalelordReckoner extends CardImpl {
|
||||
|
||||
public ScalelordReckoner(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{W}{W}");
|
||||
|
||||
this.subtype.add("Dragon");
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, destroy target nonland permanent that player controls.
|
||||
this.addAbility(new ScalelardReckonerTriggeredAbility(new DestroyTargetEffect()));
|
||||
}
|
||||
|
||||
public ScalelordReckoner(final ScalelordReckoner card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalelordReckoner copy() {
|
||||
return new ScalelordReckoner(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ScalelardReckonerTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("Dragon creature you control");
|
||||
|
||||
static {
|
||||
filter.add(new SubtypePredicate(SubType.DRAGON));
|
||||
}
|
||||
|
||||
public ScalelardReckonerTriggeredAbility(Effect effect) {
|
||||
super(Zone.BATTLEFIELD, new DestroyTargetEffect(), false);
|
||||
}
|
||||
|
||||
public ScalelardReckonerTriggeredAbility(final ScalelardReckonerTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ScalelardReckonerTriggeredAbility copy() {
|
||||
return new ScalelardReckonerTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == EventType.TARGETED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (game.getOpponents(this.controllerId).contains(event.getPlayerId())) {
|
||||
Permanent creature = game.getPermanent(event.getTargetId());
|
||||
if (creature != null && filter.match(creature, getSourceId(), getControllerId(), game)) {
|
||||
FilterNonlandPermanent filter = new FilterNonlandPermanent("nonland permanent that player controls");
|
||||
filter.add(new ControllerIdPredicate(event.getPlayerId()));
|
||||
// filter.setMessage("nonland permanent controlled by " + game.getPlayer(event.getTargetId()).getLogName());
|
||||
this.getTargets().clear();
|
||||
this.addTarget(new TargetPermanent(filter));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, destroy target nonland permanent that player controls.";
|
||||
}
|
||||
}
|
||||
180
Mage.Sets/src/mage/cards/t/TaigamSidisisHand.java
Normal file
180
Mage.Sets/src/mage/cards/t/TaigamSidisisHand.java
Normal file
|
|
@ -0,0 +1,180 @@
|
|||
/*
|
||||
* 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.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.common.ExileFromGraveCost;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.SkipDrawStepEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public class TaigamSidisisHand extends CardImpl {
|
||||
|
||||
public TaigamSidisisHand(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}");
|
||||
|
||||
addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add("Human");
|
||||
this.subtype.add("Wizard");
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Skip your draw step.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new SkipDrawStepEffect()));
|
||||
|
||||
// At the beginning of your upkeep, look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard.
|
||||
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new TaigamSidisisHandDrawEffect(), TargetController.YOU, false));
|
||||
|
||||
// {B}, {T}, Exile X cards from your graveyard: Target creature gets -X/-X until end of turn.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new TaigamSidisisHandEffect(), new ManaCostsImpl("{B}"));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addCost(new ExileFromGraveCost(new TargetCardInYourGraveyard(0, Integer.MAX_VALUE, new FilterCard("cards from your graveyard"))));
|
||||
ability.addTarget(new TargetCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
public TaigamSidisisHand(final TaigamSidisisHand card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaigamSidisisHand copy() {
|
||||
return new TaigamSidisisHand(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TaigamSidisisHandEffect extends OneShotEffect {
|
||||
|
||||
public TaigamSidisisHandEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "creature gets -X/-X until end of turn";
|
||||
}
|
||||
|
||||
public TaigamSidisisHandEffect(final TaigamSidisisHandEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaigamSidisisHandEffect copy() {
|
||||
return new TaigamSidisisHandEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
Permanent targetCreature = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
|
||||
if (targetCreature != null) {
|
||||
int amount = 0;
|
||||
for (Cost cost : source.getCosts()) {
|
||||
if (cost instanceof ExileFromGraveCost) {
|
||||
amount = ((ExileFromGraveCost) cost).getExiledCards().size();
|
||||
ContinuousEffect effect = new BoostTargetEffect(-amount, -amount, Duration.EndOfTurn);
|
||||
effect.setTargetPointer(new FixedTarget(source.getTargets().getFirstTarget()));
|
||||
game.addEffect(effect, source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class TaigamSidisisHandDrawEffect extends OneShotEffect {
|
||||
|
||||
public TaigamSidisisHandDrawEffect() {
|
||||
super(Outcome.DrawCard);
|
||||
this.staticText = "Look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard";
|
||||
}
|
||||
|
||||
public TaigamSidisisHandDrawEffect(final TaigamSidisisHandDrawEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TaigamSidisisHandDrawEffect copy() {
|
||||
return new TaigamSidisisHandDrawEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
|
||||
if (controller != null) {
|
||||
Cards cards = new CardsImpl();
|
||||
cards.addAll(controller.getLibrary().getTopCards(game, 3));
|
||||
if (!cards.isEmpty()) {
|
||||
controller.lookAtCards("Taigam, Sidisi's Hand", cards, game);
|
||||
TargetCard target = new TargetCard(Zone.LIBRARY, new FilterCard("card to put in your hand"));
|
||||
if (controller.choose(Outcome.Benefit, cards, target, game)) {
|
||||
Card card = cards.get(target.getFirstTarget(), game);
|
||||
if (card != null) {
|
||||
controller.moveCards(card, Zone.HAND, source, game);
|
||||
cards.remove(card);
|
||||
}
|
||||
}
|
||||
controller.moveCards(cards, Zone.GRAVEYARD, source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
139
Mage.Sets/src/mage/cards/t/TeferisProtection.java
Normal file
139
Mage.Sets/src/mage/cards/t/TeferisProtection.java
Normal file
|
|
@ -0,0 +1,139 @@
|
|||
/*
|
||||
* 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.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileSpellEffect;
|
||||
import mage.abilities.effects.common.NameACardEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControllerEffect;
|
||||
import mage.abilities.effects.common.continuous.LifeTotalCantChangeControllerEffect;
|
||||
import mage.abilities.keyword.ProtectionAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.FilterObject;
|
||||
import mage.filter.common.FilterControlledPermanent;
|
||||
import mage.filter.predicate.mageobject.NamePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public class TeferisProtection extends CardImpl {
|
||||
|
||||
public TeferisProtection(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{W}");
|
||||
|
||||
// Until your next turn, your life total can't change, and you have protection from everything. All permanents you control phase out. (While they're phased out, they're treated as though they don't exist. They phase in before you untap during your untap step.)
|
||||
this.getSpellAbility().addEffect(new LifeTotalCantChangeControllerEffect(Duration.UntilYourNextTurn));
|
||||
this.getSpellAbility().addEffect(new TeferisProtectionEffect());
|
||||
this.getSpellAbility().addEffect(new TeferisProtectionPhaseOutEffect());
|
||||
|
||||
// Exile Teferi's Protection.
|
||||
this.getSpellAbility().addEffect(ExileSpellEffect.getInstance());
|
||||
}
|
||||
|
||||
public TeferisProtection(final TeferisProtection card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeferisProtection copy() {
|
||||
return new TeferisProtection(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TeferisProtectionEffect extends OneShotEffect {
|
||||
|
||||
public TeferisProtectionEffect() {
|
||||
super(Outcome.Protect);
|
||||
staticText = "<br/><br/>You have protection from everything<i>(You can't be targeted, dealt damage, or enchanted by anything.)</i>";
|
||||
}
|
||||
|
||||
public TeferisProtectionEffect(final TeferisProtectionEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
String cardName = (String) game.getState().getValue(source.getSourceId().toString() + NameACardEffect.INFO_KEY);
|
||||
if (controller != null) {
|
||||
FilterObject filter = new FilterObject("the name [everything]");
|
||||
filter.add(new NamePredicate("everything"));
|
||||
ContinuousEffect effect = new GainAbilityControllerEffect(new ProtectionAbility(filter), Duration.Custom);
|
||||
game.addEffect(effect, source);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeferisProtectionEffect copy() {
|
||||
return new TeferisProtectionEffect(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TeferisProtectionPhaseOutEffect extends OneShotEffect {
|
||||
|
||||
private FilterControlledPermanent permanentsYouControl = new FilterControlledPermanent("all permanents you control");
|
||||
|
||||
public TeferisProtectionPhaseOutEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "All permanents you control phase out. (While they're phased out, they're treated as though they don't exist. They phase in before you untap during your untap step.)";
|
||||
}
|
||||
|
||||
public TeferisProtectionPhaseOutEffect(final TeferisProtectionPhaseOutEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TeferisProtectionPhaseOutEffect copy() {
|
||||
return new TeferisProtectionPhaseOutEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
for (Permanent permanent : game.getBattlefield().getActivePermanents(permanentsYouControl, controller.getId(), game)) {
|
||||
permanent.phaseOut(game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
113
Mage.Sets/src/mage/cards/t/TraverseTheOutlands.java
Normal file
113
Mage.Sets/src/mage/cards/t/TraverseTheOutlands.java
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
/*
|
||||
* 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.t;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterLandPermanent;
|
||||
import mage.filter.predicate.permanent.ControllerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public class TraverseTheOutlands extends CardImpl {
|
||||
|
||||
public TraverseTheOutlands(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{G}");
|
||||
|
||||
// Search your library for up to X basic land cards, where X is the greatest power among creatures you control. Put those cards onto the battlefield tapped, then shuffle your library.
|
||||
this.getSpellAbility().addEffect(new TraverseTheOutlandsEffect());
|
||||
}
|
||||
|
||||
public TraverseTheOutlands(final TraverseTheOutlands card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraverseTheOutlands copy() {
|
||||
return new TraverseTheOutlands(this);
|
||||
}
|
||||
}
|
||||
|
||||
class TraverseTheOutlandsEffect extends OneShotEffect {
|
||||
|
||||
public TraverseTheOutlandsEffect() {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = "Search your library for up to X basic land cards, where X is the greatest power among creatures you control. Put those cards onto the battlefield tapped, then shuffle your library.";
|
||||
}
|
||||
|
||||
public TraverseTheOutlandsEffect(final TraverseTheOutlandsEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TraverseTheOutlandsEffect copy() {
|
||||
return new TraverseTheOutlandsEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
FilterLandPermanent filter = new FilterLandPermanent();
|
||||
filter.add(new ControllerPredicate(TargetController.YOU));
|
||||
|
||||
List<Permanent> creatures = game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, controller.getId(), game);
|
||||
int amount = 0;
|
||||
for (Permanent creature : creatures) {
|
||||
int power = creature.getPower().getValue();
|
||||
if (amount < power) {
|
||||
amount = power;
|
||||
}
|
||||
}
|
||||
|
||||
TargetCardInLibrary target = new TargetCardInLibrary(0, amount, StaticFilters.FILTER_BASIC_LAND_CARD);
|
||||
if (controller.searchLibrary(target, game)) {
|
||||
controller.moveCards(new CardsImpl(target.getTargets()).getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null);
|
||||
}
|
||||
controller.shuffleLibrary(source, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -47,10 +47,22 @@ public class Commander2017 extends ExpansionSet {
|
|||
super("Commander 2017 Edition", "C17", ExpansionSet.buildDate(2017, 8, 25), SetType.SUPPLEMENTAL);
|
||||
this.blockName = "Command Zone";
|
||||
|
||||
cards.add(new SetCardInfo("Balan, Wandering Knight", 2, Rarity.RARE, mage.cards.b.BalanWanderingKnight.class));
|
||||
cards.add(new SetCardInfo("Bloodsworn Steward", 22, Rarity.RARE, mage.cards.b.BloodswornSteward.class));
|
||||
cards.add(new SetCardInfo("Herald's Horn", 53, Rarity.UNCOMMON, mage.cards.h.HeraldsHorn.class));
|
||||
cards.add(new SetCardInfo("Hungry Lynx", 31, Rarity.RARE, mage.cards.h.HungryLynx.class));
|
||||
cards.add(new SetCardInfo("Crimson Honor Guard", 23, Rarity.RARE, mage.cards.c.CrimsonHonorGuard.class));
|
||||
cards.add(new SetCardInfo("Nazahn, Revered Bladesmith", 44, Rarity.MYTHIC, mage.cards.n.NazahnReveredBladesmith.class));
|
||||
cards.add(new SetCardInfo("O-Kagachi, Vengeful Kami", 45, Rarity.MYTHIC, mage.cards.o.OKagachiVengefulKami.class));
|
||||
cards.add(new SetCardInfo("Patron of the Vein", 20, Rarity.RARE, mage.cards.p.PatronOfTheVein.class));
|
||||
cards.add(new SetCardInfo("Qasali Slingers", 33, Rarity.RARE, mage.cards.q.QasaliSlingers.class));
|
||||
cards.add(new SetCardInfo("Ramos, Dragon Engine", 55, Rarity.MYTHIC, mage.cards.r.RamosDragonEngine.class));
|
||||
cards.add(new SetCardInfo("Scalelord Reckoner", 6, Rarity.RARE, mage.cards.s.ScalelordReckoner.class));
|
||||
cards.add(new SetCardInfo("Taigam, Ojutai Master", 46, Rarity.MYTHIC, mage.cards.t.TaigamOjutaiMaster.class));
|
||||
cards.add(new SetCardInfo("Taigam, Sidisi's Hand", 47, Rarity.RARE, mage.cards.t.TaigamSidisisHand.class));
|
||||
cards.add(new SetCardInfo("Teferi's Protection", 8, Rarity.RARE, mage.cards.t.TeferisProtection.class));
|
||||
cards.add(new SetCardInfo("Traverse the Outlands", 34, Rarity.RARE, mage.cards.t.TraverseTheOutlands.class));
|
||||
cards.add(new SetCardInfo("Wasitora, Nekoru Queen", 49, Rarity.MYTHIC, mage.cards.w.WasitoraNekoruQueen.class));
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -44,6 +44,8 @@ public class HasconPromo2017 extends ExpansionSet {
|
|||
|
||||
private HasconPromo2017() {
|
||||
super("HASCON Promo 2017", "H17", ExpansionSet.buildDate(2017, 9, 8), SetType.JOKESET);
|
||||
cards.add(new ExpansionSet.SetCardInfo("Grimlock, Dinobot Leader", 1, Rarity.MYTHIC, mage.cards.g.GrimlockDinobotLeader.class));
|
||||
cards.add(new ExpansionSet.SetCardInfo("Grimlock, Ferocious King", 1, Rarity.MYTHIC, mage.cards.g.GrimlockFerociousKing.class));
|
||||
cards.add(new ExpansionSet.SetCardInfo("Sword of Dungeons & Dragons", 3, Rarity.MYTHIC, mage.cards.s.SwordOfDungeonsAndDragons.class));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification, are
|
||||
* permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||
* conditions and the following disclaimer.
|
||||
*
|
||||
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||
* of conditions and the following disclaimer in the documentation and/or other materials
|
||||
* provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
|
||||
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* The views and conclusions contained in the software and documentation are those of the
|
||||
* authors and should not be interpreted as representing official policies, either expressed
|
||||
* or implied, of BetaSteward_at_googlemail.com.
|
||||
*/
|
||||
package mage.abilities.condition.common;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Describes condition when creature is equipped with more than one Equipment.
|
||||
*
|
||||
* @author Saga
|
||||
*/
|
||||
public enum EquippedMultipleSourceCondition implements Condition {
|
||||
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getBattlefield().getPermanent(source.getSourceId());
|
||||
int countEquipped = 0;
|
||||
if (permanent != null) {
|
||||
for (UUID uuid : permanent.getAttachments()) {
|
||||
Permanent attached = game.getBattlefield().getPermanent(uuid);
|
||||
if (attached != null && attached.getSubtype(game).contains("Equipment")) {
|
||||
countEquipped++;
|
||||
if (countEquipped >= 2) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "has multiple Equipments attached";
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,159 @@
|
|||
/*
|
||||
* 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.abilities.effects.common.search;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.SearchEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LokiX, BetaSteward_at_googlemail.com (spjspj)
|
||||
*/
|
||||
public class SearchLibraryPutInHandOrOnBattlefieldEffect extends SearchEffect {
|
||||
|
||||
private boolean revealCards = false;
|
||||
private boolean forceShuffle;
|
||||
private String rulePrefix;
|
||||
private String nameToPutOnBattlefield = null;
|
||||
|
||||
public SearchLibraryPutInHandOrOnBattlefieldEffect(TargetCardInLibrary target, String nameToPutOnBattlefield) {
|
||||
this(target, false, true, nameToPutOnBattlefield);
|
||||
}
|
||||
|
||||
public SearchLibraryPutInHandOrOnBattlefieldEffect(TargetCardInLibrary target, boolean revealCards, String nameToPutOnBattlefield) {
|
||||
this(target, revealCards, true, nameToPutOnBattlefield);
|
||||
}
|
||||
|
||||
public SearchLibraryPutInHandOrOnBattlefieldEffect(TargetCardInLibrary target, boolean revealCards, boolean forceShuffle, String nameToPutOnBattlefield) {
|
||||
this(target, revealCards, forceShuffle, "search your library for ", nameToPutOnBattlefield);
|
||||
}
|
||||
|
||||
public SearchLibraryPutInHandOrOnBattlefieldEffect(TargetCardInLibrary target, boolean revealCards, boolean forceShuffle, String rulePrefix, String nameToPutOnBattlefield) {
|
||||
super(target, Outcome.DrawCard);
|
||||
this.revealCards = revealCards;
|
||||
this.forceShuffle = forceShuffle;
|
||||
this.rulePrefix = rulePrefix;
|
||||
this.nameToPutOnBattlefield = nameToPutOnBattlefield;
|
||||
setText();
|
||||
}
|
||||
|
||||
public SearchLibraryPutInHandOrOnBattlefieldEffect(final SearchLibraryPutInHandOrOnBattlefieldEffect effect) {
|
||||
super(effect);
|
||||
this.revealCards = effect.revealCards;
|
||||
this.forceShuffle = effect.forceShuffle;
|
||||
this.rulePrefix = effect.rulePrefix;
|
||||
this.nameToPutOnBattlefield = effect.nameToPutOnBattlefield;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SearchLibraryPutInHandOrOnBattlefieldEffect copy() {
|
||||
return new SearchLibraryPutInHandOrOnBattlefieldEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
target.clearChosen();
|
||||
if (controller.searchLibrary(target, game)) {
|
||||
if (!target.getTargets().isEmpty()) {
|
||||
Cards cards = new CardsImpl();
|
||||
boolean askToPutOntoBf = false;
|
||||
Card cardToPutOnBf = null;
|
||||
for (UUID cardId : target.getTargets()) {
|
||||
Card card = game.getCard(cardId);
|
||||
if (card != null) {
|
||||
if (card.getName().equals(nameToPutOnBattlefield)) {
|
||||
askToPutOntoBf = true;
|
||||
cardToPutOnBf = card;
|
||||
} else {
|
||||
cards.add(card);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (askToPutOntoBf && cardToPutOnBf != null) {
|
||||
if (controller.chooseUse(Outcome.PutCardInPlay, "Put " + cardToPutOnBf.getLogName() + " onto the battlefield instead?", source, game)) {
|
||||
controller.moveCards(cards, Zone.BATTLEFIELD, source, game);
|
||||
} else {
|
||||
controller.moveCards(cards, Zone.HAND, source, game);
|
||||
}
|
||||
} else {
|
||||
controller.moveCards(cards, Zone.HAND, source, game);
|
||||
}
|
||||
|
||||
if (revealCards) {
|
||||
String name = "Reveal";
|
||||
Card sourceCard = game.getCard(source.getSourceId());
|
||||
if (sourceCard != null) {
|
||||
name = sourceCard.getIdName();
|
||||
}
|
||||
controller.revealCards(name, cards, game);
|
||||
}
|
||||
}
|
||||
controller.shuffleLibrary(source, game);
|
||||
return true;
|
||||
}
|
||||
if (forceShuffle) {
|
||||
controller.shuffleLibrary(source, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setText() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(rulePrefix);
|
||||
if (target.getNumberOfTargets() == 0 && target.getMaxNumberOfTargets() > 0) {
|
||||
sb.append("up to ").append(CardUtil.numberToText(target.getMaxNumberOfTargets())).append(' ');
|
||||
sb.append(target.getTargetName()).append(revealCards ? ", reveal them," : "").append(" and put them into your hand");
|
||||
} else {
|
||||
sb.append("a ").append(target.getTargetName()).append(revealCards ? ", reveal it," : "").append(" and put that card into your hand");
|
||||
}
|
||||
if (nameToPutOnBattlefield != null) {
|
||||
sb.append(". If you reveal a card named " + nameToPutOnBattlefield + " you may put it onto the battlefield instead");
|
||||
}
|
||||
if (forceShuffle) {
|
||||
sb.append(". Then shuffle your library");
|
||||
} else {
|
||||
sb.append(". If you do, shuffle your library");
|
||||
}
|
||||
staticText = sb.toString();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -57,6 +57,7 @@ public enum SubType {
|
|||
ATOG("Atog", SubTypeSet.CreatureType, false),
|
||||
ATAT("AT-AT", SubTypeSet.CreatureType, true),
|
||||
AUROCHS("Aurochs", SubTypeSet.CreatureType, false),
|
||||
AUTOBOT("Autobot", SubTypeSet.CreatureType, true), // H17, Grimlock
|
||||
AVATAR("Avatar", SubTypeSet.CreatureType, false),
|
||||
// B
|
||||
BADGER("Badger", SubTypeSet.CreatureType, false),
|
||||
|
|
@ -100,6 +101,7 @@ public enum SubType {
|
|||
DEMON("Demon", SubTypeSet.CreatureType, false),
|
||||
DESERTER("Deserter", SubTypeSet.CreatureType, false),
|
||||
DEVIL("Devil", SubTypeSet.CreatureType, false),
|
||||
DINOSAUR("Dinosaur", SubTypeSet.CreatureType, true), // only Grimlock right now, until Ixalan
|
||||
DJINN("Djinn", SubTypeSet.CreatureType, false),
|
||||
DRAGON("Dragon", SubTypeSet.CreatureType, false),
|
||||
DRAKE("Drake", SubTypeSet.CreatureType, false),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* 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.filter.predicate.permanent;
|
||||
|
||||
import mage.filter.predicate.Predicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Saga
|
||||
*/
|
||||
public class TransformedPredicate implements Predicate<Permanent> {
|
||||
|
||||
@Override
|
||||
public boolean apply(Permanent input, Game game) {
|
||||
return input.isTransformed();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Transformed" ;
|
||||
}
|
||||
}
|
||||
|
|
@ -442,6 +442,8 @@ public interface Game extends MageItem, Serializable {
|
|||
|
||||
UUID getStartingPlayerId();
|
||||
|
||||
void setStartingPlayerId(UUID startingPlayerId);
|
||||
|
||||
void saveRollBackGameState();
|
||||
|
||||
boolean canRollbackTurns(int turnsToRollback);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,10 @@
|
|||
*/
|
||||
package mage.game;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
import mage.MageException;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.*;
|
||||
|
|
@ -92,11 +96,6 @@ import mage.watchers.Watchers;
|
|||
import mage.watchers.common.*;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.*;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
public abstract class GameImpl implements Game, Serializable {
|
||||
|
||||
private static final int ROLLBACK_TURNS_MAX = 4;
|
||||
|
|
@ -855,31 +854,33 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
}
|
||||
|
||||
//20091005 - 103.2
|
||||
TargetPlayer targetPlayer = new TargetPlayer();
|
||||
targetPlayer.setTargetName("starting player");
|
||||
Player choosingPlayer = null;
|
||||
if (choosingPlayerId != null) {
|
||||
choosingPlayer = this.getPlayer(choosingPlayerId);
|
||||
if (choosingPlayer != null && !choosingPlayer.isInGame()) {
|
||||
choosingPlayer = null;
|
||||
if (startingPlayerId == null) {
|
||||
TargetPlayer targetPlayer = new TargetPlayer();
|
||||
targetPlayer.setTargetName("starting player");
|
||||
if (choosingPlayerId != null) {
|
||||
choosingPlayer = this.getPlayer(choosingPlayerId);
|
||||
if (choosingPlayer != null && !choosingPlayer.isInGame()) {
|
||||
choosingPlayer = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (choosingPlayer == null) {
|
||||
choosingPlayerId = pickChoosingPlayer();
|
||||
if (choosingPlayerId == null) {
|
||||
if (choosingPlayer == null) {
|
||||
choosingPlayerId = pickChoosingPlayer();
|
||||
if (choosingPlayerId == null) {
|
||||
return;
|
||||
}
|
||||
choosingPlayer = getPlayer(choosingPlayerId);
|
||||
}
|
||||
if (choosingPlayer == null) {
|
||||
return;
|
||||
}
|
||||
getState().setChoosingPlayerId(choosingPlayerId); // needed to start/stop the timer if active
|
||||
if (choosingPlayer.choose(Outcome.Benefit, targetPlayer, null, this)) {
|
||||
startingPlayerId = targetPlayer.getTargets().get(0);
|
||||
} else if (getState().getPlayers().size() < 3) {
|
||||
// not possible to choose starting player, choosing player has probably conceded, so stop here
|
||||
return;
|
||||
}
|
||||
choosingPlayer = getPlayer(choosingPlayerId);
|
||||
}
|
||||
if (choosingPlayer == null) {
|
||||
return;
|
||||
}
|
||||
getState().setChoosingPlayerId(choosingPlayerId); // needed to start/stop the timer if active
|
||||
if (choosingPlayer.choose(Outcome.Benefit, targetPlayer, null, this)) {
|
||||
startingPlayerId = targetPlayer.getTargets().get(0);
|
||||
} else if (getState().getPlayers().size() < 3) {
|
||||
// not possible to choose starting player, choosing player has probably conceded, so stop here
|
||||
return;
|
||||
}
|
||||
if (startingPlayerId == null) {
|
||||
// choose any available player as starting player
|
||||
|
|
@ -898,15 +899,7 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
logger.debug("Starting player not found. playerId:" + startingPlayerId);
|
||||
return;
|
||||
}
|
||||
StringBuilder message = new StringBuilder(choosingPlayer.getLogName()).append(" chooses that ");
|
||||
if (choosingPlayer.getId().equals(startingPlayerId)) {
|
||||
message.append("he or she");
|
||||
} else {
|
||||
message.append(startingPlayer.getLogName());
|
||||
}
|
||||
message.append(" takes the first turn");
|
||||
|
||||
this.informPlayers(message.toString());
|
||||
sendStartMessage(choosingPlayer, startingPlayer);
|
||||
|
||||
//20091005 - 103.3
|
||||
int startingHandSize = 7;
|
||||
|
|
@ -1019,6 +1012,21 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
|
||||
}
|
||||
|
||||
protected void sendStartMessage(Player choosingPlayer, Player startingPlayer) {
|
||||
StringBuilder message = new StringBuilder();
|
||||
if (choosingPlayer != null) {
|
||||
message.append(choosingPlayer.getLogName()).append(" chooses that ");
|
||||
}
|
||||
if (choosingPlayer != null && choosingPlayer.getId().equals(startingPlayer.getId())) {
|
||||
message.append("he or she");
|
||||
} else {
|
||||
message.append(startingPlayer.getLogName());
|
||||
}
|
||||
message.append(" takes the first turn");
|
||||
|
||||
this.informPlayers(message.toString());
|
||||
}
|
||||
|
||||
protected UUID findWinnersAndLosers() {
|
||||
UUID winnerIdFound = null;
|
||||
for (Player player : state.getPlayers().values()) {
|
||||
|
|
@ -2834,6 +2842,11 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
return startingPlayerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStartingPlayerId(UUID startingPlayerId) {
|
||||
this.startingPlayerId = startingPlayerId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLife() {
|
||||
return startLife;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* 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.game.permanent.token;
|
||||
import mage.constants.CardType;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.keyword.DeathtouchAbility;
|
||||
import mage.constants.SubType;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Saga
|
||||
*/
|
||||
public class DeathtouchRatToken extends Token {
|
||||
|
||||
public DeathtouchRatToken() {
|
||||
super("Rat", "1/1 black Rat creature token with deathtouch");
|
||||
this.setExpansionSetCodeForImage("C17");
|
||||
this.cardType.add(CardType.CREATURE);
|
||||
this.color.setBlack(true);
|
||||
this.subtype.add(SubType.RAT);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
this.addAbility(DeathtouchAbility.getInstance());
|
||||
}
|
||||
}
|
||||
|
|
@ -31684,32 +31684,62 @@ Forest|Planechase Anthology|153|L||Basic Land - Forest||||
|
|||
Forest|Planechase Anthology|154|L||Basic Land - Forest||||
|
||||
Forest|Planechase Anthology|155|L||Basic Land - Forest||||
|
||||
Forest|Planechase Anthology|156|L||Basic Land - Forest||||
|
||||
The Ur-Dragon|Commander 2017|48|M|{4}{W}{U}{B}{R}{G}|Legendary Creature - Dragon Avatar|10|10|<i>Eminence</i> - As long as The Ur-Dragon is in the command zone or on the battlefield, other Dragon spells you cast cost {1} less to cast.$Flying$Whenever one or more Dragons you control attack, draw that many cards, then you may put a permanent card from your hand onto the battlefield|
|
||||
O-Kagachi, Vengeful Kami|Commander 2017|45|M|{1}{W}{U}{B}{R}{G}|Legendary Creature - Dragon Spirit|6|6|Flying, Trample$Whenever O-Kagachi, Vengeful Kami deals combat damage to a player, if that player attacked you during his or her last turn, exile target nonland permanent that player controls|
|
||||
Arahbo, Roar of the World|Commander 2017|35|M|{3}{G}{W}|Legendary Creature - Cat Avatar|5|5|Eminence — At the beginning of combat on your turn, if Arahbo, Roar of the World is in the command zone or on the battlefield, another target Cat you control gets +3/+3 until end of turn.$Whenever another Cat you control attacks, you may pay {1}{G}{W}. If you do, it gains trample and gets +X/+X until end of turn, where X is its power.|
|
||||
Taigam, Ojutai Master|Commander 2017|46|R|{2}{W}{U}|Legendary Creature - Human Monk|3|4|Instant, sorcery, and Dragon spells you control can't be countered by spells or abilities.$Whenever you cast an instant or sorcery spell from your hand, if Taigam, Ojutai Master attacked this turn, that spell gains rebound. <i>(Exile the spell as it resolves. At the beginning of your next upkeep, you may cast that card from exile without paying its mana cost.)</i>|
|
||||
Wasitora, Nekoru Queen|Commander 2017|49|M|{2}{B}{R}{G}|Legendary Creature - Cat Dragon|5|4|Flying, trample$Whenever Wasitora, Nekoru Queen deals combat damage to a player, that player sacrifices a creature. If the player can't, you create a 3/3 black, red, and green Cat Dragon creature token with flying|
|
||||
Ramos, Dragon Engine|Commander 2017|55|M|{6}|Legendary Artifact Creature - Dragon|4|4|Flying$Whenever you cast a spell, put a +1/+1 counter on Ramos, Dragon Engine for each of that spell's colors. Remove five +1/+1 counters from Ramos: Add {W}{W}{U}{U}{B}{B}{R}{R}{G}{G} to your mana pool. Activate this ability only once each turn.|
|
||||
Licia, Sanguine Tribune|Commander 2017|40|M|{5}{R}{W}{B}|Legendary Creature - Vampire Soldier|4|4|Licia, Sanguine Tribune costs 1 less to cast for each 1 life you gained this turn.$First strike, lifelink$Pay 5 life: Put three +1/+1 counters on Licia. Activate this ability only on your turn and only once each turn.|
|
||||
Mirri, Weatherlight Duelist|Commander 2017|43|M|{1}{G}{W}|Legendary Creature - Cat Warrior|3|2|First Strike$Whenever Mirri, Weatherlight Duelist attacks, each opponent can't block with more than one creature this combat.$As long as Mirri, Weatherlight Duelist is tapped, no more than one creature can attack you each combat.|
|
||||
Nazahn, Revered Bladesmith|Commander 2017|44|M|{4}{G}{W}|Legendary Creature - Cat Artificer|5|4|When Nazahn, Revered Bladesmith enters the battlefield, search your library for an Equipment card and reveal it. If you reveal a card named Hammer of Nazahn this way, put it onto the battlefield. Otherwise, put that card into your hand. Then shuffle your library.$Whenever an equipped creature you control attacks, you may tap target creature defending player controls.|
|
||||
Bloodforged War Axe|Commander 2017|50|R|{1}|Artifact - Equipment|||Equipped creature gets +2/+0.$Whenever equipped creature deals combat damage to a player, create a token that's a copy of Bloodforged War Axe.$Equip 2|
|
||||
Hammer of Nazahn|Commander 2017|51|R|{4}|Legendary Artifact - Equipment|||Whenever Hammer of Nazahn or another Equipment enters the battlefield under your control, you may attach that Equipment to target creature you control.$Equipped creature gets +2/+0 and has indestructible.$Equip 4|
|
||||
Herald's Horn|Commander 2017|53|U|{3}|Artifact|||When Herald's Horn enters the battlefield, choose a creature type.$Creature spells you cast of the chosen type cost 1 less.$At the beginning of your upkeep, look at the top card of your library. If it's a creature card of the chosen type, you may reveal it and put it into your hand.|
|
||||
Fractured Identity|Commander 2017|37|R|{3}{W}{U}|Sorcery|||Exile target nonland permanent. Each player other than its controller creates a token that's a copy of it.|
|
||||
Path of Ancestry|Commander 2017|56|C||Land|||Path of Ancestry enters the battlefield tapped.$T: Add to your mana pool one mana of any color in your commander's color identity. When that mana is spent to cast a creature spell that shares a creature type with your commander, scry 1.|
|
||||
Fortunate Few|Commander 2017|4|R|{3}{W}{W}|Sorcery|||Choose a nonland permanent you don't control, then each other player chooses a nonland permanent he or she doesn't control that hasn't been chosen this way. Destroy all other nonland permanents.|
|
||||
Scalelord Reckoner|Commander 2017|??|R|{5}{W}{W}|Creature - Dragon|4|4|Flying$Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, destroy target nonland permanent that player controls.|
|
||||
Alms Collector|Commander 2017|1|R|{3}{W}|Creature - Cat Cleric|3|4|Flash$If an opponent would draw two or more cards, instead you and that player each draw a card.|
|
||||
Arahbo, Roar of the World|Commander 2017|35|M|{3}{G}{W}|Legendary Creature - Cat Avatar|5|5|Eminence — At the beginning of combat on your turn, if Arahbo, Roar of the World is in the command zone or on the battlefield, another target Cat you control gets +3/+3 until end of turn.$Whenever another Cat you control attacks, you may pay {1}{G}{W}. If you do, it gains trample and gets +X/+X until end of turn, where X is its power.|
|
||||
Balan, Wandering Knight|Commander 2017|2|R|{2}{W}{W}|Legendary Creature - Cat Knight|3|3|First strike$Balan, Wandering Knight has double strike as long as two or more Equipment are attached to it.$1W: Attach all Equipment you control to Balan.|
|
||||
Stalking Leonin|Commander 2017|7|R|{2}{W}|Creature - Cat Archer|3|3|When Stalking Leonin enters the battlefield, secretly choose an opponent.$Reveal the player you chose: Exile target creature that's attacking you if it's controlled by the chosen player. Activate this ability only once.|
|
||||
Kindred Discovery|Commander 2017|11|R|{3}{U}{U}|Enchantment|||As Kindred Discovery enters the battlefield, choose a creature type.$Whenever a creature you control of the chose type enters the battlefield or attacks, draw a card.|
|
||||
Boneyard Scourge|Commander 2017|15|R|{2}{B}{B}|Creature - Zombie Dragon|4|3|Flying$Whenever a Dragon you control dies while Boneyard Scourge is in your graveyard, you may pay 1B. If you do, return Boneyard Scourge from your graveyard to the battlefield.|
|
||||
Bloodforged War Axe|Commander 2017|50|R|{1}|Artifact - Equipment|||Equipped creature gets +2/+0.$Whenever equipped creature deals combat damage to a player, create a token that's a copy of Bloodforged War Axe.$Equip 2|
|
||||
Bloodline Necromancer|Commander 2017|14|U|{4}{B}|Creature - Vampire Wizard|3|2|Lifelink$When Bloodline Necromancer enters the battlefield, you may return target Vampire or Wizard creature card from your graveyard to the battlefield.|
|
||||
Territorial Hellkite|Commander 2017|??|R|{2}{R}{R}|Creature - Dragon|6|5|Flying, haste$At the beginning of combat on your turn, choose an opponent at random that Territorial Hellkite didn't attack during your last combat. Territorial Hellkite attacks that player this combat if able. If you can't choose an opponent this way, tap Territorial Hellkite.|
|
||||
Summon the Tribe|Commander 2017|32|R|{5}{G}{G}|Instant|||Choose a creature type. Reveal the top card card of your library until you reveal X creatures of the chosen type, where X is the number of creatures you control of the chosen type, and place them onto the battlefield. Shuffle the other revealed cards into your library.|
|
||||
Bloodsworn Steward|Commander 2017|22|R|{2}{R}{R}|Creature - Vampire Knight|4|4|Flying$Commander creatures you control get +2/+2 and have haste.|
|
||||
Boneyard Scourge|Commander 2017|15|R|{2}{B}{B}|Creature - Zombie Dragon|4|3|Flying$Whenever a Dragon you control dies while Boneyard Scourge is in your graveyard, you may pay 1B. If you do, return Boneyard Scourge from your graveyard to the battlefield.|
|
||||
Crimson Honor Guard|Commander 2017|23|R|{3}{R}{R}|Creature - Vampire Knight|4|5|Trample$At the beginning of each player's end step, Crimson Honor Guard deals 4 damage to that player unless he or she controls a commander.|
|
||||
Curse of Bounty|Commander 2017|30|U|{1}{G}|Enchantment - Aura Curse|||Enchant player$Whenever enchanted player is attacked, untap all nonland permanents you control. Each opponent attacking that player untaps all nonland permanents he or she controls.|
|
||||
Curse of Disturbance|Commander 2017|16|U|{2}{B}|Enchantment - Aura Curse|||Enchant player$Whenever enchanted player is attacked, create a 2/2 black Zombie creature token. Each opponent attacking that player does the same.|
|
||||
Curse of Opulence|Commander 2017|24|U|{R}|Enchantment - Aura Curse|||Enchant player$Whenever enchanted player is attacked, create a colorless artifact token named Gold. It has "sacrifice this artifact: Add one mana of any color to your mana pool." Each opponent attacking that player does the same.|
|
||||
Curse of Verbosity|Commander 2017|9|U|{2}{U}|Enchantment - Aura Curse|||Enchant player$Whenever enchanted player is attacked, draw a card. Each opponent attacking that player does the same.|
|
||||
Curse of Vitality|Commander 2017|3|U|{2}{W}|Enchantment - Aura Curse|||Enchant player$Whenever enchanted player is attacked, you gain 2 life. Each opponent attacking that player does the same.|
|
||||
Disrupt Decorum|Commander 2017|25|R|{2}{R}{R}|Sorcery|||Goad all creatures you don't control.$(Until your next turn, those creatures attack each combat if able and attack a player other than you if able.)|
|
||||
Edgar Markov|Commander 2017|36|M|{3}{R}{W}{B}|Legendary Creature - Vampire Knight|4|4|Eminence - Whenever you cast another Vampire spell, if Edgar Markov is in the command zone or on the battlefield, create a 1/1 black Vampire creature token.$First strike, haste$Whenever Edgar Markov attacks, put a +1/+1 counter on each Vampire you control.|
|
||||
Fortunate Few|Commander 2017|4|R|{3}{W}{W}|Sorcery|||Choose a nonland permanent you don't control, then each other player chooses a nonland permanent he or she doesn't control that hasn't been chosen this way. Destroy all other nonland permanents.|
|
||||
Fractured Identity|Commander 2017|37|R|{3}{W}{U}|Sorcery|||Exile target nonland permanent. Each player other than its controller creates a token that's a copy of it.|
|
||||
Galecaster Colossus|Commander 2017|10|R|{5}{U}{U}|Creature - Giant Wizard|5|6|Tap an untapped Wizard you control: Return target non-land permanent you don't control to its owner's hand.|
|
||||
Hammer of Nazahn|Commander 2017|51|R|{4}|Legendary Artifact - Equipment|||Whenever Hammer of Nazahn or another Equipment enters the battlefield under your control, you may attach that Equipment to target creature you control.$Equipped creature gets +2/+0 and has indestructible.$Equip 4|
|
||||
Heirloom Blade|Commander 2017|52|U|{3}|Artifact - Equipment|||Equipped creature gets +3/+1.$Whenever equipped creature dies, you may reveal cards from the top of your library until you reveal a creature card that shares a creature type with it. Put that card into your hand and the rest on the bottom of your library in a random order.$Equip 1|
|
||||
Herald's Horn|Commander 2017|53|U|{3}|Artifact|||When Herald's Horn enters the battlefield, choose a creature type.$Creature spells you cast of the chosen type cost 1 less.$At the beginning of your upkeep, look at the top card of your library. If it's a creature card of the chosen type, you may reveal it and put it into your hand.|
|
||||
Hungry Lynx|Commander 2017|31|R|{1}{G}|Creature - Cat|2|2|Cats you control have protection from Rats. (They can't be blocked, targeted, or dealt damage by Rats.)$At the beginning of your end step, target opponent creates a 1/1 black Rat creature token with deathtouch.$Whenever a Rat dies, put a +1/+1 counter on each Cat you control.|
|
||||
Inalla, Archmage Ritualist|Commander 2017|38|M|{2}{U}{B}{R}|Legendary Creature - Human Wizard|4|5|Eminence - Whenever another nontoken Wizard enters the battlefield under your control, if Inalla, Archmage Ritualist is in the command zone or on the battlefield, you may pay {1}. If you do, create a token that's a copy of that Wizard. The token gains haste. Exile it at the beginning of the next end step.$Tap five untapped Wizards you control: Target player loses 7 life.|
|
||||
Izzet Chemister|Commander 2017|26|R|{2}{R}|Creature - Goblin Chemister|1|3|Haste$R, T: Exile target instant or sorcery card from your graveyard.$1R, T: Sacrifice Izzet Chemister: Cast any number of cards exiled with Izzet Chemister without paying their mana costs.|
|
||||
Kess, Dissident Mage|Commander 2017|39|M|{1}{U}{B}{R}|Legendary Creature - Human Wizard|3|4|Flying$During each of your turns, you may cast an instant or sorcery card from your graveyard. If a card cast this way would be put into your graveyard this turn, exile it instead.|
|
||||
Kheru Mind-Eater|Commander 2017|17|R|{2}{B}|Creature - Vampire|1|3|Menace$Whenever Kheru Mind-Eater deals combat damage to a player, that player exiles a card from his or her hand face down.$You may look at and play cards exiled with Kheru Mind-Eater.|
|
||||
Kindred Boon|Commander 2017|5|R|{2}{W}{W}|Enchantment|||When Kindred Boon enters the battlefield choose a creature type.$1W: Put a divinity counter on target creature you control with the chosen type.$All creatures you control with a divinity counter become indestructible.|
|
||||
Kindred Charge|Commander 2017|27|R|{4}{R}{R}|Sorcery|||Choose a creature type. For each creature you control of the chosen type, create a token that's a copy of that creature. Those tokens gain haste. Exile them at the beginning of the next end step.|
|
||||
Kindred Discovery|Commander 2017|11|R|{3}{U}{U}|Enchantment|||As Kindred Discovery enters the battlefield, choose a creature type.$Whenever a creature you control of the chose type enters the battlefield or attacks, draw a card.|
|
||||
Kindred Dominance|Commander 2017|18|R|{5}{B}{B}|Sorcery|||Choose a creature type. Destroy all creatures that are not the chosen type.|
|
||||
Kindred Summons|Commander 2017|32|R|{5}{G}{G}|Instant|||Choose a creature type. Reveal the top card card of your library until you reveal X creatures of the chosen type, where X is the number of creatures you control of the chosen type, and place them onto the battlefield. Shuffle the other revealed cards into your library.|
|
||||
Licia, Sanguine Tribune|Commander 2017|40|M|{5}{R}{W}{B}|Legendary Creature - Vampire Soldier|4|4|Licia, Sanguine Tribune costs 1 less to cast for each 1 life you gained this turn.$First strike, lifelink$Pay 5 life: Put three +1/+1 counters on Licia. Activate this ability only on your turn and only once each turn.|
|
||||
Magus of the Mind|Commander 2017|12|R|{4}{U}{U}|Creature - Human Wizard|4|5|U, T: Sacrifice Magus of the Mind: Shuffle your library, then exile the top X cards, where X is one plus the number of spells cast this turn. Until end of turn, you may play cards exiled this way without paying their mana costs.|
|
||||
Mairsil, the Pretender|Commander 2017|41|M|{1}{U}{B}{R}|Legendary Creature - Human Wizard|4|4|When Mairsil, the Pretender enters the battlefield, you may exile an artifact or creature card from your hand or graveyard and put a cage counter on it.$Mairsil, the Pretender has all activated abilities of all cards you own in exile with cage counters on them. You may activate each of those abilities only once each turn.|
|
||||
Mathas, Fiend Seeker|Commander 2017|42|M|{R}{W}{B}|Legendary Creature - Vampire|3|3|Menace$At the beginning of your end step, put a bounty counter on target creature an opponent controls. For as long as that creature has a bounty counter on it, it has "When this creature dies, each opponent draws a card and gains 2 life."|
|
||||
Mirri, Weatherlight Duelist|Commander 2017|43|M|{1}{G}{W}|Legendary Creature - Cat Warrior|3|2|First Strike$Whenever Mirri, Weatherlight Duelist attacks, each opponent can't block with more than one creature this combat.$As long as Mirri, Weatherlight Duelist is tapped, no more than one creature can attack you each combat.|
|
||||
Mirror of the Forebears|Commander 2017|54|U|{2}|Artifact|||As Mirror of the Forebears enters the battlefield, choose a creature type.$1: Until end of turn, Mirror of the Forebears becomes a copy of target creature you control of the chosen type, except it's an artifact in addition to its other types.|
|
||||
Nazahn, Revered Bladesmith|Commander 2017|44|M|{4}{G}{W}|Legendary Creature - Cat Artificer|5|4|When Nazahn, Revered Bladesmith enters the battlefield, search your library for an Equipment card and reveal it. If you reveal a card named Hammer of Nazahn this way, put it onto the battlefield. Otherwise, put that card into your hand. Then shuffle your library.$Whenever an equipped creature you control attacks, you may tap target creature defending player controls.|
|
||||
New Blood|Commander 2017|19|R|{2}{B}{B}|Sorcery|||As an additional cost to cast New Blood, tap an untapped Vampire you control.$Gain control of target creature. Change the text of that creature by replacing all instances of one creature type with Vampire.|
|
||||
O-Kagachi, Vengeful Kami|Commander 2017|45|M|{1}{W}{U}{B}{R}{G}|Legendary Creature - Dragon Spirit|6|6|Flying, Trample$Whenever O-Kagachi, Vengeful Kami deals combat damage to a player, if that player attacked you during his or her last turn, exile target nonland permanent that player controls|
|
||||
Path of Ancestry|Commander 2017|56|C||Land|||Path of Ancestry enters the battlefield tapped.$T: Add to your mana pool one mana of any color in your commander's color identity. When that mana is spent to cast a creature spell that shares a creature type with your commander, scry 1.|
|
||||
Patron of the Vein|Commander 2017|20|R|{4}{B}{B}|Creature - Vampire Shaman|4|4|Flying$When Patron of the Vein enters the battlefield, destroy target creature an opponent controls.$Whenever a creature an opponent controls dies, exile it and put a +1/+1 counter on each Vampire you control.|
|
||||
Portal Mage|Commander 2017|13|R|{2}{U}|Creature - Human Wizard|2|2|Flash$If Portal Mage enters the battlefield during the declare attackers step, you may reselect the player or planeswalker that the target attacking creature attacks.|
|
||||
Qasali Slingers|Commander 2017|33|R|{4}{G}|Creature - Cat Warrior|3|5|Reach$Whenever Qasali Slingers or another Cat enters the battlefield under your control, you may destroy target artifact or enchantment.|
|
||||
Ramos, Dragon Engine|Commander 2017|55|M|{6}|Legendary Artifact Creature - Dragon|4|4|Flying$Whenever you cast a spell, put a +1/+1 counter on Ramos, Dragon Engine for each of that spell's colors. Remove five +1/+1 counters from Ramos: Add {W}{W}{U}{U}{B}{B}{R}{R}{G}{G} to your mana pool. Activate this ability only once each turn.|
|
||||
Scalelord Reckoner|Commander 2017|6|R|{5}{W}{W}|Creature - Dragon|4|4|Flying$Whenever a Dragon you control becomes the target of a spell or ability an opponent controls, destroy target nonland permanent that player controls.|
|
||||
Shifting Shadow|Commander 2017|28|R|{2}{R}|Enchantment - Aura|||Enchant creature$Enchanted creature has haste and "At the beginning of your upkeep, destroy this creature. Reveal cards from the top of your library until you reveal a creature card. Put that card onto the battlefield and attach Shifting Shadow to it, then put all other cards revealed this way on the bottom of your library in a random order."|
|
||||
Stalking Leonin|Commander 2017|7|R|{2}{W}|Creature - Cat Archer|3|3|When Stalking Leonin enters the battlefield, secretly choose an opponent.$Reveal the player you chose: Exile target creature that's attacking you if it's controlled by the chosen player. Activate this ability only once.|
|
||||
Taigam, Ojutai Master|Commander 2017|46|R|{2}{W}{U}|Legendary Creature - Human Monk|3|4|Instant, sorcery, and Dragon spells you control can't be countered by spells or abilities.$Whenever you cast an instant or sorcery spell from your hand, if Taigam, Ojutai Master attacked this turn, that spell gains rebound. <i>(Exile the spell as it resolves. At the beginning of your next upkeep, you may cast that card from exile without paying its mana cost.)</i>|
|
||||
Taigam, Sidisi's Hand|Commander 2017|47|R|{3}{U}{B}|Legendary Creature - Human Wizard|3|4|Skip your draw step.$At the beginning of your upkeep, look at the top three cards of your library. Put one of them into your hand and the rest into your graveyard.$B, T, Exile X cards from your graveyard: Target creature gets -X/-X until end of turn.|
|
||||
Teferi's Protection|Commander 2017|8|R|{2}{W}|Instant|||Until your next turn, your life total can't change, and you have protection from everything. All permanents you control phase out. (While they're phased out, they're treated as though they don't exist. They phase in before you untap during your untap step.)$Exile Teferi's Protection.|
|
||||
Territorial Hellkite|Commander 2017|29|R|{2}{R}{R}|Creature - Dragon|6|5|Flying, haste$At the beginning of combat on your turn, choose an opponent at random that Territorial Hellkite didn't attack during your last combat. Territorial Hellkite attacks that player this combat if able. If you can't choose an opponent this way, tap Territorial Hellkite.|
|
||||
The Ur-Dragon|Commander 2017|48|M|{4}{W}{U}{B}{R}{G}|Legendary Creature - Dragon Avatar|10|10|<i>Eminence</i> - As long as The Ur-Dragon is in the command zone or on the battlefield, other Dragon spells you cast cost {1} less to cast.$Flying$Whenever one or more Dragons you control attack, draw that many cards, then you may put a permanent card from your hand onto the battlefield|
|
||||
Traverse the Outlands|Commander 2017|34|R|{4}{G}|Sorcery|||Search your library for up to X basic land cards, where X is the greatest power among creatures you control. Put those cards onto the battlefield tapped, then shuffle your library.|
|
||||
Vindictive Lich|Commander 2017|21|R|{3}{B}|Creature - Zombie Wizard|4|1|When Vindictive Lich dies, choose one or more. Each mode must target a different player.$*Target opponent sacrifices a creature.$*Target opponent discards two cards.$*Target opponent loses 5 life.|
|
||||
Wasitora, Nekoru Queen|Commander 2017|49|M|{2}{B}{R}{G}|Legendary Creature - Cat Dragon|5|4|Flying, trample$Whenever Wasitora, Nekoru Queen deals combat damage to a player, that player sacrifices a creature. If the player can't, you create a 3/3 black, red, and green Cat Dragon creature token with flying|
|
||||
Aegis Angel|Archenemy: Nicol Bolas|1|R|{4}{W}{W}|Creature - Angel|5|5|Flying$When Aegis Angel enters the battlefield, another target creature gains indestructible for as long as you control Aegis Angel.|
|
||||
Aerial Responder|Archenemy: Nicol Bolas|2|U|{1}{W}{W}|Creature - Dwarf Soldier|2|3|Flying, vigilance, lifelink|
|
||||
Anointer of Champions|Archenemy: Nicol Bolas|3|U|{W}|Creature - Human Cleric|1|1|{T}: Target attacking creature gets +1/+1 until end of turn.|
|
||||
|
|
@ -31866,7 +31896,7 @@ Champion of Wits|Hour of Devastation|31|R|{2}{U}|Creature - Naga Wizard|2|1|When
|
|||
Countervailing Winds|Hour of Devastation|32|C|{2}{U}|Instant|||Counter target spell unless its controller pays {1} for each card in your graveyard.$Cycling {2} <i> ({2}, Discard this card: Draw a card.)</i>|
|
||||
Cunning Survivor|Hour of Devastation|33|C|{1}{U}|Creature - Human Warrior|1|3|Whenever you cycle or discard a card, Cunning Survivor gets +1/+0 until end of turn and can't be blocked this turn.|
|
||||
Eternal of Harsh Truths|Hour of Devastation|34|U|{2}{U}|Creature - Zombie Cleric|1|3|Afflict 2 <i>(Whenever this creature becomes blocked, defending player loses 2 life.)</i>$Whenever Eternal of Harsh Truths attacks and isn't blocked, draw a card.|
|
||||
Fraying Sanity|Hour of Devastation|35|R|{2}{U}|Enchantment - Aura Curse|||Enchant player$At the beginning of each end step, enchanted player puts the top X cards of his or her library into his or her graveyard, where X is the total number of cards put into his or her graveyard from anywhere this turn. |
|
||||
Fraying Sanity|Hour of Devastation|35|R|{2}{U}|Enchantment - Aura Curse|||Enchant player$At the beginning of each end step, enchanted player puts the top X cards of his or her library into his or her graveyard, where X is the total number of cards put into his or her graveyard from anywhere this turn.|
|
||||
Hour of Eternity|Hour of Devastation|36|R|{X}{X}{U}{U}{U}|Sorcery|||Exile X target creature cards from your graveyard. For each card exiled this way, create a token that's a copy of that card, except it's a 4/4 black Zombie.|
|
||||
Imaginary Threats|Hour of Devastation|37|U|{2}{U}{U}|Instant|||Creatures target opponent controls attack this turn if able. During that player's next untap step, creatures he or she controls don't untap.$Cycling {2} <i>({2}, Discard this card: Draw a card.)</i>|
|
||||
Jace's Defeat|Hour of Devastation|38|U|{1}{U}|Instant|||Counter target blue spell. If it was a Jace planeswalker spell, scry 2.|
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue