mirror of
https://github.com/magefree/mage.git
synced 2026-01-19 01:39:58 -08:00
Merge branch 'master' into refactor/multiple-names
This commit is contained in:
commit
45f810f4ed
127 changed files with 2793 additions and 1041 deletions
|
|
@ -1578,6 +1578,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
// FIRST GUI CALL (create main window with all prepared frames, dialogs, etc)
|
||||
try {
|
||||
instance = new MageFrame();
|
||||
EDTExceptionHandler.registerMainApp(instance);
|
||||
} catch (Throwable e) {
|
||||
LOGGER.fatal("Critical error on start up, app will be closed: " + e.getMessage(), e);
|
||||
System.exit(1);
|
||||
|
|
|
|||
|
|
@ -20,11 +20,13 @@ import mage.view.TableView;
|
|||
import org.apache.log4j.Logger;
|
||||
|
||||
import javax.swing.*;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* App GUI: create new GAME
|
||||
|
|
@ -35,13 +37,22 @@ public class NewTableDialog extends MageDialog {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(NewTableDialog.class);
|
||||
|
||||
public static final int DEFAULT_COMPUTER_PLAYER_SKILL_LEVEL = 2;
|
||||
public static final String PLAYER_DATA_DELIMETER_OLD = ","; // need for compatibility with old version
|
||||
public static final String PLAYER_DATA_DELIMETER_NEW = "@@@";
|
||||
|
||||
private final CustomOptionsDialog customOptions;
|
||||
private TableView table;
|
||||
private UUID playerId;
|
||||
private UUID roomId;
|
||||
private String lastSessionId;
|
||||
private final List<TablePlayerPanel> players = new ArrayList<>();
|
||||
|
||||
// temp settings on loading players list
|
||||
private final List<PlayerType> prefPlayerTypes = new ArrayList<>();
|
||||
private final List<Integer> prefPlayerSkills = new ArrayList<>();
|
||||
private final List<String> prefPlayerDecks = new ArrayList<>();
|
||||
|
||||
|
||||
private static final String LIMITED = "Limited";
|
||||
|
||||
|
|
@ -756,27 +767,56 @@ public class NewTableDialog extends MageDialog {
|
|||
}
|
||||
|
||||
private void createPlayers(int numPlayers) {
|
||||
// add missing player panels
|
||||
// add miss panels
|
||||
if (numPlayers > players.size()) {
|
||||
while (players.size() != numPlayers) {
|
||||
TablePlayerPanel playerPanel = new TablePlayerPanel();
|
||||
PlayerType playerType = PlayerType.HUMAN;
|
||||
if (prefPlayerTypes.size() >= players.size() && !players.isEmpty()) {
|
||||
playerType = prefPlayerTypes.get(players.size() - 1);
|
||||
}
|
||||
playerPanel.init(players.size() + 2, playerType);
|
||||
players.add(playerPanel);
|
||||
playerPanel.addPlayerTypeEventListener(
|
||||
(Listener<Event>) event -> drawPlayers()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
} // remove player panels no longer needed
|
||||
else if (numPlayers < players.size()) {
|
||||
// remove un-used panels
|
||||
if (numPlayers < players.size()) {
|
||||
while (players.size() != numPlayers) {
|
||||
players.remove(players.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// load player data
|
||||
String prevGoodPlayerDeck = "";
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
TablePlayerPanel playerPanel = players.get(i);
|
||||
|
||||
// find player type
|
||||
PlayerType playerType = PlayerType.HUMAN;
|
||||
if (i < prefPlayerTypes.size()) {
|
||||
playerType = prefPlayerTypes.get(i);
|
||||
}
|
||||
|
||||
// find skill level
|
||||
int playerSkill = DEFAULT_COMPUTER_PLAYER_SKILL_LEVEL;
|
||||
if (i < prefPlayerSkills.size()) {
|
||||
playerSkill = prefPlayerSkills.get(i);
|
||||
}
|
||||
|
||||
// find deck file
|
||||
String playerDeck = "";
|
||||
if (i < prefPlayerDecks.size()) {
|
||||
playerDeck = prefPlayerDecks.get(i);
|
||||
// use prev deck if loaded not found
|
||||
if (playerDeck.isEmpty() || !(new File(playerDeck).exists())) {
|
||||
playerDeck = prevGoodPlayerDeck;
|
||||
} else {
|
||||
prevGoodPlayerDeck = playerDeck;
|
||||
}
|
||||
}
|
||||
|
||||
playerPanel.init(i + 2, playerType, playerSkill, playerDeck);
|
||||
}
|
||||
|
||||
drawPlayers();
|
||||
}
|
||||
|
||||
|
|
@ -808,11 +848,6 @@ public class NewTableDialog extends MageDialog {
|
|||
cbRange.setModel(new DefaultComboBoxModel(RangeOfInfluence.values()));
|
||||
cbAttackOption.setModel(new DefaultComboBoxModel(MultiplayerAttackOption.values()));
|
||||
cbSkillLevel.setModel(new DefaultComboBoxModel(SkillLevel.values()));
|
||||
// Update the existing player panels (neccessary if server was changes = new session)
|
||||
int i = 2;
|
||||
for (TablePlayerPanel tablePlayerPanel : players) {
|
||||
tablePlayerPanel.init(i++, tablePlayerPanel.getPlayerType());
|
||||
}
|
||||
this.setModal(true);
|
||||
setGameOptions();
|
||||
this.setLocation(150, 100);
|
||||
|
|
@ -863,11 +898,24 @@ public class NewTableDialog extends MageDialog {
|
|||
txtName.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NAME + versionStr, "Game"));
|
||||
txtPassword.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PASSWORD + versionStr, ""));
|
||||
|
||||
String playerTypes = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PLAYER_TYPES + versionStr, "Human");
|
||||
// load player data
|
||||
// player type
|
||||
String playerData = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PLAYER_TYPES + versionStr, "Human");
|
||||
prefPlayerTypes.clear();
|
||||
for (String pType : playerTypes.split(",")) {
|
||||
prefPlayerTypes.add(PlayerType.getByDescription(pType));
|
||||
for (String playerTypeStr : playerData.split(PLAYER_DATA_DELIMETER_OLD)) {
|
||||
prefPlayerTypes.add(PlayerType.getByDescription(playerTypeStr));
|
||||
}
|
||||
// player skill
|
||||
playerData = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PLAYER_SKILLS + versionStr, String.valueOf(DEFAULT_COMPUTER_PLAYER_SKILL_LEVEL));
|
||||
prefPlayerSkills.clear();
|
||||
for (String playerSkillStr : playerData.split(PLAYER_DATA_DELIMETER_NEW)) {
|
||||
prefPlayerSkills.add(Integer.parseInt(playerSkillStr));
|
||||
}
|
||||
// player deck
|
||||
playerData = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_PLAYER_DECKS + versionStr, "Human");
|
||||
prefPlayerDecks.clear();
|
||||
prefPlayerDecks.addAll(Arrays.asList(playerData.split(PLAYER_DATA_DELIMETER_NEW)));
|
||||
|
||||
this.spnNumPlayers.setValue(Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_NUMBER_PLAYERS + versionStr, "2")));
|
||||
|
||||
String gameTypeName = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_GAME_TYPE + versionStr, "Two Player Duel");
|
||||
|
|
@ -968,15 +1016,22 @@ public class NewTableDialog extends MageDialog {
|
|||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_MINIMUM_RATING + versionStr, Integer.toString(options.getMinimumRating()));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_EDH_POWER_LEVEL + versionStr, Integer.toString(options.getEdhPowerLevel()));
|
||||
|
||||
StringBuilder playerTypesString = new StringBuilder();
|
||||
for (Object player : players) {
|
||||
if (playerTypesString.length() > 0) {
|
||||
playerTypesString.append(',');
|
||||
}
|
||||
TablePlayerPanel tpp = (TablePlayerPanel) player;
|
||||
playerTypesString.append(tpp.getPlayerType());
|
||||
}
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PLAYER_TYPES + versionStr, playerTypesString.toString());
|
||||
// save player data
|
||||
// player type
|
||||
String playerData = players.stream()
|
||||
.map(panel -> panel.getPlayerType().toString())
|
||||
.collect(Collectors.joining(PLAYER_DATA_DELIMETER_OLD));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PLAYER_TYPES + versionStr, playerData);
|
||||
// player skill
|
||||
playerData = players.stream()
|
||||
.map(panel -> String.valueOf(panel.getPlayerSkill()))
|
||||
.collect(Collectors.joining(PLAYER_DATA_DELIMETER_NEW));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PLAYER_SKILLS + versionStr, playerData);
|
||||
// player deck
|
||||
playerData = players.stream()
|
||||
.map(panel -> String.valueOf(panel.getPlayerDeck()))
|
||||
.collect(Collectors.joining(PLAYER_DATA_DELIMETER_NEW));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_PLAYER_DECKS + versionStr, playerData);
|
||||
|
||||
customOptions.onSaveSettings(version, options);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import java.io.IOException;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.swing.*;
|
||||
import mage.cards.decks.Deck;
|
||||
|
|
@ -39,6 +40,10 @@ public class NewTournamentDialog extends MageDialog {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(NewTournamentDialog.class);
|
||||
|
||||
// temp settings on loading players list
|
||||
private final List<PlayerType> prefPlayerTypes = new ArrayList<>();
|
||||
private final List<Integer> prefPlayerSkills = new ArrayList<>();
|
||||
|
||||
private TableView table;
|
||||
// private UUID playerId;
|
||||
private UUID roomId;
|
||||
|
|
@ -70,6 +75,16 @@ public class NewTournamentDialog extends MageDialog {
|
|||
this.spnMinimumRating.setModel(new SpinnerNumberModel(0, 0, 3000, 10));
|
||||
}
|
||||
|
||||
private int getCurrentNumPlayers() {
|
||||
int res = (Integer) spnNumPlayers.getValue();
|
||||
return res > 0 ? res : 2;
|
||||
}
|
||||
|
||||
private int getCurrentNumSeats() {
|
||||
int res = (Integer) spnNumSeats.getValue();
|
||||
return res > 0 ? res : 2;
|
||||
}
|
||||
|
||||
public void showDialog(UUID roomId) {
|
||||
this.roomId = roomId;
|
||||
if (!lastSessionId.equals(SessionHandler.getSessionId())) {
|
||||
|
|
@ -89,11 +104,6 @@ public class NewTournamentDialog extends MageDialog {
|
|||
.filter(o -> !o.equals(TimingOption.NONE))
|
||||
.toArray())
|
||||
);
|
||||
// update player types
|
||||
int i = 2;
|
||||
for (TournamentPlayerPanel tournamentPlayerPanel : players) {
|
||||
tournamentPlayerPanel.init(i++);
|
||||
}
|
||||
cbAllowSpectators.setSelected(true);
|
||||
this.setModal(true);
|
||||
this.setLocation(150, 100);
|
||||
|
|
@ -648,7 +658,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
}// </editor-fold>//GEN-END:initComponents
|
||||
|
||||
private void cbTournamentTypeActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbTournamentTypeActionPerformed
|
||||
prepareTourneyView((Integer) this.spnNumPlayers.getValue());
|
||||
prepareTourneyView(false, prepareVersionStr(-1, false), getCurrentNumPlayers(), getCurrentNumSeats());
|
||||
|
||||
jumpstartPacksFilename = "";
|
||||
if (cbTournamentType.getSelectedItem().toString().matches(".*Jumpstart.*Custom.*")) {
|
||||
|
|
@ -703,6 +713,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
// join AI
|
||||
for (TournamentPlayerPanel player : players) {
|
||||
if (player.getPlayerType().getSelectedItem() != PlayerType.HUMAN) {
|
||||
// TODO: add support of multiple deck files per each computer player (can be implemented after combine table/tourney dialog in one)
|
||||
if (!player.joinTournamentTable(roomId, table.getTableId(), DeckImporter.importDeckFromFile(this.player1Panel.getDeckFile(), true))) {
|
||||
// error message must be send by sever
|
||||
SessionHandler.removeTable(roomId, table.getTableId());
|
||||
|
|
@ -737,7 +748,6 @@ public class NewTournamentDialog extends MageDialog {
|
|||
}//GEN-LAST:event_btnCancelActionPerformed
|
||||
|
||||
private void updateNumSeats() {
|
||||
// int numPlayers = (Integer) this.spnNumPlayers.getValue();
|
||||
int numSeats = (Integer) this.spnNumSeats.getValue();
|
||||
|
||||
if (numSeats > 2) {
|
||||
|
|
@ -756,7 +766,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
}
|
||||
|
||||
private void spnNumPlayersStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnNumPlayersStateChanged
|
||||
int numPlayers = (Integer) this.spnNumPlayers.getValue();
|
||||
int numPlayers = getCurrentNumPlayers();
|
||||
createPlayers(numPlayers - 1);
|
||||
int numSeats = (Integer) this.spnNumSeats.getValue();
|
||||
if (numSeats > 2 && numPlayers != numSeats) {
|
||||
|
|
@ -771,8 +781,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
}//GEN-LAST:event_spnNumSeatsStateChanged
|
||||
|
||||
private void spnNumWinsnumPlayersChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_spnNumWinsnumPlayersChanged
|
||||
int numSeats = (Integer) this.spnNumSeats.getValue();
|
||||
int numWins = (Integer) this.spnNumSeats.getValue();
|
||||
int numSeats = getCurrentNumSeats();
|
||||
if (numSeats > 2) {
|
||||
spnNumWins.setValue(1);
|
||||
}
|
||||
|
|
@ -878,32 +887,47 @@ public class NewTournamentDialog extends MageDialog {
|
|||
}//GEN-LAST:event_btnCustomOptionsActionPerformed
|
||||
|
||||
private void setGameOptions() {
|
||||
GameTypeView gameType = (GameTypeView) cbGameType.getSelectedItem();
|
||||
// int oldValue = (Integer) this.spnNumPlayers.getValue();
|
||||
// this.spnNumPlayers.setModel(new SpinnerNumberModel(gameType.getMinPlayers(), gameType.getMinPlayers(), gameType.getMaxPlayers(), 1));
|
||||
// this.spnNumPlayers.setEnabled(gameType.getMinPlayers() != gameType.getMaxPlayers());
|
||||
// if (oldValue >= gameType.getMinPlayers() && oldValue <= gameType.getMaxPlayers()){
|
||||
// this.spnNumPlayers.setBoostedValue(oldValue);
|
||||
// }
|
||||
// this.cbAttackOption.setEnabled(gameType.isUseAttackOption());
|
||||
// this.cbRange.setEnabled(gameType.isUseRange());
|
||||
createPlayers((Integer) spnNumPlayers.getValue() - 1);
|
||||
createPlayers(getCurrentNumPlayers() - 1);
|
||||
}
|
||||
|
||||
private void prepareTourneyView(int numPlayers) {
|
||||
private void prepareTourneyView(boolean loadPlayerSettings, String versionStr, int numPlayers, int numSeats) {
|
||||
TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem();
|
||||
activatePanelElements(tournamentType);
|
||||
|
||||
// players
|
||||
if (numPlayers < tournamentType.getMinPlayers() || numPlayers > tournamentType.getMaxPlayers()) {
|
||||
numPlayers = tournamentType.getMinPlayers();
|
||||
createPlayers(numPlayers - 1); // ?
|
||||
}
|
||||
this.spnNumPlayers.setModel(new SpinnerNumberModel(numPlayers, tournamentType.getMinPlayers(), tournamentType.getMaxPlayers(), 1));
|
||||
this.spnNumPlayers.setEnabled(tournamentType.getMinPlayers() != tournamentType.getMaxPlayers());
|
||||
createPlayers((Integer) spnNumPlayers.getValue() - 1);
|
||||
this.spnNumSeats.setModel(new SpinnerNumberModel(2, 2, tournamentType.getMaxPlayers(), 1));
|
||||
|
||||
// manual call change events to apply players/seats restrictions and create miss panels
|
||||
// TODO: refactor to use isLoading and restrictions from a code instead restrictions from a component
|
||||
this.spnNumPlayers.setValue(numPlayers);
|
||||
spnNumPlayersStateChanged(null);
|
||||
this.spnNumSeats.setValue(numSeats);
|
||||
spnNumSeatsStateChanged(null);
|
||||
|
||||
if (loadPlayerSettings) {
|
||||
// load player data
|
||||
// player type
|
||||
String playerData = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PLAYER_TYPES + versionStr, "Human");
|
||||
prefPlayerTypes.clear();
|
||||
for (String playerTypeStr : playerData.split(NewTableDialog.PLAYER_DATA_DELIMETER_NEW)) {
|
||||
prefPlayerTypes.add(PlayerType.getByDescription(playerTypeStr));
|
||||
}
|
||||
// player skill
|
||||
playerData = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PLAYER_SKILLS + versionStr, String.valueOf(NewTableDialog.DEFAULT_COMPUTER_PLAYER_SKILL_LEVEL));
|
||||
prefPlayerSkills.clear();
|
||||
for (String playerSkillStr : playerData.split(NewTableDialog.PLAYER_DATA_DELIMETER_NEW)) {
|
||||
prefPlayerSkills.add(Integer.parseInt(playerSkillStr));
|
||||
}
|
||||
// player deck
|
||||
// no deck files support here yet (single deck for all computers)
|
||||
}
|
||||
|
||||
createPlayers(numPlayers - 1);
|
||||
|
||||
// packs
|
||||
preparePacksView(tournamentType);
|
||||
}
|
||||
|
|
@ -921,10 +945,10 @@ public class NewTournamentDialog extends MageDialog {
|
|||
}
|
||||
}
|
||||
|
||||
private void setNumberOfSwissRoundsMin(int numPlayers) {
|
||||
private void setNumberOfSwissRoundsMin(int additionalNumPlayers) {
|
||||
// set 3 rounds default if more than 4 players
|
||||
// don't set 4 rounds by default, as 3 rounds generally preferred
|
||||
int minRounds = (numPlayers + 1 > 4) ? 3 : 2;
|
||||
int minRounds = (additionalNumPlayers + 1 > 4) ? 3 : 2;
|
||||
int newValue = Math.max((Integer) spnNumRounds.getValue(), minRounds);
|
||||
this.spnNumRounds.setModel(new SpinnerNumberModel(newValue, 2, 10, 1));
|
||||
this.pack();
|
||||
|
|
@ -1136,24 +1160,46 @@ public class NewTournamentDialog extends MageDialog {
|
|||
}
|
||||
}
|
||||
|
||||
private void createPlayers(int numPlayers) {
|
||||
// add/remove player panels
|
||||
if (numPlayers > players.size()) {
|
||||
while (players.size() != numPlayers) {
|
||||
private void createPlayers(int additionalNumPlayers) {
|
||||
// add miss panels
|
||||
if (additionalNumPlayers > players.size()) {
|
||||
while (players.size() != additionalNumPlayers) {
|
||||
TournamentPlayerPanel playerPanel = new TournamentPlayerPanel();
|
||||
playerPanel.init(players.size() + 2);
|
||||
|
||||
players.add(playerPanel);
|
||||
}
|
||||
} else if (numPlayers < players.size()) {
|
||||
while (players.size() != numPlayers) {
|
||||
}
|
||||
|
||||
// remove un-used panels
|
||||
if (additionalNumPlayers < players.size()) {
|
||||
while (players.size() != additionalNumPlayers) {
|
||||
players.remove(players.size() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
// load player data
|
||||
for (int i = 0; i < players.size(); i++) {
|
||||
TournamentPlayerPanel playerPanel = players.get(i);
|
||||
|
||||
// find player type
|
||||
PlayerType playerType = PlayerType.HUMAN;
|
||||
if (i < prefPlayerTypes.size()) {
|
||||
playerType = prefPlayerTypes.get(i);
|
||||
}
|
||||
|
||||
// find skill level
|
||||
int playerSkill = NewTableDialog.DEFAULT_COMPUTER_PLAYER_SKILL_LEVEL;
|
||||
if (i < prefPlayerSkills.size()) {
|
||||
playerSkill = prefPlayerSkills.get(i);
|
||||
}
|
||||
|
||||
// find deck file
|
||||
// no deck files support here yet (single deck for all computers)
|
||||
playerPanel.init(i + 2, playerType, playerSkill);
|
||||
}
|
||||
|
||||
drawPlayers();
|
||||
|
||||
setNumberOfSwissRoundsMin(numPlayers);
|
||||
|
||||
setNumberOfSwissRoundsMin(additionalNumPlayers);
|
||||
}
|
||||
|
||||
private void drawPlayers() {
|
||||
|
|
@ -1347,7 +1393,6 @@ public class NewTournamentDialog extends MageDialog {
|
|||
|
||||
private void onLoadSettings(int version) {
|
||||
String versionStr = prepareVersionStr(version, false);
|
||||
int numPlayers;
|
||||
txtName.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NAME + versionStr, "Tournament"));
|
||||
txtPassword.setText(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PASSWORD + versionStr, ""));
|
||||
int timeLimit = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_TIME_LIMIT + versionStr, "1500"));
|
||||
|
|
@ -1365,7 +1410,7 @@ public class NewTournamentDialog extends MageDialog {
|
|||
break;
|
||||
}
|
||||
}
|
||||
String skillLevelDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TABLE_SKILL_LEVEL + versionStr, "Casual");
|
||||
String skillLevelDefault = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_SKILL_LEVEL + versionStr, "Casual");
|
||||
for (SkillLevel skillLevel : SkillLevel.values()) {
|
||||
if (skillLevel.toString().equals(skillLevelDefault)) {
|
||||
this.cbSkillLevel.setSelectedItem(skillLevel);
|
||||
|
|
@ -1391,11 +1436,11 @@ public class NewTournamentDialog extends MageDialog {
|
|||
TournamentTypeView tournamentType = (TournamentTypeView) cbTournamentType.getSelectedItem();
|
||||
activatePanelElements(tournamentType);
|
||||
|
||||
int defaultNumberPlayers = 2;
|
||||
int defaultNumberSeats = 2;
|
||||
if (tournamentType.isLimited()) {
|
||||
if (tournamentType.isDraft()) {
|
||||
numPlayers = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PLAYERS_DRAFT + versionStr, "4"));
|
||||
prepareTourneyView(numPlayers);
|
||||
|
||||
defaultNumberPlayers = 4;
|
||||
if (tournamentType.isRandom() || tournamentType.isRichMan() || tournamentType.isReshuffled()) {
|
||||
loadRandomPacks(version);
|
||||
} else {
|
||||
|
|
@ -1410,8 +1455,6 @@ public class NewTournamentDialog extends MageDialog {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
numPlayers = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PLAYERS_SEALED + versionStr, "2"));
|
||||
prepareTourneyView(numPlayers);
|
||||
loadBoosterPacks(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PACKS_SEALED + versionStr, ""));
|
||||
}
|
||||
}
|
||||
|
|
@ -1419,6 +1462,15 @@ public class NewTournamentDialog extends MageDialog {
|
|||
this.chkRollbackTurnsAllowed.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS + versionStr, "Yes").equals("Yes"));
|
||||
this.chkRated.setSelected(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_RATED + versionStr, "No").equals("Yes"));
|
||||
|
||||
String deckFile = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_DECK_FILE + versionStr, "");
|
||||
if (deckFile != null && !deckFile.isEmpty() && new File(deckFile).exists()) {
|
||||
this.player1Panel.setDeckFile(deckFile);
|
||||
}
|
||||
|
||||
int numPlayers = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_PLAYERS + versionStr, String.valueOf(defaultNumberPlayers)));
|
||||
int numSeats = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_SEATS + versionStr, String.valueOf(defaultNumberSeats)));
|
||||
prepareTourneyView(true, versionStr, numPlayers, numSeats);
|
||||
|
||||
this.customOptions.onLoadSettings(version);
|
||||
}
|
||||
|
||||
|
|
@ -1443,20 +1495,14 @@ public class NewTournamentDialog extends MageDialog {
|
|||
|
||||
if (tOptions.getTournamentType().startsWith("Sealed")) {
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PACKS_SEALED + versionStr, tOptions.getLimitedOptions().getSetCodes().toString());
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PLAYERS_SEALED + versionStr, Integer.toString(tOptions.getPlayerTypes().size()));
|
||||
}
|
||||
|
||||
if (tOptions.getTournamentType().startsWith("Booster")) {
|
||||
DraftOptions draftOptions = (DraftOptions) tOptions.getLimitedOptions();
|
||||
if (draftOptions != null) {
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PACKS_DRAFT + versionStr, draftOptions.getSetCodes().toString());
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PLAYERS_DRAFT + versionStr, Integer.toString(tOptions.getPlayerTypes().size()));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_DRAFT_TIMING + versionStr, draftOptions.getTiming().name());
|
||||
}
|
||||
String deckFile = this.player1Panel.getDeckFile();
|
||||
if (deckFile != null && !deckFile.isEmpty()) {
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TABLE_DECK_FILE + versionStr, deckFile);
|
||||
}
|
||||
if (tOptions.getLimitedOptions().getIsRandom() || tOptions.getLimitedOptions().getIsRichMan() || tOptions.getLimitedOptions().getIsReshuffled()) {
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PACKS_RANDOM_DRAFT + versionStr, String.join(";", this.randomPackSelector.getSelectedPacks()));
|
||||
}
|
||||
|
|
@ -1464,6 +1510,30 @@ public class NewTournamentDialog extends MageDialog {
|
|||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_SPECTATORS + versionStr, (tOptions.isWatchingAllowed() ? "Yes" : "No"));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_ALLOW_ROLLBACKS + versionStr, (tOptions.getMatchOptions().isRollbackTurnsAllowed() ? "Yes" : "No"));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_RATED + versionStr, (tOptions.getMatchOptions().isRated() ? "Yes" : "No"));
|
||||
|
||||
String deckFile = this.player1Panel.getDeckFile();
|
||||
if (deckFile != null && !deckFile.isEmpty()) {
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_DECK_FILE + versionStr, deckFile);
|
||||
}
|
||||
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_PLAYERS + versionStr, Integer.toString(tOptions.getPlayerTypes().size()));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_NUMBER_SEATS + versionStr, Integer.toString((Integer) this.spnNumSeats.getValue()));
|
||||
|
||||
// save player data
|
||||
// player type
|
||||
String playerData = players.stream()
|
||||
.map(panel -> ((PlayerType) panel.getPlayerType().getSelectedItem()))
|
||||
.map(panel -> panel.toString())
|
||||
.collect(Collectors.joining(NewTableDialog.PLAYER_DATA_DELIMETER_NEW));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PLAYER_TYPES + versionStr, playerData);
|
||||
// player skill
|
||||
playerData = players.stream()
|
||||
.map(panel -> String.valueOf(panel.getPlayerSkill()))
|
||||
.collect(Collectors.joining(NewTableDialog.PLAYER_DATA_DELIMETER_NEW));
|
||||
PreferencesDialog.saveValue(PreferencesDialog.KEY_NEW_TOURNAMENT_PLAYER_SKILLS + versionStr, playerData);
|
||||
// player deck
|
||||
// no deck files support here yet (single deck for all computers)
|
||||
|
||||
customOptions.onSaveSettings(version, tOptions.getMatchOptions());
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2184,7 +2184,7 @@
|
|||
<Component id="panelCardStyles" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Component id="panelCardImages" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace pref="244" max="32767" attributes="0"/>
|
||||
<EmptySpace pref="306" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
|
|
@ -2219,22 +2219,13 @@
|
|||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="cbSaveToZipFiles" min="-2" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="labelNumberOfDownloadThreads" alignment="0" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="labelPreferredImageLanguage" alignment="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
|
||||
<Component id="labelPreferredImageLanguage" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="cbPreferredImageLanguage" min="-2" pref="153" max="-2" attributes="0"/>
|
||||
<Group type="102" alignment="0" attributes="0">
|
||||
<Component id="cbNumberOfDownloadThreads" min="-2" pref="153" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="labelHint1" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<Component id="cbPreferredImageLanguage" min="-2" pref="153" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace min="0" pref="122" max="32767" attributes="0"/>
|
||||
<EmptySpace min="0" pref="480" max="32767" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
|
|
@ -2251,17 +2242,12 @@
|
|||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Component id="cbSaveToZipFiles" min="-2" max="-2" attributes="0"/>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="labelNumberOfDownloadThreads" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cbNumberOfDownloadThreads" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="labelHint1" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="labelPreferredImageLanguage" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cbPreferredImageLanguage" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
</Group>
|
||||
</Group>
|
||||
</DimensionLayout>
|
||||
|
|
@ -2318,29 +2304,6 @@
|
|||
<Property name="focusable" type="boolean" value="false"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="labelNumberOfDownloadThreads">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Default download threads:"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="cbNumberOfDownloadThreads">
|
||||
<Properties>
|
||||
<Property name="maximumRowCount" type="int" value="20"/>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
<StringArray count="4">
|
||||
<StringItem index="0" value="Item 1"/>
|
||||
<StringItem index="1" value="Item 2"/>
|
||||
<StringItem index="2" value="Item 3"/>
|
||||
<StringItem index="3" value="Item 4"/>
|
||||
</StringArray>
|
||||
</Property>
|
||||
</Properties>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="labelHint1">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="(change it to 1-3 if image source bans your IP for too many connections)"/>
|
||||
</Properties>
|
||||
</Component>
|
||||
</SubComponents>
|
||||
</Container>
|
||||
<Container class="javax.swing.JPanel" name="panelCardStyles">
|
||||
|
|
|
|||
|
|
@ -84,8 +84,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
|
||||
public static final String KEY_CARD_IMAGES_USE_DEFAULT = "cardImagesUseDefault";
|
||||
public static final String KEY_CARD_IMAGES_PATH = "cardImagesPath";
|
||||
public static final String KEY_CARD_IMAGES_THREADS = "cardImagesThreads";
|
||||
public static final String KEY_CARD_IMAGES_THREADS_DEFAULT = "3";
|
||||
public static final String KEY_CARD_IMAGES_SAVE_TO_ZIP = "cardImagesSaveToZip";
|
||||
public static final String KEY_CARD_IMAGES_PREF_LANGUAGE = "cardImagesPreferredImageLaguage";
|
||||
|
||||
|
|
@ -219,6 +217,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
public static final String KEY_NEW_TABLE_SKILL_LEVEL = "newTableSkillLevel";
|
||||
public static final String KEY_NEW_TABLE_NUMBER_PLAYERS = "newTableNumberPlayers";
|
||||
public static final String KEY_NEW_TABLE_PLAYER_TYPES = "newTablePlayerTypes";
|
||||
public static final String KEY_NEW_TABLE_PLAYER_SKILLS = "newTablePlayerSkills";
|
||||
public static final String KEY_NEW_TABLE_PLAYER_DECKS = "newTablePlayerDecks";
|
||||
public static final String KEY_NEW_TABLE_QUIT_RATIO = "newTableQuitRatio";
|
||||
public static final String KEY_NEW_TABLE_MINIMUM_RATING = "newTableMinimumRating";
|
||||
public static final String KEY_NEW_TABLE_RATED = "newTableRated";
|
||||
|
|
@ -233,6 +233,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
public static final String KEY_NEW_TOURNAMENT_TIME_LIMIT = "newTournamentTimeLimit";
|
||||
public static final String KEY_NEW_TOURNAMENT_BUFFER_TIME = "newTournamentBufferTime";
|
||||
public static final String KEY_NEW_TOURNAMENT_CONSTR_TIME = "newTournamentConstructionTime";
|
||||
public static final String KEY_NEW_TOURNAMENT_SKILL_LEVEL = "newTournamentSkillLevel";
|
||||
public static final String KEY_NEW_TOURNAMENT_TYPE = "newTournamentType";
|
||||
public static final String KEY_NEW_TOURNAMENT_NUMBER_OF_FREE_MULLIGANS = "newTournamentNumberOfFreeMulligans";
|
||||
public static final String KEY_NEW_TOURNAMENT_MULLIGUN_TYPE = "newTournamentMulliganType";
|
||||
|
|
@ -244,8 +245,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
public static final String KEY_NEW_TOURNAMENT_PACKS_SEALED = "newTournamentPacksSealed";
|
||||
public static final String KEY_NEW_TOURNAMENT_PACKS_DRAFT = "newTournamentPacksDraft";
|
||||
public static final String KEY_NEW_TOURNAMENT_PACKS_RANDOM_DRAFT = "newTournamentPacksRandomDraft";
|
||||
public static final String KEY_NEW_TOURNAMENT_PLAYERS_SEALED = "newTournamentPlayersSealed";
|
||||
public static final String KEY_NEW_TOURNAMENT_PLAYERS_DRAFT = "newTournamentPlayersDraft";
|
||||
public static final String KEY_NEW_TOURNAMENT_NUMBER_PLAYERS = "newTournamentNumberPlayers";
|
||||
public static final String KEY_NEW_TOURNAMENT_NUMBER_SEATS = "newTournamentNumberSeats";
|
||||
public static final String KEY_NEW_TOURNAMENT_PLAYER_TYPES = "newTournamentPlayerTypes";
|
||||
public static final String KEY_NEW_TOURNAMENT_PLAYER_SKILLS = "newTournamentPlayerSkills";
|
||||
public static final String KEY_NEW_TOURNAMENT_DRAFT_TIMING = "newTournamentDraftTiming";
|
||||
public static final String KEY_NEW_TOURNAMENT_ALLOW_SPECTATORS = "newTournamentAllowSpectators";
|
||||
public static final String KEY_NEW_TOURNAMENT_PLANE_CHASE = "newTournamentPlaneChase";
|
||||
|
|
@ -659,10 +662,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
return CardLanguage.valueByCode(getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, CardLanguage.ENGLISH.getCode()));
|
||||
}
|
||||
|
||||
public static Integer getPrefDownloadThreads() {
|
||||
return Integer.parseInt(getCachedValue(KEY_CARD_IMAGES_THREADS, KEY_CARD_IMAGES_THREADS_DEFAULT));
|
||||
}
|
||||
|
||||
private static class ImageFileFilter extends FileFilter {
|
||||
|
||||
@Override
|
||||
|
|
@ -759,7 +758,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
}
|
||||
|
||||
cbPreferredImageLanguage.setModel(new DefaultComboBoxModel<>(CardLanguage.toList()));
|
||||
cbNumberOfDownloadThreads.setModel(new DefaultComboBoxModel<>(new String[]{"10", "9", "8", "7", "6", "5", "4", "3", "2", "1"}));
|
||||
}
|
||||
|
||||
private void createSizeSetting(Integer position, String key, Integer defaultValue, boolean useExample, String name, String hint) {
|
||||
|
|
@ -960,9 +958,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
cbSaveToZipFiles = new javax.swing.JCheckBox();
|
||||
cbPreferredImageLanguage = new javax.swing.JComboBox<>();
|
||||
labelPreferredImageLanguage = new javax.swing.JLabel();
|
||||
labelNumberOfDownloadThreads = new javax.swing.JLabel();
|
||||
cbNumberOfDownloadThreads = new javax.swing.JComboBox();
|
||||
labelHint1 = new javax.swing.JLabel();
|
||||
panelCardStyles = new javax.swing.JPanel();
|
||||
cbCardRenderImageFallback = new javax.swing.JCheckBox();
|
||||
cbCardRenderIconsForAbilities = new javax.swing.JCheckBox();
|
||||
|
|
@ -2287,13 +2282,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
labelPreferredImageLanguage.setText("Default images language:");
|
||||
labelPreferredImageLanguage.setFocusable(false);
|
||||
|
||||
labelNumberOfDownloadThreads.setText("Default download threads:");
|
||||
|
||||
cbNumberOfDownloadThreads.setMaximumRowCount(20);
|
||||
cbNumberOfDownloadThreads.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
|
||||
|
||||
labelHint1.setText("(change it to 1-3 if image source bans your IP for too many connections)");
|
||||
|
||||
org.jdesktop.layout.GroupLayout panelCardImagesLayout = new org.jdesktop.layout.GroupLayout(panelCardImages);
|
||||
panelCardImages.setLayout(panelCardImagesLayout);
|
||||
panelCardImagesLayout.setHorizontalGroup(
|
||||
|
|
@ -2310,17 +2298,11 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(cbSaveToZipFiles)
|
||||
.add(panelCardImagesLayout.createSequentialGroup()
|
||||
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(labelNumberOfDownloadThreads)
|
||||
.add(labelPreferredImageLanguage))
|
||||
.add(6, 6, 6)
|
||||
.add(labelPreferredImageLanguage)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(cbPreferredImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.add(panelCardImagesLayout.createSequentialGroup()
|
||||
.add(cbNumberOfDownloadThreads, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
|
||||
.add(labelHint1)))))
|
||||
.add(0, 122, Short.MAX_VALUE)))
|
||||
.add(cbPreferredImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
|
||||
.add(0, 480, Short.MAX_VALUE)))
|
||||
.addContainerGap())
|
||||
);
|
||||
panelCardImagesLayout.setVerticalGroup(
|
||||
|
|
@ -2332,15 +2314,11 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
.add(btnBrowseImageLocation))
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
|
||||
.add(cbSaveToZipFiles)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
|
||||
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
|
||||
.add(labelNumberOfDownloadThreads)
|
||||
.add(cbNumberOfDownloadThreads, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.add(labelHint1))
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
|
||||
.add(labelPreferredImageLanguage)
|
||||
.add(cbPreferredImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
|
||||
.add(cbPreferredImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
|
||||
.addContainerGap())
|
||||
);
|
||||
|
||||
panelCardStyles.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Card styles (restart xmage to apply new settings)"));
|
||||
|
|
@ -2383,7 +2361,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
.add(panelCardStyles, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(panelCardImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.addContainerGap(244, Short.MAX_VALUE))
|
||||
.addContainerGap(306, Short.MAX_VALUE))
|
||||
);
|
||||
|
||||
tabsPanel.addTab("GUI Images", tabGuiImages);
|
||||
|
|
@ -3054,7 +3032,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
save(prefs, dialog.cbUseDefaultImageFolder, KEY_CARD_IMAGES_USE_DEFAULT, "true", "false");
|
||||
saveImagesPath(prefs);
|
||||
save(prefs, dialog.cbSaveToZipFiles, KEY_CARD_IMAGES_SAVE_TO_ZIP, "true", "false");
|
||||
save(prefs, dialog.cbNumberOfDownloadThreads, KEY_CARD_IMAGES_THREADS);
|
||||
save(prefs, dialog.cbPreferredImageLanguage, KEY_CARD_IMAGES_PREF_LANGUAGE);
|
||||
|
||||
save(prefs, dialog.cbUseDefaultBackground, KEY_BACKGROUND_IMAGE_DEFAULT, "true", "false");
|
||||
|
|
@ -3505,7 +3482,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
updateCache(KEY_CARD_IMAGES_PATH, path);
|
||||
}
|
||||
load(prefs, dialog.cbSaveToZipFiles, KEY_CARD_IMAGES_SAVE_TO_ZIP, "true");
|
||||
dialog.cbNumberOfDownloadThreads.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_IMAGES_THREADS, KEY_CARD_IMAGES_THREADS_DEFAULT));
|
||||
dialog.cbPreferredImageLanguage.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_IMAGES_PREF_LANGUAGE, CardLanguage.ENGLISH.getCode()));
|
||||
|
||||
// rendering settings
|
||||
|
|
@ -4080,7 +4056,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
private javax.swing.JCheckBox cbGameJsonLogAutoSave;
|
||||
private javax.swing.JCheckBox cbGameLogAutoSave;
|
||||
private javax.swing.JCheckBox cbLimitedDeckAutoSave;
|
||||
private javax.swing.JComboBox cbNumberOfDownloadThreads;
|
||||
private javax.swing.JCheckBox cbPassPriorityActivation;
|
||||
private javax.swing.JCheckBox cbPassPriorityCast;
|
||||
private javax.swing.JComboBox<String> cbPreferredImageLanguage;
|
||||
|
|
@ -4196,10 +4171,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
private javax.swing.JLabel labelCancel;
|
||||
private javax.swing.JLabel labelConfirm;
|
||||
private javax.swing.JLabel labelEndStep;
|
||||
private javax.swing.JLabel labelHint1;
|
||||
private javax.swing.JLabel labelMainStep;
|
||||
private javax.swing.JLabel labelNextTurn;
|
||||
private javax.swing.JLabel labelNumberOfDownloadThreads;
|
||||
private javax.swing.JLabel labelPreferredImageLanguage;
|
||||
private javax.swing.JLabel labelPriorEnd;
|
||||
private javax.swing.JLabel labelSizeGroup1;
|
||||
|
|
|
|||
|
|
@ -17,19 +17,12 @@ public class NewPlayerPanel extends javax.swing.JPanel {
|
|||
|
||||
private final JFileChooser fcSelectDeck;
|
||||
|
||||
/**
|
||||
* Creates new form NewPlayerPanel
|
||||
*/
|
||||
public NewPlayerPanel() {
|
||||
initComponents();
|
||||
fcSelectDeck = new JFileChooser();
|
||||
fcSelectDeck.setAcceptAllFileFilterUsed(false);
|
||||
fcSelectDeck.addChoosableFileFilter(new DeckFileFilter("dck", "XMage's deck files (*.dck)"));
|
||||
String deckPath = MageFrame.getPreferences().get("defaultDeckPath", "");
|
||||
if (deckPath.isEmpty()) {
|
||||
deckPath = ClientDefaultSettings.deckPath;
|
||||
}
|
||||
this.txtPlayerDeck.setText(deckPath);
|
||||
this.txtPlayerDeck.setText("");
|
||||
this.txtPlayerName.setText(ClientDefaultSettings.computerName);
|
||||
}
|
||||
|
||||
|
|
@ -62,7 +55,6 @@ public class NewPlayerPanel extends javax.swing.JPanel {
|
|||
return;
|
||||
}
|
||||
this.txtPlayerDeck.setText(path);
|
||||
MageFrame.getPreferences().put("defaultDeckPath", path);
|
||||
}
|
||||
|
||||
public String getPlayerName() {
|
||||
|
|
@ -77,7 +69,11 @@ public class NewPlayerPanel extends javax.swing.JPanel {
|
|||
this.txtPlayerDeck.setText(deckFile);
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
public void setSkillLevel(int level) {
|
||||
this.spnLevel.setValue(level);
|
||||
}
|
||||
|
||||
public int getSkillLevel() {
|
||||
return (Integer) spnLevel.getValue();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -18,16 +18,12 @@ public class TablePlayerPanel extends javax.swing.JPanel {
|
|||
|
||||
protected final PlayerTypeEventSource playerTypeEventSource = new PlayerTypeEventSource();
|
||||
|
||||
|
||||
/**
|
||||
* Creates new form TablePlayerPanel
|
||||
*/
|
||||
public TablePlayerPanel() {
|
||||
initComponents();
|
||||
this.newPlayerPanel.setVisible(false);
|
||||
}
|
||||
|
||||
public void init(int playerNum, PlayerType playerType) {
|
||||
public void init(int playerNum, PlayerType playerType, int playerSkill, String playerDeck) {
|
||||
cbPlayerType.setModel(new DefaultComboBoxModel(SessionHandler.getPlayerTypes()));
|
||||
this.lblPlayerNum.setText("Player " + playerNum);
|
||||
if (ClientDefaultSettings.otherPlayerIndex != null) {
|
||||
|
|
@ -41,11 +37,13 @@ public class TablePlayerPanel extends javax.swing.JPanel {
|
|||
if (playerType != null) {
|
||||
this.cbPlayerType.setSelectedItem(playerType);
|
||||
}
|
||||
this.newPlayerPanel.setDeckFile(playerDeck);
|
||||
this.newPlayerPanel.setSkillLevel(playerSkill);
|
||||
}
|
||||
|
||||
public boolean joinTable(UUID roomId, UUID tableId) throws IOException, ClassNotFoundException {
|
||||
if (this.cbPlayerType.getSelectedItem() != PlayerType.HUMAN) {
|
||||
return SessionHandler.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), (PlayerType) this.cbPlayerType.getSelectedItem(), this.newPlayerPanel.getLevel(), DeckImporter.importDeckFromFile(this.newPlayerPanel.getDeckFile(), true), "");
|
||||
return SessionHandler.joinTable(roomId, tableId, this.newPlayerPanel.getPlayerName(), (PlayerType) this.cbPlayerType.getSelectedItem(), this.newPlayerPanel.getSkillLevel(), DeckImporter.importDeckFromFile(this.newPlayerPanel.getDeckFile(), true), "");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -54,6 +52,14 @@ public class TablePlayerPanel extends javax.swing.JPanel {
|
|||
return PlayerType.getByDescription(this.cbPlayerType.getSelectedItem().toString());
|
||||
}
|
||||
|
||||
public int getPlayerSkill() {
|
||||
return newPlayerPanel.getSkillLevel();
|
||||
}
|
||||
|
||||
public String getPlayerDeck() {
|
||||
return newPlayerPanel.getDeckFile();
|
||||
}
|
||||
|
||||
public void addPlayerTypeEventListener(Listener<Event> listener) {
|
||||
playerTypeEventSource.addListener(listener);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
private static final Logger LOGGER = Logger.getLogger(TablesPanel.class);
|
||||
private static final int[] DEFAULT_COLUMNS_WIDTH = {35, 150, 100, 50, 120, 180, 80, 120, 80, 60, 40, 40, 60};
|
||||
|
||||
// ping timeout (warning, must be less than SERVER_TIMEOUTS_USER_INFORM_OPPONENTS_ABOUT_DISCONNECT_AFTER_SECS)
|
||||
// ping timeout (warning, must be less than UserManagerImpl.USER_CONNECTION_TIMEOUTS_CHECK_SECS)
|
||||
public static final int PING_SERVER_SECS = 20;
|
||||
|
||||
// refresh timeouts for data downloads from server
|
||||
|
|
|
|||
|
|
@ -22,15 +22,31 @@ public class TournamentPlayerPanel extends javax.swing.JPanel {
|
|||
this.pnlPlayerName.setVisible(false);
|
||||
}
|
||||
|
||||
public void init(int playerNum) {
|
||||
public void init(int playerNum, PlayerType playerType, int playerSkill) {
|
||||
cbPlayerType.setModel(new DefaultComboBoxModel(SessionHandler.getPlayerTypes()));
|
||||
this.lblPlayerNum.setText("Player " + playerNum);
|
||||
if (ClientDefaultSettings.otherPlayerIndex != null) {
|
||||
Integer index = Integer.parseInt(ClientDefaultSettings.otherPlayerIndex);
|
||||
if (index >= cbPlayerType.getItemCount()) {
|
||||
cbPlayerType.setSelectedIndex(cbPlayerType.getItemCount() - 1);
|
||||
} else {
|
||||
cbPlayerType.setSelectedIndex(index);
|
||||
}
|
||||
}
|
||||
if (playerType != null) {
|
||||
this.cbPlayerType.setSelectedItem(playerType);
|
||||
}
|
||||
this.spnLevel.setValue(playerSkill);
|
||||
}
|
||||
|
||||
public JComboBox getPlayerType() {
|
||||
return this.cbPlayerType;
|
||||
}
|
||||
|
||||
public int getPlayerSkill() {
|
||||
return (Integer) this.spnLevel.getValue();
|
||||
}
|
||||
|
||||
public boolean joinTournamentTable(UUID roomId, UUID tableId, DeckCardLists deckCardLists) {
|
||||
if (this.cbPlayerType.getSelectedItem() != PlayerType.HUMAN) {
|
||||
return SessionHandler.joinTournamentTable(
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ public final class ClientDefaultSettings {
|
|||
public static final CardDimensions dimensions;
|
||||
public static final CardDimensions dimensionsEnlarged;
|
||||
|
||||
public static final String deckPath;
|
||||
public static final String otherPlayerIndex;
|
||||
public static final String computerName;
|
||||
|
||||
|
|
@ -26,7 +25,6 @@ public final class ClientDefaultSettings {
|
|||
cardScalingFactor = 0.4;
|
||||
cardScalingFactorEnlarged = 0.5;
|
||||
handScalingFactor = 1.3;
|
||||
deckPath = "";
|
||||
otherPlayerIndex = "1"; // combobox default, example: 0: Human, 1: Computer - mad, 2: Computer - Draft Bot
|
||||
computerName = "Computer";
|
||||
dimensions = new CardDimensions(cardScalingFactor);
|
||||
|
|
|
|||
|
|
@ -1,17 +1,23 @@
|
|||
|
||||
|
||||
package mage.client.util;
|
||||
|
||||
import mage.client.MageFrame;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* GUI helper class - catch all app's unhandled errors
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
* @author BetaSteward_at_googlemail.com, JayDi85
|
||||
*/
|
||||
public class EDTExceptionHandler implements Thread.UncaughtExceptionHandler {
|
||||
|
||||
private static final Logger logger = Logger.getLogger(EDTExceptionHandler.class);
|
||||
|
||||
private static final Map<String, Integer> foundErrors = new HashMap<>();
|
||||
private static MageFrame mainApp = null; // app to show error dialogs
|
||||
|
||||
@Override
|
||||
public void uncaughtException(Thread t, Throwable e) {
|
||||
handle(e);
|
||||
|
|
@ -20,8 +26,18 @@ public class EDTExceptionHandler implements Thread.UncaughtExceptionHandler {
|
|||
public void handle(Throwable throwable) {
|
||||
try {
|
||||
logger.fatal("MAGE Client UI error", throwable);
|
||||
// JOptionPane.showMessageDialog(MageFrame.getDesktop(), throwable, "MAGE Client UI error", JOptionPane.ERROR_MESSAGE);
|
||||
} catch (Throwable t) {}
|
||||
|
||||
// show error dialog for better users feedback about client side errors
|
||||
// with some protection from dialogs spam on screen (e.g. on render/graphic errors)
|
||||
String errorKey = throwable.toString();
|
||||
int foundCount = foundErrors.getOrDefault(errorKey, 0);
|
||||
if (foundCount < 5 && mainApp != null) {
|
||||
mainApp.showErrorDialog("CLIENT - unhandled error in GUI", throwable);
|
||||
foundCount++;
|
||||
}
|
||||
foundErrors.put(errorKey, foundCount);
|
||||
} catch (Throwable ignore) {
|
||||
}
|
||||
}
|
||||
|
||||
public static void registerExceptionHandler() {
|
||||
|
|
@ -29,4 +45,8 @@ public class EDTExceptionHandler implements Thread.UncaughtExceptionHandler {
|
|||
System.setProperty("sun.awt.exception.handler", EDTExceptionHandler.class.getName());
|
||||
}
|
||||
|
||||
public static void registerMainApp(MageFrame app) {
|
||||
mainApp = app;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
private static final List<String> basicList = Arrays.asList("Plains", "Island", "Swamp", "Mountain", "Forest");
|
||||
|
||||
private static final int MAX_ERRORS_COUNT_BEFORE_CANCEL = 50;
|
||||
private static final int DEFAULT_DOWNLOAD_THREADS = 5;
|
||||
|
||||
// protect from wrong data save
|
||||
// there are possible land images with small sizes, so must research content in check
|
||||
|
|
@ -78,7 +79,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
|
||||
private List<CardInfo> cardsAll;
|
||||
private List<CardDownloadData> cardsMissing;
|
||||
private final List<CardDownloadData> cardsDownloadQueue;
|
||||
private List<CardDownloadData> cardsDownloadQueue;
|
||||
|
||||
private final List<String> selectedSets = new ArrayList<>();
|
||||
private CardImageSource selectedSource;
|
||||
|
|
@ -200,7 +201,7 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
|
||||
// DOWNLOAD THREADS
|
||||
uiDialog.getDownloadThreadsCombo().setModel(new DefaultComboBoxModel<>(new String[]{"10", "9", "8", "7", "6", "5", "4", "3", "2", "1"}));
|
||||
uiDialog.getDownloadThreadsCombo().setSelectedItem(PreferencesDialog.getPrefDownloadThreads().toString());
|
||||
uiDialog.getDownloadThreadsCombo().setSelectedItem(String.valueOf(DEFAULT_DOWNLOAD_THREADS));
|
||||
|
||||
// REDOWNLOAD
|
||||
uiDialog.getRedownloadCheckbox().setSelected(false);
|
||||
|
|
@ -975,9 +976,16 @@ public class DownloadPicturesService extends DefaultBoundedRangeModel implements
|
|||
});
|
||||
|
||||
// remove all downloaded cards, missing must be remains
|
||||
// TODO: too slow on finished, must be reworked (e.g. run full check instead remove)
|
||||
this.cardsDownloadQueue.removeAll(downloadedCards);
|
||||
this.cardsMissing.removeAll(downloadedCards);
|
||||
// workaround for fast remove
|
||||
Set<CardDownloadData> finished = new HashSet<>(downloadedCards);
|
||||
this.cardsDownloadQueue = Collections.synchronizedList(this.cardsDownloadQueue.stream()
|
||||
.filter(c -> !finished.contains(c))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
this.cardsMissing = Collections.synchronizedList(this.cardsMissing.stream()
|
||||
.filter(c -> !finished.contains(c))
|
||||
.collect(Collectors.toList())
|
||||
);
|
||||
|
||||
if (this.cardsDownloadQueue.isEmpty()) {
|
||||
// stop download
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import java.util.UUID;
|
|||
*/
|
||||
public class ClientCallback implements Serializable {
|
||||
|
||||
// for debug only: simulate bad connection on client side, use launcher's client param like -Dxmage.badconnection
|
||||
private static final String SIMULATE_BAD_CONNECTION_PROP = "xmage.badconnection";
|
||||
// for debug only: simulate bad connection on client side, use launcher's client param like -Dxmage.badConnection
|
||||
private static final String SIMULATE_BAD_CONNECTION_PROP = "xmage.badConnection";
|
||||
public static final boolean SIMULATE_BAD_CONNECTION;
|
||||
|
||||
static {
|
||||
|
|
|
|||
|
|
@ -42,6 +42,11 @@ public class SessionImpl implements Session {
|
|||
|
||||
private static final Logger logger = Logger.getLogger(SessionImpl.class);
|
||||
|
||||
// connection validation on client side (jboss's ping implementation, depends on server config's leasePeriod)
|
||||
// client's validation ping must be less than server's leasePeriod
|
||||
private static final int SESSION_VALIDATOR_PING_PERIOD_SECS = 4;
|
||||
private static final int SESSION_VALIDATOR_PING_TIMEOUT_SECS = 3;
|
||||
|
||||
public static final String ADMIN_NAME = "Admin"; // if you change here then change in User too
|
||||
public static final String KEEP_MY_OLD_SESSION = "keep_my_old_session"; // for disconnects without active session lose (keep tables/games)
|
||||
|
||||
|
|
@ -65,16 +70,11 @@ public class SessionImpl implements Session {
|
|||
private static final int PING_CYCLES = 10;
|
||||
private final LinkedList<Long> pingTime = new LinkedList<>();
|
||||
private String lastPingInfo = "";
|
||||
private static boolean debugMode = false;
|
||||
|
||||
private boolean canceled = false;
|
||||
private boolean jsonLogActive = false;
|
||||
private String lastError = "";
|
||||
|
||||
static {
|
||||
debugMode = System.getProperty("debug.mage") != null;
|
||||
}
|
||||
|
||||
public SessionImpl(MageClient client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
|
@ -381,7 +381,7 @@ public class SessionImpl implements Session {
|
|||
clientMetadata.put("generalizeSocketException", "true");
|
||||
|
||||
/* A remoting server also has the capability to detect when a client is no longer available.
|
||||
* This is done by estabilishing a lease with the remoting clients that connect to a server.
|
||||
* This is done by establishing a lease with the remoting clients that connect to a server.
|
||||
* On the client side, an org.jboss.remoting.LeasePinger periodically sends PING messages to
|
||||
* the server, and on the server side an org.jboss.remoting.Lease informs registered listeners
|
||||
* if the PING doesn't arrive withing the specified timeout period. */
|
||||
|
|
@ -437,15 +437,10 @@ public class SessionImpl implements Session {
|
|||
clientMetadata.put(Remoting.USE_CLIENT_CONNECTION_IDENTITY, "true");
|
||||
callbackClient = new Client(clientLocator, "callback", clientMetadata);
|
||||
|
||||
// client side connection validator (jboss's implementation of ping)
|
||||
Map<String, String> listenerMetadata = new HashMap<>();
|
||||
if (debugMode) {
|
||||
// prevent client from disconnecting while debugging
|
||||
listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, "1000000");
|
||||
listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_TIMEOUT, "900000");
|
||||
} else {
|
||||
listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, "15000");
|
||||
listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_TIMEOUT, "13000");
|
||||
}
|
||||
listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_PERIOD, String.valueOf(SESSION_VALIDATOR_PING_PERIOD_SECS * 1000));
|
||||
listenerMetadata.put(ConnectionValidator.VALIDATOR_PING_TIMEOUT, String.valueOf(SESSION_VALIDATOR_PING_TIMEOUT_SECS * 1000));
|
||||
callbackClient.connect(new MageClientConnectionListener(), listenerMetadata);
|
||||
|
||||
Map<String, String> callbackMetadata = new HashMap<>();
|
||||
|
|
@ -589,13 +584,13 @@ public class SessionImpl implements Session {
|
|||
|
||||
@Override
|
||||
public synchronized boolean sendFeedback(String title, String type, String message, String email) {
|
||||
if (isConnected()) {
|
||||
try {
|
||||
try {
|
||||
if (isConnected()) {
|
||||
server.serverAddFeedbackMessage(sessionId, connection.getUsername(), title, type, message, email);
|
||||
return true;
|
||||
} catch (MageException e) {
|
||||
logger.error(e);
|
||||
}
|
||||
} catch (MageException ex) {
|
||||
handleMageException(ex);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -1706,10 +1701,12 @@ public class SessionImpl implements Session {
|
|||
@Override
|
||||
public void ping() {
|
||||
try {
|
||||
// client side connection validator (xmage's implementation)
|
||||
//
|
||||
// jboss uses lease mechanic for connection check but xmage needs additional data like pings stats
|
||||
// ping must work after login only, all other actions are single call (example: register new user)
|
||||
// sessionId fills on connection
|
||||
// serverState fills on good login
|
||||
// - sessionId fills on connection
|
||||
// - serverState fills on good login
|
||||
if (!isConnected() || sessionId == null || serverState == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,6 +109,7 @@ public class DuelCommander extends Commander {
|
|||
bannedCommander.add("Raffine, Scheming Seer");
|
||||
bannedCommander.add("Rofellos, Llanowar Emissary");
|
||||
bannedCommander.add("Shorikai, Genesis Engine");
|
||||
bannedCommander.add("Tamiyo, Inquisitive Student");
|
||||
bannedCommander.add("Tasigur, the Golden Fang");
|
||||
bannedCommander.add("Urza, Lord High Artificer");
|
||||
bannedCommander.add("Vial Smasher the Fierce");
|
||||
|
|
|
|||
|
|
@ -54,13 +54,13 @@ public final class Main {
|
|||
private static final MageVersion version = new MageVersion(Main.class);
|
||||
|
||||
// Server threads:
|
||||
// - worker threads: creates for each connection, controls by maxPoolSize;
|
||||
// - acceptor threads: processing requests to start a new connection, controls by numAcceptThreads;
|
||||
// - backlog threads: processing waiting queue if maxPoolSize reached, controls by backlogSize;
|
||||
// - acceptor threads: processing new connection from a client, controlled by numAcceptThreads;
|
||||
// - worker threads (server threads): processing income requests from a client, controlled by maxPoolSize;
|
||||
// - backlog: max size of income queue if maxPoolSize reached, controlled by backlogSize;
|
||||
// Usage hints:
|
||||
// - if maxPoolSize reached then new clients will freeze in connection dialog until backlog queue overflow;
|
||||
// - so for active server must increase maxPoolSize to big value like "max online * 10" or enable worker idle timeout
|
||||
// - worker idle time will free unused worker thread, so new client can connect;
|
||||
// - so for active server must increase maxPoolSize to bigger value like "max online * 20"
|
||||
// - worker idle timeout will free unused worker thread, so new client can connect again;
|
||||
private static final int SERVER_WORKER_THREAD_IDLE_TIMEOUT_SECS = 5 * 60; // no needs to config, must be enabled for all
|
||||
|
||||
// arg settings can be setup by run script or IDE's program arguments like -xxx=yyy
|
||||
|
|
@ -85,6 +85,7 @@ public final class Main {
|
|||
// - fast game buttons;
|
||||
// - cheat commands;
|
||||
// - no deck validation;
|
||||
// - no connection validation by pings (no disconnects on IDE's debugger usage)
|
||||
// - load any deck in sideboarding;
|
||||
// - simplified registration and login (no password check);
|
||||
// - debug main menu for GUI and rendering testing (must use -debug arg for client app);
|
||||
|
|
@ -342,6 +343,8 @@ public final class Main {
|
|||
|
||||
@Override
|
||||
public void handleConnectionException(Throwable throwable, Client client) {
|
||||
// called on client disconnect or on failed network (depends on server config's leasePeriod)
|
||||
|
||||
String sessionId = client.getSessionId();
|
||||
Session session = managerFactory.sessionManager().getSession(sessionId).orElse(null);
|
||||
if (session == null) {
|
||||
|
|
@ -357,7 +360,7 @@ public final class Main {
|
|||
} else {
|
||||
sessionInfo.append("[no user]");
|
||||
}
|
||||
sessionInfo.append(" at ").append(session.getHost()).append(", sessionId: ").append(session.getId());
|
||||
sessionInfo.append(" at ").append(session.getHost());
|
||||
|
||||
// check disconnection reason
|
||||
// lease ping is inner jboss feature to check connection status
|
||||
|
|
@ -371,13 +374,13 @@ public final class Main {
|
|||
} else if (throwable == null) {
|
||||
// lease timeout (ping), so server lost connection with a client
|
||||
// must keep tables
|
||||
logger.info("LOST CONNECTION - " + sessionInfo);
|
||||
logger.info("LOST CONNECTION (bad network) - " + sessionInfo);
|
||||
logger.debug("- cause: lease expired");
|
||||
managerFactory.sessionManager().disconnect(client.getSessionId(), DisconnectReason.LostConnection, true);
|
||||
} else {
|
||||
// unknown error
|
||||
// must keep tables
|
||||
logger.info("LOST CONNECTION - " + sessionInfo);
|
||||
logger.info("LOST CONNECTION (unknown) - " + sessionInfo);
|
||||
logger.debug("- cause: unknown error - " + throwable);
|
||||
managerFactory.sessionManager().disconnect(client.getSessionId(), DisconnectReason.LostConnection, true);
|
||||
}
|
||||
|
|
@ -396,7 +399,8 @@ public final class Main {
|
|||
connector.addInvocationHandler("callback", serverInvocationHandler); // commands processing
|
||||
|
||||
// connection monitoring and errors processing
|
||||
connector.setLeasePeriod(managerFactory.configSettings().getLeasePeriod());
|
||||
boolean isTestMode = ((MageServerImpl) target).getServerState().isTestMode();
|
||||
connector.setLeasePeriod(isTestMode ? 3600 * 1000 : managerFactory.configSettings().getLeasePeriod());
|
||||
connector.addConnectionListener(new MageServerConnectionListener(managerFactory));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.MageException;
|
|||
import mage.players.net.UserData;
|
||||
import mage.server.managers.ManagerFactory;
|
||||
import mage.server.managers.SessionManager;
|
||||
import mage.util.ThreadUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.jboss.remoting.callback.InvokerCallbackHandler;
|
||||
|
||||
|
|
@ -63,8 +64,7 @@ public class SessionManagerImpl implements SessionManager {
|
|||
if (session != null) {
|
||||
String errorMessage = session.connectUser(userName, password, restoreSessionId);
|
||||
if (errorMessage == null) {
|
||||
logger.info(userName + " connected to server by sessionId " + sessionId
|
||||
+ (restoreSessionId.isEmpty() ? "" : ", restoreSessionId " + restoreSessionId));
|
||||
logger.info(userName + " connected to server" + (restoreSessionId.isEmpty() ? "" : " with restored session"));
|
||||
if (detailsMode) {
|
||||
logger.info("- details: " + userInfo);
|
||||
}
|
||||
|
|
@ -153,6 +153,8 @@ public class SessionManagerImpl implements SessionManager {
|
|||
}
|
||||
|
||||
user.showUserMessage("Admin action", "Your session was disconnected by admin");
|
||||
ThreadUtils.sleep(1000);
|
||||
logger.warn(user.getName() + " disconnected by admin");
|
||||
disconnect(userSessionId, DisconnectReason.DisconnectedByAdmin, true);
|
||||
admin.showUserMessage("Admin result", "User " + user.getName() + " was disconnected");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -234,6 +234,7 @@ public class UserManagerImpl implements UserManager {
|
|||
}
|
||||
if (isBadSession) {
|
||||
// full disconnect
|
||||
logger.info(user.getName() + " disconnected due connection problems");
|
||||
disconnect(user.getId(), DisconnectReason.SessionExpired);
|
||||
}
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -127,7 +127,7 @@ public enum ServerMessagesUtil {
|
|||
|
||||
private String getServerStatsMessage() {
|
||||
long current = System.currentTimeMillis();
|
||||
long hours = ((current - startDate) / (1000 * 60 * 60));
|
||||
long hours = startDate <= 0 ? 0 : ((current - startDate) / (1000 * 60 * 60));
|
||||
String updated = new Date().toInstant().atOffset(ZoneOffset.UTC).toLocalDateTime().format(DateTimeFormatter.ofPattern("HH:mm:ss"));
|
||||
return String.format("Server uptime: %d hours; max online: %d; active games: %d of %d, tourneys: %d of %d; stats from %s",
|
||||
hours,
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ import mage.game.Game;
|
|||
import mage.game.events.EntersTheBattlefieldEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.NumberOfTriggersEvent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -75,26 +76,25 @@ class AncientGreenwardenEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!(event instanceof NumberOfTriggersEvent)) {
|
||||
return false;
|
||||
}
|
||||
NumberOfTriggersEvent numberOfTriggersEvent = (NumberOfTriggersEvent) event;
|
||||
// Only triggers for the source controller
|
||||
if (!source.isControlledBy(event.getPlayerId())) {
|
||||
return false;
|
||||
}
|
||||
GameEvent sourceEvent = numberOfTriggersEvent.getSourceEvent();
|
||||
GameEvent sourceEvent = ((NumberOfTriggersEvent) event).getSourceEvent();
|
||||
// Only EtB triggers of lands
|
||||
if (sourceEvent == null
|
||||
|| sourceEvent.getType() != GameEvent.EventType.ENTERS_THE_BATTLEFIELD
|
||||
|| !(sourceEvent instanceof EntersTheBattlefieldEvent)
|
||||
|| !((EntersTheBattlefieldEvent) sourceEvent).getTarget().isLand(game)) {
|
||||
return false;
|
||||
}
|
||||
return game.getPermanent(numberOfTriggersEvent.getSourceId()) != null;
|
||||
// Only for triggers of permanents
|
||||
return game.getPermanent(event.getSourceId()) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
event.setAmount(event.getAmount() + 1);
|
||||
event.setAmount(CardUtil.overflowInc(event.getAmount(), 1));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import mage.filter.FilterPermanent;
|
|||
import mage.filter.common.FilterCreatureOrPlaneswalkerPermanent;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.NumberOfTriggersEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.util.CardUtil;
|
||||
|
|
@ -79,10 +78,7 @@ class AnnieJoinsUpEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!(event instanceof NumberOfTriggersEvent)) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(((NumberOfTriggersEvent) event).getSourceId());
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
return permanent != null
|
||||
&& permanent.isControlledBy(source.getControllerId())
|
||||
&& permanent.isLegendary(game)
|
||||
|
|
|
|||
88
Mage.Sets/src/mage/cards/b/BecomeAnonymous.java
Normal file
88
Mage.Sets/src/mage/cards/b/BecomeAnonymous.java
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.keyword.ManifestEffect;
|
||||
import mage.cards.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.permanent.TokenPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.TargetPermanent;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BecomeAnonymous extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent("nontoken creature you own");
|
||||
|
||||
static {
|
||||
filter.add(TokenPredicate.FALSE);
|
||||
filter.add(TargetController.YOU.getOwnerPredicate());
|
||||
}
|
||||
|
||||
public BecomeAnonymous(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{U}{U}");
|
||||
|
||||
// Exile target nontoken creature you own and the top two cards of your library in a face-down pile, shuffle that pile, then cloak those cards. They enter the battlefield tapped.
|
||||
this.getSpellAbility().addEffect(new BecomeAnonymousEffect());
|
||||
this.getSpellAbility().addTarget(new TargetPermanent(filter));
|
||||
}
|
||||
|
||||
private BecomeAnonymous(final BecomeAnonymous card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomeAnonymous copy() {
|
||||
return new BecomeAnonymous(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BecomeAnonymousEffect extends OneShotEffect {
|
||||
|
||||
BecomeAnonymousEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "exile target nontoken creature you own and the top two cards of your library in " +
|
||||
"a face-down pile, shuffle that pile, then cloak those cards. They enter the battlefield tapped";
|
||||
}
|
||||
|
||||
private BecomeAnonymousEffect(final BecomeAnonymousEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BecomeAnonymousEffect copy() {
|
||||
return new BecomeAnonymousEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Permanent permanent = game.getPermanent(getTargetPointer().getFirst(game, source));
|
||||
if (player == null || permanent == null) {
|
||||
return false;
|
||||
}
|
||||
Cards cards = new CardsImpl(player.getLibrary().getTopCards(game, 2));
|
||||
cards.add(permanent);
|
||||
player.moveCards(cards, Zone.EXILED, source, game);
|
||||
cards.retainZone(Zone.EXILED, game);
|
||||
if (cards.isEmpty()) {
|
||||
return true;
|
||||
}
|
||||
Set<Card> cardSet = cards.getCards(game);
|
||||
cardSet.stream().forEach(card -> card.setFaceDown(true, game));
|
||||
game.processAction();
|
||||
return !ManifestEffect.doManifestCards(game, source, player, cardSet, true, true).isEmpty();
|
||||
}
|
||||
}
|
||||
49
Mage.Sets/src/mage/cards/b/Boltbender.java
Normal file
49
Mage.Sets/src/mage/cards/b/Boltbender.java
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.ChooseNewTargetsTargetEffect;
|
||||
import mage.abilities.keyword.DisguiseAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.TargetStackObject;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class Boltbender extends CardImpl {
|
||||
|
||||
public Boltbender(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}");
|
||||
|
||||
this.subtype.add(SubType.GOBLIN);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(2);
|
||||
|
||||
// Disguise {1}{R}
|
||||
this.addAbility(new DisguiseAbility(this, new ManaCostsImpl<>("{1}{R}")));
|
||||
|
||||
// When Boltbender is turned face up, you may choose new targets for any number of other spells and/or abilities.
|
||||
Ability ability = new TurnedFaceUpSourceTriggeredAbility(new ChooseNewTargetsTargetEffect()
|
||||
.setText("you may choose new targets for any number of other spells and/or abilities"));
|
||||
ability.addTarget(new TargetStackObject(0, Integer.MAX_VALUE, StaticFilters.FILTER_SPELL_OR_ABILITY));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private Boltbender(final Boltbender card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boltbender copy() {
|
||||
return new Boltbender(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -69,7 +69,7 @@ class ChangeOfPlansEffect extends OneShotEffect {
|
|||
.getTargetPointer()
|
||||
.getTargets(game, source)
|
||||
.stream()
|
||||
.map(game::getPermanent)
|
||||
.map(game::getPermanentOrLKIBattlefield)
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toSet());
|
||||
if (permanents.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.NumberOfTriggersEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
|
|
@ -75,10 +74,7 @@ class ClaraOswaldEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!(event instanceof NumberOfTriggersEvent)) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(((NumberOfTriggersEvent) event).getSourceId());
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
return permanent != null
|
||||
&& permanent.isControlledBy(source.getControllerId())
|
||||
&& permanent.hasSubtype(SubType.DOCTOR, game);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public final class DevouringHellion extends CardImpl {
|
|||
// As Devouring Hellion enters the battlefield, you may sacrifice any number of creatures and/or planeswalkers. If you do, it enters with twice that many +1/+1 counters on it.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.ALL,
|
||||
new DevourEffect(2, StaticFilters.FILTER_CONTROLLED_PERMANENT_CREATURE_OR_PLANESWALKER)
|
||||
.setText("As {this} enters the battlefield, you may sacrifice any number of creatures and/or planeswalkers."
|
||||
.setText("As {this} enters, you may sacrifice any number of creatures and/or planeswalkers."
|
||||
+ " If you do, it enters with twice that many +1/+1 counters on it")
|
||||
));
|
||||
}
|
||||
|
|
|
|||
127
Mage.Sets/src/mage/cards/d/DragonhawkFatesTempest.java
Normal file
127
Mage.Sets/src/mage/cards/d/DragonhawkFatesTempest.java
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldOrAttacksSourceTriggeredAbility;
|
||||
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.dynamicvalue.common.PermanentsOnBattlefieldCount;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTopXMayPlayUntilEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.FixedTargets;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author notgreat
|
||||
*/
|
||||
public final class DragonhawkFatesTempest extends CardImpl {
|
||||
|
||||
private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("creatures you control with power 4 or greater");
|
||||
|
||||
static {
|
||||
filter.add(new PowerPredicate(ComparisonType.OR_GREATER, 4));
|
||||
}
|
||||
|
||||
public DragonhawkFatesTempest(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{R}{R}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.BIRD);
|
||||
this.subtype.add(SubType.DRAGON);
|
||||
this.power = new MageInt(5);
|
||||
this.toughness = new MageInt(5);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Whenever Dragonhawk enters or attacks, exile the top X cards of your library, where X is the number of creatures you control with power 4 or greater. You may play those cards until your next end step.
|
||||
// At the beginning of your next end step, Dragonhawk deals 2 damage to each opponent for each of those cards that are still exiled.
|
||||
this.addAbility(new EntersBattlefieldOrAttacksSourceTriggeredAbility(new DragonhawkExileEffect(
|
||||
new PermanentsOnBattlefieldCount(filter, null), Duration.UntilYourNextEndStep)
|
||||
.withTextOptions("those cards", true)));
|
||||
}
|
||||
|
||||
private DragonhawkFatesTempest(final DragonhawkFatesTempest card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DragonhawkFatesTempest copy() {
|
||||
return new DragonhawkFatesTempest(this);
|
||||
}
|
||||
}
|
||||
|
||||
// Copied from ExileTopXMayPlayUntilEffect but with addDelayedTriggeredAbility
|
||||
class DragonhawkExileEffect extends ExileTopXMayPlayUntilEffect {
|
||||
|
||||
public DragonhawkExileEffect(DynamicValue amount, Duration duration) {
|
||||
super(amount, duration);
|
||||
staticText += ". At the beginning of your next end step, " + DragonhawkFatesTempestDamageEffect.STATIC_TEXT;
|
||||
}
|
||||
|
||||
private DragonhawkExileEffect(final DragonhawkExileEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DragonhawkExileEffect copy() {
|
||||
return new DragonhawkExileEffect(this);
|
||||
}
|
||||
|
||||
protected void effectCards(Game game, Ability source, Set<Card> cards) {
|
||||
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(
|
||||
new DragonhawkFatesTempestDamageEffect(new FixedTargets(cards, game)), TargetController.YOU), source);
|
||||
}
|
||||
}
|
||||
|
||||
class DragonhawkFatesTempestDamageEffect extends OneShotEffect {
|
||||
FixedTargets cards;
|
||||
public static String STATIC_TEXT = "{this} deals 2 damage to each opponent for each of those cards that are still exiled";
|
||||
|
||||
DragonhawkFatesTempestDamageEffect(FixedTargets cards) {
|
||||
super(Outcome.Benefit);
|
||||
this.staticText = STATIC_TEXT;
|
||||
this.cards = cards;
|
||||
}
|
||||
|
||||
private DragonhawkFatesTempestDamageEffect(final DragonhawkFatesTempestDamageEffect effect) {
|
||||
super(effect);
|
||||
cards = effect.cards;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DragonhawkFatesTempestDamageEffect copy() {
|
||||
return new DragonhawkFatesTempestDamageEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
int count = cards.getTargets(game, source).size(); //Automatically filters out moved cards
|
||||
if (count < 1) {
|
||||
return false;
|
||||
}
|
||||
for (UUID playerId : game.getOpponents(source.getControllerId())) {
|
||||
player = game.getPlayer(playerId);
|
||||
if (playerId == null) {
|
||||
continue;
|
||||
}
|
||||
player.damage(count * 2, source.getSourceId(), source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,22 +1,16 @@
|
|||
|
||||
package mage.cards.d;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -33,7 +27,7 @@ public final class DryadMilitant extends CardImpl {
|
|||
this.toughness = new MageInt(1);
|
||||
|
||||
// If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new DryadMilitantReplacementEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(StaticFilters.FILTER_CARD_INSTANT_OR_SORCERY, false)));
|
||||
}
|
||||
|
||||
private DryadMilitant(final DryadMilitant card) {
|
||||
|
|
@ -45,42 +39,3 @@ public final class DryadMilitant extends CardImpl {
|
|||
return new DryadMilitant(this);
|
||||
}
|
||||
}
|
||||
|
||||
class DryadMilitantReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
DryadMilitantReplacementEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Exile);
|
||||
staticText = "If an instant or sorcery card would be put into a graveyard from anywhere, exile it instead";
|
||||
}
|
||||
|
||||
private DryadMilitantReplacementEffect(final DryadMilitantReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public DryadMilitantReplacementEffect copy() {
|
||||
return new DryadMilitantReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD) {
|
||||
Card card = game.getCard(event.getTargetId());
|
||||
if (card != null && (card.isSorcery(game) || card.isInstant(game))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import mage.filter.FilterSpell;
|
|||
import mage.filter.predicate.mageobject.ColorlessPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.NumberOfTriggersEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.stack.Spell;
|
||||
import mage.util.CardUtil;
|
||||
|
|
@ -78,11 +77,10 @@ class EchoesOfEternityEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!(event instanceof NumberOfTriggersEvent)
|
||||
|| !source.isControlledBy(game.getControllerId(event.getSourceId()))) {
|
||||
if (!source.isControlledBy(event.getPlayerId())) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(event.getSourceId());
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
if (permanent != null && permanent.getColor(game).isColorless()
|
||||
&& !permanent.getId().equals(source.getSourceId())) {
|
||||
return true;
|
||||
|
|
|
|||
97
Mage.Sets/src/mage/cards/f/FestivalOfEmbers.java
Normal file
97
Mage.Sets/src/mage/cards/f/FestivalOfEmbers.java
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
package mage.cards.f;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.common.PayLifeCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.AsThoughEffectImpl;
|
||||
import mage.abilities.effects.common.SacrificeSourceEffect;
|
||||
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author notgreat
|
||||
*/
|
||||
public final class FestivalOfEmbers extends CardImpl {
|
||||
|
||||
public FestivalOfEmbers(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{4}{R}");
|
||||
|
||||
|
||||
// During your turn, you may cast instant and sorcery spells from your graveyard by paying 1 life in addition to their other costs.
|
||||
this.addAbility(new SimpleStaticAbility(new FestivalOfEmbersCastEffect()));
|
||||
|
||||
// If a card or token would be put into your graveyard from anywhere, exile it instead.
|
||||
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(true, true)));
|
||||
|
||||
// {1}{R}: Sacrifice Festival of Embers.
|
||||
this.addAbility(new SimpleActivatedAbility(new SacrificeSourceEffect(), new ManaCostsImpl<>("{1}{R}")));
|
||||
}
|
||||
|
||||
private FestivalOfEmbers(final FestivalOfEmbers card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FestivalOfEmbers copy() {
|
||||
return new FestivalOfEmbers(this);
|
||||
}
|
||||
}
|
||||
|
||||
//Based on Osteomancer Adept
|
||||
class FestivalOfEmbersCastEffect extends AsThoughEffectImpl {
|
||||
|
||||
FestivalOfEmbersCastEffect() {
|
||||
super(AsThoughEffectType.CAST_FROM_NOT_OWN_HAND_ZONE, Duration.WhileOnBattlefield, Outcome.AIDontUseIt);
|
||||
staticText = "During your turn, you may cast instant and sorcery spells from your graveyard by paying 1 life in addition to their other costs.";
|
||||
}
|
||||
|
||||
private FestivalOfEmbersCastEffect(final FestivalOfEmbersCastEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public FestivalOfEmbersCastEffect copy() {
|
||||
return new FestivalOfEmbersCastEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(UUID objectId, Ability source, UUID affectedControllerId, Game game) {
|
||||
if (!source.isControlledBy(affectedControllerId)) {
|
||||
return false;
|
||||
}
|
||||
Card card = game.getCard(objectId);
|
||||
Player player = game.getPlayer(affectedControllerId);
|
||||
if (card == null
|
||||
|| player == null
|
||||
|| !game.getActivePlayerId().equals(affectedControllerId)
|
||||
|| !card.isOwnedBy(affectedControllerId)
|
||||
|| !card.isInstantOrSorcery(game)
|
||||
|| !game.getState().getZone(objectId).match(Zone.GRAVEYARD)) {
|
||||
return false;
|
||||
}
|
||||
Costs<Cost> newCosts = new CostsImpl<>();
|
||||
newCosts.addAll(card.getSpellAbility().getCosts());
|
||||
newCosts.add(new PayLifeCost(1));
|
||||
player.setCastSourceIdWithAlternateMana(
|
||||
card.getId(), card.getManaCost(), newCosts
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
|
||||
package mage.cards.f;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
|
|
@ -14,12 +14,11 @@ import mage.constants.Outcome;
|
|||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Quercitron
|
||||
|
|
@ -30,9 +29,9 @@ public final class ForbiddenCrypt extends CardImpl {
|
|||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{B}{B}");
|
||||
|
||||
// If you would draw a card, return a card from your graveyard to your hand instead. If you can't, you lose the game.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ForbiddenCryptDrawCardReplacementEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new ForbiddenCryptDrawCardReplacementEffect()));
|
||||
// If a card would be put into your graveyard from anywhere, exile that card instead.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ForbiddenCryptPutIntoYourGraveyardReplacementEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(true, false)));
|
||||
}
|
||||
|
||||
private ForbiddenCrypt(final ForbiddenCrypt card) {
|
||||
|
|
@ -96,47 +95,4 @@ class ForbiddenCryptDrawCardReplacementEffect extends ReplacementEffectImpl {
|
|||
return event.getPlayerId().equals(source.getControllerId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class ForbiddenCryptPutIntoYourGraveyardReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
ForbiddenCryptPutIntoYourGraveyardReplacementEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||
this.staticText = "If a card would be put into your graveyard from anywhere, exile that card instead";
|
||||
}
|
||||
|
||||
private ForbiddenCryptPutIntoYourGraveyardReplacementEffect(final ForbiddenCryptPutIntoYourGraveyardReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ForbiddenCryptPutIntoYourGraveyardReplacementEffect copy() {
|
||||
return new ForbiddenCryptPutIntoYourGraveyardReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
|
||||
Card card = game.getCard(event.getTargetId());
|
||||
if (card != null && card.isOwnedBy(source.getControllerId())) {
|
||||
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
|
||||
if (!(permanent instanceof PermanentToken)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,23 +1,19 @@
|
|||
package mage.cards.g;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
|
||||
import mage.abilities.keyword.SuspendAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author weirddan455
|
||||
|
|
@ -36,7 +32,7 @@ public final class GaeasWill extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new GaeasWillGraveyardEffect());
|
||||
|
||||
// If a card would be put into your graveyard from anywhere this turn, exile that card instead.
|
||||
this.getSpellAbility().addEffect(new GaeassWillReplacementEffect());
|
||||
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(Duration.EndOfTurn)));
|
||||
}
|
||||
|
||||
private GaeasWill(final GaeasWill card) {
|
||||
|
|
@ -79,45 +75,3 @@ class GaeasWillGraveyardEffect extends ContinuousEffectImpl {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class GaeassWillReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
GaeassWillReplacementEffect() {
|
||||
super(Duration.EndOfTurn, Outcome.Detriment);
|
||||
this.staticText = "<br>If a card would be put into your graveyard from anywhere this turn, exile that card instead";
|
||||
}
|
||||
|
||||
private GaeassWillReplacementEffect(final GaeassWillReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GaeassWillReplacementEffect copy() {
|
||||
return new GaeassWillReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
|
||||
Card card = game.getCard(event.getTargetId());
|
||||
if (card != null && card.isOwnedBy(source.getControllerId())) {
|
||||
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
|
||||
if (!(permanent instanceof PermanentToken)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ import mage.constants.Outcome;
|
|||
import mage.constants.SubType;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.NumberOfTriggersEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -72,10 +72,7 @@ class HarmonicProdigyEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!(event instanceof NumberOfTriggersEvent)) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(((NumberOfTriggersEvent) event).getSourceId());
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
return permanent != null
|
||||
&& permanent.isControlledBy(source.getControllerId())
|
||||
&& (permanent.hasSubtype(SubType.SHAMAN, game)
|
||||
|
|
@ -85,7 +82,7 @@ class HarmonicProdigyEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
event.setAmount(event.getAmount() + 1);
|
||||
event.setAmount(CardUtil.overflowInc(event.getAmount(), 1));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
94
Mage.Sets/src/mage/cards/h/HavocEater.java
Normal file
94
Mage.Sets/src/mage/cards/h/HavocEater.java
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package mage.cards.h;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.combat.GoadTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubType;
|
||||
import mage.counters.CounterType;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
import mage.target.targetadjustment.ForEachOpponentTargetsAdjuster;
|
||||
import mage.target.targetpointer.EachTargetPointer;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class HavocEater extends CardImpl {
|
||||
|
||||
public HavocEater(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{5}{R}{R}");
|
||||
|
||||
this.subtype.add(SubType.ELEMENTAL);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// When Havoc Eater enters the battlefield, for each opponent, goad up to one target creature that opponent controls. Put X +1/+1 counters on Havoc Eater, where X is the total power of creatures goaded this way.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(
|
||||
new GoadTargetEffect()
|
||||
.setText("for each opponent, goad up to one target creature that opponent controls")
|
||||
.setTargetPointer(new EachTargetPointer()));
|
||||
ability.addEffect(new HavocEaterEffect());
|
||||
ability.addTarget(new TargetCreaturePermanent(0, 1));
|
||||
this.addAbility(ability.setTargetAdjuster(new ForEachOpponentTargetsAdjuster()));
|
||||
}
|
||||
|
||||
private HavocEater(final HavocEater card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HavocEater copy() {
|
||||
return new HavocEater(this);
|
||||
}
|
||||
}
|
||||
|
||||
class HavocEaterEffect extends OneShotEffect {
|
||||
|
||||
HavocEaterEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "put X +1/+1 counters on {this}, where X is the total power of creatures goaded this way";
|
||||
this.setTargetPointer(new EachTargetPointer());
|
||||
}
|
||||
|
||||
private HavocEaterEffect(final HavocEaterEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HavocEaterEffect copy() {
|
||||
return new HavocEaterEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
int amount = this
|
||||
.getTargetPointer()
|
||||
.getTargets(game, source)
|
||||
.stream()
|
||||
.map(game::getPermanent)
|
||||
.filter(Objects::nonNull)
|
||||
.map(MageObject::getPower)
|
||||
.mapToInt(MageInt::getValue)
|
||||
.sum();
|
||||
return amount > 0 && permanent.addCounters(CounterType.P1P1.createInstance(amount), source, game);
|
||||
}
|
||||
}
|
||||
111
Mage.Sets/src/mage/cards/h/HotPursuit.java
Normal file
111
Mage.Sets/src/mage/cards/h/HotPursuit.java
Normal file
|
|
@ -0,0 +1,111 @@
|
|||
package mage.cards.h;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfCombatTriggeredAbility;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.condition.Condition;
|
||||
import mage.abilities.decorator.ConditionalInterveningIfTriggeredAbility;
|
||||
import mage.abilities.effects.common.SuspectTargetEffect;
|
||||
import mage.abilities.effects.common.combat.GoadTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.GainControlAllUntapGainHasteEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.WatcherScope;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.permanent.GoadedPredicate;
|
||||
import mage.filter.predicate.permanent.SuspectedPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.target.common.TargetOpponentsCreaturePermanent;
|
||||
import mage.watchers.Watcher;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class HotPursuit extends CardImpl {
|
||||
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent("goaded and/or suspected creatures");
|
||||
|
||||
static {
|
||||
filter.add(Predicates.or(
|
||||
GoadedPredicate.instance,
|
||||
SuspectedPredicate.instance
|
||||
));
|
||||
}
|
||||
|
||||
public HotPursuit(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{R}");
|
||||
|
||||
// When Hot Pursuit enters the battlefield, suspect target creature an opponent controls. As long as Hot Pursuit remains on the battlefield, that creature is also goaded.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new SuspectTargetEffect());
|
||||
ability.addEffect(new GoadTargetEffect(Duration.UntilSourceLeavesBattlefield)
|
||||
.setText("as long as {this} remains on the battlefield, that creature is also goaded"));
|
||||
ability.addTarget(new TargetOpponentsCreaturePermanent());
|
||||
this.addAbility(ability);
|
||||
|
||||
// At the beginning of combat on your turn, if two or more players have lost the game, gain control of all goaded and/or suspected creatures until end of turn. Untap them. They gain haste until end of turn.
|
||||
this.addAbility(new ConditionalInterveningIfTriggeredAbility(
|
||||
new BeginningOfCombatTriggeredAbility(
|
||||
new GainControlAllUntapGainHasteEffect(filter),
|
||||
TargetController.YOU, false
|
||||
), HotPursuitCondition.instance, "At the beginning of combat on your turn, " +
|
||||
"if two or more players have lost the game, gain control of all goaded and/or " +
|
||||
"suspected creatures until end of turn. Untap them. They gain haste until end of turn."
|
||||
), new HotPursuitWatcher());
|
||||
}
|
||||
|
||||
private HotPursuit(final HotPursuit card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HotPursuit copy() {
|
||||
return new HotPursuit(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum HotPursuitCondition implements Condition {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return HotPursuitWatcher.checkCondition(game);
|
||||
}
|
||||
}
|
||||
|
||||
class HotPursuitWatcher extends Watcher {
|
||||
|
||||
private final Set<UUID> players = new HashSet<>();
|
||||
|
||||
HotPursuitWatcher() {
|
||||
super(WatcherScope.GAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void watch(GameEvent event, Game game) {
|
||||
switch (event.getType()) {
|
||||
case BEGINNING_PHASE_PRE:
|
||||
players.clear();
|
||||
return;
|
||||
case LOST:
|
||||
players.add(event.getPlayerId());
|
||||
}
|
||||
}
|
||||
|
||||
static boolean checkCondition(Game game) {
|
||||
return game
|
||||
.getState()
|
||||
.getWatcher(HotPursuitWatcher.class)
|
||||
.players
|
||||
.size() >= 2;
|
||||
}
|
||||
}
|
||||
91
Mage.Sets/src/mage/cards/j/JackdawSavior.java
Normal file
91
Mage.Sets/src/mage/cards/j/JackdawSavior.java
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
package mage.cards.j;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.DiesThisOrAnotherTriggeredAbility;
|
||||
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterControlledCreaturePermanent;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.AbilityPredicate;
|
||||
import mage.filter.predicate.mageobject.MageObjectReferencePredicate;
|
||||
import mage.filter.predicate.mageobject.ManaValuePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author notgreat
|
||||
*/
|
||||
public final class JackdawSavior extends CardImpl {
|
||||
public JackdawSavior(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||
|
||||
this.subtype.add(SubType.BIRD);
|
||||
this.subtype.add(SubType.CLERIC);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(1);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Whenever Jackdaw Savior or another creature you control with flying dies, return another target creature card with lesser mana value from your graveyard to the battlefield.
|
||||
this.addAbility(new JackdawSaviorDiesThisOrAnotherTriggeredAbility());
|
||||
}
|
||||
|
||||
private JackdawSavior(final JackdawSavior card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JackdawSavior copy() {
|
||||
return new JackdawSavior(this);
|
||||
}
|
||||
}
|
||||
|
||||
class JackdawSaviorDiesThisOrAnotherTriggeredAbility extends DiesThisOrAnotherTriggeredAbility {
|
||||
private static final FilterControlledCreaturePermanent flyingFilter = new FilterControlledCreaturePermanent("creature you control with flying");
|
||||
|
||||
static {
|
||||
flyingFilter.add(new AbilityPredicate(FlyingAbility.class));
|
||||
}
|
||||
|
||||
public JackdawSaviorDiesThisOrAnotherTriggeredAbility() {
|
||||
super(new ReturnFromGraveyardToBattlefieldTargetEffect().setText(
|
||||
"return another target creature card with lesser mana value from your graveyard to the battlefield"),
|
||||
false, flyingFilter);
|
||||
}
|
||||
|
||||
protected JackdawSaviorDiesThisOrAnotherTriggeredAbility(final JackdawSaviorDiesThisOrAnotherTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public JackdawSaviorDiesThisOrAnotherTriggeredAbility copy() {
|
||||
return new JackdawSaviorDiesThisOrAnotherTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (super.checkTrigger(event, game)) {
|
||||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||
FilterCard filter = new FilterCreatureCard();
|
||||
filter.add(Predicates.not(new MageObjectReferencePredicate(zEvent.getTargetId(), game)));
|
||||
filter.add(new ManaValuePredicate(ComparisonType.FEWER_THAN, zEvent.getTarget().getManaValue()));
|
||||
filter.setMessage("target creature card other than "+zEvent.getTarget().getLogName()+" with mana value less than "+zEvent.getTarget().getManaValue());
|
||||
this.getTargets().clear();
|
||||
this.addTarget(new TargetCardInYourGraveyard(filter));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -39,7 +39,7 @@ public final class LandscaperColos extends CardImpl {
|
|||
Ability ability = new EntersBattlefieldTriggeredAbility(new PutOnLibraryTargetEffect(
|
||||
false, "put target card from an opponent's graveyard on the bottom of their library"
|
||||
));
|
||||
ability.addTarget(new TargetCardInGraveyard());
|
||||
ability.addTarget(new TargetCardInGraveyard(filter));
|
||||
this.addAbility(ability);
|
||||
|
||||
// Basic landcycling {1}{W}
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ class MageHuntersOnslaughtDelayedTriggeredAbility extends DelayedTriggeredAbilit
|
|||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
Permanent permanent = game.getPermanent(event.getTargetId());
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(event.getTargetId());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.m;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
|
|
@ -9,24 +8,15 @@ import mage.abilities.costs.common.ExileSourceCost;
|
|||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author fireshoes
|
||||
|
|
@ -44,7 +34,7 @@ public final class MagusOfTheWill extends CardImpl {
|
|||
// {2}{B}, {T}, Exile Magus of the Will: Until end of turn, you may play cards from your graveyard.
|
||||
// If a card would be put into your graveyard from anywhere else this turn, exile that card instead.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new CanPlayCardsFromGraveyardEffect(), new ManaCostsImpl<>("{2}{B}"));
|
||||
ability.addEffect(new MagusOfTheWillReplacementEffect());
|
||||
ability.addEffect(new GraveyardFromAnywhereExileReplacementEffect(Duration.EndOfTurn));
|
||||
ability.addCost(new TapSourceCost());
|
||||
ability.addCost(new ExileSourceCost());
|
||||
this.addAbility(ability);
|
||||
|
|
@ -91,46 +81,3 @@ class CanPlayCardsFromGraveyardEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
class MagusOfTheWillReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
MagusOfTheWillReplacementEffect() {
|
||||
super(Duration.EndOfTurn, Outcome.Detriment);
|
||||
this.staticText = "If a card would be put into your graveyard from anywhere else this turn, exile that card instead";
|
||||
}
|
||||
|
||||
private MagusOfTheWillReplacementEffect(final MagusOfTheWillReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public MagusOfTheWillReplacementEffect copy() {
|
||||
return new MagusOfTheWillReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
|
||||
Card card = game.getCard(event.getTargetId());
|
||||
if (card != null && card.isOwnedBy(source.getControllerId())) {
|
||||
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
|
||||
if (!(permanent instanceof PermanentToken)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,18 +6,13 @@ import mage.abilities.common.SimpleStaticAbility;
|
|||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.common.PayLifeCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.SkipDrawStepEffect;
|
||||
import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -50,7 +45,7 @@ public final class Necrodominance extends CardImpl {
|
|||
));
|
||||
|
||||
// If a card or token would be put into your graveyard from anywhere, exile it instead.
|
||||
this.addAbility(new SimpleStaticAbility(new NecrodominanceReplacementEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(true, true)));
|
||||
}
|
||||
|
||||
private Necrodominance(final Necrodominance card) {
|
||||
|
|
@ -95,50 +90,4 @@ class NecrodominanceEffect extends OneShotEffect {
|
|||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Inspired by [Rest in Peace] and [Wheel of Sun and Moon]
|
||||
class NecrodominanceReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
NecrodominanceReplacementEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Exile);
|
||||
staticText = "If a card or token would be put into your graveyard from anywhere, exile it instead";
|
||||
}
|
||||
|
||||
private NecrodominanceReplacementEffect(final NecrodominanceReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NecrodominanceReplacementEffect copy() {
|
||||
return new NecrodominanceReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
|
||||
if (zEvent.getToZone() != Zone.GRAVEYARD) {
|
||||
return false;
|
||||
}
|
||||
Card card = game.getCard(event.getTargetId());
|
||||
if (card != null && card.isOwnedBy(source.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
Permanent token = game.getPermanent(event.getTargetId());
|
||||
if (token != null && token instanceof PermanentToken && token.isOwnedBy(source.getControllerId())) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -95,7 +95,7 @@ class NikoLightOfHopeEffect extends OneShotEffect {
|
|||
FilterPermanent filter = new FilterPermanent("shards");
|
||||
filter.add(SubType.SHARD.getPredicate());
|
||||
for (Permanent copyTo : game.getBattlefield().getAllActivePermanents(filter, controller.getId(), game)) {
|
||||
game.copyPermanent(Duration.UntilTheNextEndStep, permanent, copyTo.getId(), source, new EmptyCopyApplier());
|
||||
game.copyPermanent(Duration.UntilNextEndStep, permanent, copyTo.getId(), source, new EmptyCopyApplier());
|
||||
}
|
||||
ExileZone exile = game.getExile().getExileZone(source.getSourceId());
|
||||
if (exile != null && !exile.isEmpty()) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public final class OverwhelmingRemorse extends CardImpl {
|
|||
// This spell costs {1} less to cast for each creature card in your graveyard.
|
||||
this.addAbility(new SimpleStaticAbility(
|
||||
Zone.ALL, new SpellCostReductionForEachSourceEffect(1, xValue)
|
||||
).addHint(hint));
|
||||
).addHint(hint).setRuleAtTheTop(true));
|
||||
|
||||
// Exile target creature or planeswalker.
|
||||
this.getSpellAbility().addEffect(new ExileTargetEffect());
|
||||
|
|
|
|||
58
Mage.Sets/src/mage/cards/r/RansomNote.java
Normal file
58
Mage.Sets/src/mage/cards/r/RansomNote.java
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package mage.cards.r;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleActivatedAbility;
|
||||
import mage.abilities.costs.common.SacrificeSourceCost;
|
||||
import mage.abilities.costs.mana.GenericManaCost;
|
||||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.combat.GoadTargetEffect;
|
||||
import mage.abilities.effects.keyword.ManifestEffect;
|
||||
import mage.abilities.effects.keyword.SurveilEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class RansomNote extends CardImpl {
|
||||
|
||||
public RansomNote(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{1}");
|
||||
|
||||
this.subtype.add(SubType.CLUE);
|
||||
|
||||
// When Ransom Note enters the battlefield, surveil 1.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(new SurveilEffect(1)));
|
||||
|
||||
// {2}, Sacrifice Ransom Note: Choose one --
|
||||
// * Cloak the top card of your library.
|
||||
Ability ability = new SimpleActivatedAbility(
|
||||
new ManifestEffect(StaticValue.get(1), false, true), new GenericManaCost(2)
|
||||
);
|
||||
ability.addCost(new SacrificeSourceCost());
|
||||
|
||||
// * Goad target creature.
|
||||
ability.addMode(new Mode(new GoadTargetEffect()).addTarget(new TargetCreaturePermanent()));
|
||||
|
||||
// * Draw a card.
|
||||
ability.addMode(new Mode(new DrawCardSourceControllerEffect(1)));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private RansomNote(final RansomNote card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RansomNote copy() {
|
||||
return new RansomNote(this);
|
||||
}
|
||||
}
|
||||
72
Mage.Sets/src/mage/cards/r/RedressFate.java
Normal file
72
Mage.Sets/src/mage/cards/r/RedressFate.java
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
package mage.cards.r;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.MiracleAbility;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterArtifactOrEnchantmentCard;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author RobertFrosty
|
||||
*/
|
||||
public final class RedressFate extends CardImpl {
|
||||
|
||||
public RedressFate(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{W}{W}");
|
||||
|
||||
|
||||
// Return all artifact and enchantment cards from your graveyard to the battlefield.
|
||||
this.getSpellAbility().addEffect(new RedressFateEffect());
|
||||
|
||||
// Miracle {3}{W}
|
||||
this.addAbility(new MiracleAbility("{3}{W}"));
|
||||
|
||||
}
|
||||
|
||||
private RedressFate(final RedressFate card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedressFate copy() {
|
||||
return new RedressFate(this);
|
||||
}
|
||||
}
|
||||
|
||||
class RedressFateEffect extends OneShotEffect {
|
||||
|
||||
private static final FilterCard filter = new FilterArtifactOrEnchantmentCard();
|
||||
|
||||
RedressFateEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "return all artifact and enchantment cards from your graveyard to the battlefield";
|
||||
}
|
||||
|
||||
private RedressFateEffect(final RedressFateEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RedressFateEffect copy() {
|
||||
return new RedressFateEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null) {
|
||||
return false;
|
||||
}
|
||||
return controller.moveCards(controller.getGraveyard().getCards(filter, game), Zone.BATTLEFIELD, source, game);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,25 +1,15 @@
|
|||
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.ExileGraveyardAllPlayersEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -49,7 +39,7 @@ public final class RestInPeace extends CardImpl {
|
|||
this.addAbility(new EntersBattlefieldTriggeredAbility(new ExileGraveyardAllPlayersEffect()));
|
||||
|
||||
// If a card or token would be put into a graveyard from anywhere, exile it instead.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new RestInPeaceReplacementEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(false, true)));
|
||||
}
|
||||
|
||||
private RestInPeace(final RestInPeace card) {
|
||||
|
|
@ -61,36 +51,3 @@ public final class RestInPeace extends CardImpl {
|
|||
return new RestInPeace(this);
|
||||
}
|
||||
}
|
||||
|
||||
class RestInPeaceReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
RestInPeaceReplacementEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Exile);
|
||||
staticText = "If a card or token would be put into a graveyard from anywhere, exile it instead";
|
||||
}
|
||||
|
||||
private RestInPeaceReplacementEffect(final RestInPeaceReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RestInPeaceReplacementEffect copy() {
|
||||
return new RestInPeaceReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return ((ZoneChangeEvent)event).getToZone() == Zone.GRAVEYARD;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import mage.filter.common.FilterControlledCreaturePermanent;
|
|||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.NumberOfTriggersEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -90,14 +90,10 @@ class RoamingThroneReplacementEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (!(event instanceof NumberOfTriggersEvent)) {
|
||||
return false;
|
||||
}
|
||||
NumberOfTriggersEvent numberOfTriggersEvent = (NumberOfTriggersEvent) event;
|
||||
if (!source.isControlledBy(event.getPlayerId())) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanentSource = game.getPermanentOrLKIBattlefield(numberOfTriggersEvent.getSourceId());
|
||||
Permanent permanentSource = game.getPermanentOrLKIBattlefield(event.getSourceId());
|
||||
return permanentSource != null
|
||||
&& filter.match(permanentSource, source.getControllerId(), source, game)
|
||||
&& permanentSource.hasSubtype(ChooseCreatureTypeEffect.getChosenCreatureType(source.getSourceId(), game), game);
|
||||
|
|
@ -105,7 +101,7 @@ class RoamingThroneReplacementEffect extends ReplacementEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
event.setAmount(event.getAmount() + 1);
|
||||
event.setAmount(CardUtil.overflowInc(event.getAmount(), 1));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
127
Mage.Sets/src/mage/cards/s/SeanceBoard.java
Normal file
127
Mage.Sets/src/mage/cards/s/SeanceBoard.java
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.abilities.condition.common.MorbidCondition;
|
||||
import mage.abilities.costs.common.TapSourceCost;
|
||||
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
|
||||
import mage.abilities.effects.mana.ManaEffect;
|
||||
import mage.abilities.hint.common.MorbidHint;
|
||||
import mage.abilities.mana.SimpleManaAbility;
|
||||
import mage.abilities.mana.builder.ConditionalManaBuilder;
|
||||
import mage.abilities.mana.conditional.ConditionalSpellManaBuilder;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.choices.ChoiceColor;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterSpell;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
|
||||
/**
|
||||
* @author Cguy7777
|
||||
*/
|
||||
public final class SeanceBoard extends CardImpl {
|
||||
|
||||
public SeanceBoard(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{2}");
|
||||
|
||||
// Morbid -- At the beginning of each end step, if a creature died this turn, put a soul counter on Seance Board.
|
||||
this.addAbility(new BeginningOfEndStepTriggeredAbility(
|
||||
new AddCountersSourceEffect(CounterType.SOUL.createInstance()),
|
||||
TargetController.ANY,
|
||||
MorbidCondition.instance,
|
||||
false
|
||||
).addHint(MorbidHint.instance).setAbilityWord(AbilityWord.MORBID));
|
||||
|
||||
// {T}: Add X mana of any one color, where X is the number of soul counters on Seance Board.
|
||||
// Spend this mana only to cast instant, sorcery, Demon, and Spirit spells.
|
||||
this.addAbility(new SimpleManaAbility(new SeanceBoardManaEffect(), new TapSourceCost()));
|
||||
}
|
||||
|
||||
private SeanceBoard(final SeanceBoard card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeanceBoard copy() {
|
||||
return new SeanceBoard(this);
|
||||
}
|
||||
}
|
||||
|
||||
class SeanceBoardManaEffect extends ManaEffect {
|
||||
|
||||
private static final FilterSpell filter = new FilterSpell("instant, sorcery, Demon, and Spirit spells");
|
||||
|
||||
static {
|
||||
filter.add(Predicates.or(
|
||||
CardType.INSTANT.getPredicate(),
|
||||
CardType.SORCERY.getPredicate(),
|
||||
SubType.DEMON.getPredicate(),
|
||||
SubType.SPIRIT.getPredicate()));
|
||||
}
|
||||
|
||||
private final ConditionalManaBuilder manaBuilder
|
||||
= new ConditionalSpellManaBuilder(filter);
|
||||
|
||||
SeanceBoardManaEffect() {
|
||||
this.staticText = "Add X mana of any one color, where X is the number of soul counters on {this}. "
|
||||
+ manaBuilder.getRule();
|
||||
}
|
||||
|
||||
private SeanceBoardManaEffect(final SeanceBoardManaEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Mana> getNetMana(Game game, Ability source) {
|
||||
List<Mana> netMana = new ArrayList<>();
|
||||
if (game == null) {
|
||||
return netMana;
|
||||
}
|
||||
Permanent permanent = source.getSourcePermanentOrLKI(game);
|
||||
if (permanent != null) {
|
||||
int soulCounters = permanent.getCounters(game).getCount(CounterType.SOUL);
|
||||
netMana.add(manaBuilder.setMana(Mana.BlackMana(soulCounters), source, game).build());
|
||||
netMana.add(manaBuilder.setMana(Mana.BlueMana(soulCounters), source, game).build());
|
||||
netMana.add(manaBuilder.setMana(Mana.RedMana(soulCounters), source, game).build());
|
||||
netMana.add(manaBuilder.setMana(Mana.GreenMana(soulCounters), source, game).build());
|
||||
netMana.add(manaBuilder.setMana(Mana.WhiteMana(soulCounters), source, game).build());
|
||||
}
|
||||
return netMana;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Mana produceMana(Game game, Ability source) {
|
||||
Mana mana = new Mana();
|
||||
if (game == null) {
|
||||
return mana;
|
||||
}
|
||||
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
Permanent permanent = source.getSourcePermanentOrLKI(game);
|
||||
if (controller == null || permanent == null) {
|
||||
return mana;
|
||||
}
|
||||
|
||||
ChoiceColor choice = new ChoiceColor();
|
||||
if (!controller.choose(Outcome.PutManaInPool, choice, game)) {
|
||||
return mana;
|
||||
}
|
||||
Mana chosen = choice.getMana(permanent.getCounters(game).getCount(CounterType.SOUL));
|
||||
return manaBuilder.setMana(chosen, source, game).build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SeanceBoardManaEffect copy() {
|
||||
return new SeanceBoardManaEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -39,6 +39,7 @@ public final class SpymastersVault extends CardImpl {
|
|||
|
||||
// {T}: Add {B}.
|
||||
this.addAbility(new BlackManaAbility());
|
||||
|
||||
// {B}, {T}: Target creature you control connives X, where X is the number of creatures that died this turn.
|
||||
Ability ability = new SimpleActivatedAbility(new SpymastersVaultEffect(), new ManaCostsImpl<>("{B}"));
|
||||
ability.addCost(new TapSourceCost());
|
||||
|
|
@ -80,7 +81,7 @@ class SpymastersVaultEffect extends OneShotEffect {
|
|||
if (deaths < 1) {
|
||||
return false;
|
||||
}
|
||||
Permanent permanent = game.getPermanent(source.getFirstTarget());
|
||||
Permanent permanent = game.getPermanentOrLKIBattlefield(source.getFirstTarget());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
93
Mage.Sets/src/mage/cards/s/StoneDrake.java
Normal file
93
Mage.Sets/src/mage/cards/s/StoneDrake.java
Normal file
|
|
@ -0,0 +1,93 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.Mode;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.common.DrawCardSourceControllerEffect;
|
||||
import mage.abilities.effects.common.TapTargetEffect;
|
||||
import mage.abilities.effects.common.discard.DiscardTargetEffect;
|
||||
import mage.abilities.hint.Hint;
|
||||
import mage.abilities.hint.ValueHint;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.game.Game;
|
||||
import mage.target.TargetPlayer;
|
||||
import mage.target.common.TargetLandPermanent;
|
||||
import mage.watchers.common.CastSpellLastTurnWatcher;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author Cguy7777
|
||||
*/
|
||||
public final class StoneDrake extends CardImpl {
|
||||
|
||||
private static final Hint hint
|
||||
= new ValueHint("Spells you've cast this turn", StoneDrakeDynamicValue.instance);
|
||||
|
||||
public StoneDrake(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{U}{B}");
|
||||
|
||||
this.subtype.add(SubType.DRAKE);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// When Stone Drake enters the battlefield, choose one —
|
||||
//• Distract — Tap target land. Draw a card.
|
||||
Ability ability = new EntersBattlefieldTriggeredAbility(new TapTargetEffect());
|
||||
ability.addEffect(new DrawCardSourceControllerEffect(1));
|
||||
ability.addTarget(new TargetLandPermanent());
|
||||
ability.withFirstModeFlavorWord("Distract");
|
||||
|
||||
//• Enthrall — Target player discards a card for each spell you've cast this turn.
|
||||
ability.addMode(new Mode(new DiscardTargetEffect(StoneDrakeDynamicValue.instance))
|
||||
.addTarget(new TargetPlayer())
|
||||
.withFlavorWord("Enthrall"));
|
||||
this.addAbility(ability.addHint(hint));
|
||||
}
|
||||
|
||||
private StoneDrake(final StoneDrake card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoneDrake copy() {
|
||||
return new StoneDrake(this);
|
||||
}
|
||||
}
|
||||
|
||||
enum StoneDrakeDynamicValue implements DynamicValue {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return game
|
||||
.getState()
|
||||
.getWatcher(CastSpellLastTurnWatcher.class)
|
||||
.getAmountOfSpellsPlayerCastOnCurrentTurn(sourceAbility.getControllerId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public StoneDrakeDynamicValue copy() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "1";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMessage() {
|
||||
return "spell you've cast this turn";
|
||||
}
|
||||
}
|
||||
118
Mage.Sets/src/mage/cards/t/TheInfamousCruelclaw.java
Normal file
118
Mage.Sets/src/mage/cards/t/TheInfamousCruelclaw.java
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
package mage.cards.t;
|
||||
|
||||
import mage.ApprovingObject;
|
||||
import mage.MageInt;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.SpellAbility;
|
||||
import mage.abilities.common.DealsCombatDamageToAPlayerTriggeredAbility;
|
||||
import mage.abilities.costs.Cost;
|
||||
import mage.abilities.costs.Costs;
|
||||
import mage.abilities.costs.CostsImpl;
|
||||
import mage.abilities.costs.common.DiscardCardCost;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.keyword.MenaceAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author notgreat
|
||||
*/
|
||||
public final class TheInfamousCruelclaw extends CardImpl {
|
||||
|
||||
public TheInfamousCruelclaw(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{B}{R}");
|
||||
|
||||
this.supertype.add(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.WEASEL);
|
||||
this.subtype.add(SubType.MERCENARY);
|
||||
this.power = new MageInt(3);
|
||||
this.toughness = new MageInt(3);
|
||||
|
||||
// Menace
|
||||
this.addAbility(new MenaceAbility(false));
|
||||
|
||||
// Whenever The Infamous Cruelclaw deals combat damage to a player, exile cards from the top of your library until you exile a nonland card. You may cast that card by discarding a card rather than paying its mana cost.
|
||||
this.addAbility(new DealsCombatDamageToAPlayerTriggeredAbility(new InfamousCruelclawEffect()));
|
||||
}
|
||||
|
||||
private TheInfamousCruelclaw(final TheInfamousCruelclaw card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public TheInfamousCruelclaw copy() {
|
||||
return new TheInfamousCruelclaw(this);
|
||||
}
|
||||
}
|
||||
|
||||
//Based on Amped Raptor
|
||||
class InfamousCruelclawEffect extends OneShotEffect {
|
||||
|
||||
InfamousCruelclawEffect() {
|
||||
super(Outcome.PlayForFree);
|
||||
staticText = "exile cards from the top of your library until you exile a nonland card. "
|
||||
+ "You may cast that card by discarding a card rather than paying its mana cost.";
|
||||
}
|
||||
|
||||
private InfamousCruelclawEffect(final InfamousCruelclawEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InfamousCruelclawEffect copy() {
|
||||
return new InfamousCruelclawEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller == null || !controller.getLibrary().hasCards()) {
|
||||
return false;
|
||||
}
|
||||
for (Card card : controller.getLibrary().getCards(game)) {
|
||||
controller.moveCards(card, Zone.EXILED, source, game);
|
||||
if (!card.isLand(game)) {
|
||||
List<Card> castableComponents = CardUtil.getCastableComponents(card, null, source, controller, game, null, false);
|
||||
if (castableComponents.isEmpty()) {
|
||||
break;
|
||||
}
|
||||
String partsInfo = castableComponents
|
||||
.stream()
|
||||
.map(MageObject::getLogName)
|
||||
.collect(Collectors.joining(" or "));
|
||||
if (!controller.chooseUse(Outcome.PlayForFree, "Cast spell by discarding a card instead of mana (" + partsInfo + ")?", source, game)) {
|
||||
break;
|
||||
}
|
||||
castableComponents.forEach(partCard -> game.getState().setValue("PlayFromNotOwnHandZone" + partCard.getId(), Boolean.TRUE));
|
||||
SpellAbility chosenAbility = controller.chooseAbilityForCast(card, game, true);
|
||||
if (chosenAbility != null) {
|
||||
Card faceCard = game.getCard(chosenAbility.getSourceId());
|
||||
if (faceCard != null) {
|
||||
// discard instead of mana cost
|
||||
Costs<Cost> newCosts = new CostsImpl<>();
|
||||
newCosts.add(new DiscardCardCost());
|
||||
newCosts.addAll(chosenAbility.getCosts());
|
||||
controller.setCastSourceIdWithAlternateMana(faceCard.getId(), null, newCosts);
|
||||
controller.cast(
|
||||
chosenAbility, game, true,
|
||||
new ApprovingObject(source, game)
|
||||
);
|
||||
}
|
||||
}
|
||||
castableComponents.forEach(partCard -> game.getState().setValue("PlayFromNotOwnHandZone" + partCard.getId(), null));
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -64,6 +64,11 @@ class TheMindskinnerEffect extends PreventionEffectImpl {
|
|||
return new TheMindskinnerEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.DAMAGE_PLAYER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
int amount = event.getAmount();
|
||||
|
|
@ -79,6 +84,6 @@ class TheMindskinnerEffect extends PreventionEffectImpl {
|
|||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
return super.applies(event, source, game) && source.isControlledBy(game.getControllerId(event.getSourceId()));
|
||||
return super.applies(event, source, game) && source.isControlledBy(game.getControllerId(event.getSourceId())) && game.getOpponents(game.getControllerId(event.getSourceId())).contains(event.getTargetId());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import mage.constants.SubType;
|
|||
import mage.game.Controllable;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetOpponentsCreaturePermanent;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Optional;
|
||||
|
|
@ -75,9 +76,11 @@ class WicksPatrolEffect extends OneShotEffect {
|
|||
if (player == null || player.millCards(3, source, game).size() < 3) {
|
||||
return false;
|
||||
}
|
||||
game.fireReflexiveTriggeredAbility(new ReflexiveTriggeredAbility(
|
||||
ReflexiveTriggeredAbility ability = new ReflexiveTriggeredAbility(
|
||||
new BoostTargetEffect(WicksPatrolValue.instance, WicksPatrolValue.instance), false
|
||||
), source);
|
||||
).setTriggerPhrase("When you do, ");
|
||||
ability.addTarget(new TargetOpponentsCreaturePermanent());
|
||||
game.fireReflexiveTriggeredAbility(ability, source);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -87,7 +90,7 @@ enum WicksPatrolValue implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public int calculate(Game game, Ability sourceAbility, Effect effect) {
|
||||
return Optional
|
||||
return -1 * Optional
|
||||
.ofNullable(sourceAbility)
|
||||
.map(Controllable::getControllerId)
|
||||
.map(game::getPlayer)
|
||||
|
|
@ -112,6 +115,11 @@ enum WicksPatrolValue implements DynamicValue {
|
|||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "X";
|
||||
return "-X";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSign() {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,29 +1,19 @@
|
|||
|
||||
package mage.cards.y;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.continuous.CantCastMoreThanOneSpellEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -34,11 +24,11 @@ public final class YawgmothsAgenda extends CardImpl {
|
|||
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{3}{B}{B}");
|
||||
|
||||
// You can't cast more than one spell each turn.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new CantCastMoreThanOneSpellEffect(TargetController.YOU)));
|
||||
this.addAbility(new SimpleStaticAbility(new CantCastMoreThanOneSpellEffect(TargetController.YOU)));
|
||||
// You may play cards from your graveyard.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YawgmothsAgendaCanPlayCardsFromGraveyardEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new YawgmothsAgendaCanPlayCardsFromGraveyardEffect()));
|
||||
// If a card would be put into your graveyard from anywhere, exile it instead.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new YawgmothsAgendaReplacementEffect()));
|
||||
this.addAbility(new SimpleStaticAbility(new GraveyardFromAnywhereExileReplacementEffect(true, false)));
|
||||
}
|
||||
|
||||
private YawgmothsAgenda(final YawgmothsAgenda card) {
|
||||
|
|
@ -80,48 +70,4 @@ class YawgmothsAgendaCanPlayCardsFromGraveyardEffect extends ContinuousEffectImp
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class YawgmothsAgendaReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
YawgmothsAgendaReplacementEffect() {
|
||||
super(Duration.WhileOnBattlefield, Outcome.Detriment);
|
||||
this.staticText = "If a card would be put into your graveyard from anywhere, exile it instead";
|
||||
}
|
||||
|
||||
private YawgmothsAgendaReplacementEffect(final YawgmothsAgendaReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public YawgmothsAgendaReplacementEffect copy() {
|
||||
return new YawgmothsAgendaReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
|
||||
Card card = game.getCard(event.getTargetId());
|
||||
if (card != null && card.isOwnedBy(source.getControllerId())) {
|
||||
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
|
||||
if (!(permanent instanceof PermanentToken)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +1,17 @@
|
|||
|
||||
package mage.cards.y;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.cards.Card;
|
||||
import mage.abilities.effects.common.replacement.GraveyardFromAnywhereExileReplacementEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Layer;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SubLayer;
|
||||
import mage.constants.Zone;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.events.GameEvent.EventType;
|
||||
import mage.game.events.ZoneChangeEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.PermanentToken;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
|
|
@ -35,7 +25,7 @@ public final class YawgmothsWill extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new CanPlayCardsFromGraveyardEffect());
|
||||
|
||||
// If a card would be put into your graveyard from anywhere this turn, exile that card instead.
|
||||
this.getSpellAbility().addEffect(new YawgmothsWillReplacementEffect());
|
||||
this.getSpellAbility().addEffect(new GraveyardFromAnywhereExileReplacementEffect(Duration.EndOfTurn).concatBy("<br>"));
|
||||
}
|
||||
|
||||
private YawgmothsWill(final YawgmothsWill card) {
|
||||
|
|
@ -79,46 +69,3 @@ class CanPlayCardsFromGraveyardEffect extends ContinuousEffectImpl {
|
|||
}
|
||||
|
||||
}
|
||||
|
||||
class YawgmothsWillReplacementEffect extends ReplacementEffectImpl {
|
||||
|
||||
YawgmothsWillReplacementEffect() {
|
||||
super(Duration.EndOfTurn, Outcome.Detriment);
|
||||
this.staticText = "If a card would be put into your graveyard from anywhere this turn, exile that card instead";
|
||||
}
|
||||
|
||||
private YawgmothsWillReplacementEffect(final YawgmothsWillReplacementEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public YawgmothsWillReplacementEffect copy() {
|
||||
return new YawgmothsWillReplacementEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
((ZoneChangeEvent) event).setToZone(Zone.EXILED);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ZONE_CHANGE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
if (((ZoneChangeEvent) event).getToZone() == Zone.GRAVEYARD) {
|
||||
Card card = game.getCard(event.getTargetId());
|
||||
if (card != null && card.isOwnedBy(source.getControllerId())) {
|
||||
Permanent permanent = ((ZoneChangeEvent) event).getTarget();
|
||||
if (!(permanent instanceof PermanentToken)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,13 @@ package mage.sets;
|
|||
import mage.cards.ExpansionSet;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
import mage.collation.BoosterCollator;
|
||||
import mage.collation.BoosterStructure;
|
||||
import mage.collation.CardRun;
|
||||
import mage.collation.RarityConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class Apocalypse extends ExpansionSet {
|
||||
|
||||
|
|
@ -167,4 +174,38 @@ public final class Apocalypse extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Yavimaya's Embrace", 127, Rarity.RARE, mage.cards.y.YavimayasEmbrace.class));
|
||||
cards.add(new SetCardInfo("Zombie Boa", 54, Rarity.COMMON, mage.cards.z.ZombieBoa.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoosterCollator createCollator() {
|
||||
return new ApocalypseCollator();
|
||||
}
|
||||
}
|
||||
|
||||
// Booster collation info from https://www.lethe.xyz/mtg/collation/jud.html
|
||||
// Using US collation - commons only
|
||||
class ApocalypseCollator implements BoosterCollator {
|
||||
private final CardRun commonA = new CardRun(true, "49", "81", "103", "19", "66", "118", "4", "37", "125", "90", "28", "101", "57", "14", "117", "37", "78", "119", "19", "57", "109", "13", "53", "101", "78", "27", "109", "66", "14", "119", "54", "81", "117", "27", "71", "103", "4", "53", "120", "90", "22", "93", "64", "3", "125", "49", "85", "93", "28", "71", "122", "3", "54", "120", "85", "22", "118", "64", "13", "122");
|
||||
private final CardRun commonB = new CardRun(true, "42", "76", "30", "56", "8", "43", "89", "25", "56", "16", "51", "76", "26", "60", "18", "44", "86", "25", "60", "1", "51", "86", "26", "65", "1", "41", "89", "29", "69", "18", "42", "82", "35", "69", "8", "44", "73", "30", "70", "16", "43", "82", "29", "70", "15", "41", "73", "35", "65", "15");
|
||||
private final CardRun uncommon = new CardRun(false, "91", "74", "58", "133", "92", "20", "2", "96", "5", "7", "134", "135", "61", "97", "136", "9", "23", "128", "99", "39", "77", "102", "62", "40", "12", "63", "129", "106", "79", "130", "110", "111", "45", "131", "132", "83", "48", "67", "123", "52", "87", "31", "33", "34");
|
||||
private final CardRun rare = new CardRun(false, "75", "139", "55", "140", "21", "94", "95", "6", "38", "59", "10", "98", "100", "11", "104", "105", "24", "80", "107", "137", "108", "141", "138", "112", "46", "113", "84", "114", "47", "50", "115", "116", "68", "142", "17", "121", "124", "88", "32", "126", "36", "72", "143", "127");
|
||||
|
||||
private final BoosterStructure AAAAAABBBBB = new BoosterStructure(
|
||||
commonA, commonA, commonA, commonA, commonA, commonA,
|
||||
commonB, commonB, commonB, commonB, commonB
|
||||
);
|
||||
private final BoosterStructure U3 = new BoosterStructure(uncommon, uncommon, uncommon);
|
||||
private final BoosterStructure R1 = new BoosterStructure(rare);
|
||||
|
||||
private final RarityConfiguration commonRuns = new RarityConfiguration(AAAAAABBBBB);
|
||||
private final RarityConfiguration uncommonRuns = new RarityConfiguration(U3);
|
||||
private final RarityConfiguration rareRuns = new RarityConfiguration(R1);
|
||||
|
||||
@Override
|
||||
public List<String> makeBooster() {
|
||||
List<String> booster = new ArrayList<>();
|
||||
booster.addAll(commonRuns.getNext().makeRun());
|
||||
booster.addAll(uncommonRuns.getNext().makeRun());
|
||||
booster.addAll(rareRuns.getNext().makeRun());
|
||||
return booster;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ public final class AssassinsCreed extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Basim Ibn Ishaq", 49, Rarity.RARE, mage.cards.b.BasimIbnIshaq.class));
|
||||
cards.add(new SetCardInfo("Battlefield Improvisation", 276, Rarity.COMMON, mage.cards.b.BattlefieldImprovisation.class));
|
||||
cards.add(new SetCardInfo("Bayek of Siwa", 50, Rarity.RARE, mage.cards.b.BayekOfSiwa.class));
|
||||
cards.add(new SetCardInfo("Become Anonymous", 14, Rarity.UNCOMMON, mage.cards.b.BecomeAnonymous.class));
|
||||
cards.add(new SetCardInfo("Black Market Connections", 87, Rarity.RARE, mage.cards.b.BlackMarketConnections.class));
|
||||
cards.add(new SetCardInfo("Bleeding Effect", 51, Rarity.UNCOMMON, mage.cards.b.BleedingEffect.class));
|
||||
cards.add(new SetCardInfo("Brotherhood Ambushers", 285, Rarity.UNCOMMON, mage.cards.b.BrotherhoodAmbushers.class));
|
||||
|
|
|
|||
|
|
@ -80,6 +80,7 @@ public final class Bloomburrow extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Diresight", 91, Rarity.COMMON, mage.cards.d.Diresight.class));
|
||||
cards.add(new SetCardInfo("Dour Port-Mage", 47, Rarity.RARE, mage.cards.d.DourPortMage.class));
|
||||
cards.add(new SetCardInfo("Downwind Ambusher", 92, Rarity.UNCOMMON, mage.cards.d.DownwindAmbusher.class));
|
||||
cards.add(new SetCardInfo("Dragonhawk, Fate's Tempest", 132, Rarity.MYTHIC, mage.cards.d.DragonhawkFatesTempest.class));
|
||||
cards.add(new SetCardInfo("Dreamdew Entrancer", 211, Rarity.RARE, mage.cards.d.DreamdewEntrancer.class));
|
||||
cards.add(new SetCardInfo("Driftgloom Coyote", 11, Rarity.UNCOMMON, mage.cards.d.DriftgloomCoyote.class));
|
||||
cards.add(new SetCardInfo("Druid of the Spade", 170, Rarity.COMMON, mage.cards.d.DruidOfTheSpade.class));
|
||||
|
|
@ -94,6 +95,7 @@ public final class Bloomburrow extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Fecund Greenshell", 362, Rarity.RARE, mage.cards.f.FecundGreenshell.class, NON_FULL_USE_VARIOUS));
|
||||
cards.add(new SetCardInfo("Feed the Cycle", 94, Rarity.UNCOMMON, mage.cards.f.FeedTheCycle.class));
|
||||
cards.add(new SetCardInfo("Fell", 95, Rarity.UNCOMMON, mage.cards.f.Fell.class));
|
||||
cards.add(new SetCardInfo("Festival of Embers", 134, Rarity.RARE, mage.cards.f.FestivalOfEmbers.class));
|
||||
cards.add(new SetCardInfo("Finch Formation", 50, Rarity.COMMON, mage.cards.f.FinchFormation.class));
|
||||
cards.add(new SetCardInfo("Finneas, Ace Archer", 212, Rarity.RARE, mage.cards.f.FinneasAceArcher.class));
|
||||
cards.add(new SetCardInfo("Fireglass Mentor", 213, Rarity.UNCOMMON, mage.cards.f.FireglassMentor.class));
|
||||
|
|
@ -135,6 +137,7 @@ public final class Bloomburrow extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Intrepid Rabbit", 17, Rarity.COMMON, mage.cards.i.IntrepidRabbit.class));
|
||||
cards.add(new SetCardInfo("Iridescent Vinelasher", 99, Rarity.RARE, mage.cards.i.IridescentVinelasher.class));
|
||||
cards.add(new SetCardInfo("Island", 266, Rarity.LAND, mage.cards.basiclands.Island.class, FULL_ART_BFZ_VARIOUS));
|
||||
cards.add(new SetCardInfo("Jackdaw Savior", 18, Rarity.RARE, mage.cards.j.JackdawSavior.class));
|
||||
cards.add(new SetCardInfo("Jolly Gerbils", 19, Rarity.UNCOMMON, mage.cards.j.JollyGerbils.class));
|
||||
cards.add(new SetCardInfo("Junkblade Bruiser", 220, Rarity.COMMON, mage.cards.j.JunkbladeBruiser.class));
|
||||
cards.add(new SetCardInfo("Kastral, the Windcrested", 221, Rarity.RARE, mage.cards.k.KastralTheWindcrested.class));
|
||||
|
|
@ -259,6 +262,7 @@ public final class Bloomburrow extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Teapot Slinger", 157, Rarity.UNCOMMON, mage.cards.t.TeapotSlinger.class));
|
||||
cards.add(new SetCardInfo("Tempest Angler", 235, Rarity.COMMON, mage.cards.t.TempestAngler.class));
|
||||
cards.add(new SetCardInfo("Tender Wildguide", 196, Rarity.RARE, mage.cards.t.TenderWildguide.class));
|
||||
cards.add(new SetCardInfo("The Infamous Cruelclaw", 219, Rarity.MYTHIC, mage.cards.t.TheInfamousCruelclaw.class));
|
||||
cards.add(new SetCardInfo("Thieving Otter", 390, Rarity.COMMON, mage.cards.t.ThievingOtter.class));
|
||||
cards.add(new SetCardInfo("Thistledown Players", 35, Rarity.COMMON, mage.cards.t.ThistledownPlayers.class));
|
||||
cards.add(new SetCardInfo("Thornplate Intimidator", 117, Rarity.COMMON, mage.cards.t.ThornplateIntimidator.class));
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Burnished Hart", 243, Rarity.UNCOMMON, mage.cards.b.BurnishedHart.class));
|
||||
cards.add(new SetCardInfo("Cackling Counterpart", 72, Rarity.RARE, mage.cards.c.CacklingCounterpart.class));
|
||||
cards.add(new SetCardInfo("Canyon Slough", 266, Rarity.RARE, mage.cards.c.CanyonSlough.class));
|
||||
cards.add(new SetCardInfo("Carrion Grub", 134, Rarity.COMMON, mage.cards.c.CarrionGrub.class));
|
||||
cards.add(new SetCardInfo("Carrion Grub", 134, Rarity.UNCOMMON, mage.cards.c.CarrionGrub.class));
|
||||
cards.add(new SetCardInfo("Cast Out", 98, Rarity.UNCOMMON, mage.cards.c.CastOut.class));
|
||||
cards.add(new SetCardInfo("Castle Vantress", 267, Rarity.RARE, mage.cards.c.CastleVantress.class));
|
||||
cards.add(new SetCardInfo("Caves of Koilos", 268, Rarity.RARE, mage.cards.c.CavesOfKoilos.class));
|
||||
|
|
@ -115,8 +115,8 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Giant Adephage", 179, Rarity.MYTHIC, mage.cards.g.GiantAdephage.class));
|
||||
cards.add(new SetCardInfo("Gleeful Arsonist", 27, Rarity.RARE, mage.cards.g.GleefulArsonist.class));
|
||||
cards.add(new SetCardInfo("Gnarlwood Dryad", 180, Rarity.UNCOMMON, mage.cards.g.GnarlwoodDryad.class));
|
||||
cards.add(new SetCardInfo("Golgari Rot Farm", 279, Rarity.COMMON, mage.cards.g.GolgariRotFarm.class));
|
||||
cards.add(new SetCardInfo("Golgari Signet", 246, Rarity.COMMON, mage.cards.g.GolgariSignet.class));
|
||||
cards.add(new SetCardInfo("Golgari Rot Farm", 279, Rarity.UNCOMMON, mage.cards.g.GolgariRotFarm.class));
|
||||
cards.add(new SetCardInfo("Golgari Signet", 246, Rarity.UNCOMMON, mage.cards.g.GolgariSignet.class));
|
||||
cards.add(new SetCardInfo("Goryo's Vengeance", 372, Rarity.MYTHIC, mage.cards.g.GoryosVengeance.class));
|
||||
cards.add(new SetCardInfo("Grapple with the Past", 82, Rarity.COMMON, mage.cards.g.GrappleWithThePast.class));
|
||||
cards.add(new SetCardInfo("Graven Cairns", 280, Rarity.RARE, mage.cards.g.GravenCairns.class));
|
||||
|
|
@ -205,6 +205,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Read the Bones", 154, Rarity.COMMON, mage.cards.r.ReadTheBones.class));
|
||||
cards.add(new SetCardInfo("Reality Shift", 125, Rarity.UNCOMMON, mage.cards.r.RealityShift.class));
|
||||
cards.add(new SetCardInfo("Reanimate", 155, Rarity.RARE, mage.cards.r.Reanimate.class));
|
||||
cards.add(new SetCardInfo("Redress Fate", 9, Rarity.RARE, mage.cards.r.RedressFate.class));
|
||||
cards.add(new SetCardInfo("Reliquary Tower", 295, Rarity.UNCOMMON, mage.cards.r.ReliquaryTower.class));
|
||||
cards.add(new SetCardInfo("Retreat to Coralhelm", 126, Rarity.UNCOMMON, mage.cards.r.RetreatToCoralhelm.class));
|
||||
cards.add(new SetCardInfo("Return to Dust", 102, Rarity.UNCOMMON, mage.cards.r.ReturnToDust.class));
|
||||
|
|
@ -213,6 +214,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Scavenging Ooze", 196, Rarity.RARE, mage.cards.s.ScavengingOoze.class));
|
||||
cards.add(new SetCardInfo("Scroll of Fate", 251, Rarity.RARE, mage.cards.s.ScrollOfFate.class));
|
||||
cards.add(new SetCardInfo("Scute Swarm", 197, Rarity.RARE, mage.cards.s.ScuteSwarm.class));
|
||||
cards.add(new SetCardInfo("Seance Board", 40, Rarity.RARE, mage.cards.s.SeanceBoard.class));
|
||||
cards.add(new SetCardInfo("Shadowblood Ridge", 296, Rarity.RARE, mage.cards.s.ShadowbloodRidge.class));
|
||||
cards.add(new SetCardInfo("Shark Typhoon", 127, Rarity.RARE, mage.cards.s.SharkTyphoon.class));
|
||||
cards.add(new SetCardInfo("Shigeki, Jukai Visionary", 198, Rarity.RARE, mage.cards.s.ShigekiJukaiVisionary.class));
|
||||
|
|
@ -262,7 +264,7 @@ public final class DuskmournHouseOfHorrorCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("They Came from the Pipes", 14, Rarity.RARE, mage.cards.t.TheyCameFromThePipes.class));
|
||||
cards.add(new SetCardInfo("Thirst for Meaning", 129, Rarity.COMMON, mage.cards.t.ThirstForMeaning.class));
|
||||
cards.add(new SetCardInfo("Thornwood Falls", 314, Rarity.COMMON, mage.cards.t.ThornwoodFalls.class));
|
||||
cards.add(new SetCardInfo("Thought Vessel", 256, Rarity.UNCOMMON, mage.cards.t.ThoughtVessel.class));
|
||||
cards.add(new SetCardInfo("Thought Vessel", 256, Rarity.COMMON, mage.cards.t.ThoughtVessel.class));
|
||||
cards.add(new SetCardInfo("Thriving Heath", 315, Rarity.COMMON, mage.cards.t.ThrivingHeath.class));
|
||||
cards.add(new SetCardInfo("Thriving Isle", 316, Rarity.COMMON, mage.cards.t.ThrivingIsle.class));
|
||||
cards.add(new SetCardInfo("Thriving Moor", 317, Rarity.COMMON, mage.cards.t.ThrivingMoor.class));
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
|
||||
package mage.sets;
|
||||
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
import mage.collation.BoosterCollator;
|
||||
import mage.collation.BoosterStructure;
|
||||
import mage.collation.CardRun;
|
||||
import mage.collation.RarityConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -29,6 +35,7 @@ public final class Judgment extends ExpansionSet {
|
|||
this.numBoosterRare = 1;
|
||||
this.ratioBoosterMythic = 0;
|
||||
this.hasUnbalancedColors = true;
|
||||
|
||||
cards.add(new SetCardInfo("Ancestor's Chosen", 1, Rarity.UNCOMMON, mage.cards.a.AncestorsChosen.class));
|
||||
cards.add(new SetCardInfo("Anger", 77, Rarity.UNCOMMON, mage.cards.a.Anger.class));
|
||||
cards.add(new SetCardInfo("Anurid Barkripper", 104, Rarity.COMMON, mage.cards.a.AnuridBarkripper.class));
|
||||
|
|
@ -172,4 +179,39 @@ public final class Judgment extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Wormfang Newt", 59, Rarity.COMMON, mage.cards.w.WormfangNewt.class));
|
||||
cards.add(new SetCardInfo("Wormfang Turtle", 60, Rarity.UNCOMMON, mage.cards.w.WormfangTurtle.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoosterCollator createCollator() {
|
||||
return new JudgmentCollator();
|
||||
}
|
||||
}
|
||||
|
||||
// Booster collation info from https://www.lethe.xyz/mtg/collation/jud.html
|
||||
// Using US collation - commons only
|
||||
class JudgmentCollator implements BoosterCollator {
|
||||
private final CardRun commonA = new CardRun(true, "22", "59", "118", "100", "4", "76", "126", "59", "95", "17", "57", "120", "28", "63", "106", "4", "91", "129", "42", "22", "89", "125", "47", "63", "57", "123", "86", "43", "5", "106", "33", "89", "30", "104", "43", "17", "34", "6", "123", "79", "76", "125", "42", "95", "126", "33", "100", "129", "28", "79", "120", "30", "86", "47", "118", "6", "91", "5", "104", "34");
|
||||
private final CardRun commonB = new CardRun(true, "121", "102", "108", "45", "71", "132", "14", "87", "41", "109", "13", "115", "39", "78", "18", "74", "20", "65", "46", "10", "121", "38", "20", "80", "132", "10", "87", "71", "41", "102", "136", "74", "7", "80", "109", "39", "13", "136", "46", "108", "7", "94", "18", "45", "115", "78", "65", "38", "94", "14");
|
||||
private final CardRun uncommon = new CardRun(false, "1", "77", "105", "2", "3", "107", "82", "62", "36", "8", "85", "111", "114", "66", "88", "40", "116", "67", "119", "92", "122", "141", "44", "142", "127", "16", "97", "49", "143", "131", "25", "99", "26", "27", "72", "101", "75", "135", "31", "32", "53", "54", "56", "60");
|
||||
// Shaman's Trance (rare, #98) not implemented, so has been omitted
|
||||
private final CardRun rare = new CardRun(false, "137", "61", "81", "83", "35", "9", "110", "37", "64", "84", "112", "113", "90", "117", "11", "12", "68", "138", "93", "96", "124", "69", "139", "48", "70", "15", "128", "140", "19", "50", "130", "21", "23", "24", "51", "73", "133", "52", "29", "134", "103", "55", "58");
|
||||
|
||||
private final BoosterStructure AAAAAABBBBB = new BoosterStructure(
|
||||
commonA, commonA, commonA, commonA, commonA, commonA,
|
||||
commonB, commonB, commonB, commonB, commonB
|
||||
);
|
||||
private final BoosterStructure U3 = new BoosterStructure(uncommon, uncommon, uncommon);
|
||||
private final BoosterStructure R1 = new BoosterStructure(rare);
|
||||
|
||||
private final RarityConfiguration commonRuns = new RarityConfiguration(AAAAAABBBBB);
|
||||
private final RarityConfiguration uncommonRuns = new RarityConfiguration(U3);
|
||||
private final RarityConfiguration rareRuns = new RarityConfiguration(R1);
|
||||
|
||||
@Override
|
||||
public List<String> makeBooster() {
|
||||
List<String> booster = new ArrayList<>();
|
||||
booster.addAll(commonRuns.getNext().makeRun());
|
||||
booster.addAll(uncommonRuns.getNext().makeRun());
|
||||
booster.addAll(rareRuns.getNext().makeRun());
|
||||
return booster;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public final class MurdersAtKarlovManorCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Black Sun's Zenith", 126, Rarity.RARE, mage.cards.b.BlackSunsZenith.class));
|
||||
cards.add(new SetCardInfo("Bloodthirsty Blade", 225, Rarity.UNCOMMON, mage.cards.b.BloodthirstyBlade.class));
|
||||
cards.add(new SetCardInfo("Bojuka Bog", 250, Rarity.COMMON, mage.cards.b.BojukaBog.class));
|
||||
cards.add(new SetCardInfo("Boltbender", 30, Rarity.RARE, mage.cards.b.Boltbender.class));
|
||||
cards.add(new SetCardInfo("Boros Garrison", 251, Rarity.UNCOMMON, mage.cards.b.BorosGarrison.class));
|
||||
cards.add(new SetCardInfo("Boros Reckoner", 201, Rarity.RARE, mage.cards.b.BorosReckoner.class));
|
||||
cards.add(new SetCardInfo("Brainstorm", 96, Rarity.COMMON, mage.cards.b.Brainstorm.class));
|
||||
|
|
@ -125,10 +126,12 @@ public final class MurdersAtKarlovManorCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Graf Mole", 170, Rarity.UNCOMMON, mage.cards.g.GrafMole.class));
|
||||
cards.add(new SetCardInfo("Grave Titan", 129, Rarity.MYTHIC, mage.cards.g.GraveTitan.class));
|
||||
cards.add(new SetCardInfo("Gruul Turf", 265, Rarity.UNCOMMON, mage.cards.g.GruulTurf.class));
|
||||
cards.add(new SetCardInfo("Havoc Eater", 31, Rarity.RARE, mage.cards.h.HavocEater.class));
|
||||
cards.add(new SetCardInfo("Hidden Dragonslayer", 69, Rarity.RARE, mage.cards.h.HiddenDragonslayer.class));
|
||||
cards.add(new SetCardInfo("Hooded Hydra", 171, Rarity.MYTHIC, mage.cards.h.HoodedHydra.class));
|
||||
cards.add(new SetCardInfo("Hornet Queen", 172, Rarity.RARE, mage.cards.h.HornetQueen.class));
|
||||
cards.add(new SetCardInfo("Hostile Desert", 266, Rarity.RARE, mage.cards.h.HostileDesert.class));
|
||||
cards.add(new SetCardInfo("Hot Pursuit", 32, Rarity.RARE, mage.cards.h.HotPursuit.class));
|
||||
cards.add(new SetCardInfo("Hydroid Krasis", 212, Rarity.RARE, mage.cards.h.HydroidKrasis.class));
|
||||
cards.add(new SetCardInfo("Idol of Oblivion", 229, Rarity.RARE, mage.cards.i.IdolOfOblivion.class));
|
||||
cards.add(new SetCardInfo("Imperial Hellkite", 155, Rarity.RARE, mage.cards.i.ImperialHellkite.class));
|
||||
|
|
@ -212,6 +215,7 @@ public final class MurdersAtKarlovManorCommander extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Prisoner's Dilemma", 34, Rarity.RARE, mage.cards.p.PrisonersDilemma.class));
|
||||
cards.add(new SetCardInfo("Promise of Loyalty", 79, Rarity.RARE, mage.cards.p.PromiseOfLoyalty.class));
|
||||
cards.add(new SetCardInfo("Psychosis Crawler", 234, Rarity.RARE, mage.cards.p.PsychosisCrawler.class));
|
||||
cards.add(new SetCardInfo("Ransom Note", 45, Rarity.RARE, mage.cards.r.RansomNote.class));
|
||||
cards.add(new SetCardInfo("Ravenous Chupacabra", 136, Rarity.UNCOMMON, mage.cards.r.RavenousChupacabra.class));
|
||||
cards.add(new SetCardInfo("Reanimate", 137, Rarity.RARE, mage.cards.r.Reanimate.class));
|
||||
cards.add(new SetCardInfo("Redemption Arc", 13, Rarity.RARE, mage.cards.r.RedemptionArc.class));
|
||||
|
|
|
|||
|
|
@ -232,6 +232,7 @@ public class MysteryBooster2 extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Static Orb", 234, Rarity.RARE, mage.cards.s.StaticOrb.class));
|
||||
cards.add(new SetCardInfo("Stifle", 173, Rarity.RARE, mage.cards.s.Stifle.class));
|
||||
cards.add(new SetCardInfo("Stitcher's Supplier", 48, Rarity.UNCOMMON, mage.cards.s.StitchersSupplier.class));
|
||||
cards.add(new SetCardInfo("Stone Drake", 363, Rarity.RARE, mage.cards.s.StoneDrake.class));
|
||||
cards.add(new SetCardInfo("Stony Silence", 21, Rarity.RARE, mage.cards.s.StonySilence.class));
|
||||
cards.add(new SetCardInfo("Street Wraith", 49, Rarity.COMMON, mage.cards.s.StreetWraith.class));
|
||||
cards.add(new SetCardInfo("Summoner's Pact", 74, Rarity.RARE, mage.cards.s.SummonersPact.class));
|
||||
|
|
|
|||
|
|
@ -3,6 +3,14 @@ package mage.sets;
|
|||
import mage.cards.ExpansionSet;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
import mage.collation.BoosterCollator;
|
||||
import mage.collation.BoosterStructure;
|
||||
import mage.collation.CardRun;
|
||||
import mage.collation.RarityConfiguration;
|
||||
import mage.util.RandomUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author North
|
||||
|
|
@ -379,4 +387,127 @@ public final class Odyssey extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Zombify", 171, Rarity.UNCOMMON, mage.cards.z.Zombify.class));
|
||||
cards.add(new SetCardInfo("Zoologist", 285, Rarity.RARE, mage.cards.z.Zoologist.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoosterCollator createCollator() {
|
||||
return new OdysseyCollator();
|
||||
}
|
||||
}
|
||||
|
||||
// Booster collation info from https://www.lethe.xyz/mtg/collation/ody.html
|
||||
// Using US collation - commons only
|
||||
class OdysseyCollator implements BoosterCollator {
|
||||
|
||||
private final CardRun commonA = new CardRun(true, "189", "93", "269", "136", "44", "199", "59", "233", "119", "14", "213", "109", "262", "145", "30", "185", "93", "268", "148", "56", "189", "109", "284", "136", "14", "199", "99", "269", "154", "44", "185", "87", "284", "145", "56", "225", "99", "262", "154", "16", "213", "59", "268", "119", "30", "225", "87", "233", "148", "16");
|
||||
private final CardRun commonB = new CardRun(true, "184", "312", "251", "150", "38", "172", "330", "247", "115", "7", "324", "95", "272", "151", "3", "314", "83", "251", "167", "312", "172", "103", "272", "115", "314", "190", "70", "246", "324", "27", "196", "103", "247", "325", "38", "184", "95", "246", "151", "7", "196", "70", "325", "150", "27", "190", "83", "330", "167", "3");
|
||||
private final CardRun commonC = new CardRun(true, "45", "140", "194", "63", "237", "8", "138", "224", "72", "253", "46", "139", "175", "98", "237", "45", "131", "181", "63", "263", "22", "125", "215", "65", "283", "25", "131", "193", "74", "253", "22", "138", "194", "98", "263", "36", "140", "181", "65", "282", "8", "139", "215", "74", "240", "25", "156", "193", "91", "282", "46", "125", "175", "72", "283", "36", "156", "224", "91", "240");
|
||||
private final CardRun commonD = new CardRun(true, "60", "232", "168", "42", "202", "114", "265", "130", "41", "173", "84", "252", "169", "40", "219", "60", "249", "124", "15", "202", "107", "252", "146", "41", "211", "114", "232", "124", "35", "217", "107", "238", "130", "15", "178", "100", "264", "137", "42", "217", "84", "238", "168", "5", "178", "81", "249", "146", "40", "211", "100", "265", "137", "5", "219", "81", "264", "169", "35", "173");
|
||||
private final CardRun uncommon = new CardRun(false, "4", "62", "6", "64", "66", "67", "68", "313", "176", "177", "230", "11", "12", "118", "315", "122", "316", "71", "317", "76", "123", "78", "235", "299", "128", "236", "80", "183", "129", "239", "186", "188", "191", "133", "134", "135", "86", "195", "23", "241", "244", "144", "88", "200", "301", "201", "289", "31", "203", "147", "250", "302", "206", "304", "34", "254", "258", "37", "322", "260", "155", "306", "291", "261", "94", "214", "292", "96", "97", "216", "43", "266", "158", "159", "307", "293", "160", "308", "221", "161", "162", "309", "270", "47", "222", "48", "49", "50", "51", "52", "53", "274", "102", "310", "223", "275", "311", "277", "54", "55", "295", "104", "106", "111", "279", "281", "226", "228", "170", "171");
|
||||
private final CardRun rare = new CardRun(false, "58", "1", "61", "2", "174", "286", "9", "10", "69", "229", "116", "179", "117", "180", "120", "121", "231", "13", "297", "73", "75", "182", "298", "234", "77", "318", "79", "126", "319", "127", "287", "17", "320", "18", "82", "19", "20", "187", "21", "132", "192", "85", "24", "141", "242", "142", "143", "243", "197", "288", "245", "300", "198", "26", "28", "248", "29", "204", "32", "149", "205", "303", "207", "152", "321", "208", "33", "290", "255", "256", "209", "153", "257", "259", "210", "305", "212", "89", "90", "92", "323", "39", "157", "218", "220", "267", "326", "294", "101", "327", "271", "273", "163", "276", "328", "164", "329", "278", "105", "108", "165", "110", "166", "112", "113", "296", "280", "227", "57", "285");
|
||||
|
||||
// either A then B, or B then A
|
||||
private final BoosterStructure AAABB = new BoosterStructure(
|
||||
commonA, commonA, commonA,
|
||||
commonB, commonB
|
||||
);
|
||||
private final BoosterStructure AABBB = new BoosterStructure(
|
||||
commonA, commonA,
|
||||
commonB, commonB, commonB
|
||||
);
|
||||
private final BoosterStructure AAAAB = new BoosterStructure(
|
||||
commonA, commonA, commonA, commonA,
|
||||
commonB
|
||||
);
|
||||
private final BoosterStructure ABBBB = new BoosterStructure(
|
||||
commonA,
|
||||
commonB, commonB, commonB, commonB
|
||||
);
|
||||
private final BoosterStructure BBBAA = new BoosterStructure(
|
||||
commonB, commonB, commonB,
|
||||
commonA, commonA
|
||||
);
|
||||
private final BoosterStructure BBAAA = new BoosterStructure(
|
||||
commonB, commonB,
|
||||
commonA, commonA, commonA
|
||||
);
|
||||
private final BoosterStructure BBBBA = new BoosterStructure(
|
||||
commonB, commonB, commonB, commonB,
|
||||
commonA
|
||||
);
|
||||
private final BoosterStructure BAAAA = new BoosterStructure(
|
||||
commonB,
|
||||
commonA, commonA, commonA, commonA
|
||||
);
|
||||
|
||||
// either C then D, or D then C
|
||||
private final BoosterStructure CCCDDD = new BoosterStructure(
|
||||
commonC, commonC, commonC,
|
||||
commonD, commonD, commonD
|
||||
);
|
||||
private final BoosterStructure CCCCDD = new BoosterStructure(
|
||||
commonC, commonC, commonC, commonC,
|
||||
commonD, commonD
|
||||
);
|
||||
private final BoosterStructure CCDDDD = new BoosterStructure(
|
||||
commonC, commonC,
|
||||
commonD, commonD, commonD, commonD
|
||||
);
|
||||
private final BoosterStructure DDDCCC = new BoosterStructure(
|
||||
commonD, commonD, commonD,
|
||||
commonC, commonC, commonC
|
||||
);
|
||||
private final BoosterStructure DDDDCC = new BoosterStructure(
|
||||
commonD, commonD, commonD, commonD,
|
||||
commonC, commonC
|
||||
);
|
||||
private final BoosterStructure DDCCCC = new BoosterStructure(
|
||||
commonD, commonD,
|
||||
commonC, commonC, commonC, commonC
|
||||
);
|
||||
|
||||
private final BoosterStructure U3 = new BoosterStructure(uncommon, uncommon, uncommon);
|
||||
private final BoosterStructure R1 = new BoosterStructure(rare);
|
||||
|
||||
// no definitive ratio here, just "most" packs 3-2
|
||||
private final RarityConfiguration commonRunsAB = new RarityConfiguration(
|
||||
AAAAB,
|
||||
AAABB, AAABB, AAABB, AAABB, AAABB, AAABB, AAABB, AAABB, AAABB,
|
||||
AABBB, AABBB, AABBB, AABBB, AABBB, AABBB, AABBB, AABBB, AABBB,
|
||||
ABBBB,
|
||||
BBBBA,
|
||||
BBBAA, BBBAA, BBBAA, BBBAA, BBBAA, BBBAA, BBBAA, BBBAA, BBBAA,
|
||||
BBAAA, BBAAA, BBAAA, BBAAA, BBAAA, BBAAA, BBAAA, BBAAA, BBAAA,
|
||||
BAAAA
|
||||
);
|
||||
|
||||
// similar collation structure for INV suggests some evidence for 1/6 packs 4-2
|
||||
private final RarityConfiguration commonRunsCD = new RarityConfiguration(
|
||||
CCCCDD,
|
||||
CCCDDD, CCCDDD, CCCDDD, CCCDDD, CCCDDD,
|
||||
CCCDDD, CCCDDD, CCCDDD, CCCDDD, CCCDDD,
|
||||
CCDDDD,
|
||||
DDDDCC,
|
||||
DDDCCC, DDDCCC, DDDCCC, DDDCCC, DDDCCC,
|
||||
DDDCCC, DDDCCC, DDDCCC, DDDCCC, DDDCCC,
|
||||
DDCCCC
|
||||
);
|
||||
|
||||
private final RarityConfiguration uncommonRuns = new RarityConfiguration(U3);
|
||||
private final RarityConfiguration rareRuns = new RarityConfiguration(R1);
|
||||
|
||||
@Override
|
||||
public List<String> makeBooster() {
|
||||
List<String> booster = new ArrayList<>();
|
||||
// either A/B or C/D can be first in the pack
|
||||
if (RandomUtil.nextBoolean()) {
|
||||
booster.addAll(commonRunsAB.getNext().makeRun());
|
||||
booster.addAll(commonRunsCD.getNext().makeRun());
|
||||
} else {
|
||||
booster.addAll(commonRunsCD.getNext().makeRun());
|
||||
booster.addAll(commonRunsAB.getNext().makeRun());
|
||||
}
|
||||
booster.addAll(uncommonRuns.getNext().makeRun());
|
||||
booster.addAll(rareRuns.getNext().makeRun());
|
||||
return booster;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,13 @@ import mage.cards.CardGraphicInfo;
|
|||
import mage.cards.ExpansionSet;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
import mage.collation.BoosterCollator;
|
||||
import mage.collation.BoosterStructure;
|
||||
import mage.collation.CardRun;
|
||||
import mage.collation.RarityConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public final class Onslaught extends ExpansionSet {
|
||||
|
||||
|
|
@ -24,6 +31,7 @@ public final class Onslaught extends ExpansionSet {
|
|||
this.numBoosterUncommon = 3;
|
||||
this.numBoosterRare = 1;
|
||||
this.ratioBoosterMythic = 0;
|
||||
|
||||
cards.add(new SetCardInfo("Accursed Centaur", 123, Rarity.COMMON, mage.cards.a.AccursedCentaur.class));
|
||||
cards.add(new SetCardInfo("Aether Charge", 184, Rarity.UNCOMMON, mage.cards.a.AetherCharge.class));
|
||||
cards.add(new SetCardInfo("Aggravated Assault", 185, Rarity.RARE, mage.cards.a.AggravatedAssault.class));
|
||||
|
|
@ -374,4 +382,55 @@ public final class Onslaught extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Words of Worship", 61, Rarity.RARE, mage.cards.w.WordsOfWorship.class));
|
||||
cards.add(new SetCardInfo("Wretched Anurid", 183, Rarity.COMMON, mage.cards.w.WretchedAnurid.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoosterCollator createCollator() {
|
||||
return new OnslaughtCollator();
|
||||
}
|
||||
}
|
||||
|
||||
// Booster collation info from https://www.lethe.xyz/mtg/collation/ons.html
|
||||
// Using US collation
|
||||
class OnslaughtCollator implements BoosterCollator {
|
||||
private final CardRun commonAC = new CardRun(true, "154", "50", "243", "77", "275", "138", "33", "188", "111", "304", "168", "49", "243", "114", "296", "166", "21", "210", "111", "253", "168", "29", "201", "106", "296", "172", "21", "202", "112", "253", "138", "18", "188", "99", "275", "154", "29", "215", "106", "304", "125", "18", "201", "114", "293", "166", "50", "202", "99", "302", "172", "33", "215", "77", "293", "125", "49", "210", "112", "302", "145", "9", "221", "70", "248", "162", "32", "209", "62", "260", "148", "9", "216", "94", "248", "124", "52", "220", "91", "301", "148", "19", "221", "62", "288", "145", "52", "194", "91", "290", "134", "31", "220", "74", "260", "162", "19", "209", "94", "288", "124", "31", "216", "70", "301", "134", "32", "194", "74", "290");
|
||||
private final CardRun commonB = new CardRun(true, "183", "35", "191", "113", "257", "176", "58", "208", "78", "256", "175", "25", "190", "88", "303", "139", "24", "191", "78", "255", "183", "23", "237", "119", "257", "139", "16", "208", "113", "300", "176", "23", "230", "90", "273", "158", "35", "196", "119", "255", "123", "25", "237", "115", "303", "175", "24", "190", "90", "300", "123", "58", "230", "115", "256", "158", "16", "196", "88", "273");
|
||||
private final CardRun commonD = new CardRun(true, "165", "26", "228", "68", "326", "312", "20", "227", "81", "284", "135", "34", "317", "68", "272", "159", "324", "234", "93", "246", "174", "26", "235", "320", "283", "159", "47", "317", "87", "272", "135", "20", "235", "81", "326", "165", "324", "227", "87", "284", "312", "34", "228", "93", "283", "174", "47", "234", "320", "246");
|
||||
private final CardRun uncommonA = new CardRun(true, "130", "41", "186", "98", "280", "157", "37", "193", "96", "292", "329", "126", "1", "241", "83", "251", "127", "41", "200", "95", "292", "315", "137", "37", "195", "83", "277", "128", "11", "233", "79", "262", "325", "170", "22", "203", "92", "277", "163", "11", "241", "85", "254", "323", "130", "38", "193", "76", "280", "129", "40", "203", "69", "298", "318", "137", "48", "242", "85", "262", "157", "22", "232", "96", "294", "329", "149", "40", "233", "98", "295", "170", "17", "186", "76", "287", "315", "149", "48", "232", "79", "295", "163", "8", "195", "82", "287", "325", "128", "1", "187", "95", "298", "126", "17", "242", "69", "251", "323", "127", "8", "187", "92", "254", "129", "38", "200", "82", "294", "318");
|
||||
private final CardRun uncommonB = new CardRun(true, "132", "12", "204", "107", "250", "144", "56", "226", "109", "249", "167", "15", "184", "65", "269", "171", "4", "199", "80", "258", "156", "51", "222", "73", "276", "180", "27", "184", "109", "258", "171", "5", "197", "110", "271", "177", "12", "217", "107", "279", "156", "54", "225", "64", "276", "131", "51", "231", "110", "252", "147", "27", "236", "100", "267", "167", "42", "225", "86", "265", "144", "54", "204", "63", "252", "131", "4", "240", "64", "269", "177", "45", "236", "63", "263", "151", "5", "199", "100", "279", "147", "45", "231", "105", "250", "181", "60", "217", "65", "267", "151", "56", "240", "105", "271", "180", "15", "197", "80", "249", "132", "60", "222", "86", "263", "181", "42", "226", "73", "265");
|
||||
// Artificial Evolution (rare, #67) not implemented, so removed from the run
|
||||
private final CardRun rare = new CardRun(false, "75", "153", "213", "245", "13", "118", "140", "39", "198", "286", "322", "30", "291", "244", "161", "101", "36", "308", "274", "212", "160", "66", "316", "43", "247", "207", "306", "164", "102", "28", "270", "214", "178", "84", "313", "3", "285", "185", "97", "136", "229", "282", "310", "53", "71", "182", "319", "238", "261", "55", "281", "141", "108", "307", "57", "297", "205", "143", "104", "330", "179", "61", "211", "103", "305", "173", "10", "189", "120", "268", "155", "327", "7", "223", "116", "278", "309", "146", "2", "218", "72", "289", "152", "328", "44", "239", "122", "264", "133", "46", "219", "117", "311", "259", "150", "321", "59", "192", "121", "266", "142", "14", "224", "299", "169", "6", "314", "206", "89");
|
||||
|
||||
private final BoosterStructure AAAAABBBDDD = new BoosterStructure(
|
||||
commonAC, commonAC, commonAC, commonAC, commonAC,
|
||||
commonB, commonB, commonB,
|
||||
commonD, commonD, commonD
|
||||
);
|
||||
private final BoosterStructure AAAAAABBBDD = new BoosterStructure(
|
||||
commonAC, commonAC, commonAC, commonAC, commonAC, commonAC,
|
||||
commonB, commonB, commonB,
|
||||
commonD, commonD
|
||||
);
|
||||
private final BoosterStructure ABB = new BoosterStructure(uncommonA, uncommonB, uncommonB);
|
||||
private final BoosterStructure AAB = new BoosterStructure(uncommonA, uncommonA, uncommonB);
|
||||
|
||||
private final BoosterStructure R1 = new BoosterStructure(rare);
|
||||
|
||||
private final RarityConfiguration commonRuns = new RarityConfiguration(
|
||||
AAAAABBBDDD, AAAAAABBBDD
|
||||
);
|
||||
|
||||
private final RarityConfiguration uncommonRuns = new RarityConfiguration(
|
||||
AAB, ABB
|
||||
);
|
||||
|
||||
private final RarityConfiguration rareRuns = new RarityConfiguration(R1);
|
||||
|
||||
@Override
|
||||
public List<String> makeBooster() {
|
||||
List<String> booster = new ArrayList<>();
|
||||
booster.addAll(commonRuns.getNext().makeRun());
|
||||
booster.addAll(uncommonRuns.getNext().makeRun());
|
||||
booster.addAll(rareRuns.getNext().makeRun());
|
||||
return booster;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ public final class StarWars extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Hidden Base", 733, Rarity.UNCOMMON, mage.cards.h.HiddenBase.class));
|
||||
cards.add(new SetCardInfo("Hold Captive", 723, Rarity.COMMON, mage.cards.h.HoldCaptive.class));
|
||||
cards.add(new SetCardInfo("Holochess", 708, Rarity.UNCOMMON, mage.cards.h.Holochess.class));
|
||||
cards.add(new SetCardInfo("Hot Pursuit", 188, Rarity.RARE, mage.cards.h.HotPursuitStarWars.class));
|
||||
cards.add(new SetCardInfo("Hot Pursuit (Star Wars)", 188, Rarity.RARE, mage.cards.h.HotPursuitStarWars.class));
|
||||
cards.add(new SetCardInfo("Hungry Dragonsnake", 138, Rarity.COMMON, mage.cards.h.HungryDragonsnake.class));
|
||||
cards.add(new SetCardInfo("Hunt to Extinction", 189, Rarity.RARE, mage.cards.h.HuntToExtinction.class));
|
||||
cards.add(new SetCardInfo("Hutt Crime Lord", 139, Rarity.UNCOMMON, mage.cards.h.HuttCrimeLord.class));
|
||||
|
|
|
|||
|
|
@ -557,9 +557,12 @@ class TheLostCavernsOfIxalanCollator implements BoosterCollator {
|
|||
private final BoosterStructure L1 = new BoosterStructure(land);
|
||||
|
||||
// In order for equal numbers of each common to exist, the average booster must contain:
|
||||
// 2.06250 A commons (66 / 32)
|
||||
// 2.06250 A commons (66 / 32) whilst paper 3A packs not observed, 6¼% 3A packs required where foils are ignored
|
||||
// 3.46875 B commons (111 / 32)
|
||||
// 3.46875 C commons (111 / 32)
|
||||
// Because xmage doesn't consider foils displacing commons, this requires the possibility of packs with 3 A commons
|
||||
// Taking into account foils changes the numbers to 1.986 A, 3.340 B, 3.340 C
|
||||
// which would mean 3x FABBBBCCC,3x FABBBCCCC,138x FAABBBCCC,144x AABBBBCCC,144x AABBBCCCC
|
||||
private final RarityConfiguration commonRuns = new RarityConfiguration(
|
||||
AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC,
|
||||
AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC, AABBBCCCC,
|
||||
|
|
|
|||
|
|
@ -1,9 +1,15 @@
|
|||
|
||||
package mage.sets;
|
||||
|
||||
import mage.cards.ExpansionSet;
|
||||
import mage.constants.Rarity;
|
||||
import mage.constants.SetType;
|
||||
import mage.collation.BoosterCollator;
|
||||
import mage.collation.BoosterStructure;
|
||||
import mage.collation.CardRun;
|
||||
import mage.collation.RarityConfiguration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
*
|
||||
|
|
@ -29,6 +35,7 @@ public final class Torment extends ExpansionSet {
|
|||
this.numBoosterRare = 1;
|
||||
this.ratioBoosterMythic = 0;
|
||||
this.hasUnbalancedColors = true;
|
||||
|
||||
cards.add(new SetCardInfo("Accelerate", 90, Rarity.COMMON, mage.cards.a.Accelerate.class));
|
||||
cards.add(new SetCardInfo("Acorn Harvest", 118, Rarity.COMMON, mage.cards.a.AcornHarvest.class));
|
||||
cards.add(new SetCardInfo("Ambassador Laquatus", 23, Rarity.RARE, mage.cards.a.AmbassadorLaquatus.class));
|
||||
|
|
@ -172,4 +179,39 @@ public final class Torment extends ExpansionSet {
|
|||
cards.add(new SetCardInfo("Waste Away", 88, Rarity.COMMON, mage.cards.w.WasteAway.class));
|
||||
cards.add(new SetCardInfo("Zombie Trailblazer", 89, Rarity.UNCOMMON, mage.cards.z.ZombieTrailblazer.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoosterCollator createCollator() {
|
||||
return new TormentCollator();
|
||||
}
|
||||
}
|
||||
|
||||
// Booster collation info from https://www.lethe.xyz/mtg/collation/tor.html
|
||||
// Using US collation - commons only
|
||||
class TormentCollator implements BoosterCollator {
|
||||
private final CardRun commonA = new CardRun(true, "102", "9", "58", "123", "39", "81", "103", "15", "75", "123", "47", "88", "103", "5", "53", "36", "121", "69", "93", "5", "85", "129", "32", "77", "97", "2", "60", "132", "24", "88", "102", "11", "60", "129", "36", "62", "93", "9", "75", "132", "32", "69", "107", "15", "53", "125", "39", "62", "97", "11", "85", "125", "47", "81", "107", "2", "58", "121", "24", "77");
|
||||
private final CardRun commonB = new CardRun(true, "99", "30", "6", "87", "96", "43", "130", "78", "90", "27", "12", "79", "43", "109", "51", "118", "35", "54", "115", "6", "79", "27", "109", "76", "128", "38", "54", "92", "12", "78", "30", "130", "115", "87", "41", "99", "18", "51", "92", "41", "128", "52", "90", "35", "18", "76", "96", "38", "118", "52");
|
||||
private final CardRun uncommon = new CardRun(false, "119", "120", "25", "50", "26", "139", "55", "122", "28", "29", "57", "33", "3", "34", "94", "124", "4", "98", "61", "63", "40", "7", "70", "72", "134", "105", "106", "110", "112", "138", "83", "84", "16", "17", "86", "48", "140", "141", "142", "143", "116", "19", "117", "89");
|
||||
// Alter Reality (rare, #22) not implemented, so has been omitted
|
||||
private final CardRun rare = new CardRun(false, "23", "1", "91", "31", "56", "59", "95", "37", "100", "126", "101", "64", "65", "66", "127", "67", "68", "42", "8", "10", "71", "73", "131", "133", "74", "135", "104", "136", "108", "44", "45", "111", "137", "13", "113", "14", "46", "80", "82", "114", "20", "49", "21");
|
||||
|
||||
private final BoosterStructure AAAAAABBBBB = new BoosterStructure(
|
||||
commonA, commonA, commonA, commonA, commonA, commonA,
|
||||
commonB, commonB, commonB, commonB, commonB
|
||||
);
|
||||
private final BoosterStructure U3 = new BoosterStructure(uncommon, uncommon, uncommon);
|
||||
private final BoosterStructure R1 = new BoosterStructure(rare);
|
||||
|
||||
private final RarityConfiguration commonRuns = new RarityConfiguration(AAAAAABBBBB);
|
||||
private final RarityConfiguration uncommonRuns = new RarityConfiguration(U3);
|
||||
private final RarityConfiguration rareRuns = new RarityConfiguration(R1);
|
||||
|
||||
@Override
|
||||
public List<String> makeBooster() {
|
||||
List<String> booster = new ArrayList<>();
|
||||
booster.addAll(commonRuns.getNext().makeRun());
|
||||
booster.addAll(uncommonRuns.getNext().makeRun());
|
||||
booster.addAll(rareRuns.getNext().makeRun());
|
||||
return booster;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ public class BlitzTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, decoy + withBlitz);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Double Major",decoy);
|
||||
|
||||
setChoice(playerA, ""); //stack triggers
|
||||
setChoice(playerA, "At the beginning of the next end step"); // x2 triggers
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
|
|
|||
|
|
@ -3,11 +3,23 @@ package org.mage.test.cards.abilities.keywords;
|
|||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.player.TestPlayer;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author Alex-Vasile
|
||||
* To have a creature connive, draw a card, then discard a card. If you discarded a nonland card, put a +1/+1 counter on that creature.
|
||||
* @author Alex-Vasile, JayDi85
|
||||
* <p>
|
||||
* Once an ability that causes a creature to connive begins to resolve, no player may take any other actions until it's done. Notably, opponents can't try to remove the conniving creature after you discard cards but before it receives +1/+1 counters, if any.
|
||||
* (2024-06-07)
|
||||
* <p>
|
||||
* To have a creature connive, draw a card, then discard a card. If you discarded a nonland card,
|
||||
* put a +1/+1 counter on that creature.
|
||||
* <p>
|
||||
* If no cards are discarded, most likely because that player's hand is empty and an effect says they can't draw cards, the conniving creature does not receive any +1/+1 counters.
|
||||
* (2024-06-07)
|
||||
* <p>
|
||||
* If a resolving spell or ability instructs a specific creature to connive but that creature has left the battlefield, the creature still connives, although you can't put any +1/+1 counters on it. Abilities that trigger "when [that creature] connives" will trigger.
|
||||
* (2024-06-07)
|
||||
*/
|
||||
public class ConniveTest extends CardTestPlayerBase {
|
||||
|
||||
|
|
@ -16,7 +28,7 @@ public class ConniveTest extends CardTestPlayerBase {
|
|||
* Creature should not get a +1/+1 counter.
|
||||
*/
|
||||
@Test
|
||||
public void conniveDiscardLand() {
|
||||
public void test_OnLandDiscard() {
|
||||
// P/T : 1/2
|
||||
// {3}: Hypnotic Grifter connives
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Hypnotic Grifter");
|
||||
|
|
@ -41,7 +53,7 @@ public class ConniveTest extends CardTestPlayerBase {
|
|||
* Creature should get a +1/+1 counter.
|
||||
*/
|
||||
@Test
|
||||
public void conniveDiscardCreature() {
|
||||
public void test_OnCreatureDiscard() {
|
||||
// P/T : 1/2
|
||||
// {3}: Hypnotic Grifter connives
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Hypnotic Grifter");
|
||||
|
|
@ -66,7 +78,7 @@ public class ConniveTest extends CardTestPlayerBase {
|
|||
* Creature should get a +1/+1 counter and the madness card gets played.
|
||||
*/
|
||||
@Test
|
||||
public void conniveMadness() {
|
||||
public void test_Madness() {
|
||||
// P/T : 1/2
|
||||
// {3}: Hypnotic Grifter connives
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Hypnotic Grifter");
|
||||
|
|
@ -95,10 +107,10 @@ public class ConniveTest extends CardTestPlayerBase {
|
|||
|
||||
/**
|
||||
* Obscura Confluence allows you to connive a creature you don't control.
|
||||
* It's the only card that causes you to coonnive a creature you don't control.
|
||||
* It's the only card that causes you to connive a creature you don't control.
|
||||
*/
|
||||
@Test
|
||||
public void conniveNonControlledCreature() {
|
||||
public void test_OpponentCreature() {
|
||||
// Choose three. You may choose the same mode more than once.
|
||||
//• Until end of turn, target creature loses all abilities and has base power and toughness 1/1.
|
||||
//• Target creature connives. (Draw a card, then discard a card. If you discarded a nonland card, put a +1/+1 counter on that creature.)
|
||||
|
|
@ -135,16 +147,16 @@ public class ConniveTest extends CardTestPlayerBase {
|
|||
/**
|
||||
* Reported bug: https://github.com/magefree/mage/issues/9252
|
||||
* Connive fizzles if the creature that connived leaves the battlefield before connive resolves.
|
||||
*
|
||||
* <p>
|
||||
* Ruling:
|
||||
* If a resolving spell or ability instructs a specific creature to connive but that creature has left the battlefield,
|
||||
* the creature still connives.
|
||||
* If you discard a nonland card this way, you won’t put a +1/+1 counter on anything.
|
||||
* Abilities that trigger “when [that creature] connives” will trigger.
|
||||
* (2022-04-29)
|
||||
* If a resolving spell or ability instructs a specific creature to connive but that creature has left the battlefield,
|
||||
* the creature still connives.
|
||||
* If you discard a nonland card this way, you won’t put a +1/+1 counter on anything.
|
||||
* Abilities that trigger “when [that creature] connives” will trigger.
|
||||
* (2022-04-29)
|
||||
*/
|
||||
@Test
|
||||
public void conniveDoesNotFizzle() {
|
||||
public void test_LKI_PsychicPickpocket() {
|
||||
// {4}{U}
|
||||
// 3/2
|
||||
// When Psychic Pickpocket enters the battlefield, it connives.
|
||||
|
|
@ -175,4 +187,187 @@ public class ConniveTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerA, "Psychic Pickpocket", 1); // Destroyed by Lightning Bolt
|
||||
assertGraveyardCount(playerB, "Lightning Bolt", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LKI_Source_Normal() {
|
||||
// When Raffine’s Silencer enters, it connives.
|
||||
addCard(Zone.HAND, playerA, "Raffine's Silencer");// {2}{B}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
|
||||
addCard(Zone.HAND, playerA, "Grizzly Bears", 1);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raffine's Silencer");
|
||||
setChoice(playerA, "Grizzly Bears"); // to discard
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPowerToughness(playerA, "Raffine's Silencer", 1 + 1, 1 + 1);
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LKI_Source_Dies() {
|
||||
// When Raffine’s Silencer enters, it connives.
|
||||
addCard(Zone.HAND, playerA, "Raffine's Silencer");// {2}{B}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 3);
|
||||
addCard(Zone.HAND, playerA, "Grizzly Bears", 1);
|
||||
//
|
||||
addCard(Zone.HAND, playerA, "Lightning Bolt", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 1);
|
||||
|
||||
// cast and kill raffine before cannive resolve
|
||||
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {B}", 3);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Raffine's Silencer");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, 1); // resolve raffine
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Lightning Bolt");
|
||||
addTarget(playerA, "Raffine's Silencer"); // to kill
|
||||
setChoice(playerA, "Grizzly Bears"); // to discard
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Raffine's Silencer", 1);
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LKI_SpymastersVault_Normal() {
|
||||
// {B}, {T}: Target creature you control connives X, where X is the number of creatures that died this turn.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Spymaster's Vault");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||
addCard(Zone.HAND, playerA, "Grizzly Bears", 1);
|
||||
//
|
||||
addCustomEffect_DestroyTarget(playerA);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Razorclaw Bear", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
|
||||
// prepare X = 1 from died amount
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Razorclaw Bear");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkGraveyardCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Razorclaw Bear", 1);
|
||||
|
||||
// cannive lion
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}, {T}: Target creature", "Silvercoat Lion");
|
||||
setChoice(playerA, "Grizzly Bears"); // to discard
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPowerToughness(playerA, "Silvercoat Lion", 2 + 1, 2 + 1);
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LKI_SpymastersVault_Fizzle() {
|
||||
// {B}, {T}: Target creature you control connives X, where X is the number of creatures that died this turn.
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Spymaster's Vault");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
|
||||
addCard(Zone.HAND, playerA, "Grizzly Bears", 1);
|
||||
//
|
||||
addCustomEffect_DestroyTarget(playerA);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Razorclaw Bear", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
|
||||
// prepare X = 1 from died amount
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Razorclaw Bear");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkGraveyardCount("prepare", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Razorclaw Bear", 1);
|
||||
|
||||
// cannive lion
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{B}, {T}: Target creature", "Silvercoat Lion");
|
||||
// destroy lion before cannive
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Silvercoat Lion");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
// no connive due invalid target (ability fizzled)
|
||||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LKI_ChangeOfPlans_Normal() {
|
||||
// Each of X target creatures you control connive. You may have any number of them phase out.
|
||||
addCard(Zone.HAND, playerA, "Change of Plans"); // {X}{1}{U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
addCard(Zone.HAND, playerA, "Grizzly Bears", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
|
||||
// connive lion
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Change of Plans");
|
||||
setChoice(playerA, "X=1");
|
||||
addTarget(playerA, "Silvercoat Lion");
|
||||
setChoice(playerA, "Grizzly Bears"); // to discard
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP); // no phase out
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPowerToughness(playerA, "Silvercoat Lion", 2 + 1, 2 + 1);
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LKI_ChangeOfPlans_Fizzle() {
|
||||
// Each of X target creatures you control connive. You may have any number of them phase out.
|
||||
addCard(Zone.HAND, playerA, "Change of Plans"); // {X}{1}{U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 3);
|
||||
addCard(Zone.HAND, playerA, "Grizzly Bears", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
//
|
||||
addCustomEffect_DestroyTarget(playerA);
|
||||
|
||||
// connive lion
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Change of Plans");
|
||||
setChoice(playerA, "X=1");
|
||||
addTarget(playerA, "Silvercoat Lion");
|
||||
// destroy lion before connive resolve
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Silvercoat Lion");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
// no connive due invalid target (ability fizzled)
|
||||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_LKI_ChangeOfPlans_MultipleTargets() {
|
||||
// Each of X target creatures you control connive. You may have any number of them phase out.
|
||||
addCard(Zone.HAND, playerA, "Change of Plans"); // {X}{1}{U}
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Island", 4);
|
||||
addCard(Zone.HAND, playerA, "Grizzly Bears", 1);
|
||||
//
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Razorclaw Bear", 1);
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Silvercoat Lion", 1);
|
||||
//
|
||||
addCustomEffect_DestroyTarget(playerA);
|
||||
|
||||
// connive lion
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Change of Plans");
|
||||
setChoice(playerA, "X=2");
|
||||
addTarget(playerA, "Silvercoat Lion^Razorclaw Bear");
|
||||
// destroy lion before connive resolve
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "target destroy", "Silvercoat Lion");
|
||||
setChoice(playerA, "Grizzly Bears"); // to discard first conni
|
||||
setChoice(playerA, TestPlayer.CHOICE_SKIP); // no phase out
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
// spell is valid for 1 target
|
||||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||
assertPowerToughness(playerA, "Razorclaw Bear", 3 + 1, 3 + 1);
|
||||
assertGraveyardCount(playerA, "Grizzly Bears", 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,7 +67,7 @@ public class DisguiseTest extends CardTestPlayerBase {
|
|||
});
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPermanentCount("after face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, "Dog Walker", 0);
|
||||
checkPermanentCount("after face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
checkPermanentCount("after face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
runCode("after face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
// server side
|
||||
Permanent permanent = currentGame.getBattlefield().getAllPermanents()
|
||||
|
|
@ -145,7 +145,7 @@ public class DisguiseTest extends CardTestPlayerBase {
|
|||
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{R/W}{R/W}: Turn");
|
||||
waitStackResolved(1, PhaseStep.POSTCOMBAT_MAIN, playerA);
|
||||
checkPermanentCount("after face up", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Dog Walker", 1);
|
||||
checkPermanentCount("after face up", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
checkPermanentCount("after face up", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
checkPermanentCount("after face up", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Dog Token", 2);
|
||||
runCode("after face up", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
Permanent permanent = currentGame.getBattlefield().getAllPermanents()
|
||||
|
|
@ -193,7 +193,7 @@ public class DisguiseTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
assertLife(playerA, 20);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -88,8 +88,7 @@ public class EchoTest extends CardTestPlayerBase {
|
|||
setChoice(playerA, true);
|
||||
setChoice(playerA, "Deranged Hermit");
|
||||
|
||||
setChoice(playerA, ""); //stack triggers
|
||||
setChoice(playerA, "");
|
||||
setChoice(playerA, "Echo {3}{G}{G}", 2); // x3 triggers from Deranged Hermit
|
||||
|
||||
setChoice(playerA, true); //Pay echo costs
|
||||
setChoice(playerA, true);
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -54,7 +54,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -74,8 +74,8 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -89,32 +89,32 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
});
|
||||
|
||||
// turn 1
|
||||
checkPermanentCount("turn 1.A - no face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
checkPermanentCount("turn 1.B - no face down", 1, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
checkPermanentCount("turn 1.A - no face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
checkPermanentCount("turn 1.B - no face down", 1, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
|
||||
// turn 2
|
||||
checkPermanentCount("turn 2.A - +1 face down", 2, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
checkPermanentCount("turn 2.B - no face down", 2, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
checkPermanentCount("turn 2.A - +1 face down", 2, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
checkPermanentCount("turn 2.B - no face down", 2, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
|
||||
// turn 3
|
||||
checkPermanentCount("turn 3.A - +1 face down", 3, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
checkPermanentCount("turn 3.B - no face down", 3, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
checkPermanentCount("turn 3.A - +1 face down", 3, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
checkPermanentCount("turn 3.B - no face down", 3, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
|
||||
// turn 4
|
||||
checkPermanentCount("turn 4.A - +2 face down", 4, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
|
||||
checkPermanentCount("turn 4.B - no face down", 4, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
checkPermanentCount("turn 4.A - +2 face down", 4, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2);
|
||||
checkPermanentCount("turn 4.B - no face down", 4, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
|
||||
// turn 5
|
||||
checkPermanentCount("turn 5.A - +2 face down", 5, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
|
||||
checkPermanentCount("turn 5.B - no face down", 5, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
checkPermanentCount("turn 5.A - +2 face down", 5, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2);
|
||||
checkPermanentCount("turn 5.B - no face down", 5, PhaseStep.PRECOMBAT_MAIN, playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(5, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
Assert.assertEquals("manifested cards must be taken from opponent's library", 2, playerA.getLibrary().size() - playerB.getLibrary().size());
|
||||
}
|
||||
|
||||
|
|
@ -136,12 +136,12 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
// manifest
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Soul Summons");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPermanentCount("need face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
checkPermanentCount("need face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
|
||||
// blink
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudshift", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
checkPermanentCount("need no face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
checkPermanentCount("need no face down", 1, PhaseStep.PRECOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
|
||||
runCode("after blink", 1, PhaseStep.PRECOMBAT_MAIN, playerA, (info, player, game) -> {
|
||||
if (cardAfterBlink == null) {
|
||||
|
|
@ -238,10 +238,10 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
// a facedown creature is on the battlefield
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
// not tapped
|
||||
assertTapped(EmptyNames.FACE_DOWN_CREATURE.toString(), false);
|
||||
assertTapped(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -270,8 +270,8 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 20);
|
||||
// a facedown creature is on the battlefield
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
// PlayerB's Silvercoat Lion should not have get -1/-1/
|
||||
assertPermanentCount(playerB, "Silvercoat Lion", 1);
|
||||
assertPowerToughness(playerB, "Silvercoat Lion", 2, 2);
|
||||
|
|
@ -307,8 +307,8 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerB, "Reality Shift", 1);
|
||||
assertExileCount("Silvercoat Lion", 1);
|
||||
// a facedown creature is on the battlefield
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
// PlayerA's Pillarfield Ox should not have get -1/-1/
|
||||
assertPermanentCount(playerB, "Pillarfield Ox", 1);
|
||||
assertPowerToughness(playerB, "Pillarfield Ox", 2, 4);
|
||||
|
|
@ -345,8 +345,8 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerB, "Reality Shift", 1);
|
||||
assertExileCount("Silvercoat Lion", 1);
|
||||
// a facedown creature is on the battlefield
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -381,8 +381,8 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerB, "Reality Shift", 1);
|
||||
assertExileCount("Silvercoat Lion", 1);
|
||||
// a facedown creature is on the battlefield
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
assertPowerToughness(playerA, "Foundry Street Denizen", 1, 1);
|
||||
|
||||
}
|
||||
|
|
@ -412,7 +412,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Reality Shift", "Silvercoat Lion");
|
||||
// showBattlefield("A battle", 1, PhaseStep.POSTCOMBAT_MAIN, playerA);
|
||||
// showBattlefield("B battle", 1, PhaseStep.POSTCOMBAT_MAIN, playerB);
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Silence the Believers", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Silence the Believers", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
|
@ -424,7 +424,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
assertExileCount("Silvercoat Lion", 1);
|
||||
assertExileCount("Gore Swine", 1);
|
||||
// no facedown creature is on the battlefield
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
|
||||
for (Card card : currentGame.getExile().getAllCards(currentGame)) {
|
||||
if (card.hasName("Gore Swine", currentGame)) {
|
||||
|
|
@ -463,7 +463,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
||||
|
||||
// a facedown creature is on the battlefield
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -503,7 +503,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
|
||||
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
||||
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
assertPermanentCount(playerB, "Aerie Bowmasters", 1);
|
||||
assertPowerToughness(playerB, "Aerie Bowmasters", 4, 5); // 3/4 and the +1/+1 counter from Megamorph
|
||||
Permanent aerie = getPermanent("Aerie Bowmasters", playerB);
|
||||
|
|
@ -541,7 +541,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
|
||||
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
||||
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
assertPermanentCount(playerB, "Aerie Bowmasters", 1);
|
||||
assertPowerToughness(playerB, "Aerie Bowmasters", 3, 4); // 3/4 without counter (megamorph not used)
|
||||
Permanent aerie = getPermanent("Aerie Bowmasters", playerB);
|
||||
|
|
@ -581,7 +581,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
|
||||
assertGraveyardCount(playerB, "Silvercoat Lion", 1);
|
||||
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -618,7 +618,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerB, "Whisperwood Elemental", 1);
|
||||
assertGraveyardCount(playerB, "Silvercoat Lion", 2);
|
||||
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -652,7 +652,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
setChoice(playerB, "Silvercoat Lion");
|
||||
|
||||
activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerB, "Sacrifice a creature");
|
||||
setChoice(playerB, EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
setChoice(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.END_TURN);
|
||||
|
|
@ -703,7 +703,7 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
|
||||
waitStackResolved(2, PhaseStep.PRECOMBAT_MAIN, playerB);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cloudshift", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Cloudshift", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.END_TURN);
|
||||
|
|
@ -843,8 +843,8 @@ public class ManifestTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, excommunicate, 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
assertLife(playerA, 20);
|
||||
assertLife(playerB, 18);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -73,7 +73,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker using Morph");
|
||||
|
||||
attack(3, playerA, EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
attack(3, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{4}{G}: Turn this face-down permanent face up.");
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
|
|
@ -81,7 +81,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertLife(playerB, 18);
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
assertPermanentCount(playerA, "Pine Walker", 1);
|
||||
assertPowerToughness(playerA, "Pine Walker", 5, 5);
|
||||
assertTapped("Pine Walker", false);
|
||||
|
|
@ -104,8 +104,8 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker using Morph");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Icefeather Aven using Morph", TestPlayer.NO_TARGET, "Pine Walker", StackClause.WHILE_NOT_ON_STACK);
|
||||
|
||||
attack(3, playerA, EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
attack(3, playerA, EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
attack(3, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
attack(3, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
activateAbility(3, PhaseStep.DECLARE_BLOCKERS, playerA, "{1}{G}{U}: Turn this face-down permanent face up.");
|
||||
setChoice(playerA, false); // Don't use return permanent to hand effect
|
||||
|
||||
|
|
@ -117,7 +117,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertHandCount(playerA, "Pine Walker", 0);
|
||||
assertHandCount(playerA, "Icefeather Aven", 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPermanentCount(playerA, "Icefeather Aven", 1);
|
||||
assertTapped("Icefeather Aven", true);
|
||||
|
||||
|
|
@ -148,7 +148,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertLife(playerB, 20); // and not 21
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPermanentCount(playerB, "Soldier of the Pantheon", 1);
|
||||
|
||||
}
|
||||
|
|
@ -175,10 +175,10 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -212,7 +212,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertHandCount(playerA, "Pine Walker", 0);
|
||||
assertHandCount(playerB, "Doomwake Giant", 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
assertPermanentCount(playerB, "Doomwake Giant", 1);
|
||||
assertPermanentCount(playerA, "Pine Walker", 1);
|
||||
assertPowerToughness(playerA, "Pine Walker", 4, 4);
|
||||
|
|
@ -252,7 +252,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertHandCount(playerA, "Ponyback Brigade", 0);
|
||||
assertHandCount(playerB, "Doomwake Giant", 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
assertPermanentCount(playerA, "Goblin Token", 3);
|
||||
assertPowerToughness(playerA, "Goblin Token", 1, 1, Filter.ComparisonScope.Any);
|
||||
assertPermanentCount(playerB, "Doomwake Giant", 1);
|
||||
|
|
@ -323,7 +323,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
assertHandCount(playerA, "Sagu Mauler", 0);
|
||||
assertHandCount(playerB, "Disdainful Stroke", 1); // can't be cast
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -351,7 +351,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sagu Mauler using Morph", TestPlayer.NO_TARGET, "Sagu Mauler", StackClause.WHILE_NOT_ON_STACK);
|
||||
|
||||
// showBattlefield("A battle", 1, PhaseStep.POSTCOMBAT_MAIN, playerA);
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Echoing Decay", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Echoing Decay", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
// showBattlefield("A battle after", 1, PhaseStep.END_TURN, playerA);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
|
@ -364,7 +364,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertHandCount(playerA, "Sagu Mauler", 0);
|
||||
assertHandCount(playerB, "Echoing Decay", 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertGraveyardCount(playerA, "Sagu Mauler", 1);
|
||||
}
|
||||
|
||||
|
|
@ -410,7 +410,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Birchlore Rangers using Morph");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Swords to Plowshares", "");
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Swords to Plowshares", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
|
@ -449,7 +449,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ashcloud Phoenix using Morph");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
|
@ -489,7 +489,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Ashcloud Phoenix using Morph");
|
||||
|
||||
attack(2, playerB, "Mirri, Cat Warrior");
|
||||
block(2, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), "Mirri, Cat Warrior");
|
||||
block(2, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), "Mirri, Cat Warrior");
|
||||
|
||||
setStopAt(2, PhaseStep.POSTCOMBAT_MAIN);
|
||||
execute();
|
||||
|
|
@ -528,7 +528,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Akroma, Angel of Fury using Morph");
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Supplant Form");
|
||||
addTarget(playerB, EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
addTarget(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
|
@ -536,13 +536,13 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
assertLife(playerB, 20);
|
||||
|
||||
assertHandCount(playerA, "Akroma, Angel of Fury", 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
assertPermanentCount(playerA, "Akroma, Angel of Fury", 0);
|
||||
assertGraveyardCount(playerB, "Supplant Form", 1);
|
||||
|
||||
assertPermanentCount(playerB, "Akroma, Angel of Fury", 0);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_TOKEN.toString(), 1);
|
||||
assertPowerToughness(playerB, EmptyNames.FACE_DOWN_TOKEN.toString(), 2, 2);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_TOKEN.getTestCommand(), 1);
|
||||
assertPowerToughness(playerB, EmptyNames.FACE_DOWN_TOKEN.getTestCommand(), 2, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -567,7 +567,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertLife(playerA, 20);
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -591,7 +591,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Pine Walker using Morph");
|
||||
|
||||
attack(3, playerA, EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
attack(3, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
activateAbility(3, PhaseStep.POSTCOMBAT_MAIN, playerA, "{4}{G}: Turn this face-down permanent face up.");
|
||||
setStopAt(3, PhaseStep.END_TURN);
|
||||
|
|
@ -599,7 +599,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertLife(playerB, 18);
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
assertPermanentCount(playerA, "Pine Walker", 1);
|
||||
assertPowerToughness(playerA, "Pine Walker", 5, 5);
|
||||
assertTapped("Pine Walker", false);
|
||||
|
|
@ -652,7 +652,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
assertPermanentCount(playerA, "Reflector Mage", 1);
|
||||
assertPermanentCount(playerB, "Rattleclaw Mystic", 0);
|
||||
assertHandCount(playerB, "Rattleclaw Mystic", 1); // can't play
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 0); // don't try as morph
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0); // don't try as morph
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -685,7 +685,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
assertPermanentCount(playerA, "Reflector Mage", 1);
|
||||
assertPermanentCount(playerB, "Rattleclaw Mystic", 0);
|
||||
assertHandCount(playerB, "Rattleclaw Mystic", 0); // able cast as morph
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerB, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -809,7 +809,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertGraveyardCount(playerB, "Fatal Push", 1);
|
||||
assertGraveyardCount(playerA, "Pine Walker", 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -842,7 +842,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertHandCount(playerA, 0);
|
||||
|
||||
assertTappedCount("Island", true, 3);
|
||||
|
|
@ -877,7 +877,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Quicksilver Dragon using Morph");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
setStopAt(2, PhaseStep.UPKEEP);
|
||||
execute();
|
||||
|
|
@ -959,7 +959,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Zoetic Cavern", 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -984,7 +984,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
assertPermanentCount(playerA, "Island", 1);
|
||||
assertPermanentCount(playerA, "Zoetic Cavern", 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1007,7 +1007,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Zoetic Cavern", 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1048,7 +1048,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1069,7 +1069,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1099,7 +1099,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(3, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1122,7 +1122,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1134,10 +1134,10 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Monastery Flock using Morph");
|
||||
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 3);
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.BEGIN_COMBAT, playerA, "{U}: Turn this", true);
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Tamiyo's Compleation", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Tamiyo's Compleation", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{U}: Turn this", false);
|
||||
|
||||
|
|
@ -1145,9 +1145,9 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertTapped(EmptyNames.FACE_DOWN_CREATURE.toString(), true);
|
||||
assertAttachedTo(playerA, "Tamiyo's Compleation", EmptyNames.FACE_DOWN_CREATURE.toString(), true);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
assertTapped(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), true);
|
||||
assertAttachedTo(playerA, "Tamiyo's Compleation", EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), true);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1162,10 +1162,10 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Sage-Eye Harrier using Morph");
|
||||
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.BEGIN_COMBAT, playerA, "{3}{W}: Turn this", true);
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Minimus Containment", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Minimus Containment", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.POSTCOMBAT_MAIN, playerA, "{3}{W}: Turn this", false);
|
||||
|
||||
|
|
@ -1175,10 +1175,10 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(2, PhaseStep.UPKEEP);
|
||||
execute();
|
||||
|
||||
assertSubtype(EmptyNames.FACE_DOWN_CREATURE.toString(), SubType.TREASURE);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.ARTIFACT, true);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.CREATURE, false);
|
||||
assertAttachedTo(playerA, "Minimus Containment", EmptyNames.FACE_DOWN_CREATURE.toString(), true);
|
||||
assertSubtype(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), SubType.TREASURE);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), CardType.ARTIFACT, true);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), CardType.CREATURE, false);
|
||||
assertAttachedTo(playerA, "Minimus Containment", EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1190,7 +1190,7 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Monastery Flock using Morph");
|
||||
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 3);
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.BEGIN_COMBAT, playerA, "{U}: Turn this", true);
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Mycosynth Lattice");
|
||||
|
|
@ -1201,10 +1201,10 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.ARTIFACT, true);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.CREATURE, true);
|
||||
assertNotSubtype(EmptyNames.FACE_DOWN_CREATURE.toString(), SubType.BIRD);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), CardType.ARTIFACT, true);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), CardType.CREATURE, true);
|
||||
assertNotSubtype(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), SubType.BIRD);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -1216,17 +1216,17 @@ public class MorphTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Monastery Flock using Morph");
|
||||
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
checkPT("face down", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 3);
|
||||
checkPlayableAbility("unmorph", 1, PhaseStep.BEGIN_COMBAT, playerA, "{U}: Turn this", true);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.ARTIFACT, true);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.toString(), CardType.CREATURE, true);
|
||||
assertNotSubtype(EmptyNames.FACE_DOWN_CREATURE.toString(), SubType.BIRD);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), CardType.ARTIFACT, true);
|
||||
assertType(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), CardType.CREATURE, true);
|
||||
assertNotSubtype(EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), SubType.BIRD);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 3);
|
||||
}
|
||||
|
||||
private void assertMorphedFaceDownColor(String info, String needColor) {
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ public class CastFromGraveyardOnceTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ public class UnboundFlourishingTest extends CardTestPlayerBase {
|
|||
// cast with X=3, but double it twice
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Endless One");
|
||||
setChoice(playerA, "X=3");
|
||||
setChoice(playerA, ""); //stack triggers
|
||||
setChoice(playerA, "Whenever you cast a permanent spell"); // x2 triggers from Unbound Flourishing
|
||||
checkPermanentCounters("after", 1, PhaseStep.BEGIN_COMBAT, playerA, "Endless One", CounterType.P1P1, 3 * 2 * 2);
|
||||
|
||||
setStrictChooseMode(true);
|
||||
|
|
|
|||
|
|
@ -105,8 +105,8 @@ public class ExileAndReturnUnderYourControl extends CardTestPlayerBase {
|
|||
assertExileCount("Secret Plans", 0);
|
||||
assertPermanentCount(playerA, "Secret Plans", 1);
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 3);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -225,6 +225,6 @@ public class GontiLordOfLuxuryEffectTest extends CardTestPlayerBase {
|
|||
setStopAt(4, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ public class AlteredEgoTest extends CardTestPlayerBase {
|
|||
setChoice(playerB, "X=3");
|
||||
setChoice(playerB, true); // use copy
|
||||
setChoice(playerB, "Endless One"); // copy target
|
||||
setChoice(playerB, ""); // Order place counters effects
|
||||
setChoice(playerB, "Endless One"); // x2 replacement effects from Endless One
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(2, PhaseStep.BEGIN_COMBAT);
|
||||
|
|
|
|||
|
|
@ -271,7 +271,7 @@ public class CloneTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Clone");
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
setChoice(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(3, PhaseStep.BEGIN_COMBAT);
|
||||
|
|
@ -283,8 +283,8 @@ public class CloneTest extends CardTestPlayerBase {
|
|||
assertPermanentCount(playerA, "Ixidron", 1);
|
||||
assertPowerToughness(playerA, "Ixidron", 1, 1);
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2, Filter.ComparisonScope.All);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2, Filter.ComparisonScope.All);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ public class DefilersTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear);
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, "");
|
||||
setChoice(playerA, "Whenever you cast a green permanent spell"); // x2 triggers from Defiler of Vigor
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
|
|
@ -83,7 +83,7 @@ public class DefilersTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, tusker);
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, "");
|
||||
setChoice(playerA, "Whenever you cast a green permanent spell"); // x2 triggers from Defiler of Vigor
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
setStrictChooseMode(true);
|
||||
|
|
@ -103,7 +103,7 @@ public class DefilersTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, goblin);
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, "");
|
||||
setChoice(playerA, "Whenever you cast a green permanent spell"); // x2 triggers from red and green defilers
|
||||
addTarget(playerA, playerB);
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ public class RoleTest extends CardTestPlayerBase {
|
|||
|
||||
addTarget(playerA, bear + "^" + wardens);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, become);
|
||||
setChoice(playerA, ""); // order triggers
|
||||
setChoice(playerA, "<i>Constellation</i>"); // x2 triggers from Nexus Wardens
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Thopter Spy Network", 0);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(),
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(),
|
||||
2, 2, Filter.ComparisonScope.All); // the manifested cards
|
||||
assertPermanentCount(playerA, "Starfield of Nyx", 1);
|
||||
assertPowerToughness(playerA, "Thopter Spy Network", 4, 4, Filter.ComparisonScope.All);
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ public class GhastlyConscriptionTest extends CardTestPlayerBase {
|
|||
assertLife(playerB, 20);
|
||||
|
||||
assertGraveyardCount(playerA, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class ObscuringAetherTest extends CardTestPlayerBase {
|
|||
assertHandCount(playerA, "Obscuring Aether", 0);
|
||||
assertGraveyardCount(playerA, "Obscuring Aether", 0);
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public class PrimordialMistTest extends CardTestPlayerBase {
|
|||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN, playerA);
|
||||
|
||||
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Exile a face-down permanent you control");
|
||||
setChoice(playerA, EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
setChoice(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Brine Elemental");
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class TriggerTest extends CardTestPlayerBase {
|
|||
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package org.mage.test.cards.replacement.entersBattlefield;
|
|||
import mage.abilities.keyword.DefenderAbility;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.constants.EmptyNames;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -220,7 +221,7 @@ public class PrimalClayTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, aquamorph+" using Morph");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Savage Swipe");
|
||||
addTarget(playerA, ""); // morph
|
||||
addTarget(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand()); // morph
|
||||
addTarget(playerA, "Siege Mastodon");
|
||||
// 2/2 becomes 4/4, fights 3/5, neither dies
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ public class MeddlingMageTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Meddling Mage", 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertHandCount(playerA, "Meddling Mage", 0);
|
||||
assertHandCount(playerA, "Ainok Tracker", 0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -136,7 +136,7 @@ public class CantCastTest extends CardTestPlayerBase {
|
|||
}
|
||||
}
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 0);
|
||||
assertHandCount(playerA, "Pine Walker", 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,9 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
|
|||
public class NamePredicateTest extends CardTestPlayerBase {
|
||||
|
||||
private void assertNamePredicate(String checkName, int needAmount, String needName, boolean ignoreMtgRules) {
|
||||
//Assert.assertNotEquals("", needName); empty strings also testing here, so no need to assert it
|
||||
needName = EmptyNames.replaceTestCommandByObjectName(needName);
|
||||
|
||||
FilterPermanent filter = new FilterPermanent();
|
||||
filter.add(new NamePredicate(needName, ignoreMtgRules));
|
||||
Assert.assertEquals(checkName, needAmount, currentGame.getBattlefield().countAll(filter, playerA.getId(), currentGame));
|
||||
|
|
@ -33,20 +36,20 @@ public class NamePredicateTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertPermanentCount(playerA, 3 + 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
|
||||
// use mtg rules for name searching
|
||||
assertNamePredicate("by rules - empty choice must return zero", 0, "", false);
|
||||
assertNamePredicate("by rules - face down choice must return zero", 0, EmptyNames.FACE_DOWN_CREATURE.toString(), false);
|
||||
assertNamePredicate("by rules - face down choice must return zero", 0, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), false);
|
||||
assertNamePredicate("by rules - non existing name must return zero", 0, "Island", false);
|
||||
assertNamePredicate("by rules - existing name must work", 3, "Forest", false);
|
||||
|
||||
// use inner engine for name searching (e.g. must find face down permanents with empty names)
|
||||
if (!EmptyNames.FACE_DOWN_CREATURE.toString().isEmpty()) {
|
||||
if (!EmptyNames.FACE_DOWN_CREATURE.getObjectName().isEmpty()) {
|
||||
// if face down permanents gets inner name someday then empty choice must ignore it
|
||||
assertNamePredicate("by inner - empty choice must return zero", 0, "", true);
|
||||
}
|
||||
assertNamePredicate("by inner - face down choice must work", 1, EmptyNames.FACE_DOWN_CREATURE.toString(), true);
|
||||
assertNamePredicate("by inner - face down choice must work", 1, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), true);
|
||||
assertNamePredicate("by inner - non existing name must return zero", 0, "Island", true);
|
||||
assertNamePredicate("by inner - existing name must work", 3, "Forest", true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
package org.mage.test.cards.single.blb;
|
||||
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
import org.junit.Test;
|
||||
import org.mage.test.serverside.base.CardTestPlayerBase;
|
||||
|
||||
/**
|
||||
* @author notgreat
|
||||
*/
|
||||
public class JackdawSaviorTest extends CardTestPlayerBase {
|
||||
|
||||
/**
|
||||
* {@link mage.cards.j.JackdawSavior Jackdaw Savior} {2}{W}
|
||||
* Creature — Bird Cleric
|
||||
* Flying
|
||||
* Whenever Jackdaw Savior or another creature you control with flying dies,
|
||||
* return another target creature card with lesser mana value from your graveyard to the battlefield.
|
||||
* <p>
|
||||
* This card is unusual in that "another" here refers to "other than the creature that just died",
|
||||
* which xmage does not natively support.
|
||||
*/
|
||||
|
||||
@Test
|
||||
public void test_Simultaneous() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Jackdaw Savior"); //MV = 3, flying
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Air Elemental"); //MV = 5, flying
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Memnite"); //MV = 1
|
||||
|
||||
addCard(Zone.HAND, playerA, "Damnation");
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Damnation");
|
||||
setChoice(playerA, "Whenever {this} or another creature you control"); // x2 triggers from Jackdaw Savior
|
||||
addTarget(playerA, "Jackdaw Savior");
|
||||
addTarget(playerA, "Memnite");
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, 2); //Air Elemental+Damnation
|
||||
assertPermanentCount(playerA, "Jackdaw Savior", 1);
|
||||
assertPermanentCount(playerA, "Memnite", 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_Clones() {
|
||||
setStrictChooseMode(true);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Jackdaw Savior"); //MV = 3, flying
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Air Elemental"); //MV = 5, flying
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Spidersilk Armor"); //+0/+1 to all
|
||||
|
||||
addCard(Zone.HAND, playerA, "Phantasmal Image", 2); //MV = 2, clone
|
||||
addCard(Zone.HAND, playerA, "Murder", 2);
|
||||
|
||||
addCard(Zone.BATTLEFIELD, playerA, "Underground Sea", 10);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Image");
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, "Air Elemental");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder");
|
||||
addTarget(playerA, "Air Elemental[only copy]");
|
||||
// Can't target itself, no valid targets
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Phantasmal Image");
|
||||
setChoice(playerA, true);
|
||||
setChoice(playerA, "Air Elemental");
|
||||
waitStackResolved(1, PhaseStep.PRECOMBAT_MAIN);
|
||||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Murder");
|
||||
addTarget(playerA, "Air Elemental[only copy]");
|
||||
addTarget(playerA, "Phantasmal Image"); // Target the previous one
|
||||
setChoice(playerA, false); //Don't copy, stay on battlefield as 0/1
|
||||
|
||||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertGraveyardCount(playerA, "Murder", 2);
|
||||
assertGraveyardCount(playerA, "Phantasmal Image", 1);
|
||||
assertPermanentCount(playerA, "Phantasmal Image", 1);
|
||||
assertPermanentCount(playerA, "Jackdaw Savior", 1);
|
||||
assertPermanentCount(playerA, "Air Elemental", 1);
|
||||
}
|
||||
}
|
||||
|
|
@ -34,7 +34,7 @@ public class QarsiDeceiverTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, mystic + " using Morph");
|
||||
|
||||
checkPT("morph", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
checkPT("morph", 3, PhaseStep.POSTCOMBAT_MAIN, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
|
||||
activateAbility(5, PhaseStep.PRECOMBAT_MAIN, playerA, "{2}{U}: Turn");
|
||||
|
||||
|
|
|
|||
|
|
@ -56,8 +56,8 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 4, 4);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 4, 4);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -77,9 +77,9 @@ public class MuragandaPetroglyphsTest extends CardTestPlayerBase {
|
|||
setStopAt(1, PhaseStep.BEGIN_COMBAT);
|
||||
execute();
|
||||
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
//assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2); // no boost (permanent have haste)
|
||||
assertAbility(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), HasteAbility.getInstance(), true);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
//assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestName(), 2, 2); // no boost (permanent have haste)
|
||||
assertAbility(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), HasteAbility.getInstance(), true);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ public class AlpineHoundmasterTest extends CardTestPlayerBase {
|
|||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Alpine Houndmaster");
|
||||
setChoice(playerA, true);
|
||||
addTarget(playerA, "Igneous Cur^Alpine Watchdog");
|
||||
//addTarget(playerA, "");
|
||||
|
||||
setStrictChooseMode(true);
|
||||
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ public class AssembleThePlayersTest extends CardTestPlayerBase {
|
|||
setStopAt(3, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 2, 2);
|
||||
assertPowerToughness(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 2, 2);
|
||||
assertPowerToughness(playerA, merfolk, 2, 1);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@ public class CrypticCoatTest extends CardTestPlayerBase {
|
|||
|
||||
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, coat);
|
||||
|
||||
checkPT("Cloaked is 3/2", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 3, 2);
|
||||
checkPT("Cloaked is 3/2", 1, PhaseStep.BEGIN_COMBAT, playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 3, 2);
|
||||
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", EmptyNames.FACE_DOWN_CREATURE.toString());
|
||||
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Lightning Bolt", EmptyNames.FACE_DOWN_CREATURE.getTestCommand());
|
||||
setChoice(playerB, true); // pay for ward
|
||||
activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerA, "{1}{U}{U}: Turn this face-down permanent face up.");
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package org.mage.test.cards.single.ths;
|
||||
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.EmptyNames;
|
||||
import mage.constants.PhaseStep;
|
||||
import mage.constants.Zone;
|
||||
|
|
@ -48,7 +47,7 @@ public class PurphorosGodOfTheForgeTest extends CardTestPlayerBase {
|
|||
|
||||
assertGraveyardCount(playerB, "Reach of Shadows", 1);
|
||||
assertPermanentCount(playerA, "Ashcloud Phoenix", 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
|
||||
Permanent purphorosGodOfTheForge = getPermanent("Purphoros, God of the Forge", playerA);
|
||||
Assert.assertFalse("Purphoros may not be a creature but it is", purphorosGodOfTheForge.isCreature(currentGame));
|
||||
|
|
@ -79,7 +78,7 @@ public class PurphorosGodOfTheForgeTest extends CardTestPlayerBase {
|
|||
execute();
|
||||
|
||||
assertPermanentCount(playerA, "Ashcloud Phoenix", 0);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
|
||||
assertLife(playerA, 18); // 2 damage from Eidolon of the Great Revel
|
||||
assertLife(playerB, 18); // 2 damage from Purphoros for the morphed Phoenix
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ public class CemeteryIlluminatorTest extends CardTestPlayerBase {
|
|||
|
||||
assertExileCount(playerA, creature, 1);
|
||||
assertPermanentCount(playerA, ci, 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
assertPermanentCount(playerA, whetwheel, 0);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -119,7 +119,7 @@ public class ThijarianWitnessTest extends CardTestPlayerBase {
|
|||
|
||||
attack(1, playerA, tiny);
|
||||
block(1, playerB, tiny, tiny);
|
||||
setChoice(playerA, ""); //stack triggers
|
||||
setChoice(playerA, "<i>Bear Witness</i>"); // x2 triggers from Thijarian Witness
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
|
@ -158,8 +158,8 @@ public class ThijarianWitnessTest extends CardTestPlayerBase {
|
|||
|
||||
attack(1, playerA, tiny);
|
||||
block(1, playerB, tiny, tiny);
|
||||
setChoice(playerA, ""); //stack triggers
|
||||
setChoice(playerB, "");
|
||||
setChoice(playerA, "<i>Bear Witness</i>"); // x2 triggers from Thijarian Witness
|
||||
setChoice(playerB, "<i>Bear Witness</i>"); // x2 triggers from Thijarian Witness
|
||||
|
||||
setStopAt(1, PhaseStep.END_TURN);
|
||||
execute();
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ public class WhisperwoodElementalTest extends CardTestPlayerBase {
|
|||
assertGraveyardCount(playerA, "Silvercoat Lion", 1);
|
||||
|
||||
// Manifested creature from dying Silvercoat Lion
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.toString(), 1);
|
||||
assertPermanentCount(playerA, EmptyNames.FACE_DOWN_CREATURE.getTestCommand(), 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,6 +154,13 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
public void addChoice(String choice) {
|
||||
// prepare face down
|
||||
// how-to fix:
|
||||
// * for face down choices: use EmptyNames.XXX.getTestCommand instead toString
|
||||
// * for replacement/triggers choices: comment choice command, look at logs for triggers list and use starting text in the choice instead empty
|
||||
Assert.assertNotEquals("Choice can't be empty", "", choice);
|
||||
choice = EmptyNames.replaceTestCommandByObjectName(choice);
|
||||
|
||||
choices.add(choice);
|
||||
}
|
||||
|
||||
|
|
@ -182,6 +189,12 @@ public class TestPlayer implements Player {
|
|||
}
|
||||
|
||||
public void addTarget(String target) {
|
||||
// prepare face down
|
||||
// how-to fix: if it's a face down object then use getTestCommand instead toString
|
||||
Assert.assertNotEquals("Target can't be empty", "", target);
|
||||
|
||||
target = EmptyNames.replaceTestCommandByObjectName(target);
|
||||
|
||||
targets.add(target);
|
||||
}
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue