mirror of
https://github.com/magefree/mage.git
synced 2025-12-29 15:02:13 -08:00
Merge branch 'github-actions' of github.com:fearphage/mage into github-actions
This commit is contained in:
commit
8557860812
756 changed files with 22047 additions and 7502 deletions
13
.travis.yml
13
.travis.yml
|
|
@ -9,15 +9,4 @@ before_install:
|
|||
- echo "MAVEN_OPTS='-Xmx2g'" > ~/.mavenrc
|
||||
cache:
|
||||
directories:
|
||||
- $HOME/.m2
|
||||
install: >
|
||||
mvn
|
||||
install
|
||||
jacoco:prepare-agent
|
||||
--define jacoco.skip=false
|
||||
--define maven.javadoc.skip=true
|
||||
--define skipTests=true
|
||||
--batch-mode
|
||||
--show-version
|
||||
after_success:
|
||||
- mvn jacoco:report jacoco:report-aggregate verify coveralls:report --define jacoco.skip=false
|
||||
- $HOME/.m2
|
||||
|
|
@ -14,7 +14,6 @@
|
|||
<name>Mage Client</name>
|
||||
|
||||
<dependencies>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.mage</groupId>
|
||||
<artifactId>mage</artifactId>
|
||||
|
|
@ -31,102 +30,97 @@
|
|||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.googlecode.jspf</groupId>
|
||||
<artifactId>jspf-core</artifactId>
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage-counter-plugin</artifactId>
|
||||
<version>0.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- needs for server connection by jboss -->
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.truevfs</groupId>
|
||||
<artifactId>truevfs-profile-base</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.unbescape</groupId>
|
||||
<artifactId>unbescape</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- inner lib for jboss network implementation -->
|
||||
<groupId>net.sf.trove4j</groupId>
|
||||
<artifactId>trove4j</artifactId>
|
||||
<version>3.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- wtf lib, related to plugins system TODO: unused and can be deleted? -->
|
||||
<groupId>com.googlecode.jspf</groupId>
|
||||
<artifactId>jspf-core</artifactId>
|
||||
<version>0.9.1</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- image scaling for card images -->
|
||||
<!-- TODO: library is dead, must be replaced -->
|
||||
<groupId>com.mortennobel</groupId>
|
||||
<artifactId>java-image-scaling</artifactId>
|
||||
<version>0.8.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- graphic lib to draw GUI with effects -->
|
||||
<!-- TODO: library is dead, must be replaced -->
|
||||
<groupId>org.swinglabs</groupId>
|
||||
<artifactId>swingx</artifactId>
|
||||
<version>1.6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jetlang</groupId>
|
||||
<artifactId>jetlang</artifactId>
|
||||
<version>0.2.23</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-s3</artifactId>
|
||||
<version>1.11.827</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.jgoodies</groupId>
|
||||
<artifactId>forms</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.intellij</groupId>
|
||||
<artifactId>forms_rt</artifactId>
|
||||
<version>7.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<type>jar</type>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage-counter-plugin</artifactId>
|
||||
<version>0.1</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jdesktop</groupId>
|
||||
<artifactId>beansbinding</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- GUI lib, part of SwingX -->
|
||||
<groupId>org.swinglabs</groupId>
|
||||
<artifactId>swing-layout</artifactId>
|
||||
<version>1.0.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.14.2</version>
|
||||
<!-- multi-threading lib for symbols/images download -->
|
||||
<groupId>org.jetlang</groupId>
|
||||
<artifactId>jetlang</artifactId>
|
||||
<version>0.2.23</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<artifactId>truevfs-profile-base</artifactId>
|
||||
<groupId>net.java.truevfs</groupId>
|
||||
<type>jar</type>
|
||||
<version>0.11.1</version>
|
||||
<exclusions>
|
||||
<exclusion>
|
||||
<artifactId>truevfs-access-swing</artifactId>
|
||||
<groupId>net.java.truevfs</groupId>
|
||||
</exclusion>
|
||||
<exclusion>
|
||||
<artifactId>truecommons-key-swing</artifactId>
|
||||
<groupId>net.java.truecommons</groupId>
|
||||
</exclusion>
|
||||
</exclusions>
|
||||
<!-- amazon s3 cloud lib to upload game logs from experimental client -->
|
||||
<!-- TODO: feature must be removed as unused or implemented for all -->
|
||||
<groupId>com.amazonaws</groupId>
|
||||
<artifactId>aws-java-sdk-s3</artifactId>
|
||||
<version>1.12.78</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- GUI lib TODO: unused and can be deleted? -->
|
||||
<groupId>com.jgoodies</groupId>
|
||||
<artifactId>forms</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- GUI lib TODO: unused and can be deleted? -->
|
||||
<groupId>com.intellij</groupId>
|
||||
<artifactId>forms_rt</artifactId>
|
||||
<version>7.0.3</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<!-- GUI lib -->
|
||||
<groupId>org.jdesktop</groupId>
|
||||
<artifactId>beansbinding</artifactId>
|
||||
<version>1.2.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- scraping lib to download and parse symbols/images/svg -->
|
||||
<groupId>org.jsoup</groupId>
|
||||
<artifactId>jsoup</artifactId>
|
||||
<version>1.14.3</version>
|
||||
</dependency>
|
||||
|
||||
<!-- music player START -->
|
||||
|
|
@ -148,6 +142,7 @@
|
|||
<!-- music player END -->
|
||||
|
||||
<dependency>
|
||||
<!-- GUI lib for balloon popup TODO: unused and can be deleted? -->
|
||||
<groupId>net.java.balloontip</groupId>
|
||||
<artifactId>balloontip</artifactId>
|
||||
<version>1.2.4.1</version>
|
||||
|
|
@ -166,15 +161,11 @@
|
|||
<!-- svg support END -->
|
||||
|
||||
<dependency>
|
||||
<!-- time lib for GUI time info -->
|
||||
<groupId>org.ocpsoft.prettytime</groupId>
|
||||
<artifactId>prettytime</artifactId>
|
||||
<version>4.0.6.Final</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.unbescape</groupId>
|
||||
<artifactId>unbescape</artifactId>
|
||||
<version>1.1.6.RELEASE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<!-- to get the reference to local repository with com\googlecode\jspf\jspf-core\0.9.1\ -->
|
||||
|
|
|
|||
|
|
@ -186,14 +186,22 @@ public class CardArea extends JPanel implements CardEventProducer {
|
|||
card = tmp;
|
||||
}
|
||||
|
||||
CardIconRenderSettings customIconsRender = new CardIconRenderSettings()
|
||||
.withDebugMode(true)
|
||||
.withCustomPosition(customCardIconPosition)
|
||||
.withCustomOrder(customCardIconOrder)
|
||||
.withCustomColor(customCardIconColor)
|
||||
.withCustomMaxVisibleCount(customCardIconsMaxVisibleCount)
|
||||
.withCustomIconSizePercent(30);
|
||||
MageCard cardPanel = Plugins.instance.getMageCard(card, bigCard, customIconsRender, cardDimension, gameId, true, true,
|
||||
CardIconRenderSettings currentIconsRender;
|
||||
if (this.customRenderMode >= 0) {
|
||||
// debug
|
||||
currentIconsRender = new CardIconRenderSettings()
|
||||
.withDebugMode(true)
|
||||
.withCustomPosition(customCardIconPosition)
|
||||
.withCustomOrder(customCardIconOrder)
|
||||
.withCustomColor(customCardIconColor)
|
||||
.withCustomMaxVisibleCount(customCardIconsMaxVisibleCount)
|
||||
.withCustomIconSizePercent(30);
|
||||
} else {
|
||||
// default
|
||||
currentIconsRender = new CardIconRenderSettings();
|
||||
}
|
||||
|
||||
MageCard cardPanel = Plugins.instance.getMageCard(card, bigCard, currentIconsRender, cardDimension, gameId, true, true,
|
||||
customRenderMode != -1 ? customRenderMode : PreferencesDialog.getRenderMode(), customNeedFullPermanentRender);
|
||||
cardPanel.setCardContainerRef(this);
|
||||
cardPanel.update(card);
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ import java.util.List;
|
|||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Dialog for choosing abilities.
|
||||
* GUI: Dialog for choosing abilities (list)
|
||||
*
|
||||
* @author nantuko, JayDi85
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -4378,11 +4378,11 @@
|
|||
<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="labelPreferedImageLanguage" alignment="0" max="-2" attributes="0"/>
|
||||
<Component id="labelPreferredImageLanguage" alignment="0" max="-2" attributes="0"/>
|
||||
</Group>
|
||||
<EmptySpace max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="0" attributes="0">
|
||||
<Component id="cbPreferedImageLanguage" min="-2" pref="153" max="-2" 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"/>
|
||||
|
|
@ -4416,8 +4416,8 @@
|
|||
</Group>
|
||||
<EmptySpace type="unrelated" max="-2" attributes="0"/>
|
||||
<Group type="103" groupAlignment="3" attributes="0">
|
||||
<Component id="labelPreferedImageLanguage" alignment="3" min="-2" max="-2" attributes="0"/>
|
||||
<Component id="cbPreferedImageLanguage" alignment="3" min="-2" max="-2" 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>
|
||||
</Group>
|
||||
</Group>
|
||||
|
|
@ -4453,7 +4453,7 @@
|
|||
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbSaveToZipFilesActionPerformed"/>
|
||||
</Events>
|
||||
</Component>
|
||||
<Component class="javax.swing.JComboBox" name="cbPreferedImageLanguage">
|
||||
<Component class="javax.swing.JComboBox" name="cbPreferredImageLanguage">
|
||||
<Properties>
|
||||
<Property name="maximumRowCount" type="int" value="20"/>
|
||||
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
|
||||
|
|
@ -4469,7 +4469,7 @@
|
|||
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="<String>"/>
|
||||
</AuxValues>
|
||||
</Component>
|
||||
<Component class="javax.swing.JLabel" name="labelPreferedImageLanguage">
|
||||
<Component class="javax.swing.JLabel" name="labelPreferredImageLanguage">
|
||||
<Properties>
|
||||
<Property name="text" type="java.lang.String" value="Default images language:"/>
|
||||
<Property name="focusable" type="boolean" value="false"/>
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
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 = "cardImagesPreferedImageLaguage";
|
||||
public static final String KEY_CARD_IMAGES_PREF_LANGUAGE = "cardImagesPreferredImageLaguage";
|
||||
|
||||
public static final String KEY_CARD_RENDERING_FALLBACK = "cardRenderingFallback";
|
||||
public static final String KEY_CARD_RENDERING_ICONS_FOR_ABILITIES = "cardRenderingIconsForAbilities";
|
||||
|
|
@ -400,7 +400,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
cbTheme.setModel(new DefaultComboBoxModel<>(ThemeType.values()));
|
||||
addAvatars();
|
||||
|
||||
cbPreferedImageLanguage.setModel(new DefaultComboBoxModel<>(CardLanguage.toList()));
|
||||
cbPreferredImageLanguage.setModel(new DefaultComboBoxModel<>(CardLanguage.toList()));
|
||||
cbNumberOfDownloadThreads.setModel(new DefaultComboBoxModel<>(new String[]{"10", "9", "8", "7", "6", "5", "4", "3", "2", "1"}));
|
||||
}
|
||||
|
||||
|
|
@ -509,8 +509,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
txtImageFolderPath = new javax.swing.JTextField();
|
||||
btnBrowseImageLocation = new javax.swing.JButton();
|
||||
cbSaveToZipFiles = new javax.swing.JCheckBox();
|
||||
cbPreferedImageLanguage = new javax.swing.JComboBox<>();
|
||||
labelPreferedImageLanguage = new javax.swing.JLabel();
|
||||
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();
|
||||
|
|
@ -1658,11 +1658,11 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
}
|
||||
});
|
||||
|
||||
cbPreferedImageLanguage.setMaximumRowCount(20);
|
||||
cbPreferedImageLanguage.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
|
||||
cbPreferredImageLanguage.setMaximumRowCount(20);
|
||||
cbPreferredImageLanguage.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));
|
||||
|
||||
labelPreferedImageLanguage.setText("Default images language:");
|
||||
labelPreferedImageLanguage.setFocusable(false);
|
||||
labelPreferredImageLanguage.setText("Default images language:");
|
||||
labelPreferredImageLanguage.setFocusable(false);
|
||||
|
||||
labelNumberOfDownloadThreads.setText("Default download threads:");
|
||||
|
||||
|
|
@ -1689,10 +1689,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
.add(panelCardImagesLayout.createSequentialGroup()
|
||||
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(labelNumberOfDownloadThreads)
|
||||
.add(labelPreferedImageLanguage))
|
||||
.add(labelPreferredImageLanguage))
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
|
||||
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
|
||||
.add(cbPreferedImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 153, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
|
||||
.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)
|
||||
|
|
@ -1716,8 +1716,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
.add(labelHint1))
|
||||
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
|
||||
.add(panelCardImagesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
|
||||
.add(labelPreferedImageLanguage)
|
||||
.add(cbPreferedImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
|
||||
.add(labelPreferredImageLanguage)
|
||||
.add(cbPreferredImageLanguage, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
|
||||
);
|
||||
|
||||
panelCardStyles.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Card styles (restart xmage to apply new settings)"));
|
||||
|
|
@ -2945,7 +2945,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
saveImagesPath(prefs);
|
||||
save(prefs, dialog.cbSaveToZipFiles, KEY_CARD_IMAGES_SAVE_TO_ZIP, "true", "false", UPDATE_CACHE_POLICY);
|
||||
save(prefs, dialog.cbNumberOfDownloadThreads, KEY_CARD_IMAGES_THREADS);
|
||||
save(prefs, dialog.cbPreferedImageLanguage, KEY_CARD_IMAGES_PREF_LANGUAGE);
|
||||
save(prefs, dialog.cbPreferredImageLanguage, KEY_CARD_IMAGES_PREF_LANGUAGE);
|
||||
|
||||
save(prefs, dialog.cbUseDefaultBackground, KEY_BACKGROUND_IMAGE_DEFAULT, "true", "false", UPDATE_CACHE_POLICY);
|
||||
save(prefs, dialog.cbUseDefaultBattleImage, KEY_BATTLEFIELD_IMAGE_DEFAULT, "true", "false", UPDATE_CACHE_POLICY);
|
||||
|
|
@ -3518,7 +3518,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
}
|
||||
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.cbPreferedImageLanguage.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_IMAGES_PREF_LANGUAGE, CardLanguage.ENGLISH.getCode()));
|
||||
dialog.cbPreferredImageLanguage.setSelectedItem(MageFrame.getPreferences().get(KEY_CARD_IMAGES_PREF_LANGUAGE, CardLanguage.ENGLISH.getCode()));
|
||||
|
||||
// rendering settings
|
||||
load(prefs, dialog.cbCardRenderImageFallback, KEY_CARD_RENDERING_FALLBACK, "true", "false");
|
||||
|
|
@ -4073,7 +4073,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
private javax.swing.JComboBox cbNumberOfDownloadThreads;
|
||||
private javax.swing.JCheckBox cbPassPriorityActivation;
|
||||
private javax.swing.JCheckBox cbPassPriorityCast;
|
||||
private javax.swing.JComboBox<String> cbPreferedImageLanguage;
|
||||
private javax.swing.JComboBox<String> cbPreferredImageLanguage;
|
||||
private javax.swing.JComboBox<ProxyType> cbProxyType;
|
||||
private javax.swing.JCheckBox cbSaveToZipFiles;
|
||||
private javax.swing.JCheckBox cbShowStormCounter;
|
||||
|
|
@ -4176,7 +4176,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
|
|||
private javax.swing.JLabel labelMainStep;
|
||||
private javax.swing.JLabel labelNextTurn;
|
||||
private javax.swing.JLabel labelNumberOfDownloadThreads;
|
||||
private javax.swing.JLabel labelPreferedImageLanguage;
|
||||
private javax.swing.JLabel labelPreferredImageLanguage;
|
||||
private javax.swing.JLabel labelPriorEnd;
|
||||
private javax.swing.JLabel labelSkipStep;
|
||||
private javax.swing.JLabel labelStackWidth;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import org.apache.log4j.Logger;
|
|||
import java.awt.*;
|
||||
import java.awt.event.ActionEvent;
|
||||
import java.io.Serializable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Executors;
|
||||
|
|
@ -38,6 +39,7 @@ public class FeedbackPanel extends javax.swing.JPanel {
|
|||
private MageDialog connectedDialog;
|
||||
private ChatPanelBasic connectedChatPanel;
|
||||
private int lastMessageId;
|
||||
private Map<String, Serializable> lastOptions = new HashMap<>();
|
||||
|
||||
private static final ScheduledExecutorService WORKER = Executors.newSingleThreadScheduledExecutor();
|
||||
|
||||
|
|
@ -63,8 +65,8 @@ public class FeedbackPanel extends javax.swing.JPanel {
|
|||
private void setGUISize() {
|
||||
}
|
||||
|
||||
public void getFeedback(FeedbackMode mode, String message, boolean special, Map<String, Serializable> options,
|
||||
int messageId, boolean gameNeedUserFeedback, TurnPhase gameTurnPhase) {
|
||||
public void prepareFeedback(FeedbackMode mode, String message, boolean special, Map<String, Serializable> options,
|
||||
int messageId, boolean gameNeedUserFeedback, TurnPhase gameTurnPhase) {
|
||||
synchronized (this) {
|
||||
if (messageId < this.lastMessageId) {
|
||||
// if too many warning messages here then look at GAME_REDRAW_GUI event logic
|
||||
|
|
@ -72,13 +74,16 @@ public class FeedbackPanel extends javax.swing.JPanel {
|
|||
return;
|
||||
}
|
||||
this.lastMessageId = messageId;
|
||||
this.lastOptions = options;
|
||||
this.mode = mode;
|
||||
}
|
||||
|
||||
this.helper.setBasicMessage(message);
|
||||
this.helper.setOriginalId(null); // reference to the feedback causing ability
|
||||
String lblText = addAdditionalText(message, options);
|
||||
this.helper.setTextArea(lblText);
|
||||
|
||||
this.mode = mode;
|
||||
|
||||
switch (this.mode) {
|
||||
case INFORM:
|
||||
setButtonState("", "", mode);
|
||||
|
|
@ -113,7 +118,7 @@ public class FeedbackPanel extends javax.swing.JPanel {
|
|||
}
|
||||
|
||||
requestFocusIfPossible();
|
||||
handleOptions(options);
|
||||
updateOptions(options);
|
||||
|
||||
this.revalidate();
|
||||
this.repaint();
|
||||
|
|
@ -167,29 +172,35 @@ public class FeedbackPanel extends javax.swing.JPanel {
|
|||
WORKER.schedule(task, 8, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void handleOptions(Map<String, Serializable> options) {
|
||||
// clear already opened dialog (second request)
|
||||
if (connectedDialog != null) {
|
||||
connectedDialog.removeDialog();
|
||||
connectedDialog = null;
|
||||
}
|
||||
public void updateOptions(Map<String, Serializable> options) {
|
||||
this.lastOptions = options;
|
||||
|
||||
if (options != null) {
|
||||
if (options.containsKey("UI.left.btn.text")) {
|
||||
String text = (String) options.get("UI.left.btn.text");
|
||||
if (this.lastOptions != null) {
|
||||
if (this.lastOptions.containsKey("UI.left.btn.text")) {
|
||||
String text = (String) this.lastOptions.get("UI.left.btn.text");
|
||||
this.btnLeft.setText(text);
|
||||
this.helper.setLeft(text, !text.isEmpty());
|
||||
}
|
||||
if (options.containsKey("UI.right.btn.text")) {
|
||||
String text = (String) options.get("UI.right.btn.text");
|
||||
if (this.lastOptions.containsKey("UI.right.btn.text")) {
|
||||
String text = (String) this.lastOptions.get("UI.right.btn.text");
|
||||
this.btnRight.setText(text);
|
||||
this.helper.setRight(text, !text.isEmpty());
|
||||
}
|
||||
if (options.containsKey("dialog")) {
|
||||
connectedDialog = (MageDialog) options.get("dialog");
|
||||
}
|
||||
|
||||
updateConnectedDialog((MageDialog) this.lastOptions.getOrDefault("dialog", null));
|
||||
this.helper.autoSizeButtonsAndFeedbackState();
|
||||
} else {
|
||||
updateConnectedDialog(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void updateConnectedDialog(MageDialog newDialog) {
|
||||
if (this.connectedDialog != null && this.connectedDialog != newDialog) {
|
||||
// remove old
|
||||
this.connectedDialog.removeDialog();
|
||||
}
|
||||
this.connectedDialog = newDialog;
|
||||
if (this.connectedDialog != null) {
|
||||
this.connectedDialog.setVisible(true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -244,10 +255,7 @@ public class FeedbackPanel extends javax.swing.JPanel {
|
|||
}
|
||||
|
||||
private void btnRightActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnRightActionPerformed
|
||||
if (connectedDialog != null) {
|
||||
connectedDialog.removeDialog();
|
||||
connectedDialog = null;
|
||||
}
|
||||
updateConnectedDialog(null);
|
||||
if (mode == FeedbackMode.SELECT && (evt.getModifiers() & ActionEvent.CTRL_MASK) == ActionEvent.CTRL_MASK) {
|
||||
SessionHandler.sendPlayerInteger(gameId, 0);
|
||||
} else if (mode == FeedbackMode.END) {
|
||||
|
|
|
|||
|
|
@ -274,8 +274,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
windowDialog.removeDialog();
|
||||
}
|
||||
|
||||
clearPickTargetDialogs();
|
||||
clearPickPileDialogs();
|
||||
clearPickDialogs();
|
||||
|
||||
Plugins.instance.getActionCallback().hideOpenComponents();
|
||||
try {
|
||||
|
|
@ -288,18 +287,34 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
this.bigCard = null;
|
||||
}
|
||||
|
||||
private void hidePickDialogs() {
|
||||
// temporary hide opened dialog on redraw/update
|
||||
for (ShowCardsDialog dialog : this.pickTarget) {
|
||||
dialog.setVisible(false);
|
||||
}
|
||||
for (PickPileDialog dialog : this.pickPile) {
|
||||
dialog.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
private void clearPickDialogs() {
|
||||
// remove dialogs forever on clean or full update
|
||||
clearPickTargetDialogs();
|
||||
clearPickPileDialogs();
|
||||
}
|
||||
|
||||
private void clearPickTargetDialogs() {
|
||||
for (ShowCardsDialog pickTargetDialog : this.pickTarget) {
|
||||
pickTargetDialog.cleanUp();
|
||||
pickTargetDialog.removeDialog();
|
||||
for (ShowCardsDialog dialog : this.pickTarget) {
|
||||
dialog.cleanUp();
|
||||
dialog.removeDialog();
|
||||
}
|
||||
this.pickTarget.clear();
|
||||
}
|
||||
|
||||
private void clearPickPileDialogs() {
|
||||
for (PickPileDialog pickPileDialog : this.pickPile) {
|
||||
pickPileDialog.cleanUp();
|
||||
pickPileDialog.removeDialog();
|
||||
for (PickPileDialog dialog : this.pickPile) {
|
||||
dialog.cleanUp();
|
||||
dialog.removeDialog();
|
||||
}
|
||||
this.pickPile.clear();
|
||||
}
|
||||
|
|
@ -929,6 +944,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
}
|
||||
|
||||
feedbackPanel.disableUndo();
|
||||
feedbackPanel.updateOptions(lastGameData.options);
|
||||
|
||||
this.revalidate();
|
||||
this.repaint();
|
||||
|
|
@ -1344,7 +1360,7 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
|
||||
public void ask(String question, GameView gameView, int messageId, Map<String, Serializable> options) {
|
||||
updateGame(gameView, false, options, null);
|
||||
this.feedbackPanel.getFeedback(FeedbackMode.QUESTION, question, false, options, messageId, true, gameView.getPhase());
|
||||
this.feedbackPanel.prepareFeedback(FeedbackMode.QUESTION, question, false, options, messageId, true, gameView.getPhase());
|
||||
}
|
||||
|
||||
private void keepLastGameData(GameView game, boolean showPlayable, Map<String, Serializable> options, Set<UUID> targets) {
|
||||
|
|
@ -1604,11 +1620,16 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
* @param options
|
||||
* @param messageId
|
||||
*/
|
||||
public void pickTarget(String message, CardsView cardsView, GameView gameView, Set<UUID> targets, boolean required, Map<String, Serializable> options, int messageId) {
|
||||
public void pickTarget(GameView gameView, Map<String, Serializable> options, String message, CardsView cardsView, Set<UUID> targets, boolean required, int messageId) {
|
||||
updateGame(gameView, false, options, targets);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
clearPickTargetDialogs();
|
||||
|
||||
PopUpMenuType popupMenuType = null;
|
||||
if (options != null) {
|
||||
if (lastGameData.options != null) {
|
||||
if (options.containsKey("queryType")) {
|
||||
PlayerQueryEvent.QueryType needType = (PlayerQueryEvent.QueryType) options.get("queryType");
|
||||
PlayerQueryEvent.QueryType needType = (PlayerQueryEvent.QueryType) lastGameData.options.get("queryType");
|
||||
switch (needType) {
|
||||
case PICK_ABILITY:
|
||||
popupMenuType = PopUpMenuType.TRIGGER_ORDER;
|
||||
|
|
@ -1622,17 +1643,13 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
updateGame(gameView, false, options, targets);
|
||||
|
||||
Map<String, Serializable> options0 = options == null ? new HashMap<>() : options;
|
||||
Map<String, Serializable> options0 = lastGameData.options == null ? new HashMap<>() : lastGameData.options;
|
||||
ShowCardsDialog dialog = null;
|
||||
if (cardsView != null && !cardsView.isEmpty()) {
|
||||
// clear old dialogs before the new
|
||||
clearPickTargetDialogs();
|
||||
dialog = showCards(message, cardsView, required, options0, popupMenuType);
|
||||
dialog = prepareCardsDialog(message, cardsView, required, options0, popupMenuType);
|
||||
options0.put("dialog", dialog);
|
||||
}
|
||||
this.feedbackPanel.getFeedback(required ? FeedbackMode.INFORM : FeedbackMode.CANCEL, message, gameView.getSpecial(), options0, messageId, true, gameView.getPhase());
|
||||
this.feedbackPanel.prepareFeedback(required ? FeedbackMode.INFORM : FeedbackMode.CANCEL, message, gameView.getSpecial(), options0, messageId, true, gameView.getPhase());
|
||||
if (dialog != null) {
|
||||
this.pickTarget.add(dialog);
|
||||
}
|
||||
|
|
@ -1640,15 +1657,23 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
|
||||
public void inform(String information, GameView gameView, int messageId) {
|
||||
updateGame(gameView);
|
||||
this.feedbackPanel.getFeedback(FeedbackMode.INFORM, information, gameView.getSpecial(), null, messageId, false, gameView.getPhase());
|
||||
this.feedbackPanel.prepareFeedback(FeedbackMode.INFORM, information, gameView.getSpecial(), null, messageId, false, gameView.getPhase());
|
||||
}
|
||||
|
||||
public void endMessage(String message, int messageId) {
|
||||
this.feedbackPanel.getFeedback(FeedbackMode.END, message, false, null, messageId, true, null);
|
||||
public void endMessage(GameView gameView, Map<String, Serializable> options, String message, int messageId) {
|
||||
updateGame(gameView, false, options, null);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
|
||||
this.feedbackPanel.prepareFeedback(FeedbackMode.END, message, false, null, messageId, true, null);
|
||||
ArrowBuilder.getBuilder().removeAllArrows(gameId);
|
||||
}
|
||||
|
||||
public void select(String message, GameView gameView, int messageId, Map<String, Serializable> options) {
|
||||
public void select(GameView gameView, Map<String, Serializable> options, String message, int messageId) {
|
||||
updateGame(gameView, true, options, null);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
|
||||
this.abilityPicker.setVisible(false);
|
||||
|
||||
holdingPriority = false;
|
||||
|
|
@ -1659,8 +1684,6 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
PreferencesDialog.getCachedValue(KEY_USE_FIRST_MANA_ABILITY, "false").equals("true"),
|
||||
false);
|
||||
|
||||
updateGame(gameView, true, options, null);
|
||||
|
||||
boolean controllingPlayer = false;
|
||||
for (PlayerView playerView : gameView.getPlayers()) {
|
||||
if (playerView.getPlayerId().equals(playerId)) {
|
||||
|
|
@ -1675,8 +1698,8 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
|
||||
}
|
||||
Map<String, Serializable> panelOptions = new HashMap<>();
|
||||
if (options != null) {
|
||||
panelOptions.putAll(options);
|
||||
if (lastGameData.options != null) {
|
||||
panelOptions.putAll(lastGameData.options);
|
||||
}
|
||||
panelOptions.put("your_turn", true);
|
||||
String activePlayerText;
|
||||
|
|
@ -1690,39 +1713,45 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
priorityPlayerText = " / priority " + gameView.getPriorityPlayerName();
|
||||
}
|
||||
String messageToDisplay = message + FeedbackPanel.getSmallText(activePlayerText + " / " + gameView.getStep().toString() + priorityPlayerText);
|
||||
this.feedbackPanel.getFeedback(FeedbackMode.SELECT, messageToDisplay, gameView.getSpecial(), panelOptions, messageId, true, gameView.getPhase());
|
||||
this.feedbackPanel.prepareFeedback(FeedbackMode.SELECT, messageToDisplay, gameView.getSpecial(), panelOptions, messageId, true, gameView.getPhase());
|
||||
}
|
||||
|
||||
public void playMana(String message, GameView gameView, Map<String, Serializable> options, int messageId) {
|
||||
public void playMana(GameView gameView, Map<String, Serializable> options, String message, int messageId) {
|
||||
updateGame(gameView, true, options, null);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
this.feedbackPanel.getFeedback(FeedbackMode.CANCEL, message, gameView.getSpecial(), options, messageId, true, gameView.getPhase());
|
||||
|
||||
this.feedbackPanel.prepareFeedback(FeedbackMode.CANCEL, message, gameView.getSpecial(), options, messageId, true, gameView.getPhase());
|
||||
}
|
||||
|
||||
public void playXMana(String message, GameView gameView, int messageId) {
|
||||
updateGame(gameView, true, null, null);
|
||||
public void playXMana(GameView gameView, Map<String, Serializable> options, String message, int messageId) {
|
||||
updateGame(gameView, true, options, null);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
this.feedbackPanel.getFeedback(FeedbackMode.CONFIRM, message, gameView.getSpecial(), null, messageId, true, gameView.getPhase());
|
||||
|
||||
this.feedbackPanel.prepareFeedback(FeedbackMode.CONFIRM, message, gameView.getSpecial(), null, messageId, true, gameView.getPhase());
|
||||
}
|
||||
|
||||
public void replayMessage(String message) {
|
||||
//TODO: implement this
|
||||
}
|
||||
|
||||
public void pickAbility(AbilityPickerView choices) {
|
||||
public void pickAbility(GameView gameView, Map<String, Serializable> options, AbilityPickerView choices) {
|
||||
updateGame(gameView, false, options, null);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
|
||||
this.abilityPicker.show(choices, MageFrame.getDesktop().getMousePosition());
|
||||
}
|
||||
|
||||
private void hideAll() {
|
||||
hidePickDialogs();
|
||||
this.abilityPicker.setVisible(false);
|
||||
ActionCallback callback = Plugins.instance.getActionCallback();
|
||||
((MageActionCallback) callback).hideGameUpdate(gameId);
|
||||
}
|
||||
|
||||
private ShowCardsDialog showCards(String title, CardsView cards, boolean required, Map<String, Serializable> options, PopUpMenuType popupMenuType) {
|
||||
hideAll();
|
||||
private ShowCardsDialog prepareCardsDialog(String title, CardsView cards, boolean required, Map<String, Serializable> options, PopUpMenuType popupMenuType) {
|
||||
ShowCardsDialog showCards = new ShowCardsDialog();
|
||||
JPopupMenu popupMenu = null;
|
||||
if (PopUpMenuType.TRIGGER_ORDER == popupMenuType) {
|
||||
|
|
@ -1732,7 +1761,11 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
return showCards;
|
||||
}
|
||||
|
||||
public void getAmount(int min, int max, String message) {
|
||||
public void getAmount(GameView gameView, Map<String, Serializable> options, int min, int max, String message) {
|
||||
updateGame(gameView, false, options, null);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
|
||||
pickNumber.showDialog(min, max, message);
|
||||
if (pickNumber.isCancel()) {
|
||||
SessionHandler.sendPlayerBoolean(gameId, false);
|
||||
|
|
@ -1741,13 +1774,20 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
}
|
||||
}
|
||||
|
||||
public void getMultiAmount(List<String> messages, int min, int max, Map<String, Serializable> options) {
|
||||
pickMultiNumber.showDialog(messages, min, max, options);
|
||||
public void getMultiAmount(List<String> messages, GameView gameView, Map<String, Serializable> options, int min, int max) {
|
||||
updateGame(gameView, false, options, null);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
|
||||
pickMultiNumber.showDialog(messages, min, max, lastGameData.options);
|
||||
SessionHandler.sendPlayerString(gameId, pickMultiNumber.getMultiAmount());
|
||||
}
|
||||
|
||||
public void getChoice(Choice choice, UUID objectId) {
|
||||
public void getChoice(GameView gameView, Map<String, Serializable> options, Choice choice, UUID objectId) {
|
||||
updateGame(gameView, false, options, null);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
|
||||
// TODO: remember last choices and search incremental for same events?
|
||||
PickChoiceDialog pickChoice = new PickChoiceDialog();
|
||||
pickChoice.showDialog(choice, null, objectId, choiceWindowState, bigCard);
|
||||
|
|
@ -1769,8 +1809,10 @@ public final class GamePanel extends javax.swing.JPanel {
|
|||
pickChoice.removeDialog();
|
||||
}
|
||||
|
||||
public void pickPile(String message, CardsView pile1, CardsView pile2) {
|
||||
public void pickPile(GameView gameView, Map<String, Serializable> options, String message, CardsView pile1, CardsView pile2) {
|
||||
updateGame(gameView, false, options, null);
|
||||
hideAll();
|
||||
DialogManager.getManager(gameId).fadeOut();
|
||||
|
||||
// remove old dialogs before the new
|
||||
clearPickPileDialogs();
|
||||
|
|
|
|||
|
|
@ -157,7 +157,7 @@ public class CallbackClientImpl implements CallbackClient {
|
|||
case REPLAY_DONE: {
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
panel.endMessage((String) callback.getData(), callback.getMessageId());
|
||||
panel.endMessage(null, null, (String) callback.getData(), callback.getMessageId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -180,16 +180,17 @@ public class CallbackClientImpl implements CallbackClient {
|
|||
}
|
||||
|
||||
case GAME_OVER: {
|
||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
Session session = SessionHandler.getSession();
|
||||
if (session.isJsonLogActive()) {
|
||||
appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData());
|
||||
ActionData actionData = appendJsonEvent("GAME_OVER", callback.getObjectId(), callback.getData());
|
||||
String logFileName = "game-" + actionData.gameId + ".json";
|
||||
S3Uploader.upload(logFileName, actionData.gameId.toString());
|
||||
UUID gameId = callback.getObjectId();
|
||||
appendJsonEvent("GAME_OVER", callback.getObjectId(), message);
|
||||
String logFileName = "game-" + gameId + ".json";
|
||||
S3Uploader.upload(logFileName, gameId.toString());
|
||||
}
|
||||
panel.endMessage((String) callback.getData(), callback.getMessageId());
|
||||
panel.endMessage(message.getGameView(), message.getOptions(), message.getMessage(), callback.getMessageId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -209,35 +210,34 @@ public class CallbackClientImpl implements CallbackClient {
|
|||
break;
|
||||
}
|
||||
|
||||
case GAME_TARGET: // e.g. Pick triggered ability
|
||||
{
|
||||
case GAME_TARGET: {
|
||||
// e.g. Pick triggered ability
|
||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_TARGET", callback.getObjectId(), message);
|
||||
panel.pickTarget(message.getMessage(), message.getCardsView(), message.getGameView(),
|
||||
message.getTargets(), message.isFlag(), message.getOptions(), callback.getMessageId());
|
||||
panel.pickTarget(message.getGameView(), message.getOptions(), message.getMessage(),
|
||||
message.getCardsView1(), message.getTargets(), message.isFlag(), callback.getMessageId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GAME_SELECT: {
|
||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_SELECT", callback.getObjectId(), message);
|
||||
panel.select(message.getMessage(), message.getGameView(), callback.getMessageId(), message.getOptions());
|
||||
panel.select(message.getGameView(), message.getOptions(), message.getMessage(), callback.getMessageId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GAME_CHOOSE_ABILITY: {
|
||||
AbilityPickerView abilityPickerView = (AbilityPickerView) callback.getData();
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_CHOOSE_ABILITY", callback.getObjectId(), callback.getData());
|
||||
panel.pickAbility((AbilityPickerView) callback.getData());
|
||||
panel.pickAbility(abilityPickerView.getGameView(), null, abilityPickerView);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -247,19 +247,17 @@ public class CallbackClientImpl implements CallbackClient {
|
|||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_CHOOSE_PILE", callback.getObjectId(), message);
|
||||
panel.pickPile(message.getMessage(), message.getPile1(), message.getPile2());
|
||||
panel.pickPile(message.getGameView(), message.getOptions(), message.getMessage(), message.getCardsView1(), message.getCardsView2());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GAME_CHOOSE_CHOICE: {
|
||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_CHOOSE_CHOICE", callback.getObjectId(), message);
|
||||
panel.getChoice(message.getChoice(), callback.getObjectId());
|
||||
panel.getChoice(message.getGameView(), message.getOptions(), message.getChoice(), callback.getObjectId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
@ -269,53 +267,48 @@ public class CallbackClientImpl implements CallbackClient {
|
|||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_PLAY_MANA", callback.getObjectId(), message);
|
||||
panel.playMana(message.getMessage(), message.getGameView(), message.getOptions(), callback.getMessageId());
|
||||
panel.playMana(message.getGameView(), message.getOptions(), message.getMessage(), callback.getMessageId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GAME_PLAY_XMANA: {
|
||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_PLAY_XMANA", callback.getObjectId(), message);
|
||||
panel.playXMana(message.getMessage(), message.getGameView(), callback.getMessageId());
|
||||
panel.playXMana(message.getGameView(), message.getOptions(), message.getMessage(), callback.getMessageId());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GAME_GET_AMOUNT: {
|
||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_GET_AMOUNT", callback.getObjectId(), message);
|
||||
|
||||
panel.getAmount(message.getMin(), message.getMax(), message.getMessage());
|
||||
panel.getAmount(message.getGameView(), message.getOptions(), message.getMin(), message.getMax(), message.getMessage());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GAME_GET_MULTI_AMOUNT: {
|
||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_GET_MULTI_AMOUNT", callback.getObjectId(), message);
|
||||
|
||||
panel.getMultiAmount(message.getMessages(), message.getMin(), message.getMax(), message.getOptions());
|
||||
panel.getMultiAmount(message.getMessages(), message.getGameView(), message.getOptions(), message.getMin(), message.getMax());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case GAME_UPDATE: {
|
||||
GamePanel panel = MageFrame.getGame(callback.getObjectId());
|
||||
|
||||
if (panel != null) {
|
||||
appendJsonEvent("GAME_UPDATE", callback.getObjectId(), callback.getData());
|
||||
|
||||
panel.updateGame((GameView) callback.getData(), true, null, null); // update after undo
|
||||
panel.updateGame((GameView) callback.getData(), true, null, null); // update after undo wtf?!
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,14 +16,13 @@ import javax.sound.sampled.LineUnavailableException;
|
|||
import javax.sound.sampled.Mixer;
|
||||
import javax.sound.sampled.SourceDataLine;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
import mage.utils.ThreadUtils;
|
||||
|
||||
public class LinePool {
|
||||
|
||||
private final Logger log = LoggerFactory.getLogger(getClass());
|
||||
private final org.apache.log4j.Logger logger = Logger.getLogger(LinePool.class);
|
||||
private static final int LINE_CLEANUP_INTERVAL = 30000;
|
||||
|
||||
private final Queue<SourceDataLine> freeLines = new ArrayDeque<>();
|
||||
|
|
@ -55,7 +54,7 @@ public class LinePool {
|
|||
SourceDataLine line = (SourceDataLine) mixer.getLine(lineInfo);
|
||||
freeLines.add(line);
|
||||
} catch (LineUnavailableException e) {
|
||||
log.warn("Failed to get line from mixer", e);
|
||||
logger.warn("Failed to get line from mixer", e);
|
||||
}
|
||||
}
|
||||
new Timer("Line cleanup", true).scheduleAtFixedRate(new TimerTask() {
|
||||
|
|
@ -65,7 +64,7 @@ public class LinePool {
|
|||
for (SourceDataLine sourceDataLine : freeLines) {
|
||||
if (sourceDataLine.isOpen()) {
|
||||
sourceDataLine.close();
|
||||
log.debug("Closed line {}", sourceDataLine);
|
||||
logger.debug("Closed line " + sourceDataLine);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -96,13 +95,13 @@ public class LinePool {
|
|||
public void playSound(final MageClip mageClip) {
|
||||
final SourceDataLine line;
|
||||
synchronized (LinePool.this) {
|
||||
log.debug("Playing {}", mageClip.getFilename());
|
||||
logger.debug("Playing: " + mageClip.getFilename());
|
||||
logLineStats();
|
||||
line = borrowLine();
|
||||
if (line == null) {
|
||||
// no lines available, queue sound to play it when a line is available
|
||||
queue.add(mageClip);
|
||||
log.debug("Sound {} queued.", mageClip.getFilename());
|
||||
logger.debug("Sound queued: " + mageClip.getFilename());
|
||||
return;
|
||||
}
|
||||
logLineStats();
|
||||
|
|
@ -113,19 +112,19 @@ public class LinePool {
|
|||
if (!line.isOpen()) {
|
||||
line.open();
|
||||
line.addLineListener(event -> {
|
||||
log.debug("Event: {}", event);
|
||||
logger.debug("Event: " + event);
|
||||
if (event.getType() != Type.STOP) {
|
||||
return;
|
||||
}
|
||||
synchronized (LinePool.this) {
|
||||
log.debug("Before stop on line {}", line);
|
||||
logger.debug("Before stop on line " + line);
|
||||
logLineStats();
|
||||
returnLine(line);
|
||||
log.debug("After stop on line {}", line);
|
||||
logger.debug("After stop on line " + line);
|
||||
logLineStats();
|
||||
MageClip queuedSound = queue.poll();
|
||||
if (queuedSound != null) {
|
||||
log.debug("Playing queued sound {}", queuedSound);
|
||||
logger.debug("Playing queued sound " + queuedSound);
|
||||
playSound(queuedSound);
|
||||
}
|
||||
}
|
||||
|
|
@ -133,19 +132,21 @@ public class LinePool {
|
|||
}
|
||||
line.start();
|
||||
} catch (LineUnavailableException e) {
|
||||
log.warn("Failed to open line", e);
|
||||
logger.warn("Failed to open line", e);
|
||||
}
|
||||
}
|
||||
byte[] buffer = mageClip.getBuffer();
|
||||
log.debug("Before write to line {}", line);
|
||||
logger.debug("Before write to line " + line);
|
||||
line.write(buffer, 0, buffer.length);
|
||||
line.drain();
|
||||
line.stop();
|
||||
log.debug("Line completed: {}", line);
|
||||
logger.debug("Line completed: " + line);
|
||||
});
|
||||
}
|
||||
|
||||
private void logLineStats() {
|
||||
log.debug("Free lines: {} Active: {} Busy: {}", freeLines.size(), activeLines.size(), busyLines.size());
|
||||
logger.debug(String.format("Free lines: %d; Active: %d; Busy: %d",
|
||||
freeLines.size(), activeLines.size(), busyLines.size()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -315,203 +315,280 @@ public class CardViewEDHPowerLevelComparator implements CardViewComparator {
|
|||
}
|
||||
|
||||
if (card.isPlanesWalker()) {
|
||||
if (card.getName().toLowerCase(Locale.ENGLISH).equals("jace, the mind sculptor")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 6);
|
||||
}
|
||||
if (card.getName().toLowerCase(Locale.ENGLISH).equals("ugin, the spirit dragon")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
}
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
thisMaxPower = Math.max(thisMaxPower, 6);
|
||||
}
|
||||
|
||||
String cn = card.getName().toLowerCase(Locale.ENGLISH);
|
||||
if (cn.equals("ancient tomb")
|
||||
if (cn.equals("acid rain")
|
||||
|| cn.equals("agent of treachery")
|
||||
|| cn.equals("anafenza, the foremost")
|
||||
|| cn.equals("ancient tomb")
|
||||
|| cn.equals("animar, soul of element")
|
||||
|| cn.equals("animate artifact")
|
||||
|| cn.equals("apocalypse")
|
||||
|| cn.equals("archaeomancer")
|
||||
|| cn.equals("arcum dagsson")
|
||||
|| cn.equals("armageddon")
|
||||
|| cn.equals("ashnod's altar")
|
||||
|| cn.equals("atraxa, praetors' voice")
|
||||
|| cn.equals("aura flux")
|
||||
|| cn.equals("aura shards")
|
||||
|| cn.equals("avacyn, angel of hope")
|
||||
|| cn.equals("azami, lady of scrolls")
|
||||
|| cn.equals("azusa, lost but seeking")
|
||||
|| cn.equals("back to basics")
|
||||
|| cn.equals("bane of progress")
|
||||
|| cn.equals("basalt monolith")
|
||||
|| cn.equals("bend or break")
|
||||
|| cn.equals("blightsteel collossus")
|
||||
|| cn.equals("blightsteel colossus")
|
||||
|| cn.equals("blood moon")
|
||||
|| cn.equals("boil")
|
||||
|| cn.equals("boiling seas")
|
||||
|| cn.equals("brago, king eternal")
|
||||
|| cn.equals("braids, cabal minion")
|
||||
|| cn.equals("bribery")
|
||||
|| cn.equals("burning sands")
|
||||
|| cn.equals("cabal coffers")
|
||||
|| cn.equals("candelabra of tawnos")
|
||||
|| cn.equals("captain sisay")
|
||||
|| cn.equals("card view")
|
||||
|| cn.equals("cataclysm")
|
||||
|| cn.equals("catastrophe")
|
||||
|| cn.equals("celestial dawn")
|
||||
|| cn.equals("cephalid aristocrat")
|
||||
|| cn.equals("cephalid illusionist")
|
||||
|| cn.equals("changeling berserker")
|
||||
|| cn.equals("child of alara")
|
||||
|| cn.equals("chulane, teller of tales")
|
||||
|| cn.equals("cinderhaze wretch")
|
||||
|| cn.equals("coalition relic")
|
||||
|| cn.equals("confusion in the ranks")
|
||||
|| cn.equals("consecrated sphinx")
|
||||
|| cn.equals("contamination")
|
||||
|| cn.equals("craterhoof behemoth")
|
||||
|| cn.equals("cryptic gateway")
|
||||
|| cn.equals("cyclonic rift")
|
||||
|| cn.equals("deadeye navigator")
|
||||
|| cn.equals("death cloud")
|
||||
|| cn.equals("decree of annihilation")
|
||||
|| cn.equals("decree of silence")
|
||||
|| cn.equals("deepglow skate")
|
||||
|| cn.equals("demonic consultation")
|
||||
|| cn.equals("derevi, empyrial tactician")
|
||||
|| cn.equals("devastation")
|
||||
|| cn.equals("dig through time")
|
||||
|| cn.equals("divine intervention")
|
||||
|| cn.equals("dockside extortionist")
|
||||
|| cn.equals("doomsday")
|
||||
|| cn.equals("doubling season")
|
||||
|| cn.equals("drannith magistrate")
|
||||
|| cn.equals("dross scorpion")
|
||||
|| cn.equals("earthcraft")
|
||||
|| cn.equals("edric, spymaster of trest")
|
||||
|| cn.equals("elesh norn, grand cenobite")
|
||||
|| cn.equals("embargo")
|
||||
|| cn.equals("emrakul, the promised end")
|
||||
|| cn.equals("enter the infinite")
|
||||
|| cn.equals("entomb")
|
||||
|| cn.equals("force of will")
|
||||
|| cn.equals("epicenter")
|
||||
|| cn.equals("erratic portal")
|
||||
|| cn.equals("expropriate")
|
||||
|| cn.equals("exquisite blood")
|
||||
|| cn.equals("fall of the thran")
|
||||
|| cn.equals("fierce guardianship")
|
||||
|| cn.equals("food chain")
|
||||
|| cn.equals("force of negation")
|
||||
|| cn.equals("force of will")
|
||||
|| cn.equals("future sight")
|
||||
|| cn.equals("gaddock teeg")
|
||||
|| cn.equals("gaea's cradle")
|
||||
|| cn.equals("genesis chamber")
|
||||
|| cn.equals("ghave, guru of spores")
|
||||
|| cn.equals("gilded drake")
|
||||
|| cn.equals("glenn, the voice of calm")
|
||||
|| cn.equals("global ruin")
|
||||
|| cn.equals("golos, tireless pilgrim")
|
||||
|| cn.equals("grand arbiter augustin iv")
|
||||
|| cn.equals("grave pact")
|
||||
|| cn.equals("grave titan")
|
||||
|| cn.equals("great whale")
|
||||
|| cn.equals("grim monolith")
|
||||
|| cn.equals("grip of chaos")
|
||||
|| cn.equals("gush")
|
||||
|| cn.equals("hellkite charger")
|
||||
|| cn.equals("hermit druid")
|
||||
|| cn.equals("hokori, dust drinker")
|
||||
|| cn.equals("humility")
|
||||
|| cn.equals("impending disaster")
|
||||
|| cn.equals("imperial seal")
|
||||
|| cn.equals("intruder alarm")
|
||||
|| cn.equals("invoke prejudice")
|
||||
|| cn.equals("iona, shield of emeria")
|
||||
|| cn.equals("jin-gitaxias, core augur")
|
||||
|| cn.equals("jokulhaups")
|
||||
|| cn.equals("kaalia of the vast")
|
||||
|| cn.equals("karador, ghost chieftain")
|
||||
|| cn.equals("karakas")
|
||||
|| cn.equals("karn, silver golem")
|
||||
|| cn.equals("kataki, war's wage")
|
||||
|| cn.equals("keldon firebombers")
|
||||
|| cn.equals("kiki-jiki, mirror breaker")
|
||||
|| cn.equals("kinnan, bonder prodigy")
|
||||
|| cn.equals("knowledge pool")
|
||||
|| cn.equals("kozilek, butcher of truth")
|
||||
|| cn.equals("krark-clan ironworks")
|
||||
|| cn.equals("krenko, mob boss")
|
||||
|| cn.equals("krosan restorer")
|
||||
|| cn.equals("laboratory maniac")
|
||||
|| cn.equals("land equilibrium")
|
||||
|| cn.equals("leonin relic-warder")
|
||||
|| cn.equals("leovold, emissary of trest")
|
||||
|| cn.equals("leyline of the void")
|
||||
|| cn.equals("linvala, keeper of silence")
|
||||
|| cn.equals("living death")
|
||||
|| cn.equals("llawan, cephalid empress")
|
||||
|| cn.equals("loyal retainers")
|
||||
|| cn.equals("maelstrom wanderer")
|
||||
|| cn.equals("magister sphinx")
|
||||
|| cn.equals("malfegor")
|
||||
|| cn.equals("master of cruelties")
|
||||
|| cn.equals("mana breach")
|
||||
|| cn.equals("mana crypt")
|
||||
|| cn.equals("mana drain")
|
||||
|| cn.equals("mana vault")
|
||||
|| cn.equals("mana vortex")
|
||||
|| cn.equals("master of cruelties")
|
||||
|| cn.equals("memnarch")
|
||||
|| cn.equals("meren of clan nel toth")
|
||||
|| cn.equals("michiko konda, truth seeker")
|
||||
|| cn.equals("mikaeus the unhallowed")
|
||||
|| cn.equals("mikaeus, the unhallowed")
|
||||
|| cn.equals("mindcrank")
|
||||
|| cn.equals("mindslaver")
|
||||
|| cn.equals("minion reflector")
|
||||
|| cn.equals("mycosynth lattice")
|
||||
|| cn.equals("myr turbine")
|
||||
|| cn.equals("narset, enlightened master")
|
||||
|| cn.equals("narset, parter of veils")
|
||||
|| cn.equals("nath of the gilt-leaf")
|
||||
|| cn.equals("natural order")
|
||||
|| cn.equals("necrotic ooze")
|
||||
|| cn.equals("negan, the cold-blooded")
|
||||
|| cn.equals("nekusar, the mindrazer")
|
||||
|| cn.equals("nether void")
|
||||
|| cn.equals("nexus of fate")
|
||||
|| cn.equals("nicol bolas")
|
||||
|| cn.equals("norin the wary")
|
||||
|| cn.equals("notion thief")
|
||||
|| cn.equals("numot, the devastator")
|
||||
|| cn.equals("oath of druids")
|
||||
|| cn.equals("obliterate")
|
||||
|| cn.equals("oko, thief of crowns")
|
||||
|| cn.equals("oloro, ageless ascetic")
|
||||
|| cn.equals("omniscience")
|
||||
|| cn.equals("opalescence")
|
||||
|| cn.equals("opposition agent")
|
||||
|| cn.equals("oppression")
|
||||
|| cn.equals("ornithopter")
|
||||
|| cn.equals("overwhelming splendor")
|
||||
|| cn.equals("palinchron")
|
||||
|| cn.equals("paradox engine")
|
||||
|| cn.equals("pattern of rebirth")
|
||||
|| cn.equals("peregrine drake")
|
||||
|| cn.equals("planar portal")
|
||||
|| cn.equals("possessed portal")
|
||||
|| cn.equals("power artifact")
|
||||
|| cn.equals("price of glory")
|
||||
|| cn.equals("prossh, skyraider of kher")
|
||||
|| cn.equals("protean hulk")
|
||||
|| cn.equals("purphoros, god of the forge")
|
||||
|| cn.equals("ravages of war")
|
||||
|| cn.equals("reclamation sage")
|
||||
|| cn.equals("rhystic study")
|
||||
|| cn.equals("rick, steadfast leader")
|
||||
|| cn.equals("rings of brighthearth")
|
||||
|| cn.equals("rising waters")
|
||||
|| cn.equals("rite of replication")
|
||||
|| cn.equals("ruination")
|
||||
|| cn.equals("sanguine bond")
|
||||
|| cn.equals("scrambleverse")
|
||||
|| cn.equals("seedborn muse")
|
||||
|| cn.equals("sen triplets")
|
||||
|| cn.equals("sensei's divining top")
|
||||
|| cn.equals("serra's sanctum")
|
||||
|| cn.equals("sheoldred, whispering one")
|
||||
|| cn.equals("sire of insanity")
|
||||
|| cn.equals("skithiryx, the blight dragon")
|
||||
|| cn.equals("smokestack")
|
||||
|| cn.equals("smothering tithe")
|
||||
|| cn.equals("sol ring")
|
||||
|| cn.equals("sorin markov")
|
||||
|| cn.equals("splinter twin")
|
||||
|| cn.equals("spore frog")
|
||||
|| cn.equals("stasis")
|
||||
|| cn.equals("static orb")
|
||||
|| cn.equals("stony silence")
|
||||
|| cn.equals("storage matrix")
|
||||
|| cn.equals("storm cauldron")
|
||||
|| cn.equals("strip mine")
|
||||
|| cn.equals("the tabernacle at pendrell vale")
|
||||
|| cn.equals("tinker")
|
||||
|| cn.equals("treasure cruise")
|
||||
|| cn.equals("urabrask the hidden")
|
||||
|| cn.equals("vorinclex, voice of hunger")
|
||||
|| cn.equals("winter orb")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
}
|
||||
|
||||
// Parts of infinite combos
|
||||
if (cn.equals("animate artifact") || cn.equals("animar, soul of element")
|
||||
|| cn.equals("archaeomancer")
|
||||
|| cn.equals("ashnod's altar") || cn.equals("azami, lady of scrolls")
|
||||
|| cn.equals("aura flux")
|
||||
|| cn.equals("basalt monolith") || cn.equals("brago, king eternal")
|
||||
|| cn.equals("candelabra of tawnos") || cn.equals("cephalid aristocrat")
|
||||
|| cn.equals("cephalid illusionist") || cn.equals("changeling berserker")
|
||||
|| cn.equals("consecrated sphinx")
|
||||
|| cn.equals("cyclonic rift")
|
||||
|| cn.equals("the chain veil")
|
||||
|| cn.equals("cinderhaze wretch") || cn.equals("cryptic gateway")
|
||||
|| cn.equals("deadeye navigator") || cn.equals("derevi, empyrial tactician")
|
||||
|| cn.equals("doubling season") || cn.equals("dross scorpion")
|
||||
|| cn.equals("earthcraft") || cn.equals("erratic portal")
|
||||
|| cn.equals("enter the infinite") || cn.equals("omniscience")
|
||||
|| cn.equals("exquisite blood") || cn.equals("future sight")
|
||||
|| cn.equals("genesis chamber")
|
||||
|| cn.equals("ghave, guru of spores")
|
||||
|| cn.equals("grave pact")
|
||||
|| cn.equals("grave titan") || cn.equals("great whale")
|
||||
|| cn.equals("grim monolith") || cn.equals("gush")
|
||||
|| cn.equals("hellkite charger") || cn.equals("intruder alarm")
|
||||
|| cn.equals("hermit druid")
|
||||
|| cn.equals("humility")
|
||||
|| cn.equals("iona, shield of emeria")
|
||||
|| cn.equals("karn, silver golem") || cn.equals("kiki-jiki, mirror breaker")
|
||||
|| cn.equals("krark-clan ironworks") || cn.equals("krenko, mob boss")
|
||||
|| cn.equals("krosan restorer") || cn.equals("laboratory maniac")
|
||||
|| cn.equals("leovold, emissary of trest")
|
||||
|| cn.equals("leonin relic-warder") || cn.equals("leyline of the void")
|
||||
|| cn.equals("memnarch")
|
||||
|| cn.equals("meren of clan nel toth") || cn.equals("mikaeus, the unhallowed")
|
||||
|| cn.equals("mindcrank") || cn.equals("mindslaver")
|
||||
|| cn.equals("minion reflector") || cn.equals("mycosynth lattice")
|
||||
|| cn.equals("myr turbine") || cn.equals("narset, enlightened master")
|
||||
|| cn.equals("nekusar, the mindrazer") || cn.equals("norin the wary")
|
||||
|| cn.equals("notion thief")
|
||||
|| cn.equals("opalescence") || cn.equals("ornithopter")
|
||||
|| cn.equals("paradox engine")
|
||||
|| cn.equals("purphoros, god of the forge")
|
||||
|| cn.equals("peregrine drake") || cn.equals("palinchron")
|
||||
|| cn.equals("planar portal") || cn.equals("power artifact")
|
||||
|| cn.equals("rings of brighthearth") || cn.equals("rite of replication")
|
||||
|| cn.equals("sanguine bond") || cn.equals("sensei's divining top")
|
||||
|| cn.equals("splinter twin") || cn.equals("stony silence")
|
||||
|| cn.equals("sunder")
|
||||
|| cn.equals("storm cauldron") || cn.equals("teferi's puzzle box")
|
||||
|| cn.equals("survival of the fittest")
|
||||
|| cn.equals("table view")
|
||||
|| cn.equals("tainted aether")
|
||||
|| cn.equals("tangle wire")
|
||||
|| cn.equals("tectonic break")
|
||||
|| cn.equals("teferi's protection")
|
||||
|| cn.equals("teferi's puzzle box")
|
||||
|| cn.equals("teferi, mage of zhalfir")
|
||||
|| cn.equals("tezzeret the seeker") || cn.equals("time stretch")
|
||||
|| cn.equals("time warp") || cn.equals("training grounds")
|
||||
|| cn.equals("triskelavus") || cn.equals("triskelion")
|
||||
|| cn.equals("turnabout") || cn.equals("umbral mantle")
|
||||
|| cn.equals("uyo, silent prophet") || cn.equals("voltaic key")
|
||||
|| cn.equals("workhorse") || cn.equals("worldgorger dragon")
|
||||
|| cn.equals("worthy cause") || cn.equals("yawgmoth's will")
|
||||
|| cn.equals("zealous conscripts")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 12);
|
||||
}
|
||||
|
||||
if (cn.equals("animar, soul of element")
|
||||
|| cn.equals("azami, lady of scrolls")
|
||||
|| cn.equals("braids, cabal minion")
|
||||
|| cn.equals("child of alara")
|
||||
|| cn.equals("derevi, empyrial tactician")
|
||||
|| cn.equals("edric, spymaster of trest")
|
||||
|| cn.equals("gaddock teeg")
|
||||
|| cn.equals("grand arbiter augustin iv")
|
||||
|| cn.equals("hokori, dust drinker")
|
||||
|| cn.equals("iona, shield of emeria")
|
||||
|| cn.equals("jin-gitaxias, core augur")
|
||||
|| cn.equals("kaalia of the vast")
|
||||
|| cn.equals("karador, ghost chieftain")
|
||||
|| cn.equals("leovold, emissary of trest")
|
||||
|| cn.equals("linvala, keeper of silence")
|
||||
|| cn.equals("llawan, cephalid empress")
|
||||
|| cn.equals("memnarch")
|
||||
|| cn.equals("meren of clan nel toth")
|
||||
|| cn.equals("michiko konda, truth seeker")
|
||||
|| cn.equals("narset, enlightened master")
|
||||
|| cn.equals("nekusar, the mindrazer")
|
||||
|| cn.equals("norin the wary")
|
||||
|| cn.equals("numot, the devastator")
|
||||
|| cn.equals("sheoldred, whispering one")
|
||||
|| cn.equals("teferi, mage of zhalfir")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 12);
|
||||
}
|
||||
|
||||
if (cn.equals("anafenza, the foremost")
|
||||
|| cn.equals("arcum dagsson")
|
||||
|| cn.equals("azusa, lost but seeking")
|
||||
|| cn.equals("brago, king eternal")
|
||||
|| cn.equals("captain sisay")
|
||||
|| cn.equals("elesh norn, grand cenobite")
|
||||
|| cn.equals("malfegor")
|
||||
|| cn.equals("maelstrom wanderer")
|
||||
|| cn.equals("mikaeus the unhallowed")
|
||||
|| cn.equals("nath of the gilt-leaf")
|
||||
|| cn.equals("prossh, skyraider of kher")
|
||||
|| cn.equals("purphoros, god of the forge")
|
||||
|| cn.equals("sen triplets")
|
||||
|| cn.equals("teferi, master of time")
|
||||
|| cn.equals("teferi, time raveler")
|
||||
|| cn.equals("temporal manipulation")
|
||||
|| cn.equals("tergrid, god of fright")
|
||||
|| cn.equals("text view")
|
||||
|| cn.equals("tezzeret the seeker")
|
||||
|| cn.equals("thassa's oracle")
|
||||
|| cn.equals("the chain veil")
|
||||
|| cn.equals("the tabernacle at pendrell vale")
|
||||
|| cn.equals("thieves' auction")
|
||||
|| cn.equals("thoughts of ruin")
|
||||
|| cn.equals("thrasios, triton hero")
|
||||
|| cn.equals("time stretch")
|
||||
|| cn.equals("time warp")
|
||||
|| cn.equals("tinker")
|
||||
|| cn.equals("tooth and nail")
|
||||
|| cn.equals("torment of hailfire")
|
||||
|| cn.equals("torpor orb")
|
||||
|| cn.equals("training grounds")
|
||||
|| cn.equals("treasure cruise")
|
||||
|| cn.equals("triskelavus")
|
||||
|| cn.equals("triskelion")
|
||||
|| cn.equals("triumph of the hordes")
|
||||
|| cn.equals("turnabout")
|
||||
|| cn.equals("ugin, the spirit dragon")
|
||||
|| cn.equals("ulamog, the ceaseless hunger")
|
||||
|| cn.equals("ulamog, the infinite gyre")
|
||||
|| cn.equals("umbral mantle")
|
||||
|| cn.equals("urabrask the hidden")
|
||||
|| cn.equals("vorinclex, voice of hunger")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 10);
|
||||
}
|
||||
|| cn.equals("urza, lord high artificer")
|
||||
|| cn.equals("uyo, silent prophet")
|
||||
|| cn.equals("void winnower")
|
||||
|| cn.equals("voltaic key")
|
||||
|| cn.equals("vorinclex, voice of hunger")
|
||||
|| cn.equals("wake of destruction")
|
||||
|| cn.equals("warp world")
|
||||
|| cn.equals("winter orb")
|
||||
|| cn.equals("workhorse")
|
||||
|| cn.equals("worldgorger dragon")
|
||||
|| cn.equals("worthy cause")
|
||||
|| cn.equals("xanathar, guild kingpin")
|
||||
|| cn.equals("yawgmoth's will")
|
||||
|| cn.equals("zealous conscripts")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 12);
|
||||
}
|
||||
return thisMaxPower;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -225,8 +225,9 @@ public abstract class CardRenderer {
|
|||
// Call the template methods
|
||||
drawBorder(g);
|
||||
drawBackground(g);
|
||||
lessOpaqueRulesTextBox = false;
|
||||
drawArt(g);
|
||||
drawFrame(g, attribs, image);
|
||||
drawFrame(g, attribs, image, lessOpaqueRulesTextBox);
|
||||
if (!cardView.isAbility()) {
|
||||
drawOverlays(g);
|
||||
drawCounters(g);
|
||||
|
|
@ -241,7 +242,7 @@ public abstract class CardRenderer {
|
|||
|
||||
protected abstract void drawArt(Graphics2D g);
|
||||
|
||||
protected abstract void drawFrame(Graphics2D g, CardPanelAttributes attribs, BufferedImage image);
|
||||
protected abstract void drawFrame(Graphics2D g, CardPanelAttributes attribs, BufferedImage image, boolean lessOpaqueRulesTextBox);
|
||||
|
||||
// Template methods that are possible to override, but unlikely to be
|
||||
// overridden.
|
||||
|
|
@ -318,7 +319,8 @@ public abstract class CardRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
protected void drawFaceArtIntoRect(Graphics2D g, int x, int y, int w, int h, Rectangle2D artRect, boolean shouldPreserveAspect) {
|
||||
private boolean lessOpaqueRulesTextBox = false;
|
||||
protected void drawFaceArtIntoRect(Graphics2D g, int x, int y, int w, int h, int alternate_h, Rectangle2D artRect, boolean shouldPreserveAspect) {
|
||||
// Perform a process to make sure that the art is scaled uniformly to fill the frame, cutting
|
||||
// off the minimum amount necessary to make it completely fill the frame without "squashing" it.
|
||||
double fullCardImgWidth = faceArtImage.getWidth();
|
||||
|
|
@ -346,10 +348,18 @@ public abstract class CardRenderer {
|
|||
RenderingHints.KEY_INTERPOLATION,
|
||||
RenderingHints.VALUE_INTERPOLATION_BICUBIC);
|
||||
g.setRenderingHints(rh);
|
||||
g.drawImage(faceArtImage,
|
||||
x, y,
|
||||
(int) targetWidth, (int) targetHeight,
|
||||
null);
|
||||
if (fullCardImgWidth > fullCardImgHeight) {
|
||||
g.drawImage(faceArtImage,
|
||||
x, y,
|
||||
(int) targetWidth, (int) targetHeight,
|
||||
null);
|
||||
} else {
|
||||
g.drawImage(faceArtImage,
|
||||
x, y,
|
||||
(int) targetWidth, alternate_h, // alernate_h is roughly (targetWidth / 0.74)
|
||||
null);
|
||||
lessOpaqueRulesTextBox = true;
|
||||
}
|
||||
} catch (RasterFormatException e) {
|
||||
// At very small card sizes we may encounter a problem with rounding error making the rect not fit
|
||||
System.out.println(e);
|
||||
|
|
|
|||
|
|
@ -450,9 +450,11 @@ public class ModernCardRenderer extends CardRenderer {
|
|||
|
||||
// Normal drawing of art from a source part of the card frame into the rect
|
||||
if (useFaceArt) {
|
||||
int alternate_height = cardHeight - boxHeight * 2 - totalContentInset;
|
||||
drawFaceArtIntoRect(g,
|
||||
totalContentInset + 1, totalContentInset + boxHeight,
|
||||
contentWidth - 2, typeLineY - totalContentInset - boxHeight,
|
||||
alternate_height,
|
||||
sourceRect, shouldPreserveAspect);
|
||||
} else if (!isZendikarFullArtLand()) {
|
||||
drawArtIntoRect(g,
|
||||
|
|
@ -464,14 +466,14 @@ public class ModernCardRenderer extends CardRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void drawFrame(Graphics2D g, CardPanelAttributes attribs, BufferedImage image) {
|
||||
protected void drawFrame(Graphics2D g, CardPanelAttributes attribs, BufferedImage image, boolean lessOpaqueRulesTextBox) {
|
||||
// Get the card colors to base the frame on
|
||||
ObjectColor frameColors = getFrameObjectColor();
|
||||
|
||||
// Get the border paint
|
||||
Color boxColor = getBoxColor(frameColors, cardView.getCardTypes(), attribs.isTransformed);
|
||||
Color additionalBoxColor = getAdditionalBoxColor(frameColors, cardView.getCardTypes(), attribs.isTransformed);
|
||||
Paint textboxPaint = getTextboxPaint(frameColors, cardView.getCardTypes(), cardWidth);
|
||||
Paint textboxPaint = getTextboxPaint(frameColors, cardView.getCardTypes(), cardWidth, lessOpaqueRulesTextBox);
|
||||
Paint borderPaint = getBorderPaint(frameColors, cardView.getCardTypes(), cardWidth);
|
||||
|
||||
// Special colors
|
||||
|
|
@ -1765,21 +1767,29 @@ public class ModernCardRenderer extends CardRenderer {
|
|||
}
|
||||
}
|
||||
|
||||
private static Color getLessOpaqueColor(Color color, boolean lessOpaqueRulesTextBox) {
|
||||
if (lessOpaqueRulesTextBox) {
|
||||
Color lessOpaque = new Color (color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha() - 50);
|
||||
return lessOpaque;
|
||||
}
|
||||
return color;
|
||||
}
|
||||
|
||||
// Determine the border paint to use, based on an ObjectColors
|
||||
protected static Paint getTextboxPaint(ObjectColor colors, Collection<CardType> types, int width) {
|
||||
protected static Paint getTextboxPaint(ObjectColor colors, Collection<CardType> types, int width, boolean lessOpaqueRulesTextBox) {
|
||||
if (colors.isMulticolored()) {
|
||||
if (colors.getColorCount() == 2) {
|
||||
List<ObjectColor> twoColors = colors.getColors();
|
||||
Color[] translatedColors;
|
||||
if (types.contains(CardType.LAND)) {
|
||||
translatedColors = new Color[]{
|
||||
getLandTextboxColor(twoColors.get(0)),
|
||||
getLandTextboxColor(twoColors.get(1))
|
||||
getLessOpaqueColor(getLandTextboxColor(twoColors.get(0)), lessOpaqueRulesTextBox),
|
||||
getLessOpaqueColor(getLandTextboxColor(twoColors.get(1)), lessOpaqueRulesTextBox)
|
||||
};
|
||||
} else {
|
||||
translatedColors = new Color[]{
|
||||
getTextboxColor(twoColors.get(0)),
|
||||
getTextboxColor(twoColors.get(1))
|
||||
getLessOpaqueColor(getTextboxColor(twoColors.get(0)), lessOpaqueRulesTextBox),
|
||||
getLessOpaqueColor(getTextboxColor(twoColors.get(1)), lessOpaqueRulesTextBox)
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -1789,20 +1799,20 @@ public class ModernCardRenderer extends CardRenderer {
|
|||
new float[]{0.4f, 0.6f},
|
||||
translatedColors);
|
||||
} else if (types.contains(CardType.LAND)) {
|
||||
return LAND_TEXTBOX_GOLD;
|
||||
return getLessOpaqueColor(LAND_TEXTBOX_GOLD, lessOpaqueRulesTextBox);
|
||||
} else {
|
||||
return TEXTBOX_GOLD;
|
||||
return getLessOpaqueColor(TEXTBOX_GOLD, lessOpaqueRulesTextBox);
|
||||
}
|
||||
} else if (colors.isColorless()) {
|
||||
if (types.contains(CardType.LAND)) {
|
||||
return TEXTBOX_LAND;
|
||||
return getLessOpaqueColor(TEXTBOX_LAND, lessOpaqueRulesTextBox);
|
||||
} else {
|
||||
return TEXTBOX_COLORLESS;
|
||||
return getLessOpaqueColor(TEXTBOX_COLORLESS, lessOpaqueRulesTextBox);
|
||||
}
|
||||
} else if (types.contains(CardType.LAND)) {
|
||||
return getLandTextboxColor(colors);
|
||||
return getLessOpaqueColor(getLandTextboxColor(colors), lessOpaqueRulesTextBox);
|
||||
} else {
|
||||
return getTextboxColor(colors);
|
||||
return getLessOpaqueColor(getTextboxColor(colors), lessOpaqueRulesTextBox);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
|
|||
protected void drawSplitHalfFrame(Graphics2D g, CardPanelAttributes attribs, HalfCardProps half, int typeLineY) {
|
||||
// Get the border paint
|
||||
Color boxColor = getBoxColor(half.color, cardView.getCardTypes(), attribs.isTransformed);
|
||||
Paint textboxPaint = getTextboxPaint(half.color, cardView.getCardTypes(), cardWidth);
|
||||
Paint textboxPaint = getTextboxPaint(half.color, cardView.getCardTypes(), cardWidth, false);
|
||||
Paint borderPaint = getBorderPaint(half.color, cardView.getCardTypes(), cardWidth);
|
||||
|
||||
// Draw main frame
|
||||
|
|
@ -299,7 +299,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void drawFrame(Graphics2D g, CardPanelAttributes attribs, BufferedImage image) {
|
||||
protected void drawFrame(Graphics2D g, CardPanelAttributes attribs, BufferedImage image, boolean lessOpaqueRulesTextBox) {
|
||||
if (isAftermath()) {
|
||||
drawSplitHalfFrame(getUnmodifiedHalfContext(g), attribs, leftHalf, (int) (leftHalf.ch * TYPE_LINE_Y_FRAC));
|
||||
drawSplitHalfFrame(getAftermathHalfContext(g), attribs, rightHalf, (rightHalf.ch - boxHeight) / 2);
|
||||
|
|
@ -309,7 +309,7 @@ public class ModernSplitCardRenderer extends ModernCardRenderer {
|
|||
if (isFuse()) {
|
||||
Graphics2D g2 = getRightHalfContext(g);
|
||||
int totalFuseBoxWidth = rightHalf.cw * 2 + 2 * borderWidth + dividerSize;
|
||||
Paint boxColor = getTextboxPaint(cardView.getColor(), ONLY_LAND_TYPE, totalFuseBoxWidth);
|
||||
Paint boxColor = getTextboxPaint(cardView.getColor(), ONLY_LAND_TYPE, totalFuseBoxWidth, false);
|
||||
Paint borderPaint = getBorderPaint(cardView.getColor(), ONLY_LAND_TYPE, totalFuseBoxWidth);
|
||||
CardRendererUtils.drawRoundedBox(g2,
|
||||
-borderWidth, rightHalf.ch,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package org.mage.plugins.card.dl.sources;
|
||||
|
||||
import com.google.gson.JsonArray;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParser;
|
||||
import mage.MageException;
|
||||
import mage.client.util.CardLanguage;
|
||||
import mage.util.JsonUtil;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.mage.plugins.card.dl.DownloadServiceInfo;
|
||||
import org.mage.plugins.card.images.CardDownloadData;
|
||||
|
|
@ -173,17 +173,19 @@ public enum ScryfallImageSource implements CardImageSource {
|
|||
}
|
||||
|
||||
// OK, found card data, parse it
|
||||
JsonParser jp = new JsonParser();
|
||||
JsonElement root = jp.parse(new InputStreamReader(jsonStream));
|
||||
JsonObject jsonCard = root.getAsJsonObject();
|
||||
if (!jsonCard.has("card_faces")) {
|
||||
JsonObject jsonCard = JsonParser.parseReader(new InputStreamReader(jsonStream)).getAsJsonObject();
|
||||
JsonArray jsonFaces = JsonUtil.getAsArray(jsonCard, "card_faces");
|
||||
if (jsonFaces == null) {
|
||||
throw new MageException("Couldn't find card_faces in card's JSON data: " + jsonUrl);
|
||||
}
|
||||
JsonArray jsonCardFaces = jsonCard.getAsJsonArray("card_faces");
|
||||
JsonObject jsonCardFace = jsonCardFaces.get(card.isSecondSide() ? 1 : 0).getAsJsonObject();
|
||||
JsonObject jsonImageUris = jsonCardFace.getAsJsonObject("image_uris");
|
||||
|
||||
return jsonImageUris.get("large").getAsString();
|
||||
JsonObject jsonFace = jsonFaces.get(card.isSecondSide() ? 1 : 0).getAsJsonObject();
|
||||
JsonObject jsonImages = JsonUtil.getAsObject(jsonFace, "image_uris");
|
||||
if (jsonImages == null) {
|
||||
throw new MageException("Couldn't find image_uris in card's JSON data: " + jsonUrl);
|
||||
}
|
||||
|
||||
return JsonUtil.getAsString(jsonImages, "large");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
<artifactId>mage</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.googlecode.jspf</groupId>
|
||||
<artifactId>jspf-core</artifactId>
|
||||
|
|
@ -41,37 +42,26 @@
|
|||
<artifactId>jboss-serialization</artifactId>
|
||||
<version>4.2.2.GA</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>concurrent</groupId>
|
||||
<artifactId>concurrent</artifactId>
|
||||
<version>1.3.4</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>trove</groupId>
|
||||
<artifactId>trove</artifactId>
|
||||
<version>1.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.code.gson</groupId>
|
||||
<artifactId>gson</artifactId>
|
||||
<version>2.8.6</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<!-- to get the reference to local repository with com\googlecode\jspf\jspf-core\0.9.1\ -->
|
||||
<repositories>
|
||||
<repository>
|
||||
|
|
|
|||
|
|
@ -122,11 +122,23 @@ public class SessionImpl implements Session {
|
|||
client.showMessage("Remote task error. " + message);
|
||||
}
|
||||
|
||||
private boolean doRemoteWorkAndHandleErrors(RemotingTask remoting) {
|
||||
private boolean doRemoteWorkAndHandleErrors(boolean closeConnectionOnFinish, boolean mustWaitServerMessageOnFail,
|
||||
RemotingTask remoting) {
|
||||
// execute remote task and wait result, can be canceled
|
||||
lastRemotingTask = remoting;
|
||||
try {
|
||||
return remoting.doWork();
|
||||
boolean res = remoting.doWork();
|
||||
if (!res && mustWaitServerMessageOnFail) {
|
||||
// server send detail error as separate message by existing connection,
|
||||
// so you need wait some time before disconnect
|
||||
try {
|
||||
Thread.sleep(3000);
|
||||
} catch (InterruptedException e) {
|
||||
logger.fatal("waiting of error message had failed", e);
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
return res;
|
||||
} catch (InterruptedException | CancellationException t) {
|
||||
// was canceled by user, nothing to show
|
||||
} catch (MalformedURLException ex) {
|
||||
|
|
@ -180,13 +192,16 @@ public class SessionImpl implements Session {
|
|||
}
|
||||
} finally {
|
||||
lastRemotingTask = null;
|
||||
if (closeConnectionOnFinish) {
|
||||
disconnect(false); // it's ok on mutiple calls
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean register(final Connection connection) {
|
||||
return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(new RemotingTask() {
|
||||
return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(true, true, new RemotingTask() {
|
||||
@Override
|
||||
public boolean work() throws Throwable {
|
||||
logger.info("Registration: username " + getUserName() + " for email " + getEmail());
|
||||
|
|
@ -199,7 +214,7 @@ public class SessionImpl implements Session {
|
|||
|
||||
@Override
|
||||
public synchronized boolean emailAuthToken(final Connection connection) {
|
||||
return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(new RemotingTask() {
|
||||
return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(true, true, new RemotingTask() {
|
||||
@Override
|
||||
public boolean work() throws Throwable {
|
||||
logger.info("Auth request: requesting auth token for username " + getUserName() + " to email " + getEmail());
|
||||
|
|
@ -212,12 +227,12 @@ public class SessionImpl implements Session {
|
|||
|
||||
@Override
|
||||
public synchronized boolean resetPassword(final Connection connection) {
|
||||
return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(new RemotingTask() {
|
||||
return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(true, true, new RemotingTask() {
|
||||
@Override
|
||||
public boolean work() throws Throwable {
|
||||
logger.info("Password reset: reseting password for username " + getUserName());
|
||||
boolean result = server.resetPassword(sessionId, connection.getEmail(), connection.getAuthToken(), connection.getPassword());
|
||||
logger.info("Password reset: " + (result ? "DONE, check your email for new password" : "FAIL"));
|
||||
logger.info("Password reset: " + (result ? "DONE, now you can login with new password" : "FAIL"));
|
||||
return result;
|
||||
}
|
||||
});
|
||||
|
|
@ -225,7 +240,7 @@ public class SessionImpl implements Session {
|
|||
|
||||
@Override
|
||||
public synchronized boolean connect(final Connection connection) {
|
||||
return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(new RemotingTask() {
|
||||
return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(false, true, new RemotingTask() {
|
||||
@Override
|
||||
public boolean work() throws Throwable {
|
||||
setLastError("");
|
||||
|
|
@ -258,7 +273,6 @@ public class SessionImpl implements Session {
|
|||
}
|
||||
|
||||
logger.info("Logging: FAIL");
|
||||
disconnect(false);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
|
@ -437,7 +451,7 @@ public class SessionImpl implements Session {
|
|||
|
||||
boolean result;
|
||||
try {
|
||||
result = doRemoteWorkAndHandleErrors(lastRemotingTask);
|
||||
result = doRemoteWorkAndHandleErrors(false, false, lastRemotingTask);
|
||||
} finally {
|
||||
lastRemotingTask = null;
|
||||
}
|
||||
|
|
@ -529,6 +543,7 @@ public class SessionImpl implements Session {
|
|||
|
||||
if (sessionState == SessionState.DISCONNECTING || sessionState == SessionState.CONNECTING) {
|
||||
sessionState = SessionState.DISCONNECTED;
|
||||
serverState = null;
|
||||
logger.info("Disconnecting DONE");
|
||||
if (askForReconnect) {
|
||||
client.showError("Network error. You have been disconnected from " + connection.getHost());
|
||||
|
|
@ -1654,7 +1669,10 @@ public class SessionImpl implements Session {
|
|||
@Override
|
||||
public boolean ping() {
|
||||
try {
|
||||
if (isConnected() && sessionId != null) {
|
||||
// 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
|
||||
if (isConnected() && sessionId != null && serverState != null) {
|
||||
long startTime = System.nanoTime();
|
||||
if (!server.ping(sessionId, pingInfo)) {
|
||||
logger.error("Ping failed: " + this.getUserName() + " Session: " + sessionId + " to MAGE server at " + connection.getHost() + ':' + connection.getPort());
|
||||
|
|
|
|||
|
|
@ -17,9 +17,11 @@ public class AbilityPickerView implements Serializable {
|
|||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private Map<UUID, String> choices = new LinkedHashMap<>();
|
||||
private String message = null;
|
||||
private String message;
|
||||
private GameView gameView;
|
||||
|
||||
public AbilityPickerView(String objectName, List<? extends Ability> abilities, String message) {
|
||||
public AbilityPickerView(GameView gameView, String objectName, List<? extends Ability> abilities, String message) {
|
||||
this.gameView = gameView;
|
||||
this.message = message;
|
||||
|
||||
int num = 0;
|
||||
|
|
@ -44,6 +46,12 @@ public class AbilityPickerView implements Serializable {
|
|||
}
|
||||
}
|
||||
|
||||
public AbilityPickerView(GameView gameView, Map<UUID, String> modes, String message) {
|
||||
this.gameView = gameView;
|
||||
this.choices = modes;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
private String getAbilityRules(Ability ability, String objectName) {
|
||||
String rule = ability.getRule(objectName);
|
||||
if (rule.isEmpty()) {
|
||||
|
|
@ -55,11 +63,6 @@ public class AbilityPickerView implements Serializable {
|
|||
return rule;
|
||||
}
|
||||
|
||||
public AbilityPickerView(Map<UUID, String> modes, String message) {
|
||||
this.choices = modes;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public Map<UUID, String> getChoices() {
|
||||
return choices;
|
||||
}
|
||||
|
|
@ -67,4 +70,8 @@ public class AbilityPickerView implements Serializable {
|
|||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public GameView getGameView() {
|
||||
return gameView;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class GameClientMessage implements Serializable {
|
|||
@Expose
|
||||
private GameView gameView;
|
||||
@Expose
|
||||
private CardsView cardsView;
|
||||
private CardsView cardsView1;
|
||||
@Expose
|
||||
private CardsView cardsView2;
|
||||
@Expose
|
||||
|
|
@ -32,8 +32,6 @@ public class GameClientMessage implements Serializable {
|
|||
@Expose
|
||||
private boolean flag;
|
||||
@Expose
|
||||
private String[] strings;
|
||||
@Expose
|
||||
private Set<UUID> targets;
|
||||
@Expose
|
||||
private int min;
|
||||
|
|
@ -46,64 +44,53 @@ public class GameClientMessage implements Serializable {
|
|||
@Expose
|
||||
private List<String> messages;
|
||||
|
||||
public GameClientMessage(GameView gameView) {
|
||||
public GameClientMessage(GameView gameView, Map<String, Serializable> options) {
|
||||
this.gameView = gameView;
|
||||
}
|
||||
|
||||
public GameClientMessage(GameView gameView, String message) {
|
||||
this.gameView = gameView;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public GameClientMessage(GameView gameView, String message, Map<String, Serializable> options) {
|
||||
this.gameView = gameView;
|
||||
this.message = message;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
private GameClientMessage(GameView gameView, String question, CardsView cardView, Set<UUID> targets, boolean required) {
|
||||
public GameClientMessage(GameView gameView, Map<String, Serializable> options, String message) {
|
||||
this.gameView = gameView;
|
||||
this.message = question;
|
||||
this.cardsView = cardView;
|
||||
this.options = options;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public GameClientMessage(GameView gameView, Map<String, Serializable> options, String message, CardsView cardsView1, Set<UUID> targets, boolean required) {
|
||||
this.gameView = gameView;
|
||||
this.options = options;
|
||||
this.message = message;
|
||||
this.cardsView1 = cardsView1;
|
||||
this.targets = targets;
|
||||
this.flag = required;
|
||||
}
|
||||
|
||||
public GameClientMessage(GameView gameView, String question, CardsView cardView, Set<UUID> targets, boolean required, Map<String, Serializable> options) {
|
||||
this(gameView, question, cardView, targets, required);
|
||||
public GameClientMessage(GameView gameView, Map<String, Serializable> options, String message, int min, int max) {
|
||||
this.gameView = gameView;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public GameClientMessage(String[] choices, String message) {
|
||||
this.strings = choices;
|
||||
this.message = message;
|
||||
}
|
||||
|
||||
public GameClientMessage(String message, int min, int max) {
|
||||
this.message = message;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
}
|
||||
|
||||
public GameClientMessage(String message, CardsView pile1, CardsView pile2) {
|
||||
public GameClientMessage(GameView gameView, Map<String, Serializable> options, String message, CardsView pile1, CardsView pile2) {
|
||||
this.gameView = gameView;
|
||||
this.options = options;
|
||||
this.message = message;
|
||||
this.cardsView = pile1;
|
||||
this.cardsView1 = pile1;
|
||||
this.cardsView2 = pile2;
|
||||
}
|
||||
|
||||
public GameClientMessage(CardsView cardView, String name) {
|
||||
this.cardsView = cardView;
|
||||
this.message = name;
|
||||
}
|
||||
|
||||
public GameClientMessage(List<String> messages, int min, int max, Map<String, Serializable> options) {
|
||||
public GameClientMessage(GameView gameView, Map<String, Serializable> options, List<String> messages, int min, int max) {
|
||||
this.gameView = gameView;
|
||||
this.options = options;
|
||||
this.messages = messages;
|
||||
this.min = min;
|
||||
this.max = max;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
public GameClientMessage(Choice choice) {
|
||||
public GameClientMessage(GameView gameView, Map<String, Serializable> options, Choice choice) {
|
||||
this.gameView = gameView;
|
||||
this.options = options;
|
||||
this.choice = choice;
|
||||
}
|
||||
|
||||
|
|
@ -111,8 +98,12 @@ public class GameClientMessage implements Serializable {
|
|||
return gameView;
|
||||
}
|
||||
|
||||
public CardsView getCardsView() {
|
||||
return cardsView;
|
||||
public CardsView getCardsView1() {
|
||||
return cardsView1;
|
||||
}
|
||||
|
||||
public CardsView getCardsView2() {
|
||||
return cardsView2;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
|
|
@ -123,22 +114,10 @@ public class GameClientMessage implements Serializable {
|
|||
return flag;
|
||||
}
|
||||
|
||||
public String[] getStrings() {
|
||||
return strings;
|
||||
}
|
||||
|
||||
public Set<UUID> getTargets() {
|
||||
return targets;
|
||||
}
|
||||
|
||||
public CardsView getPile1() {
|
||||
return cardsView;
|
||||
}
|
||||
|
||||
public CardsView getPile2() {
|
||||
return cardsView2;
|
||||
}
|
||||
|
||||
public int getMin() {
|
||||
return min;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,11 +22,6 @@
|
|||
<artifactId>mage-common</artifactId>
|
||||
<version>${mage-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -24,11 +24,6 @@
|
|||
<artifactId>swingx</artifactId>
|
||||
<version>1.6.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@ public class AusHighlander extends Constructed {
|
|||
pointMap.put("Black Lotus", 4);
|
||||
pointMap.put("Time Vault", 4);
|
||||
pointMap.put("Demonic Tutor", 3);
|
||||
pointMap.put("Imperial Seal", 3);
|
||||
pointMap.put("Mana Crypt", 3);
|
||||
pointMap.put("Mox Emerald", 3);
|
||||
pointMap.put("Mox Jet", 3);
|
||||
pointMap.put("Mox Pearl", 3);
|
||||
|
|
@ -30,11 +30,12 @@ public class AusHighlander extends Constructed {
|
|||
pointMap.put("Mox Sapphire", 3);
|
||||
pointMap.put("Sol Ring", 3);
|
||||
pointMap.put("Thassa's Oracle", 3);
|
||||
pointMap.put("Underworld Breach", 3);
|
||||
pointMap.put("Vampiric Tutor", 3);
|
||||
pointMap.put("Channel", 2);
|
||||
pointMap.put("Dig Through Time", 2);
|
||||
pointMap.put("Flash", 2);
|
||||
pointMap.put("Mana Crypt", 2);
|
||||
pointMap.put("Imperial Seal", 2);
|
||||
pointMap.put("Mind Twist", 2);
|
||||
pointMap.put("Mystical Tutor", 2);
|
||||
pointMap.put("Oko, Thief of Crowns", 2);
|
||||
|
|
@ -46,11 +47,12 @@ public class AusHighlander extends Constructed {
|
|||
pointMap.put("Balance", 1);
|
||||
pointMap.put("Birthing Pod", 1);
|
||||
pointMap.put("Crop Rotation", 1);
|
||||
pointMap.put("Dark Petition", 1);
|
||||
pointMap.put("Deathrite Shaman", 1);
|
||||
pointMap.put("Doomsday", 1);
|
||||
pointMap.put("Enlightened Tutor", 1);
|
||||
pointMap.put("Fastbond", 1);
|
||||
pointMap.put("Force of Will", 1);
|
||||
pointMap.put("Gifts Ungiven", 1);
|
||||
pointMap.put("Green Sun's Zenith", 1);
|
||||
pointMap.put("Hermit Druid", 1);
|
||||
pointMap.put("Intuition", 1);
|
||||
|
|
@ -77,7 +79,7 @@ public class AusHighlander extends Constructed {
|
|||
pointMap.put("Timetwister", 1);
|
||||
pointMap.put("Tolarian Academy", 1);
|
||||
pointMap.put("Umezawa's Jitte", 1);
|
||||
pointMap.put("Underworld Breach", 1);
|
||||
pointMap.put("Uro, Titan of Nature's Wrath", 1);
|
||||
pointMap.put("Wasteland", 1);
|
||||
pointMap.put("Wishclaw Talisman", 1);
|
||||
pointMap.put("Wrenn and Six", 1);
|
||||
|
|
|
|||
|
|
@ -40,15 +40,16 @@ public class CanadianHighlander extends Constructed {
|
|||
pointMap.put("Mox Ruby", 3);
|
||||
pointMap.put("Mox Sapphire", 3);
|
||||
pointMap.put("Mystical Tutor", 2);
|
||||
pointMap.put("Natural Order", 4);
|
||||
pointMap.put("Natural Order", 3);
|
||||
pointMap.put("Price of Progress", 1);
|
||||
pointMap.put("Protean Hulk", 3);
|
||||
pointMap.put("Protean Hulk", 2);
|
||||
pointMap.put("Sol Ring", 4);
|
||||
pointMap.put("Spellseeker", 2);
|
||||
pointMap.put("Strip Mine", 3);
|
||||
pointMap.put("Summoner's Pact", 1);
|
||||
pointMap.put("Survival of the Fittest", 2);
|
||||
pointMap.put("Tainted Pact", 1);
|
||||
pointMap.put("Thassa's Oracle", 2);
|
||||
pointMap.put("Time Vault", 7);
|
||||
pointMap.put("Time Walk", 7);
|
||||
pointMap.put("Tinker", 3);
|
||||
|
|
@ -56,8 +57,8 @@ public class CanadianHighlander extends Constructed {
|
|||
pointMap.put("Transmute Artifact", 1);
|
||||
pointMap.put("Treasure Cruise", 1);
|
||||
pointMap.put("True-Name Nemesis", 1);
|
||||
pointMap.put("Umezawa's Jitte", 2);
|
||||
pointMap.put("Underworld Breach", 1);
|
||||
pointMap.put("Umezawa's Jitte", 1);
|
||||
pointMap.put("Underworld Breach", 2);
|
||||
pointMap.put("Vampiric Tutor", 2);
|
||||
pointMap.put("Wishclaw Talisman", 1);
|
||||
pointMap.put("Yawgmoth's Will", 2);
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ public class Commander extends Constructed {
|
|||
banned.add("Fastbond");
|
||||
banned.add("Flash");
|
||||
banned.add("Gifts Ungiven");
|
||||
banned.add("Golos, Tireless Pilgrim");
|
||||
banned.add("Griselbrand");
|
||||
banned.add("Hullbreacher");
|
||||
banned.add("Iona, Shield of Emeria");
|
||||
|
|
@ -74,7 +75,6 @@ public class Commander extends Constructed {
|
|||
banned.add("Tolarian Academy");
|
||||
banned.add("Trade Secrets");
|
||||
banned.add("Upheaval");
|
||||
banned.add("Worldfire");
|
||||
banned.add("Yawgmoth's Bargain");
|
||||
}
|
||||
|
||||
|
|
@ -586,13 +586,7 @@ public class Commander extends Constructed {
|
|||
}
|
||||
|
||||
if (card.isPlaneswalker()) {
|
||||
if (card.getName().toLowerCase(Locale.ENGLISH).equals("jace, the mind sculptor")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 6);
|
||||
}
|
||||
if (card.getName().toLowerCase(Locale.ENGLISH).equals("ugin, the spirit dragon")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
}
|
||||
thisMaxPower = Math.max(thisMaxPower, 4);
|
||||
thisMaxPower = Math.max(thisMaxPower, 6);
|
||||
}
|
||||
|
||||
String cn = card.getName().toLowerCase(Locale.ENGLISH);
|
||||
|
|
@ -673,7 +667,7 @@ public class Commander extends Constructed {
|
|||
|| cn.equals("vorinclex, voice of hunger")
|
||||
|| cn.equals("winter orb")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 5);
|
||||
thisMaxPower = Math.max(thisMaxPower, 12);
|
||||
}
|
||||
|
||||
// Parts of infinite combos
|
||||
|
|
@ -734,9 +728,151 @@ public class Commander extends Constructed {
|
|||
|| cn.equals("workhorse") || cn.equals("worldgorger dragon")
|
||||
|| cn.equals("worthy cause") || cn.equals("yawgmoth's will")
|
||||
|| cn.equals("zealous conscripts")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 12);
|
||||
thisMaxPower = Math.max(thisMaxPower, 15);
|
||||
numberInfinitePieces++;
|
||||
}
|
||||
|
||||
// Saltiest cards (edhrec)
|
||||
if (cn.equals("acid rain")
|
||||
|| cn.equals("agent of treachery")
|
||||
|| cn.equals("apocalypse")
|
||||
|| cn.equals("armageddon")
|
||||
|| cn.equals("atraxa, praetors' voice")
|
||||
|| cn.equals("aura shards")
|
||||
|| cn.equals("avacyn, angel of hope")
|
||||
|| cn.equals("back to basics")
|
||||
|| cn.equals("bend or break")
|
||||
|| cn.equals("blightsteel colossus")
|
||||
|| cn.equals("blood moon")
|
||||
|| cn.equals("boil")
|
||||
|| cn.equals("boiling seas")
|
||||
|| cn.equals("bribery")
|
||||
|| cn.equals("burning sands")
|
||||
|| cn.equals("card view")
|
||||
|| cn.equals("cataclysm")
|
||||
|| cn.equals("catastrophe")
|
||||
|| cn.equals("chulane, teller of tales")
|
||||
|| cn.equals("confusion in the ranks")
|
||||
|| cn.equals("consecrated sphinx")
|
||||
|| cn.equals("contamination")
|
||||
|| cn.equals("craterhoof behemoth")
|
||||
|| cn.equals("cyclonic rift")
|
||||
|| cn.equals("death cloud")
|
||||
|| cn.equals("decree of annihilation")
|
||||
|| cn.equals("decree of silence")
|
||||
|| cn.equals("demonic consultation")
|
||||
|| cn.equals("derevi, empyrial tactician")
|
||||
|| cn.equals("devastation")
|
||||
|| cn.equals("divine intervention")
|
||||
|| cn.equals("dockside extortionist")
|
||||
|| cn.equals("doomsday")
|
||||
|| cn.equals("doubling season")
|
||||
|| cn.equals("drannith magistrate")
|
||||
|| cn.equals("elesh norn, grand cenobite")
|
||||
|| cn.equals("embargo")
|
||||
|| cn.equals("emrakul, the promised end")
|
||||
|| cn.equals("epicenter")
|
||||
|| cn.equals("expropriate")
|
||||
|| cn.equals("fall of the thran")
|
||||
|| cn.equals("fierce guardianship")
|
||||
|| cn.equals("food chain")
|
||||
|| cn.equals("force of negation")
|
||||
|| cn.equals("force of will")
|
||||
|| cn.equals("gaddock teeg")
|
||||
|| cn.equals("gaea's cradle")
|
||||
|| cn.equals("gilded drake")
|
||||
|| cn.equals("glenn, the voice of calm")
|
||||
|| cn.equals("global ruin")
|
||||
|| cn.equals("golos, tireless pilgrim")
|
||||
|| cn.equals("grand arbiter augustin iv")
|
||||
|| cn.equals("grip of chaos")
|
||||
|| cn.equals("hokori, dust drinker")
|
||||
|| cn.equals("humility")
|
||||
|| cn.equals("impending disaster")
|
||||
|| cn.equals("invoke prejudice")
|
||||
|| cn.equals("iona, shield of emeria")
|
||||
|| cn.equals("jin-gitaxias, core augur")
|
||||
|| cn.equals("jokulhaups")
|
||||
|| cn.equals("keldon firebombers")
|
||||
|| cn.equals("kinnan, bonder prodigy")
|
||||
|| cn.equals("kozilek, butcher of truth")
|
||||
|| cn.equals("land equilibrium")
|
||||
|| cn.equals("linvala, keeper of silence")
|
||||
|| cn.equals("magister sphinx")
|
||||
|| cn.equals("mana breach")
|
||||
|| cn.equals("mana crypt")
|
||||
|| cn.equals("mana drain")
|
||||
|| cn.equals("mana vortex")
|
||||
|| cn.equals("mindslaver")
|
||||
|| cn.equals("narset, enlightened master")
|
||||
|| cn.equals("narset, parter of veils")
|
||||
|| cn.equals("negan, the cold-blooded")
|
||||
|| cn.equals("nether void")
|
||||
|| cn.equals("nexus of fate")
|
||||
|| cn.equals("notion thief")
|
||||
|| cn.equals("obliterate")
|
||||
|| cn.equals("oko, thief of crowns")
|
||||
|| cn.equals("oloro, ageless ascetic")
|
||||
|| cn.equals("omniscience")
|
||||
|| cn.equals("opposition agent")
|
||||
|| cn.equals("oppression")
|
||||
|| cn.equals("overwhelming splendor")
|
||||
|| cn.equals("palinchron")
|
||||
|| cn.equals("paradox engine")
|
||||
|| cn.equals("possessed portal")
|
||||
|| cn.equals("price of glory")
|
||||
|| cn.equals("protean hulk")
|
||||
|| cn.equals("ravages of war")
|
||||
|| cn.equals("rhystic study")
|
||||
|| cn.equals("rick, steadfast leader")
|
||||
|| cn.equals("rising waters")
|
||||
|| cn.equals("ruination")
|
||||
|| cn.equals("scrambleverse")
|
||||
|| cn.equals("seedborn muse")
|
||||
|| cn.equals("sen triplets")
|
||||
|| cn.equals("sire of insanity")
|
||||
|| cn.equals("skithiryx, the blight dragon")
|
||||
|| cn.equals("smokestack")
|
||||
|| cn.equals("smothering tithe")
|
||||
|| cn.equals("sorin markov")
|
||||
|| cn.equals("stasis")
|
||||
|| cn.equals("static orb")
|
||||
|| cn.equals("storage matrix")
|
||||
|| cn.equals("sunder")
|
||||
|| cn.equals("survival of the fittest")
|
||||
|| cn.equals("table view")
|
||||
|| cn.equals("tainted aether")
|
||||
|| cn.equals("tectonic break")
|
||||
|| cn.equals("teferi's protection")
|
||||
|| cn.equals("teferi, master of time")
|
||||
|| cn.equals("teferi, time raveler")
|
||||
|| cn.equals("temporal manipulation")
|
||||
|| cn.equals("tergrid, god of fright")
|
||||
|| cn.equals("text view")
|
||||
|| cn.equals("thassa's oracle")
|
||||
|| cn.equals("the tabernacle at pendrell vale")
|
||||
|| cn.equals("thieves' auction")
|
||||
|| cn.equals("thoughts of ruin")
|
||||
|| cn.equals("thrasios, triton hero")
|
||||
|| cn.equals("time stretch")
|
||||
|| cn.equals("time warp")
|
||||
|| cn.equals("tooth and nail")
|
||||
|| cn.equals("torment of hailfire")
|
||||
|| cn.equals("torpor orb")
|
||||
|| cn.equals("triumph of the hordes")
|
||||
|| cn.equals("ugin, the spirit dragon")
|
||||
|| cn.equals("ulamog, the ceaseless hunger")
|
||||
|| cn.equals("ulamog, the infinite gyre")
|
||||
|| cn.equals("urza, lord high artificer")
|
||||
|| cn.equals("void winnower")
|
||||
|| cn.equals("vorinclex, voice of hunger")
|
||||
|| cn.equals("wake of destruction")
|
||||
|| cn.equals("warp world")
|
||||
|| cn.equals("winter orb")
|
||||
|| cn.equals("xanathar, guild kingpin")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 15);
|
||||
}
|
||||
edhPowerLevel += thisMaxPower;
|
||||
}
|
||||
|
||||
|
|
@ -769,11 +905,17 @@ public class Commander extends Constructed {
|
|||
|
||||
// Least fun commanders
|
||||
if (cn.equals("animar, soul of element")
|
||||
|| cn.equals("anafenza, the foremost")
|
||||
|| cn.equals("arcum dagsson")
|
||||
|| cn.equals("azami, lady of scrolls")
|
||||
|| cn.equals("azusa, lost but seeking")
|
||||
|| cn.equals("brago, king eternal")
|
||||
|| cn.equals("braids, cabal minion")
|
||||
|| cn.equals("captain sisay")
|
||||
|| cn.equals("child of alara")
|
||||
|| cn.equals("derevi, empyrial tactician")
|
||||
|| cn.equals("edric, spymaster of trest")
|
||||
|| cn.equals("elesh norn, grand cenobite")
|
||||
|| cn.equals("gaddock teeg")
|
||||
|| cn.equals("grand arbiter augustin iv")
|
||||
|| cn.equals("hokori, dust drinker")
|
||||
|
|
@ -784,41 +926,67 @@ public class Commander extends Constructed {
|
|||
|| cn.equals("leovold, emissary of trest")
|
||||
|| cn.equals("linvala, keeper of silence")
|
||||
|| cn.equals("llawan, cephalid empress")
|
||||
|| cn.equals("maelstrom wanderer")
|
||||
|| cn.equals("malfegor")
|
||||
|| cn.equals("memnarch")
|
||||
|| cn.equals("meren of clan nel toth")
|
||||
|| cn.equals("michiko konda, truth seeker")
|
||||
|| cn.equals("mikaeus the unhallowed")
|
||||
|| cn.equals("narset, enlightened master")
|
||||
|| cn.equals("nath of the gilt-leaf")
|
||||
|| cn.equals("nekusar, the mindrazer")
|
||||
|| cn.equals("norin the wary")
|
||||
|| cn.equals("numot, the devastator")
|
||||
|| cn.equals("prossh, skyraider of kher")
|
||||
|| cn.equals("purphoros, god of the forge")
|
||||
|| cn.equals("sen triplets")
|
||||
|| cn.equals("sheoldred, whispering one")
|
||||
|| cn.equals("teferi, mage of zhalfir")
|
||||
|| cn.equals("urabrask the hidden")
|
||||
|| cn.equals("vorinclex, voice of hunger")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 25);
|
||||
}
|
||||
|
||||
// Next least fun commanders
|
||||
if (cn.equals("anafenza, the foremost")
|
||||
|| cn.equals("arcum dagsson")
|
||||
|| cn.equals("azusa, lost but seeking")
|
||||
|| cn.equals("brago, king eternal")
|
||||
|| cn.equals("captain sisay")
|
||||
// Saltiest commanders
|
||||
if (cn.equals("atraxa, praetors' voice")
|
||||
|| cn.equals("avacyn, angel of hope")
|
||||
|| cn.equals("chulane, teller of tales")
|
||||
|| cn.equals("derevi, empyrial tactician")
|
||||
|| cn.equals("elesh norn, grand cenobite")
|
||||
|| cn.equals("malfegor")
|
||||
|| cn.equals("maelstrom wanderer")
|
||||
|| cn.equals("mikaeus the unhallowed")
|
||||
|| cn.equals("nath of the gilt-leaf")
|
||||
|| cn.equals("prossh, skyraider of kher")
|
||||
|| cn.equals("purphoros, god of the forge")
|
||||
|| cn.equals("emrakul, the promised end")
|
||||
|| cn.equals("gaddock teeg")
|
||||
|| cn.equals("glenn, the voice of calm")
|
||||
|| cn.equals("golos, tireless pilgrim")
|
||||
|| cn.equals("grand arbiter augustin iv")
|
||||
|| cn.equals("hokori, dust drinker")
|
||||
|| cn.equals("iona, shield of emeria")
|
||||
|| cn.equals("jin-gitaxias, core augur")
|
||||
|| cn.equals("kinnan, bonder prodigy")
|
||||
|| cn.equals("kozilek, butcher of truth")
|
||||
|| cn.equals("linvala, keeper of silence")
|
||||
|| cn.equals("narset, enlightened master")
|
||||
|| cn.equals("negan, the cold-blooded")
|
||||
|| cn.equals("oko, thief of crowns")
|
||||
|| cn.equals("oloro, ageless ascetic")
|
||||
|| cn.equals("rick, steadfast leader")
|
||||
|| cn.equals("sen triplets")
|
||||
|| cn.equals("urabrask the hidden")
|
||||
|| cn.equals("vorinclex, voice of hunger")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 15);
|
||||
|| cn.equals("skithiryx, the blight dragon")
|
||||
|| cn.equals("teferi, master of time")
|
||||
|| cn.equals("teferi, time raveler")
|
||||
|| cn.equals("thrasios, triton hero")
|
||||
|| cn.equals("ulamog, the ceaseless hunger")
|
||||
|| cn.equals("ulamog, the infinite gyre")
|
||||
|| cn.equals("urza, lord high artificer")
|
||||
|| cn.equals("vorinclex, voice of hunger")
|
||||
|| cn.equals("xanathar, guild kingpin")
|
||||
|| cn.equals("zur the enchanter")) {
|
||||
thisMaxPower = Math.max(thisMaxPower, 20);
|
||||
}
|
||||
edhPowerLevel += thisMaxPower;
|
||||
}
|
||||
|
||||
edhPowerLevel += numberInfinitePieces * 12;
|
||||
edhPowerLevel += numberInfinitePieces * 18;
|
||||
edhPowerLevel = Math.round(edhPowerLevel / 10);
|
||||
if (edhPowerLevel >= 100) {
|
||||
edhPowerLevel = 99;
|
||||
|
|
|
|||
|
|
@ -36,8 +36,9 @@ public class DuelCommander extends Commander {
|
|||
banned.add("Karakas");
|
||||
banned.add("Library of Alexandria");
|
||||
banned.add("Lion's Eye Diamond");
|
||||
banned.add("Lutri, The Spellchaser");
|
||||
banned.add("Loyal Retainers");
|
||||
banned.add("Lutri, the Spellchaser");
|
||||
banned.add("Maddening Hex");
|
||||
banned.add("Mana Crypt");
|
||||
banned.add("Mana Drain");
|
||||
banned.add("Mana Vault");
|
||||
|
|
@ -77,6 +78,7 @@ public class DuelCommander extends Commander {
|
|||
bannedCommander.add("Akiri, Line-Slinger");
|
||||
bannedCommander.add("Arahbo, Roar of the World");
|
||||
bannedCommander.add("Ardenn, Intrepid Archaeologist");
|
||||
bannedCommander.add("Asmoranomardicadaistinaculdacar");
|
||||
bannedCommander.add("Baral, Chief of Compliance");
|
||||
bannedCommander.add("Breya, Etherium Shaper");
|
||||
bannedCommander.add("Bruse Tarl, Boorish Herder");
|
||||
|
|
@ -106,6 +108,7 @@ public class DuelCommander extends Commander {
|
|||
bannedCommander.add("Tymna, the Weaver");
|
||||
bannedCommander.add("Urza, Lord High Artificer");
|
||||
bannedCommander.add("Vial Smasher the Fierce");
|
||||
bannedCommander.add("Winota, Joiner of Forces");
|
||||
bannedCommander.add("Yuriko, the Tiger's Shadow");
|
||||
bannedCommander.add("Zurgo Bellstriker");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ public class Oathbreaker extends Vintage {
|
|||
setName("Oathbreaker");
|
||||
|
||||
// banned = vintage + oathbreaker's list: https://oathbreakermtg.org/banned-list/
|
||||
// last updated 4/24/20 - Dark Ritual banned
|
||||
banned.add("Ad Nauseam");
|
||||
banned.add("Ancestral Recall");
|
||||
banned.add("Balance");
|
||||
|
|
@ -77,7 +76,6 @@ public class Oathbreaker extends Vintage {
|
|||
banned.add("Tooth and Nail");
|
||||
banned.add("Trade Secrets");
|
||||
banned.add("Upheaval");
|
||||
banned.add("Worldfire");
|
||||
banned.add("Yawgmoth's Bargain");
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -5,7 +5,11 @@ import mage.cards.Sets;
|
|||
import mage.cards.decks.Constructed;
|
||||
import mage.constants.SetType;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
|
|
@ -17,61 +21,39 @@ public class Standard extends Constructed {
|
|||
|
||||
setCodes.addAll(makeLegalSets());
|
||||
|
||||
banned.add("Agent of Treachery");
|
||||
banned.add("Cauldron Familiar");
|
||||
banned.add("Escape to the Wilds");
|
||||
banned.add("Field of the Dead");
|
||||
banned.add("Fires of Invention");
|
||||
banned.add("Growth Spiral");
|
||||
banned.add("Lucky Clover");
|
||||
banned.add("Oko, Thief of Crowns");
|
||||
banned.add("Omnath, Locus of Creation");
|
||||
banned.add("Once Upon a Time");
|
||||
banned.add("Teferi, Time Raveler");
|
||||
banned.add("Uro, Titan of Nature's Wrath");
|
||||
banned.add("Wilderness Reclamation");
|
||||
banned.add("Veil of Summer");
|
||||
}
|
||||
|
||||
private static boolean isFallSet(ExpansionSet set) {
|
||||
Calendar cal = Calendar.getInstance();
|
||||
cal.setTime(set.getReleaseDate());
|
||||
// Fall sets are normally released during or after September
|
||||
return set.getSetType() == SetType.EXPANSION && (cal.get(Calendar.MONTH) > 7);
|
||||
// Fall sets are normally released during or after September and before November
|
||||
return set.getSetType() == SetType.EXPANSION
|
||||
&& Calendar.SEPTEMBER <= cal.get(Calendar.MONTH)
|
||||
&& cal.get(Calendar.MONTH) < Calendar.NOVEMBER;
|
||||
}
|
||||
|
||||
static List<String> makeLegalSets() {
|
||||
List<String> codes = new ArrayList<>();
|
||||
GregorianCalendar current = new GregorianCalendar();
|
||||
List<ExpansionSet> sets = new ArrayList(Sets.getInstance().values());
|
||||
Collections.sort(sets, new Comparator<ExpansionSet>() {
|
||||
@Override
|
||||
public int compare(final ExpansionSet lhs, ExpansionSet rhs) {
|
||||
return lhs.getReleaseDate().after(rhs.getReleaseDate()) ? -1 : 1;
|
||||
}
|
||||
});
|
||||
int fallSetsAdded = 0;
|
||||
Date earliestDate = null;
|
||||
// Get the second most recent fall set that's been released.
|
||||
for (ExpansionSet set : sets) {
|
||||
if (set.getReleaseDate().after(current.getTime())) {
|
||||
continue;
|
||||
}
|
||||
if (isFallSet(set)) {
|
||||
fallSetsAdded++;
|
||||
if (fallSetsAdded == 2) {
|
||||
earliestDate = set.getReleaseDate();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ExpansionSet set : sets) {
|
||||
boolean isDateCompatible = earliestDate != null && !set.getReleaseDate().before(earliestDate) /*!set.getReleaseDate().after(current.getTime())*/; // no after date restrict for early tests and beta
|
||||
if (set.getSetType().isStandardLegal() && isDateCompatible) {
|
||||
codes.add(set.getCode());
|
||||
}
|
||||
}
|
||||
return codes;
|
||||
Date earliestDate = Sets
|
||||
.getInstance()
|
||||
.values()
|
||||
.stream()
|
||||
.filter(set -> !set.getReleaseDate().after(current.getTime()))
|
||||
.filter(Standard::isFallSet)
|
||||
.sorted(ExpansionSet.getComparator())
|
||||
.skip(1)
|
||||
.findFirst()
|
||||
.get()
|
||||
.getReleaseDate();
|
||||
return Sets.getInstance()
|
||||
.values()
|
||||
.stream()
|
||||
.filter(set -> set.getSetType().isStandardLegal())
|
||||
.filter(set -> !set.getReleaseDate().before(earliestDate))
|
||||
// .filter(set -> !set.getReleaseDate().after(current.getTime())) // no after date restrict for early tests and beta
|
||||
.map(ExpansionSet::getCode)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,12 +28,12 @@ public class TinyLeaders extends Constructed {
|
|||
}
|
||||
}
|
||||
//Banned list from tinyleaders.blodspot.ca/p/ban-list.html
|
||||
//Ban list updated as of 11/08/14
|
||||
banned.add("Ancestral Recall");
|
||||
banned.add("Balance");
|
||||
banned.add("Black Lotus");
|
||||
banned.add("Black Vise");
|
||||
banned.add("Channel");
|
||||
banned.add("Codie, Vociferous Codex");
|
||||
banned.add("Counterbalance");
|
||||
banned.add("Demonic Tutor");
|
||||
banned.add("Earthcraft");
|
||||
|
|
@ -57,14 +57,16 @@ public class TinyLeaders extends Constructed {
|
|||
banned.add("Mox Pearl");
|
||||
banned.add("Mox Ruby");
|
||||
banned.add("Mox Sapphire");
|
||||
banned.add("Najeela, the Blade Blossom");
|
||||
banned.add("Najeela, the Blade-Blossom");
|
||||
banned.add("Necropotence");
|
||||
banned.add("Shahrazad");
|
||||
banned.add("Sisay, Weatherlight Captain");
|
||||
banned.add("Skullclamp");
|
||||
banned.add("Sol Ring");
|
||||
banned.add("Strip Mine");
|
||||
banned.add("Survival of the Fittest");
|
||||
banned.add("Sword of Body and Mind");
|
||||
banned.add("Thassa's Oracle");
|
||||
banned.add("The Tabernacle at Pendrell Vale");
|
||||
banned.add("Time Vault");
|
||||
banned.add("Time Walk");
|
||||
|
|
|
|||
|
|
@ -162,13 +162,12 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
boolean usedStack = false;
|
||||
while (actions.peek() != null) {
|
||||
Ability ability = actions.poll();
|
||||
// log example: ===> Act [PlayerA] Action: Cast Blessings of Nature (target 1; target 2)
|
||||
logger.info(new StringBuilder("===> Act [")
|
||||
.append(game.getPlayer(playerId).getName())
|
||||
.append("] Action: ")
|
||||
.append(ability.toString())
|
||||
.append(listTargets(game, ability.getTargets(), " (targeting %s)", ""))
|
||||
.toString());
|
||||
// example: ===> SELECTED ACTION for PlayerA: Play Swamp
|
||||
logger.info(String.format("===> SELECTED ACTION for %s: %s",
|
||||
getName(),
|
||||
ability.toString()
|
||||
+ listTargets(game, ability.getTargets(), " (targeting %s)", "")
|
||||
));
|
||||
if (!ability.getTargets().isEmpty()) {
|
||||
for (Target target : ability.getTargets()) {
|
||||
for (UUID id : target.getTargets()) {
|
||||
|
|
@ -391,6 +390,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
protected void resolve(SimulationNode2 node, int depth, Game game) {
|
||||
StackObject stackObject = game.getStack().getFirst();
|
||||
if (stackObject instanceof StackAbility) {
|
||||
// AI hint for search effects (calc all possible cards for best score)
|
||||
SearchEffect effect = getSearchEffect((StackAbility) stackObject);
|
||||
if (effect != null
|
||||
&& stackObject.getControllerId().equals(playerId)) {
|
||||
|
|
@ -477,15 +477,24 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
SimulationNode2 bestNode = null;
|
||||
List<Ability> allActions = currentPlayer.simulatePriority(game);
|
||||
optimize(game, allActions);
|
||||
int startedScore = GameStateEvaluator2.evaluate(this.getId(), node.getGame()).getTotalScore();
|
||||
if (logger.isInfoEnabled()
|
||||
&& !allActions.isEmpty()
|
||||
&& depth == maxDepth) {
|
||||
logger.info("ADDED ACTIONS (" + allActions.size() + ") " + ' ' + allActions);
|
||||
logger.info(String.format("POSSIBLE ACTIONS for %s (%d, started score: %d)%s",
|
||||
getName(),
|
||||
allActions.size(),
|
||||
startedScore,
|
||||
(actions.isEmpty() ? "" : ":")
|
||||
));
|
||||
for (int i = 0; i < allActions.size(); i++) {
|
||||
logger.info(String.format("-> #%d (%s)", i + 1, allActions.get(i)));
|
||||
}
|
||||
}
|
||||
int counter = 0;
|
||||
int actionNumber = 0;
|
||||
int bestValSubNodes = Integer.MIN_VALUE;
|
||||
for (Ability action : allActions) {
|
||||
counter++;
|
||||
actionNumber++;
|
||||
if (!COMPUTER_DISABLE_TIMEOUT_IN_GAME_SIMULATIONS
|
||||
&& Thread.interrupted()) {
|
||||
Thread.currentThread().interrupt();
|
||||
|
|
@ -503,7 +512,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
}
|
||||
if (!sim.checkIfGameIsOver()
|
||||
&& (action.isUsesStack() || action instanceof PassAbility)) {
|
||||
// only pass if the last action uses the stack
|
||||
// skip priority for opponents before stack resolve
|
||||
UUID nextPlayerId = sim.getPlayerList().get();
|
||||
do {
|
||||
sim.getPlayer(nextPlayerId).pass(game);
|
||||
|
|
@ -512,48 +521,73 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
}
|
||||
SimulationNode2 newNode = new SimulationNode2(node, sim, action, depth, currentPlayer.getId());
|
||||
sim.checkStateAndTriggered();
|
||||
int val;
|
||||
int actionScore;
|
||||
if (action instanceof PassAbility && sim.getStack().isEmpty()) {
|
||||
// Stop to simulate deeper if PassAbility and stack is empty
|
||||
val = GameStateEvaluator2.evaluate(this.getId(), sim).getTotalScore();
|
||||
// no more next actions, it's a final score
|
||||
actionScore = GameStateEvaluator2.evaluate(this.getId(), sim).getTotalScore();
|
||||
} else {
|
||||
val = addActions(newNode, depth - 1, alpha, beta);
|
||||
// resolve current action and calc all next actions to find best score (return max possible score)
|
||||
actionScore = addActions(newNode, depth - 1, alpha, beta);
|
||||
}
|
||||
logger.debug("Sim Prio " + BLANKS.substring(0, 2 + (maxDepth - depth) * 3) + '[' + depth + "]#" + counter + " <" + val + "> - (" + action + ") ");
|
||||
logger.debug("Sim Prio " + BLANKS.substring(0, 2 + (maxDepth - depth) * 3) + '[' + depth + "]#" + actionNumber + " <" + actionScore + "> - (" + action + ") ");
|
||||
|
||||
// Hints on data:
|
||||
// * node - started game with executed command (pay and put on stack)
|
||||
// * newNode - resolved game with resolved command (resolve stack)
|
||||
// * node.children - rewrites to store only best tree (e.g. contains only final data)
|
||||
// * node.score - rewrites to store max score (e.g. contains only final data)
|
||||
if (logger.isInfoEnabled()
|
||||
&& depth >= maxDepth) {
|
||||
StringBuilder sb = new StringBuilder("Sim Prio [").append(depth).append("] #").append(counter)
|
||||
.append(" <").append(val).append("> (").append(action)
|
||||
.append(action.isModal() ? " Mode = " + action.getModes().getMode().toString() : "")
|
||||
.append(listTargets(game, action.getTargets(), " (targeting %s)", "")).append(')')
|
||||
.append(logger.isTraceEnabled() ? " #" + newNode.hashCode() : "");
|
||||
// show calculated actions and score
|
||||
// example: Sim Prio [6] #1 <605> (Play Swamp)
|
||||
int currentActionScore = GameStateEvaluator2.evaluate(this.getId(), newNode.getGame()).getTotalScore();
|
||||
int diffCurrentAction = currentActionScore - startedScore;
|
||||
int diffNextActions = actionScore - startedScore - diffCurrentAction;
|
||||
logger.info(String.format("Sim Prio [%d] #%d <diff %s, %s> (%s)",
|
||||
depth,
|
||||
actionNumber,
|
||||
printDiffScore(diffCurrentAction),
|
||||
printDiffScore(diffNextActions),
|
||||
action
|
||||
+ (action.isModal() ? " Mode = " + action.getModes().getMode().toString() : "")
|
||||
+ listTargets(game, action.getTargets(), " (targeting %s)", "")
|
||||
+ (logger.isTraceEnabled() ? " #" + newNode.hashCode() : "")
|
||||
));
|
||||
// collect childs info (next actions chain)
|
||||
SimulationNode2 logNode = newNode;
|
||||
while (logNode.getChildren() != null
|
||||
&& !logNode.getChildren().isEmpty()) {
|
||||
logNode = logNode.getChildren().get(0);
|
||||
if (logNode.getAbilities() != null
|
||||
&& !logNode.getAbilities().isEmpty()) {
|
||||
sb.append(" -> [").append(logNode.getDepth()).append(']').append(logNode.getAbilities().toString()).append('<').append(logNode.getScore()).append('>');
|
||||
int logCurrentScore = GameStateEvaluator2.evaluate(this.getId(), logNode.getGame()).getTotalScore();
|
||||
int logPrevScore = GameStateEvaluator2.evaluate(this.getId(), logNode.getParent().getGame()).getTotalScore();
|
||||
logger.info(String.format("Sim Prio [%d] -> next action: [%d]%s <diff %s, %s>",
|
||||
depth,
|
||||
logNode.getDepth(),
|
||||
logNode.getAbilities().toString(),
|
||||
printDiffScore(logCurrentScore - logPrevScore),
|
||||
printDiffScore(actionScore - logCurrentScore)
|
||||
));
|
||||
}
|
||||
}
|
||||
logger.info(sb);
|
||||
}
|
||||
|
||||
if (currentPlayer.getId().equals(playerId)) {
|
||||
if (val > bestValSubNodes) {
|
||||
bestValSubNodes = val;
|
||||
if (actionScore > bestValSubNodes) {
|
||||
bestValSubNodes = actionScore;
|
||||
}
|
||||
if (depth == maxDepth
|
||||
&& action instanceof PassAbility) {
|
||||
val = val - PASSIVITY_PENALTY; // passivity penalty
|
||||
actionScore = actionScore - PASSIVITY_PENALTY; // passivity penalty
|
||||
}
|
||||
if (val > alpha
|
||||
if (actionScore > alpha
|
||||
|| (depth == maxDepth
|
||||
&& val == alpha
|
||||
&& actionScore == alpha
|
||||
&& RandomUtil.nextBoolean())) { // Adding random for equal value to get change sometimes
|
||||
alpha = val;
|
||||
alpha = actionScore;
|
||||
bestNode = newNode;
|
||||
bestNode.setScore(val);
|
||||
bestNode.setScore(actionScore);
|
||||
if (!newNode.getChildren().isEmpty()) {
|
||||
bestNode.setCombat(newNode.getChildren().get(0).getCombat());
|
||||
}
|
||||
|
|
@ -564,7 +598,7 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
.stream()
|
||||
.map(a -> a.toString() + listTargets(game, a.getTargets(), " (targeting %s)", ""))
|
||||
.collect(Collectors.joining("; "));
|
||||
logger.info("Sim Prio [" + depth + "] -- Saved best node yet <" + bestNode.getScore() + scoreInfo + "> " + abilitiesInfo);
|
||||
logger.info("Sim Prio [" + depth + "] >> BEST action chain found <" + bestNode.getScore() + scoreInfo + "> " + abilitiesInfo);
|
||||
node.children.clear();
|
||||
node.children.add(bestNode);
|
||||
node.setScore(bestNode.getScore());
|
||||
|
|
@ -572,22 +606,22 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
}
|
||||
|
||||
// no need to check other actions
|
||||
if (val == GameStateEvaluator2.WIN_GAME_SCORE) {
|
||||
if (actionScore == GameStateEvaluator2.WIN_GAME_SCORE) {
|
||||
logger.debug("Sim Prio -- win - break");
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (val < beta) {
|
||||
beta = val;
|
||||
if (actionScore < beta) {
|
||||
beta = actionScore;
|
||||
bestNode = newNode;
|
||||
bestNode.setScore(val);
|
||||
bestNode.setScore(actionScore);
|
||||
if (!newNode.getChildren().isEmpty()) {
|
||||
bestNode.setCombat(newNode.getChildren().get(0).getCombat());
|
||||
}
|
||||
}
|
||||
|
||||
// no need to check other actions
|
||||
if (val == GameStateEvaluator2.LOSE_GAME_SCORE) {
|
||||
if (actionScore == GameStateEvaluator2.LOSE_GAME_SCORE) {
|
||||
logger.debug("Sim Prio -- lose - break");
|
||||
break;
|
||||
}
|
||||
|
|
@ -622,6 +656,14 @@ public class ComputerPlayer6 extends ComputerPlayer /*implements Player*/ {
|
|||
}
|
||||
}
|
||||
|
||||
private String printDiffScore(int score) {
|
||||
if (score >= 0) {
|
||||
return "+" + score;
|
||||
} else {
|
||||
return "" + score;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Various AI optimizations for actions.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -15,11 +15,6 @@
|
|||
<name>Mage Player AI</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage</artifactId>
|
||||
|
|
|
|||
|
|
@ -540,6 +540,58 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
return setTargetPlayer(outcome, target, source, sourceId, abilityControllerId, randomOpponentId, game, required);
|
||||
}
|
||||
|
||||
// Angel of Serenity trigger
|
||||
if (target.getOriginalTarget() instanceof TargetCardInGraveyardOrBattlefield) {
|
||||
Cards cards = new CardsImpl(possibleTargets);
|
||||
List<Card> possibleCards = new ArrayList<>(cards.getCards(game));
|
||||
for (Card card : possibleCards) {
|
||||
// check permanents first; they have more intrinsic worth
|
||||
if (card instanceof Permanent) {
|
||||
Permanent p = ((Permanent) card);
|
||||
if (outcome.isGood()
|
||||
&& p.isControlledBy(abilityControllerId)) {
|
||||
if (target.canTarget(abilityControllerId, p.getId(), source, game)) {
|
||||
if (target.getTargets().size() >= target.getMaxNumberOfTargets()) {
|
||||
break;
|
||||
}
|
||||
target.addTarget(p.getId(), source, game);
|
||||
}
|
||||
}
|
||||
if (!outcome.isGood()
|
||||
&& !p.isControlledBy(abilityControllerId)) {
|
||||
if (target.canTarget(abilityControllerId, p.getId(), source, game)) {
|
||||
if (target.getTargets().size() >= target.getMaxNumberOfTargets()) {
|
||||
break;
|
||||
}
|
||||
target.addTarget(p.getId(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
// check the graveyards last
|
||||
if (game.getState().getZone(card.getId()) == Zone.GRAVEYARD) {
|
||||
if (outcome.isGood()
|
||||
&& card.isOwnedBy(abilityControllerId)) {
|
||||
if (target.canTarget(abilityControllerId, card.getId(), source, game)) {
|
||||
if (target.getTargets().size() >= target.getMaxNumberOfTargets()) {
|
||||
break;
|
||||
}
|
||||
target.addTarget(card.getId(), source, game);
|
||||
}
|
||||
}
|
||||
if (!outcome.isGood()
|
||||
&& !card.isOwnedBy(abilityControllerId)) {
|
||||
if (target.canTarget(abilityControllerId, card.getId(), source, game)) {
|
||||
if (target.getTargets().size() >= target.getMaxNumberOfTargets()) {
|
||||
break;
|
||||
}
|
||||
target.addTarget(card.getId(), source, game);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return target.isChosen();
|
||||
}
|
||||
|
||||
if (target.getOriginalTarget() instanceof TargetDiscard
|
||||
|| target.getOriginalTarget() instanceof TargetCardInHand) {
|
||||
if (outcome.isGood()) {
|
||||
|
|
@ -2841,27 +2893,38 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
|
||||
/**
|
||||
* Sets a possible target player
|
||||
* Sets a possible target player. Depends on bad/good outcome
|
||||
*
|
||||
* @param source null on choose and non-null on chooseTarget
|
||||
*/
|
||||
private boolean setTargetPlayer(Outcome outcome, Target target, Ability source, UUID sourceId, UUID abilityControllerId, UUID randomOpponentId, Game game, boolean required) {
|
||||
Outcome affectedOutcome;
|
||||
if (abilityControllerId == this.playerId) {
|
||||
// selects for itself
|
||||
affectedOutcome = outcome;
|
||||
} else {
|
||||
// selects for another player
|
||||
affectedOutcome = Outcome.inverse(outcome);
|
||||
}
|
||||
|
||||
if (target.getOriginalTarget() instanceof TargetOpponent) {
|
||||
if (source == null) {
|
||||
if (target.canTarget(randomOpponentId, game)) {
|
||||
target.add(randomOpponentId, game);
|
||||
return true;
|
||||
}
|
||||
} else if (target.canTarget(randomOpponentId, source, game)) {
|
||||
target.add(randomOpponentId, game);
|
||||
} else if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) {
|
||||
target.addTarget(randomOpponentId, source, game);
|
||||
return true;
|
||||
}
|
||||
for (UUID currentId : game.getOpponents(abilityControllerId)) {
|
||||
for (UUID possibleOpponentId : game.getOpponents(abilityControllerId)) {
|
||||
if (source == null) {
|
||||
if (target.canTarget(currentId, game)) {
|
||||
target.add(currentId, game);
|
||||
if (target.canTarget(possibleOpponentId, game)) {
|
||||
target.add(possibleOpponentId, game);
|
||||
return true;
|
||||
}
|
||||
} else if (target.canTarget(currentId, source, game)) {
|
||||
target.add(currentId, game);
|
||||
} else if (target.canTarget(abilityControllerId, possibleOpponentId, source, game)) {
|
||||
target.addTarget(possibleOpponentId, source, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2869,8 +2932,9 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
|
||||
if (target.getOriginalTarget() instanceof TargetPlayer) {
|
||||
if (outcome.isGood()) {
|
||||
if (affectedOutcome.isGood()) {
|
||||
if (source == null) {
|
||||
// good
|
||||
if (target.canTarget(abilityControllerId, game)) {
|
||||
target.add(abilityControllerId, game);
|
||||
return true;
|
||||
|
|
@ -2882,19 +2946,20 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
// good
|
||||
if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) {
|
||||
target.addTarget(playerId, source, game);
|
||||
target.addTarget(abilityControllerId, source, game);
|
||||
return true;
|
||||
}
|
||||
if (target.isRequired(sourceId, game)) {
|
||||
if (target.canTarget(randomOpponentId, game)) {
|
||||
target.add(randomOpponentId, game);
|
||||
if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) {
|
||||
target.addTarget(randomOpponentId, source, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} else if (source == null) {
|
||||
// bad
|
||||
if (target.canTarget(randomOpponentId, game)) {
|
||||
target.add(randomOpponentId, game);
|
||||
return true;
|
||||
|
|
@ -2906,13 +2971,14 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
if (target.canTarget(randomOpponentId, game)) {
|
||||
target.add(randomOpponentId, game);
|
||||
// bad
|
||||
if (target.canTarget(abilityControllerId, randomOpponentId, source, game)) {
|
||||
target.addTarget(randomOpponentId, source, game);
|
||||
return true;
|
||||
}
|
||||
if (required) {
|
||||
if (target.canTarget(abilityControllerId, game)) {
|
||||
target.add(abilityControllerId, game);
|
||||
if (target.canTarget(abilityControllerId, abilityControllerId, source, game)) {
|
||||
target.addTarget(abilityControllerId, source, game);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -2950,7 +3016,7 @@ public class ComputerPlayer extends PlayerImpl implements Player {
|
|||
|
||||
@Override
|
||||
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
|
||||
Map<UUID, ActivatedAbility> useable = PlayerImpl.getSpellAbilities(this.getId(), card, game.getState().getZone(card.getId()), game);
|
||||
Map<UUID, ActivatedAbility> useable = PlayerImpl.getCastableSpellAbilities(game, this.getId(), card, game.getState().getZone(card.getId()), noMana);
|
||||
return (SpellAbility) useable.values().stream().findFirst().orElse(null);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,11 +15,6 @@
|
|||
<name>Mage Player AI MCTS</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage</artifactId>
|
||||
|
|
|
|||
|
|
@ -15,11 +15,6 @@
|
|||
<name>Mage Player AI Minimax</name>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage</artifactId>
|
||||
|
|
|
|||
|
|
@ -351,6 +351,7 @@ public class ComputerPlayer2 extends ComputerPlayer implements Player {
|
|||
}
|
||||
|
||||
protected int simulatePriority(SimulationNode node, Game game, int alpha, int beta) {
|
||||
// NOT USED in real AI, see ComputerPlayer6
|
||||
if (Thread.interrupted()) {
|
||||
Thread.currentThread().interrupt();
|
||||
logger.debug(indent(node.depth) + "interrupted");
|
||||
|
|
|
|||
|
|
@ -25,11 +25,6 @@
|
|||
<artifactId>mage-common</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -2174,7 +2174,7 @@ public class HumanPlayer extends PlayerImpl {
|
|||
}
|
||||
|
||||
@Override
|
||||
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean nonMana) {
|
||||
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
|
||||
if (gameInCheckPlayableState(game)) {
|
||||
return null;
|
||||
}
|
||||
|
|
@ -2186,8 +2186,8 @@ public class HumanPlayer extends PlayerImpl {
|
|||
|
||||
MageObject object = game.getObject(card.getId()); // must be object to find real abilities (example: commander)
|
||||
if (object != null) {
|
||||
String message = "Choose ability to cast" + (nonMana ? " for FREE" : "") + "<br>" + object.getLogName();
|
||||
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = getSpellAbilities(playerId, object, game.getState().getZone(object.getId()), game);
|
||||
String message = "Choose ability to cast" + (noMana ? " for FREE" : "") + "<br>" + object.getLogName();
|
||||
LinkedHashMap<UUID, ActivatedAbility> useableAbilities = PlayerImpl.getCastableSpellAbilities(game, playerId, object, game.getState().getZone(object.getId()), noMana);
|
||||
if (useableAbilities != null
|
||||
&& useableAbilities.size() == 1) {
|
||||
return (SpellAbility) useableAbilities.values().iterator().next();
|
||||
|
|
|
|||
|
|
@ -19,11 +19,6 @@
|
|||
<artifactId>mage</artifactId>
|
||||
<version>${mage-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage-common</artifactId>
|
||||
|
|
@ -34,30 +29,6 @@
|
|||
<artifactId>mage-sets</artifactId>
|
||||
<version>${mage-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>2.3.3</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- server xml config needs optional dependency for jaxb-impl -->
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.slf4j</groupId>
|
||||
<artifactId>slf4j-log4j12</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage-player-ai</artifactId>
|
||||
|
|
@ -88,12 +59,6 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>[1.19,)</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage-game-commanderfreeforall</artifactId>
|
||||
|
|
@ -184,7 +149,6 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage-game-freeformcommanderfreeforall</artifactId>
|
||||
|
|
@ -203,7 +167,6 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage-game-oathbreakerduel</artifactId>
|
||||
|
|
@ -216,7 +179,6 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>${project.groupId}</groupId>
|
||||
<artifactId>mage-game-momirduel</artifactId>
|
||||
|
|
@ -229,41 +191,57 @@
|
|||
<version>${project.version}</version>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
<version>3.11</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.bind</groupId>
|
||||
<artifactId>jaxb-impl</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- server xml config needs optional dependency for jaxb-impl -->
|
||||
<groupId>org.glassfish.jaxb</groupId>
|
||||
<artifactId>jaxb-runtime</artifactId>
|
||||
<version>3.0.2</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-compress</artifactId>
|
||||
<version>[1.19,)</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.shiro</groupId>
|
||||
<artifactId>shiro-core</artifactId>
|
||||
<version>1.8.0</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.api-client</groupId>
|
||||
<artifactId>google-api-client</artifactId>
|
||||
<version>1.31.1</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.apis</groupId>
|
||||
<artifactId>google-api-services-gmail</artifactId>
|
||||
<version>v1-rev20210614-1.32.1</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.oauth-client</groupId>
|
||||
<artifactId>google-oauth-client-java6</artifactId>
|
||||
<version>1.31.0</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.google.oauth-client</groupId>
|
||||
<artifactId>google-oauth-client-jetty</artifactId>
|
||||
<version>1.31.2</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>javax.mail</groupId>
|
||||
<artifactId>mail</artifactId>
|
||||
<version>1.5.0-b01</version>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.jersey</groupId>
|
||||
|
|
@ -281,21 +259,15 @@
|
|||
<version>1.19.4</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- database support - sqlite db engine (additional db for game records and stats) -->
|
||||
<groupId>org.xerial</groupId>
|
||||
<artifactId>sqlite-jdbc</artifactId>
|
||||
<version>3.32.3.2</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test dependencies -->
|
||||
<dependency>
|
||||
<groupId>org.junit.jupiter</groupId>
|
||||
<artifactId>junit-jupiter</artifactId>
|
||||
<scope>test</scope>
|
||||
<scope>runtime</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.assertj</groupId>
|
||||
<artifactId>assertj-core</artifactId>
|
||||
<scope>test</scope>
|
||||
<groupId>org.unbescape</groupId>
|
||||
<artifactId>unbescape</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
|
|
|
|||
|
|
@ -22,9 +22,7 @@ import java.io.File;
|
|||
import java.sql.SQLException;
|
||||
import java.util.List;
|
||||
|
||||
public enum AuthorizedUserRepository {
|
||||
|
||||
instance;
|
||||
public class AuthorizedUserRepository {
|
||||
|
||||
private static final String JDBC_URL = "jdbc:h2:file:./db/authorized_user.h2;AUTO_SERVER=TRUE";
|
||||
private static final String VERSION_ENTITY_NAME = "authorized_user";
|
||||
|
|
@ -32,15 +30,20 @@ public enum AuthorizedUserRepository {
|
|||
private static final long DB_VERSION = 2;
|
||||
private static final RandomNumberGenerator rng = new SecureRandomNumberGenerator();
|
||||
|
||||
private static final AuthorizedUserRepository instance;
|
||||
static {
|
||||
instance = new AuthorizedUserRepository(JDBC_URL);
|
||||
}
|
||||
|
||||
private Dao<AuthorizedUser, Object> dao;
|
||||
|
||||
AuthorizedUserRepository() {
|
||||
public AuthorizedUserRepository(String connectionString) {
|
||||
File file = new File("db");
|
||||
if (!file.exists()) {
|
||||
file.mkdirs();
|
||||
}
|
||||
try {
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(JDBC_URL);
|
||||
ConnectionSource connectionSource = new JdbcConnectionSource(connectionString);
|
||||
TableUtils.createTableIfNotExists(connectionSource, AuthorizedUser.class);
|
||||
dao = DaoManager.createDao(connectionSource, AuthorizedUser.class);
|
||||
} catch (SQLException ex) {
|
||||
|
|
@ -48,6 +51,10 @@ public enum AuthorizedUserRepository {
|
|||
}
|
||||
}
|
||||
|
||||
public static AuthorizedUserRepository getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void add(final String userName, final String password, final String email) {
|
||||
try {
|
||||
Hash hash = new SimpleHash(Sha256Hash.ALGORITHM_NAME, password, rng.nextBytes(), 1024);
|
||||
|
|
|
|||
|
|
@ -128,7 +128,7 @@ public class ChatManagerImpl implements ChatManager {
|
|||
Matcher matchPattern = cardNamePattern.matcher(message);
|
||||
while (matchPattern.find()) {
|
||||
String cardName = matchPattern.group(1);
|
||||
CardInfo cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(cardName, true);
|
||||
CardInfo cardInfo = CardRepository.instance.findPreferredCoreExpansionCard(cardName, true);
|
||||
if (cardInfo != null) {
|
||||
String colour = "silver";
|
||||
if (cardInfo.getCard().getColor(null).isMulticolored()) {
|
||||
|
|
@ -270,7 +270,7 @@ public class ChatManagerImpl implements ChatManager {
|
|||
Matcher matchPattern = getCardTextPattern.matcher(message.toLowerCase(Locale.ENGLISH));
|
||||
if (matchPattern.find()) {
|
||||
String cardName = matchPattern.group(1);
|
||||
CardInfo cardInfo = CardRepository.instance.findPreferedCoreExpansionCard(cardName, true);
|
||||
CardInfo cardInfo = CardRepository.instance.findPreferredCoreExpansionCard(cardName, true);
|
||||
if (cardInfo != null) {
|
||||
cardInfo.getRules();
|
||||
message = "<font color=orange>" + cardInfo.getName() + "</font>: Cost:" + cardInfo.getManaCosts(CardInfo.ManaCostSide.ALL).toString() + ", Types:" + cardInfo.getTypes().toString() + ", ";
|
||||
|
|
|
|||
|
|
@ -34,8 +34,8 @@ import mage.server.util.SystemUtil;
|
|||
import mage.utils.*;
|
||||
import mage.view.*;
|
||||
import mage.view.ChatMessage.MessageColor;
|
||||
import org.apache.commons.lang3.StringEscapeUtils;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.unbescape.html.HtmlEscape;
|
||||
|
||||
import javax.management.timer.Timer;
|
||||
import java.security.SecureRandom;
|
||||
|
|
@ -83,15 +83,17 @@ public class MageServerImpl implements MageServer {
|
|||
@Override
|
||||
public boolean emailAuthToken(String sessionId, String email) throws MageException {
|
||||
if (!managerFactory.configSettings().isAuthenticationActivated()) {
|
||||
sendErrorMessageToClient(sessionId, "Registration is disabled by the server config");
|
||||
sendErrorMessageToClient(sessionId, Session.REGISTRATION_DISABLED_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByEmail(email);
|
||||
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByEmail(email);
|
||||
if (authorizedUser == null) {
|
||||
sendErrorMessageToClient(sessionId, "No user was found with the email address " + email);
|
||||
logger.info("Auth token is requested for " + email + " but there's no such user in DB");
|
||||
return false;
|
||||
}
|
||||
|
||||
String authToken = generateAuthToken();
|
||||
activeAuthTokens.put(email, authToken);
|
||||
String subject = "XMage Password Reset Auth Token";
|
||||
|
|
@ -113,23 +115,31 @@ public class MageServerImpl implements MageServer {
|
|||
@Override
|
||||
public boolean resetPassword(String sessionId, String email, String authToken, String password) throws MageException {
|
||||
if (!managerFactory.configSettings().isAuthenticationActivated()) {
|
||||
sendErrorMessageToClient(sessionId, "Registration is disabled by the server config");
|
||||
sendErrorMessageToClient(sessionId, Session.REGISTRATION_DISABLED_MESSAGE);
|
||||
return false;
|
||||
}
|
||||
|
||||
// multi-step reset:
|
||||
// - send auth token
|
||||
// - check auth token to confirm reset
|
||||
|
||||
String storedAuthToken = activeAuthTokens.get(email);
|
||||
if (storedAuthToken == null || !storedAuthToken.equals(authToken)) {
|
||||
sendErrorMessageToClient(sessionId, "Invalid auth token");
|
||||
logger.info("Invalid auth token " + authToken + " is sent for " + email);
|
||||
return false;
|
||||
}
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByEmail(email);
|
||||
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByEmail(email);
|
||||
if (authorizedUser == null) {
|
||||
sendErrorMessageToClient(sessionId, "The user is no longer in the DB");
|
||||
sendErrorMessageToClient(sessionId, "User with that email doesn't exists");
|
||||
logger.info("Auth token is valid, but the user with email address " + email + " is no longer in the DB");
|
||||
return false;
|
||||
}
|
||||
AuthorizedUserRepository.instance.remove(authorizedUser.getName());
|
||||
AuthorizedUserRepository.instance.add(authorizedUser.getName(), password, email);
|
||||
|
||||
// recreate user with new password
|
||||
AuthorizedUserRepository.getInstance().remove(authorizedUser.getName());
|
||||
AuthorizedUserRepository.getInstance().add(authorizedUser.getName(), password, email);
|
||||
activeAuthTokens.remove(email);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -479,7 +489,7 @@ public class MageServerImpl implements MageServer {
|
|||
public void sendChatMessage(final UUID chatId, final String userName, final String message) throws MageException {
|
||||
try {
|
||||
callExecutor.execute(
|
||||
() -> managerFactory.chatManager().broadcast(chatId, userName, StringEscapeUtils.escapeHtml4(message), MessageColor.BLUE, true, null, ChatMessage.MessageType.TALK, null)
|
||||
() -> managerFactory.chatManager().broadcast(chatId, userName, HtmlEscape.escapeHtml4(message), MessageColor.BLUE, true, null, ChatMessage.MessageType.TALK, null)
|
||||
);
|
||||
} catch (Exception ex) {
|
||||
handleException(ex);
|
||||
|
|
@ -1042,7 +1052,7 @@ public class MageServerImpl implements MageServer {
|
|||
@Override
|
||||
public void setActivation(final String sessionId, final String userName, boolean active) throws MageException {
|
||||
execute("setActivation", sessionId, () -> {
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByName(userName);
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByName(userName);
|
||||
Optional<User> u = managerFactory.userManager().getUserByName(userName);
|
||||
if (u.isPresent()) {
|
||||
User user = u.get();
|
||||
|
|
|
|||
|
|
@ -66,7 +66,15 @@ public final class Main {
|
|||
|
||||
public static final PluginClassLoader classLoader = new PluginClassLoader();
|
||||
private static TransporterServer server;
|
||||
|
||||
// special test mode:
|
||||
// - fast game buttons;
|
||||
// - cheat commands;
|
||||
// - no deck validation;
|
||||
// - simplified registration and login (no password check);
|
||||
// - debug main menu for GUI and rendering testing;
|
||||
private static boolean testMode;
|
||||
|
||||
private static boolean fastDbMode;
|
||||
|
||||
/**
|
||||
|
|
@ -98,7 +106,7 @@ public final class Main {
|
|||
|
||||
if (config.isAuthenticationActivated()) {
|
||||
logger.info("Check authorized user DB version ...");
|
||||
if (!AuthorizedUserRepository.instance.checkAlterAndMigrateAuthorizedUser()) {
|
||||
if (!AuthorizedUserRepository.getInstance().checkAlterAndMigrateAuthorizedUser()) {
|
||||
logger.fatal("Failed to start server.");
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@ public class Session {
|
|||
private static final Pattern alphabetsPattern = Pattern.compile("[a-zA-Z]");
|
||||
private static final Pattern digitsPattern = Pattern.compile("[0-9]");
|
||||
|
||||
public static final String REGISTRATION_DISABLED_MESSAGE = "Registration has been disabled on the server. You can use any name and empty password to login.";
|
||||
|
||||
private final ManagerFactory managerFactory;
|
||||
private final String sessionId;
|
||||
private UUID userId;
|
||||
|
|
@ -60,30 +62,36 @@ public class Session {
|
|||
|
||||
public String registerUser(String userName, String password, String email) throws MageException {
|
||||
if (!managerFactory.configSettings().isAuthenticationActivated()) {
|
||||
String returnMessage = "Registration is disabled by the server config";
|
||||
String returnMessage = REGISTRATION_DISABLED_MESSAGE;
|
||||
sendErrorMessageToClient(returnMessage);
|
||||
return returnMessage;
|
||||
}
|
||||
synchronized (AuthorizedUserRepository.instance) {
|
||||
synchronized (AuthorizedUserRepository.getInstance()) {
|
||||
// name
|
||||
String returnMessage = validateUserName(userName);
|
||||
if (returnMessage != null) {
|
||||
sendErrorMessageToClient(returnMessage);
|
||||
return returnMessage;
|
||||
}
|
||||
|
||||
// auto-generated password
|
||||
RandomString randomString = new RandomString(10);
|
||||
password = randomString.nextString();
|
||||
returnMessage = validatePassword(password, userName);
|
||||
if (returnMessage != null) {
|
||||
sendErrorMessageToClient(returnMessage);
|
||||
sendErrorMessageToClient("Auto-generated password fail, try again: " + returnMessage);
|
||||
return returnMessage;
|
||||
}
|
||||
|
||||
// email
|
||||
returnMessage = validateEmail(email);
|
||||
if (returnMessage != null) {
|
||||
sendErrorMessageToClient(returnMessage);
|
||||
return returnMessage;
|
||||
}
|
||||
AuthorizedUserRepository.instance.add(userName, password, email);
|
||||
|
||||
// create
|
||||
AuthorizedUserRepository.getInstance().add(userName, password, email);
|
||||
String text = "You are successfully registered as " + userName + '.';
|
||||
text += " Your initial, generated password is: " + password;
|
||||
|
||||
|
|
@ -95,15 +103,15 @@ public class Session {
|
|||
success = managerFactory.mailgunClient().sendMessage(email, subject, text);
|
||||
}
|
||||
if (success) {
|
||||
String ok = "Sent a registration confirmation / initial password email to " + email + " for " + userName;
|
||||
String ok = "Email with initial password sent to " + email + " for a user " + userName;
|
||||
logger.info(ok);
|
||||
sendInfoMessageToClient(ok);
|
||||
} else if (Main.isTestMode()) {
|
||||
String ok = "Server is in test mode. Your account is registered with a password of " + password + " for " + userName;
|
||||
String ok = "Email sending failed. Server is in test mode. Your account registered with a password " + password + " for a user " + userName;
|
||||
logger.info(ok);
|
||||
sendInfoMessageToClient(ok);
|
||||
} else {
|
||||
String err = "Failed sending a registration confirmation / initial password email to " + email + " for " + userName;
|
||||
String err = "Email sending failed. Try use another email address or service. Or reset password by email " + email + " for a user " + userName;
|
||||
logger.error(err);
|
||||
sendErrorMessageToClient(err);
|
||||
return err;
|
||||
|
|
@ -113,9 +121,13 @@ public class Session {
|
|||
}
|
||||
|
||||
private String validateUserName(String userName) {
|
||||
// return error message or null on good name
|
||||
|
||||
if (userName.equals("Admin")) {
|
||||
// virtual user for admin console
|
||||
return "User name Admin already in use";
|
||||
}
|
||||
|
||||
ConfigSettings config = managerFactory.configSettings();
|
||||
if (userName.length() < config.getMinUserNameLength()) {
|
||||
return "User name may not be shorter than " + config.getMinUserNameLength() + " characters";
|
||||
|
|
@ -123,15 +135,19 @@ public class Session {
|
|||
if (userName.length() > config.getMaxUserNameLength()) {
|
||||
return "User name may not be longer than " + config.getMaxUserNameLength() + " characters";
|
||||
}
|
||||
|
||||
Pattern invalidUserNamePattern = Pattern.compile(managerFactory.configSettings().getInvalidUserNamePattern(), Pattern.CASE_INSENSITIVE);
|
||||
Matcher m = invalidUserNamePattern.matcher(userName);
|
||||
if (m.find()) {
|
||||
return "User name '" + userName + "' includes not allowed characters: use a-z, A-Z and 0-9";
|
||||
}
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByName(userName);
|
||||
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByName(userName);
|
||||
if (authorizedUser != null) {
|
||||
return "User name '" + userName + "' already in use";
|
||||
}
|
||||
|
||||
// all fine
|
||||
return null;
|
||||
}
|
||||
|
||||
|
|
@ -159,7 +175,7 @@ public class Session {
|
|||
if (email == null || email.isEmpty()) {
|
||||
return "Email address cannot be blank";
|
||||
}
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.instance.getByEmail(email);
|
||||
AuthorizedUser authorizedUser = AuthorizedUserRepository.getInstance().getByEmail(email);
|
||||
if (authorizedUser != null) {
|
||||
return "Email address '" + email + "' is associated with another user";
|
||||
}
|
||||
|
|
@ -182,8 +198,8 @@ public class Session {
|
|||
this.isAdmin = false;
|
||||
AuthorizedUser authorizedUser = null;
|
||||
if (managerFactory.configSettings().isAuthenticationActivated()) {
|
||||
authorizedUser = AuthorizedUserRepository.instance.getByName(userName);
|
||||
String errorMsg = "Wrong username or password. In case you haven't, please register your account first.";
|
||||
authorizedUser = AuthorizedUserRepository.getInstance().getByName(userName);
|
||||
String errorMsg = "Wrong username or password. You must register your account first.";
|
||||
if (authorizedUser == null) {
|
||||
return errorMsg;
|
||||
}
|
||||
|
|
@ -193,16 +209,16 @@ public class Session {
|
|||
}
|
||||
|
||||
if (!authorizedUser.active) {
|
||||
return "Your profile is deactivated, you can't sign on.";
|
||||
return "Your profile has been deactivated by admin.";
|
||||
}
|
||||
if (authorizedUser.lockedUntil != null) {
|
||||
if (authorizedUser.lockedUntil.compareTo(Calendar.getInstance().getTime()) > 0) {
|
||||
return "Your profile is deactivated until " + SystemUtil.dateFormat.format(authorizedUser.lockedUntil);
|
||||
return "Your profile has need deactivated by admin until " + SystemUtil.dateFormat.format(authorizedUser.lockedUntil);
|
||||
} else {
|
||||
// unlock on timeout end
|
||||
managerFactory.userManager().createUser(userName, host, authorizedUser).ifPresent(user
|
||||
-> user.setLockedUntil(null)
|
||||
);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -817,7 +817,7 @@ public class User {
|
|||
authorizedUser.chatLockedUntil = this.chatLockedUntil;
|
||||
authorizedUser.lockedUntil = this.lockedUntil;
|
||||
authorizedUser.active = this.active;
|
||||
AuthorizedUserRepository.instance.update(authorizedUser);
|
||||
AuthorizedUserRepository.getInstance().update(authorizedUser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -810,7 +810,7 @@ public class GameController implements GameCallback {
|
|||
}
|
||||
|
||||
private synchronized void chooseAbility(UUID playerId, final String objectName, final List<? extends Ability> choices, String message) throws MageException {
|
||||
perform(playerId, playerId1 -> getGameSession(playerId1).chooseAbility(new AbilityPickerView(objectName, choices, message)));
|
||||
perform(playerId, playerId1 -> getGameSession(playerId1).chooseAbility(new AbilityPickerView(getGameView(playerId), objectName, choices, message)));
|
||||
}
|
||||
|
||||
private synchronized void choosePile(UUID playerId, final String message, final List<? extends Card> pile1, final List<? extends Card> pile2) throws MageException {
|
||||
|
|
@ -818,7 +818,7 @@ public class GameController implements GameCallback {
|
|||
}
|
||||
|
||||
private synchronized void chooseMode(UUID playerId, final Map<UUID, String> modes, final String message) throws MageException {
|
||||
perform(playerId, playerId1 -> getGameSession(playerId1).chooseAbility(new AbilityPickerView(modes, message)));
|
||||
perform(playerId, playerId1 -> getGameSession(playerId1).chooseAbility(new AbilityPickerView(getGameView(playerId), modes, message)));
|
||||
}
|
||||
|
||||
private synchronized void chooseChoice(UUID playerId, final Choice choice) throws MageException {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
|||
|
||||
public void ask(final String question, final Map<String, Serializable> options) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_ASK, game.getId(), new GameClientMessage(getGameView(), question, options)))
|
||||
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_ASK, game.getId(), new GameClientMessage(getGameView(), options, question)))
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -55,7 +55,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
|||
public void target(final String question, final CardsView cardView, final Set<UUID> targets, final boolean required, final Map<String, Serializable> options) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user -> {
|
||||
user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_TARGET, game.getId(), new GameClientMessage(getGameView(), question, cardView, targets, required, options)));
|
||||
user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_TARGET, game.getId(), new GameClientMessage(getGameView(), options, question, cardView, targets, required)));
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -63,7 +63,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
|||
|
||||
public void select(final String message, final Map<String, Serializable> options) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_SELECT, game.getId(), new GameClientMessage(getGameView(), message, options))));
|
||||
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_SELECT, game.getId(), new GameClientMessage(getGameView(), options, message))));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -78,7 +78,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
|||
public void choosePile(final String message, final CardsView pile1, final CardsView pile2) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_CHOOSE_PILE, game.getId(), new GameClientMessage(message, pile1, pile2))));
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_CHOOSE_PILE, game.getId(), new GameClientMessage(getGameView(), null, message, pile1, pile2))));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
|||
public void chooseChoice(final Choice choice) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_CHOOSE_CHOICE, game.getId(), new GameClientMessage(choice))));
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_CHOOSE_CHOICE, game.getId(), new GameClientMessage(getGameView(), null, choice))));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -94,14 +94,14 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
|||
public void playMana(final String message, final Map<String, Serializable> options) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_PLAY_MANA, game.getId(), new GameClientMessage(getGameView(), message, options))));
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_PLAY_MANA, game.getId(), new GameClientMessage(getGameView(), options, message))));
|
||||
}
|
||||
}
|
||||
|
||||
public void playXMana(final String message) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_PLAY_XMANA, game.getId(), new GameClientMessage(getGameView(), message))));
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_PLAY_XMANA, game.getId(), new GameClientMessage(getGameView(), null, message))));
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -109,7 +109,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
|||
public void getAmount(final String message, final int min, final int max) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user -> {
|
||||
user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_GET_AMOUNT, game.getId(), new GameClientMessage(message, min, max)));
|
||||
user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_GET_AMOUNT, game.getId(), new GameClientMessage(getGameView(), null, message, min, max)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -117,7 +117,7 @@ public class GameSessionPlayer extends GameSessionWatcher {
|
|||
public void getMultiAmount(final List<String> messages, final int min, final int max, final Map<String, Serializable> options) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_GET_MULTI_AMOUNT, game.getId(), new GameClientMessage(messages, min, max, options))));
|
||||
-> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_GET_MULTI_AMOUNT, game.getId(), new GameClientMessage(getGameView(), options, messages, min, max))));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -58,14 +58,14 @@ public class GameSessionWatcher {
|
|||
|
||||
public void inform(final String message) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INFORM, game.getId(), new GameClientMessage(getGameView(), message))));
|
||||
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INFORM, game.getId(), new GameClientMessage(getGameView(), null, message))));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void informPersonal(final String message) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INFORM_PERSONAL, game.getId(), new GameClientMessage(getGameView(), message))));
|
||||
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_INFORM_PERSONAL, game.getId(), new GameClientMessage(getGameView(), null, message))));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -74,7 +74,7 @@ public class GameSessionWatcher {
|
|||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user -> {
|
||||
user.removeGameWatchInfo(game.getId());
|
||||
user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_OVER, game.getId(), message));
|
||||
user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_OVER, game.getId(), new GameClientMessage(getGameView(), null, message)));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -89,7 +89,6 @@ public class GameSessionWatcher {
|
|||
public void gameError(final String message) {
|
||||
if (!killed) {
|
||||
userManager.getUser(userId).ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.GAME_ERROR, game.getId(), message)));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public class ConfigFactoryTest {
|
|||
@DisplayName("should fail if config is malformed")
|
||||
void failOnMalformed() {
|
||||
assertThatExceptionOfType(ConfigurationException.class)
|
||||
.isThrownBy(() -> ConfigFactory.loadFromFile(Paths.get("src", "test", "resources", "config_error.xml").toString()));
|
||||
.isThrownBy(() -> ConfigFactory.loadFromFile(Paths.get("src", "test", "data", "config_error.xml").toString()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -21,17 +21,6 @@
|
|||
<artifactId>mage</artifactId>
|
||||
<version>${mage-version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>log4j</groupId>
|
||||
<artifactId>log4j</artifactId>
|
||||
<type>jar</type>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>junit</groupId>
|
||||
<artifactId>junit</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public final class AbandonThePost extends CardImpl {
|
|||
this.getSpellAbility().addTarget(new TargetCreaturePermanent(0, 2));
|
||||
|
||||
// Flashback {3}{R}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl<>("{3}{R}"), TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{3}{R}")));
|
||||
}
|
||||
|
||||
private AbandonThePost(final AbandonThePost card) {
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ class AccursedWitchReturnTransformedEffect extends OneShotEffect {
|
|||
|
||||
AccursedWitchReturnTransformedEffect() {
|
||||
super(Outcome.PutCardInPlay);
|
||||
this.staticText = "Put {this} from your graveyard onto the battlefield transformed under your control attached to target opponent";
|
||||
this.staticText = "return it to the battlefield transformed under your control attached to target opponent";
|
||||
}
|
||||
|
||||
private AccursedWitchReturnTransformedEffect(final AccursedWitchReturnTransformedEffect effect) {
|
||||
|
|
@ -78,15 +78,17 @@ class AccursedWitchReturnTransformedEffect extends OneShotEffect {
|
|||
if (controller == null || !(game.getState().getZone(source.getSourceId()) == Zone.GRAVEYARD) || attachTo == null) {
|
||||
return false;
|
||||
}
|
||||
game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE);
|
||||
UUID secondFaceId = game.getCard(source.getSourceId()).getSecondCardFace().getId();
|
||||
game.getState().setValue("attachTo:" + secondFaceId, attachTo.getId());
|
||||
//note: should check for null after game.getCard
|
||||
|
||||
Card card = game.getCard(source.getSourceId());
|
||||
if (card != null) {
|
||||
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
|
||||
attachTo.addAttachment(card.getId(), source, game);
|
||||
}
|
||||
if (card == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
game.getState().setValue(TransformAbility.VALUE_KEY_ENTER_TRANSFORMED + source.getSourceId(), Boolean.TRUE);
|
||||
UUID secondFaceId = card.getSecondCardFace().getId();
|
||||
game.getState().setValue("attachTo:" + secondFaceId, attachTo.getId());
|
||||
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game)) {
|
||||
attachTo.addAttachment(card.getId(), source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public final class AcornHarvest extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new CreateTokenEffect(new SquirrelToken(), 2));
|
||||
|
||||
// Flashback-{1}{G} - Pay 3 life.
|
||||
FlashbackAbility ability = new FlashbackAbility(new ManaCostsImpl("{1}{G}"), TimingRule.SORCERY);
|
||||
FlashbackAbility ability = new FlashbackAbility(this, new ManaCostsImpl("{1}{G}"));
|
||||
ability.addCost(new PayLifeCost(3));
|
||||
this.addAbility(ability);
|
||||
|
||||
|
|
|
|||
79
Mage.Sets/src/mage/cards/a/AdelineResplendentCathar.java
Normal file
79
Mage.Sets/src/mage/cards/a/AdelineResplendentCathar.java
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksWithCreaturesTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.dynamicvalue.common.CreaturesYouControlCount;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.SetPowerSourceEffect;
|
||||
import mage.abilities.hint.common.CreaturesYouControlHint;
|
||||
import mage.constants.*;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.token.HumanToken;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author weirddan455
|
||||
*/
|
||||
public final class AdelineResplendentCathar extends CardImpl {
|
||||
|
||||
public AdelineResplendentCathar(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}{W}");
|
||||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.KNIGHT);
|
||||
this.power = new MageInt(0);
|
||||
this.toughness = new MageInt(4);
|
||||
|
||||
// Vigilance
|
||||
this.addAbility(VigilanceAbility.getInstance());
|
||||
|
||||
// Adeline, Resplendent Cathar's power is equal to the number of creatures you control.
|
||||
this.addAbility(new SimpleStaticAbility(Zone.ALL, new SetPowerSourceEffect(
|
||||
CreaturesYouControlCount.instance, Duration.EndOfGame)).addHint(CreaturesYouControlHint.instance)
|
||||
);
|
||||
|
||||
// Whenever you attack, for each opponent, create a 1/1 white Human creature token that's tapped and attacking that player or a planeswalker they control.
|
||||
this.addAbility(new AttacksWithCreaturesTriggeredAbility(new AdelineResplendentCatharEffect(), 1));
|
||||
}
|
||||
|
||||
private AdelineResplendentCathar(final AdelineResplendentCathar card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdelineResplendentCathar copy() {
|
||||
return new AdelineResplendentCathar(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AdelineResplendentCatharEffect extends OneShotEffect {
|
||||
|
||||
public AdelineResplendentCatharEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "for each opponent, create a 1/1 white Human creature token that's tapped and attacking that player or a planeswalker they control";
|
||||
}
|
||||
|
||||
private AdelineResplendentCatharEffect(final AdelineResplendentCatharEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AdelineResplendentCatharEffect copy() {
|
||||
return new AdelineResplendentCatharEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
for (UUID opponentId : game.getOpponents(source.getControllerId())) {
|
||||
new HumanToken().putOntoBattlefield(1, game, source, source.getControllerId(), true, true, opponentId);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
|
|||
|
|
@ -14,8 +14,8 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.*;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.common.FilterEquipmentPermanent;
|
||||
import mage.filter.predicate.ObjectPlayer;
|
||||
import mage.filter.predicate.ObjectPlayerPredicate;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.DefenderAttackedEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
|
|
@ -103,11 +103,11 @@ class AkiriFearlessVoyagerTriggeredAbility extends TriggeredAbilityImpl {
|
|||
|
||||
class AkiriFearlessVoyagerEffect extends OneShotEffect {
|
||||
|
||||
private static enum AkiriFearlessVoyagerPredicate implements ObjectPlayerPredicate<ObjectPlayer<Permanent>> {
|
||||
private static enum AkiriFearlessVoyagerPredicate implements ObjectSourcePlayerPredicate<Permanent> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectPlayer<Permanent> input, Game game) {
|
||||
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
|
||||
return game.getPermanent(input.getObject().getAttachedTo()) != null
|
||||
&& game.getControllerId(input.getObject().getAttachedTo()).equals(input.getPlayerId());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import com.google.common.collect.Sets;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Abilities;
|
||||
import mage.abilities.Ability;
|
||||
|
|
@ -17,6 +16,8 @@ import mage.game.Game;
|
|||
import mage.game.permanent.Permanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
|
|
@ -66,7 +67,7 @@ public final class AkromaVisionOfIxidor extends CardImpl {
|
|||
|
||||
class AkromaVisionOfIxidorEffect extends OneShotEffect {
|
||||
|
||||
private static final Set<Class<? extends Ability>> classes = Sets.newHashSet(
|
||||
private static final Set<Class<? extends Ability>> classes = new HashSet<>(Arrays.asList(
|
||||
FlyingAbility.class,
|
||||
FirstStrikeAbility.class,
|
||||
DoubleStrikeAbility.class,
|
||||
|
|
@ -81,7 +82,7 @@ class AkromaVisionOfIxidorEffect extends OneShotEffect {
|
|||
TrampleAbility.class,
|
||||
VigilanceAbility.class,
|
||||
PartnerAbility.class
|
||||
);
|
||||
));
|
||||
|
||||
AkromaVisionOfIxidorEffect() {
|
||||
super(Outcome.Benefit);
|
||||
|
|
|
|||
|
|
@ -1,38 +1,39 @@
|
|||
|
||||
package mage.cards.a;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.AttacksTriggeredAbility;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.DoIfCostPaid;
|
||||
import mage.abilities.effects.common.ReturnFromGraveyardToBattlefieldTargetEffect;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.ComparisonType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.SuperType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.filter.predicate.mageobject.PowerPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInYourGraveyard;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class AleshaWhoSmilesAtDeath extends CardImpl {
|
||||
|
||||
private static final FilterCreatureCard filter = new FilterCreatureCard("creature card with power 2 or less");
|
||||
private static final FilterCard filter
|
||||
= new FilterCreatureCard("creature card with power 2 or less from your graveyard");
|
||||
|
||||
static {
|
||||
filter.add(new PowerPredicate(ComparisonType.FEWER_THAN, 3));
|
||||
}
|
||||
|
||||
public AleshaWhoSmilesAtDeath(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{R}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{R}");
|
||||
addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.WARRIOR);
|
||||
|
|
@ -43,7 +44,10 @@ public final class AleshaWhoSmilesAtDeath extends CardImpl {
|
|||
this.addAbility(FirstStrikeAbility.getInstance());
|
||||
|
||||
// Whenever Alesha, Who Smiles at Death attacks, you may pay {W/B}{W/B}. If you do, return target creature card with power 2 or less from your graveyard to the battlefield tapped and attacking.
|
||||
Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid(new AleshaWhoSmilesAtDeathEffect(), new ManaCostsImpl("{W/B}{W/B}")), false);
|
||||
Ability ability = new AttacksTriggeredAbility(new DoIfCostPaid(
|
||||
new ReturnFromGraveyardToBattlefieldTargetEffect(true, true),
|
||||
new ManaCostsImpl<>("{W/B}{W/B}")
|
||||
), false);
|
||||
ability.addTarget(new TargetCardInYourGraveyard(filter));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
|
@ -57,38 +61,3 @@ public final class AleshaWhoSmilesAtDeath extends CardImpl {
|
|||
return new AleshaWhoSmilesAtDeath(this);
|
||||
}
|
||||
}
|
||||
|
||||
class AleshaWhoSmilesAtDeathEffect extends OneShotEffect {
|
||||
|
||||
public AleshaWhoSmilesAtDeathEffect() {
|
||||
super(Outcome.PutCreatureInPlay);
|
||||
this.staticText = "return target creature card with power 2 or less from your graveyard to the battlefield tapped and attacking";
|
||||
}
|
||||
|
||||
public AleshaWhoSmilesAtDeathEffect(final AleshaWhoSmilesAtDeathEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
|
||||
if (controller != null) {
|
||||
Card card = game.getCard(getTargetPointer().getFirst(game, source));
|
||||
if (card != null) {
|
||||
if (controller.moveCards(card, Zone.BATTLEFIELD, source, game, true, false, false, null)) {
|
||||
game.getCombat().addAttackingCreature(card.getId(), game);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AleshaWhoSmilesAtDeathEffect copy() {
|
||||
return new AleshaWhoSmilesAtDeathEffect(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
63
Mage.Sets/src/mage/cards/a/AmbitiousFarmhand.java
Normal file
63
Mage.Sets/src/mage/cards/a/AmbitiousFarmhand.java
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.ActivateIfConditionActivatedAbility;
|
||||
import mage.abilities.common.EntersBattlefieldTriggeredAbility;
|
||||
import mage.abilities.condition.common.CovenCondition;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
|
||||
import mage.abilities.hint.common.CovenHint;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AmbitiousFarmhand extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCard("basic Plains card");
|
||||
|
||||
static {
|
||||
filter.add(SuperType.BASIC.getPredicate());
|
||||
filter.add(SubType.PLAINS.getPredicate());
|
||||
}
|
||||
|
||||
public AmbitiousFarmhand(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{1}{W}");
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.PEASANT);
|
||||
this.power = new MageInt(1);
|
||||
this.toughness = new MageInt(1);
|
||||
this.transformable = true;
|
||||
this.secondSideCardClazz = mage.cards.s.SeasonedCathar.class;
|
||||
|
||||
// When Ambitious Farmhand enters the battlefield, you may search your library for a basic Plains card, reveal it, put it into your hand, then shuffle.
|
||||
this.addAbility(new EntersBattlefieldTriggeredAbility(
|
||||
new SearchLibraryPutInHandEffect(new TargetCardInLibrary(filter)), true
|
||||
));
|
||||
|
||||
// Coven—{1}{W}{W}: Transform Ambitious Farmhand. Activate only if you control three or more creatures with different powers.
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new ActivateIfConditionActivatedAbility(
|
||||
Zone.BATTLEFIELD, new TransformSourceEffect(true),
|
||||
new ManaCostsImpl<>("{1}{W}{W}"), CovenCondition.instance
|
||||
).setAbilityWord(AbilityWord.COVEN).addHint(CovenHint.instance));
|
||||
}
|
||||
|
||||
private AmbitiousFarmhand(final AmbitiousFarmhand card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AmbitiousFarmhand copy() {
|
||||
return new AmbitiousFarmhand(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,7 @@ public final class AncestralTribute extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new GainLifeEffect((new CardsInControllerGraveyardCount(new FilterCard(), 2))));
|
||||
|
||||
// Flashback {9}{W}{W}{W}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{9}{W}{W}{W}"), TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{9}{W}{W}{W}")));
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ public final class AncientGrudge extends CardImpl {
|
|||
this.getSpellAbility().addTarget(new TargetArtifactPermanent());
|
||||
|
||||
// Flashback {G}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{G}"), TimingRule.INSTANT));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{G}")));
|
||||
}
|
||||
|
||||
private AncientGrudge(final AncientGrudge card) {
|
||||
|
|
|
|||
|
|
@ -12,13 +12,12 @@ import mage.cards.CardSetInfo;
|
|||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.target.common.TargetCardInGraveyardOrBattlefield;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.filter.common.FilterCreaturePermanent;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
|
|
@ -27,10 +26,13 @@ public final class AngelOfSerenity extends CardImpl {
|
|||
|
||||
private static final String rule = "you may exile up to three other target creatures " +
|
||||
"from the battlefield and/or creature cards from graveyards.";
|
||||
private static final FilterPermanent filter = new FilterCreaturePermanent("other target creatures");
|
||||
|
||||
|
||||
private static final FilterCreatureCard filterCreatureCard = new FilterCreatureCard("creature card in a graveyard");
|
||||
|
||||
private static final FilterCreaturePermanent filterCreaturePermanent = new FilterCreaturePermanent("other target creature");
|
||||
|
||||
static {
|
||||
filter.add(AnotherPredicate.instance);
|
||||
filterCreaturePermanent.add(AnotherPredicate.instance);
|
||||
}
|
||||
|
||||
public AngelOfSerenity(UUID ownerId, CardSetInfo setInfo) {
|
||||
|
|
@ -48,7 +50,7 @@ public final class AngelOfSerenity extends CardImpl {
|
|||
new ExileTargetForSourceEffect().setText(rule), true
|
||||
);
|
||||
ability.addTarget(new TargetCardInGraveyardOrBattlefield(
|
||||
0, 3, StaticFilters.FILTER_CARD_CREATURE, filter
|
||||
0, 3, filterCreatureCard, filterCreaturePermanent
|
||||
));
|
||||
this.addAbility(ability);
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ public final class AngelfireIgnition extends CardImpl {
|
|||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
|
||||
// Flashback {2}{R}{W}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl<>("{2}{R}{W}"), TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{2}{R}{W}")));
|
||||
}
|
||||
|
||||
private AngelfireIgnition(final AngelfireIgnition card) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public final class ArcaneInfusion extends CardImpl {
|
|||
"Put the rest on the bottom of your library in a random order."));
|
||||
|
||||
// Flashback {3}{U}{R}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl<>("{3}{U}{R}"), TimingRule.INSTANT));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{3}{U}{R}")));
|
||||
}
|
||||
|
||||
private ArcaneInfusion(final ArcaneInfusion card) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public final class ArchmagesCharm extends CardImpl {
|
|||
this.getSpellAbility().addMode(mode);
|
||||
|
||||
// • Gain control of target nonland permanent with converted mana cost 1 or less.
|
||||
mode = new Mode(new GainControlTargetEffect(Duration.Custom, true));
|
||||
mode = new Mode(new GainControlTargetEffect(Duration.EndOfGame, true));
|
||||
mode.addTarget(new TargetPermanent(filter));
|
||||
this.getSpellAbility().addMode(mode);
|
||||
}
|
||||
|
|
|
|||
119
Mage.Sets/src/mage/cards/a/ArlinnTheMoonsFury.java
Normal file
119
Mage.Sets/src/mage/cards/a/ArlinnTheMoonsFury.java
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.LoyaltyAbility;
|
||||
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
|
||||
import mage.abilities.effects.ContinuousEffectImpl;
|
||||
import mage.abilities.effects.mana.BasicManaEffect;
|
||||
import mage.abilities.keyword.HasteAbility;
|
||||
import mage.abilities.keyword.IndestructibleAbility;
|
||||
import mage.abilities.keyword.NightboundAbility;
|
||||
import mage.abilities.keyword.TrampleAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class ArlinnTheMoonsFury extends CardImpl {
|
||||
|
||||
public ArlinnTheMoonsFury(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "");
|
||||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.ARLINN);
|
||||
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
|
||||
this.color.setRed(true);
|
||||
this.color.setGreen(true);
|
||||
this.transformable = true;
|
||||
this.nightCard = true;
|
||||
|
||||
// Nightbound
|
||||
this.addAbility(new NightboundAbility());
|
||||
|
||||
// +2: Add {R}{G}.
|
||||
this.addAbility(new LoyaltyAbility(new BasicManaEffect(new Mana(
|
||||
0, 0, 0, 1, 1, 0, 0, 0
|
||||
)), 2));
|
||||
|
||||
// 0: Until end of turn, Arlinn, the Moon's Fury becomes a 5/5 Werewolf creature with trample, indestructible, and haste.
|
||||
this.addAbility(new LoyaltyAbility(new ArlinnTheMoonsFuryEffect(), 0));
|
||||
}
|
||||
|
||||
private ArlinnTheMoonsFury(final ArlinnTheMoonsFury card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArlinnTheMoonsFury copy() {
|
||||
return new ArlinnTheMoonsFury(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ArlinnTheMoonsFuryEffect extends ContinuousEffectImpl {
|
||||
|
||||
ArlinnTheMoonsFuryEffect() {
|
||||
super(Duration.EndOfTurn, Outcome.Benefit);
|
||||
staticText = "until end of turn, {this} becomes a 5/5 Werewolf creature with trample, indestructible, and haste";
|
||||
}
|
||||
|
||||
private ArlinnTheMoonsFuryEffect(final ArlinnTheMoonsFuryEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArlinnTheMoonsFuryEffect copy() {
|
||||
return new ArlinnTheMoonsFuryEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Layer layer, SubLayer sublayer, Ability source, Game game) {
|
||||
Permanent permanent = source.getSourcePermanentIfItStillExists(game);
|
||||
if (permanent == null) {
|
||||
discard();
|
||||
return false;
|
||||
}
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
permanent.removeAllCardTypes(game);
|
||||
permanent.addCardType(game, CardType.CREATURE);
|
||||
permanent.removeAllCreatureTypes(game);
|
||||
permanent.addSubType(game, SubType.WEREWOLF);
|
||||
return true;
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
permanent.addAbility(TrampleAbility.getInstance(), source.getSourceId(), game);
|
||||
permanent.addAbility(IndestructibleAbility.getInstance(), source.getSourceId(), game);
|
||||
permanent.addAbility(HasteAbility.getInstance(), source.getSourceId(), game);
|
||||
return true;
|
||||
case PTChangingEffects_7:
|
||||
if (sublayer == SubLayer.SetPT_7b) {
|
||||
permanent.getPower().setValue(5);
|
||||
permanent.getToughness().setValue(5);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasLayer(Layer layer) {
|
||||
switch (layer) {
|
||||
case TypeChangingEffects_4:
|
||||
case AbilityAddingRemovingEffects_6:
|
||||
case PTChangingEffects_7:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
106
Mage.Sets/src/mage/cards/a/ArlinnThePacksHope.java
Normal file
106
Mage.Sets/src/mage/cards/a/ArlinnThePacksHope.java
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.LoyaltyAbility;
|
||||
import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
|
||||
import mage.abilities.effects.ReplacementEffectImpl;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.continuous.CastAsThoughItHadFlashAllEffect;
|
||||
import mage.abilities.keyword.DayboundAbility;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.counters.CounterType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.common.FilterCreatureCard;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.EntersTheBattlefieldEvent;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.game.permanent.token.WolfToken;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class ArlinnThePacksHope extends CardImpl {
|
||||
|
||||
private static final FilterCard filter = new FilterCreatureCard("creature spells");
|
||||
|
||||
public ArlinnThePacksHope(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{2}{R}{G}");
|
||||
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.ARLINN);
|
||||
this.addAbility(new PlaneswalkerEntersWithLoyaltyCountersAbility(4));
|
||||
this.transformable = true;
|
||||
this.secondSideCardClazz = mage.cards.a.ArlinnTheMoonsFury.class;
|
||||
|
||||
// Daybound
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new DayboundAbility());
|
||||
|
||||
// +1: Until your next turn, you may cast creature spells as though they had flash, and each creature you control enters the battlefield with an additional +1/+1 counter on it.
|
||||
Ability ability = new LoyaltyAbility(new CastAsThoughItHadFlashAllEffect(
|
||||
Duration.UntilYourNextTurn, filter
|
||||
).setText("until your next turn, you may cast creature spells as though they had flash"), 1);
|
||||
ability.addEffect(new ArlinnThePacksHopeEffect());
|
||||
this.addAbility(ability);
|
||||
|
||||
// −3: Create two 2/2 green Wolf creature tokens.
|
||||
this.addAbility(new LoyaltyAbility(new CreateTokenEffect(new WolfToken(), 2), -3));
|
||||
}
|
||||
|
||||
private ArlinnThePacksHope(final ArlinnThePacksHope card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArlinnThePacksHope copy() {
|
||||
return new ArlinnThePacksHope(this);
|
||||
}
|
||||
}
|
||||
|
||||
class ArlinnThePacksHopeEffect extends ReplacementEffectImpl {
|
||||
|
||||
ArlinnThePacksHopeEffect() {
|
||||
super(Duration.UntilYourNextTurn, Outcome.BoostCreature);
|
||||
this.staticText = ", and each creature you control enters the battlefield with an additional +1/+1 counter on it";
|
||||
}
|
||||
|
||||
private ArlinnThePacksHopeEffect(ArlinnThePacksHopeEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checksEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean applies(GameEvent event, Ability source, Game game) {
|
||||
Permanent permanent = ((EntersTheBattlefieldEvent) event).getTarget();
|
||||
return permanent != null && permanent.isControlledBy(source.getControllerId()) && permanent.isCreature(game);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
|
||||
Permanent target = ((EntersTheBattlefieldEvent) event).getTarget();
|
||||
if (target != null) {
|
||||
target.addCounters(CounterType.P1P1.createInstance(), source.getControllerId(), source, game, event.getAppliedEffects());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ArlinnThePacksHopeEffect copy() {
|
||||
return new ArlinnThePacksHopeEffect(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -23,7 +23,7 @@ public final class ArmyOfTheDamned extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new CreateTokenEffect(new ZombieToken(), 13, true, false));
|
||||
|
||||
// Flashback {7}{B}{B}{B}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{7}{B}{B}{B}"), TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{7}{B}{B}{B}")));
|
||||
}
|
||||
|
||||
private ArmyOfTheDamned(final ArmyOfTheDamned card) {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ public final class ArtfulDodge extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new CantBeBlockedTargetEffect());
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
// Flashback {U}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{U}"), TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{U}")));
|
||||
}
|
||||
|
||||
private ArtfulDodge(final ArtfulDodge card) {
|
||||
|
|
|
|||
51
Mage.Sets/src/mage/cards/a/AshmouthDragon.java
Normal file
51
Mage.Sets/src/mage/cards/a/AshmouthDragon.java
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.SpellCastControllerTriggeredAbility;
|
||||
import mage.abilities.effects.common.DamageTargetEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.target.common.TargetAnyTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AshmouthDragon extends CardImpl {
|
||||
|
||||
public AshmouthDragon(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "");
|
||||
|
||||
this.subtype.add(SubType.DRAGON);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
this.color.setRed(true);
|
||||
this.transformable = true;
|
||||
this.nightCard = true;
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Whenever you cast an instant or sorcery spell, Ashmouth Dragon deals 2 damage to any target.
|
||||
Ability ability = new SpellCastControllerTriggeredAbility(
|
||||
new DamageTargetEffect(2), StaticFilters.FILTER_SPELL_AN_INSTANT_OR_SORCERY, false
|
||||
);
|
||||
ability.addTarget(new TargetAnyTarget());
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private AshmouthDragon(final AshmouthDragon card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AshmouthDragon copy() {
|
||||
return new AshmouthDragon(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,8 +14,8 @@ import mage.constants.Outcome;
|
|||
import mage.constants.SubType;
|
||||
import mage.filter.Filter;
|
||||
import mage.filter.FilterPermanent;
|
||||
import mage.filter.predicate.ObjectPlayer;
|
||||
import mage.filter.predicate.ObjectPlayerPredicate;
|
||||
import mage.filter.predicate.ObjectSourcePlayer;
|
||||
import mage.filter.predicate.ObjectSourcePlayerPredicate;
|
||||
import mage.filter.predicate.mageobject.AnotherPredicate;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
|
|
@ -54,19 +54,19 @@ public final class AuraGraft extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
class AttachedToPermanentPredicate implements ObjectPlayerPredicate<ObjectPlayer<Permanent>> {
|
||||
class AttachedToPermanentPredicate implements ObjectSourcePlayerPredicate<Permanent> {
|
||||
|
||||
public AttachedToPermanentPredicate() {
|
||||
super();
|
||||
}
|
||||
|
||||
public boolean apply(ObjectPlayer<Permanent> input, Game game) {
|
||||
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
|
||||
Permanent attached = input.getObject();
|
||||
return attached != null && game.getPermanent(attached.getAttachedTo()) != null;
|
||||
}
|
||||
}
|
||||
|
||||
class PermanentCanBeAttachedToPredicate implements ObjectPlayerPredicate<ObjectPlayer<Permanent>> {
|
||||
class PermanentCanBeAttachedToPredicate implements ObjectSourcePlayerPredicate<Permanent> {
|
||||
|
||||
protected Permanent aura;
|
||||
|
||||
|
|
@ -76,7 +76,7 @@ class PermanentCanBeAttachedToPredicate implements ObjectPlayerPredicate<ObjectP
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectPlayer<Permanent> input, Game game) {
|
||||
public boolean apply(ObjectSourcePlayer<Permanent> input, Game game) {
|
||||
Permanent potentialAttachment = input.getObject();
|
||||
for (TargetAddress addr : TargetAddress.walk(aura)) {
|
||||
Target target = addr.getTarget(aura);
|
||||
|
|
|
|||
35
Mage.Sets/src/mage/cards/a/AwokenDemon.java
Normal file
35
Mage.Sets/src/mage/cards/a/AwokenDemon.java
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package mage.cards.a;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class AwokenDemon extends CardImpl {
|
||||
|
||||
public AwokenDemon(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "");
|
||||
|
||||
this.subtype.add(SubType.DEMON);
|
||||
this.power = new MageInt(4);
|
||||
this.toughness = new MageInt(4);
|
||||
this.color.setBlack(true);
|
||||
this.transformable = true;
|
||||
this.nightCard = true;
|
||||
}
|
||||
|
||||
private AwokenDemon(final AwokenDemon card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AwokenDemon copy() {
|
||||
return new AwokenDemon(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -93,15 +93,7 @@ class BackdraftHellkiteEffect extends ContinuousEffectImpl {
|
|||
if (card == null) {
|
||||
return;
|
||||
}
|
||||
FlashbackAbility ability = null;
|
||||
if (card.isInstant(game)) {
|
||||
ability = new FlashbackAbility(card.getManaCost(), TimingRule.INSTANT);
|
||||
} else if (card.isSorcery(game)) {
|
||||
ability = new FlashbackAbility(card.getManaCost(), TimingRule.SORCERY);
|
||||
}
|
||||
if (ability == null) {
|
||||
return;
|
||||
}
|
||||
FlashbackAbility ability = new FlashbackAbility(card, card.getManaCost());
|
||||
ability.setSourceId(cardId);
|
||||
ability.setControllerId(card.getOwnerId());
|
||||
game.getState().addOtherAbility(card, ability);
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package mage.cards.b;
|
|||
import mage.MageInt;
|
||||
import mage.abilities.costs.mana.ManaCostsImpl;
|
||||
import mage.abilities.keyword.DisturbAbility;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
|
|
@ -26,6 +27,7 @@ public final class BaithookAngler extends CardImpl {
|
|||
this.secondSideCardClazz = mage.cards.h.HookHauntDrifter.class;
|
||||
|
||||
// Disturb {1}{U}
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new DisturbAbility(new ManaCostsImpl<>("{1}{U}")));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ public final class BarteredCow extends CardImpl {
|
|||
}
|
||||
}
|
||||
|
||||
enum BarteredCowPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<MageObject>> {
|
||||
enum BarteredCowPredicate implements ObjectSourcePlayerPredicate<MageObject> {
|
||||
instance;
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public final class BashToBits extends CardImpl {
|
|||
Target target = new TargetArtifactPermanent();
|
||||
this.getSpellAbility().addTarget(target);
|
||||
// Flashback {4}{R}{R}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{4}{R}{R}"), TimingRule.INSTANT));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{4}{R}{R}")));
|
||||
}
|
||||
|
||||
private BashToBits(final BashToBits card) {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ public final class BattleScreech extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new CreateTokenEffect(new BirdToken(), 2));
|
||||
|
||||
// Flashback-Tap three untapped white creatures you control.
|
||||
this.addAbility(new FlashbackAbility(new TapTargetCost(new TargetControlledCreaturePermanent(3,3, filter, true)), TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new TapTargetCost(new TargetControlledCreaturePermanent(3,3, filter, true))));
|
||||
}
|
||||
|
||||
private BattleScreech(final BattleScreech card) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public final class BeastAttack extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new CreateTokenEffect(new BeastToken2()));
|
||||
|
||||
// Flashback {2}{G}{G}{G}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{2}{G}{G}{G}"), TimingRule.INSTANT));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{2}{G}{G}{G}")));
|
||||
}
|
||||
|
||||
private BeastAttack(final BeastAttack card) {
|
||||
|
|
|
|||
54
Mage.Sets/src/mage/cards/b/BenevolentGeist.java
Normal file
54
Mage.Sets/src/mage/cards/b/BenevolentGeist.java
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.PutIntoGraveFromAnywhereSourceAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.CantBeCounteredControlledEffect;
|
||||
import mage.abilities.effects.common.ExileSourceEffect;
|
||||
import mage.abilities.keyword.FlyingAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BenevolentGeist extends CardImpl {
|
||||
|
||||
public BenevolentGeist(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "");
|
||||
|
||||
this.subtype.add(SubType.SPIRIT);
|
||||
this.subtype.add(SubType.WIZARD);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
this.color.setBlue(true);
|
||||
this.transformable = true;
|
||||
this.nightCard = true;
|
||||
|
||||
// Flying
|
||||
this.addAbility(FlyingAbility.getInstance());
|
||||
|
||||
// Noncreature spells you control can't be countered.
|
||||
this.addAbility(new SimpleStaticAbility(new CantBeCounteredControlledEffect(
|
||||
StaticFilters.FILTER_SPELLS_NON_CREATURE, null, Duration.WhileOnBattlefield
|
||||
)));
|
||||
|
||||
// If Benevolent Geist would be put into a graveyard from anywhere, exile it instead.
|
||||
this.addAbility(new PutIntoGraveFromAnywhereSourceAbility(new ExileSourceEffect().setText("exile it instead")));
|
||||
}
|
||||
|
||||
private BenevolentGeist(final BenevolentGeist card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BenevolentGeist copy() {
|
||||
return new BenevolentGeist(this);
|
||||
}
|
||||
}
|
||||
46
Mage.Sets/src/mage/cards/b/BereavedSurvivor.java
Normal file
46
Mage.Sets/src/mage/cards/b/BereavedSurvivor.java
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.common.DiesCreatureTriggeredAbility;
|
||||
import mage.abilities.effects.common.TransformSourceEffect;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.StaticFilters;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BereavedSurvivor extends CardImpl {
|
||||
|
||||
public BereavedSurvivor(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.PEASANT);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(1);
|
||||
this.transformable = true;
|
||||
this.secondSideCardClazz = mage.cards.d.DauntlessAvenger.class;
|
||||
|
||||
// When another creature you control dies, transform Bereaved Survivor.
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new DiesCreatureTriggeredAbility(
|
||||
new TransformSourceEffect(true), false,
|
||||
StaticFilters.FILTER_CONTROLLED_ANOTHER_CREATURE
|
||||
));
|
||||
}
|
||||
|
||||
private BereavedSurvivor(final BereavedSurvivor card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BereavedSurvivor copy() {
|
||||
return new BereavedSurvivor(this);
|
||||
}
|
||||
}
|
||||
|
|
@ -100,7 +100,7 @@ class MoveCounterFromTargetToTargetEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
|
||||
class SameControllerPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<MageItem>> {
|
||||
class SameControllerPredicate implements ObjectSourcePlayerPredicate<MageItem> {
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<MageItem> input, Game game) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public final class BirdAdmirer extends CardImpl {
|
|||
|
||||
// Daybound
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(DayboundAbility.getInstance());
|
||||
this.addAbility(new DayboundAbility());
|
||||
}
|
||||
|
||||
private BirdAdmirer(final BirdAdmirer card) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public final class Bladebrand extends CardImpl {
|
|||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
|
||||
// Draw a card.
|
||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1));
|
||||
this.getSpellAbility().addEffect(new DrawCardSourceControllerEffect(1).concatBy("<br>"));
|
||||
}
|
||||
|
||||
private Bladebrand(final Bladebrand card) {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ public final class BlastFromThePast extends CardImpl {
|
|||
// Kicker {2}{R}
|
||||
this.addAbility(new KickerAbility("{2}{R}"));
|
||||
// Flashback {3}{R}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{3}{R}"), TimingRule.INSTANT));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{3}{R}")));
|
||||
// Buyback {4}{R}
|
||||
this.addAbility(new BuybackAbility("{4}{R}"));
|
||||
|
||||
|
|
|
|||
|
|
@ -1,12 +1,9 @@
|
|||
|
||||
package mage.cards.b;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.condition.LockedInCondition;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.condition.common.KickedCondition;
|
||||
import mage.abilities.decorator.ConditionalContinuousEffect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostControlledEffect;
|
||||
import mage.abilities.effects.common.continuous.BoostTargetEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityControlledEffect;
|
||||
import mage.abilities.keyword.FirstStrikeAbility;
|
||||
import mage.abilities.keyword.KickerAbility;
|
||||
|
|
@ -14,7 +11,11 @@ import mage.cards.CardImpl;
|
|||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Duration;
|
||||
import mage.constants.Outcome;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author nantuko, Loki
|
||||
|
|
@ -28,12 +29,7 @@ public final class BoldDefense extends CardImpl {
|
|||
this.addAbility(new KickerAbility("{3}{W}"));
|
||||
|
||||
// Creatures you control get +1/+1 until end of turn. If Bold Defense was kicked, instead creatures you control get +2/+2 and gain first strike until end of turn.
|
||||
this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn),
|
||||
new BoostTargetEffect(1, 1, Duration.EndOfTurn), new LockedInCondition(KickedCondition.instance),
|
||||
"Creatures you control get +1/+1 until end of turn. If this spell was kicked, instead creatures you control get +2/+2"));
|
||||
this.getSpellAbility().addEffect(new ConditionalContinuousEffect(new GainAbilityControlledEffect(FirstStrikeAbility.getInstance(), Duration.EndOfTurn, StaticFilters.FILTER_PERMANENT_CREATURE, false),
|
||||
null, new LockedInCondition(KickedCondition.instance),
|
||||
"and gain first strike until end of turn"));
|
||||
this.getSpellAbility().addEffect(new BoldDefenseEffect());
|
||||
}
|
||||
|
||||
private BoldDefense(final BoldDefense card) {
|
||||
|
|
@ -45,3 +41,35 @@ public final class BoldDefense extends CardImpl {
|
|||
return new BoldDefense(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BoldDefenseEffect extends OneShotEffect {
|
||||
|
||||
BoldDefenseEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "Creatures you control get +1/+1 until end of turn. If this spell was kicked, " +
|
||||
"instead creatures you control get +2/+2 and gain first strike until end of turn.";
|
||||
}
|
||||
|
||||
private BoldDefenseEffect(final BoldDefenseEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoldDefenseEffect copy() {
|
||||
return new BoldDefenseEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
if (KickedCondition.instance.apply(game, source)) {
|
||||
game.addEffect(new BoostControlledEffect(2, 2, Duration.EndOfTurn), source);
|
||||
game.addEffect(new GainAbilityControlledEffect(
|
||||
FirstStrikeAbility.getInstance(), Duration.EndOfTurn,
|
||||
StaticFilters.FILTER_PERMANENT_CREATURE
|
||||
), source);
|
||||
} else {
|
||||
game.addEffect(new BoostControlledEffect(1, 1, Duration.EndOfTurn), source);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ class BoreasChargerEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
|
||||
class BoreasChargerPredicate implements ObjectSourcePlayerPredicate<ObjectSourcePlayer<Player>> {
|
||||
class BoreasChargerPredicate implements ObjectSourcePlayerPredicate<Player> {
|
||||
|
||||
@Override
|
||||
public boolean apply(ObjectSourcePlayer<Player> input, Game game) {
|
||||
|
|
|
|||
|
|
@ -110,8 +110,7 @@ class BrilliantUltimatumEffect extends OneShotEffect {
|
|||
TargetCard targetExiledCard = new TargetCard(Zone.EXILED, new FilterCard());
|
||||
if (controller.chooseTarget(Outcome.PlayForFree, selectedPile, targetExiledCard, source, game)) {
|
||||
Card card = selectedPile.get(targetExiledCard.getFirstTarget(), game);
|
||||
controller.canPlayLand();
|
||||
if (controller.playCard(card, game, true, true, new ApprovingObject(source, game))) {
|
||||
if (controller.playCard(card, game, true, new ApprovingObject(source, game))) {
|
||||
selectedPileCards.remove(card);
|
||||
selectedPile.remove(card);
|
||||
}
|
||||
|
|
|
|||
99
Mage.Sets/src/mage/cards/b/BrutalCathar.java
Normal file
99
Mage.Sets/src/mage/cards/b/BrutalCathar.java
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.MageInt;
|
||||
import mage.abilities.TriggeredAbilityImpl;
|
||||
import mage.abilities.common.delayed.OnLeaveReturnExiledToBattlefieldAbility;
|
||||
import mage.abilities.effects.common.CreateDelayedTriggeredAbilityEffect;
|
||||
import mage.abilities.effects.common.ExileUntilSourceLeavesEffect;
|
||||
import mage.abilities.keyword.DayboundAbility;
|
||||
import mage.abilities.keyword.TransformAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Zone;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.target.common.TargetOpponentsCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BrutalCathar extends CardImpl {
|
||||
|
||||
public BrutalCathar(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{2}{W}");
|
||||
|
||||
this.subtype.add(SubType.HUMAN);
|
||||
this.subtype.add(SubType.SOLDIER);
|
||||
this.subtype.add(SubType.WEREWOLF);
|
||||
this.power = new MageInt(2);
|
||||
this.toughness = new MageInt(2);
|
||||
this.transformable = true;
|
||||
this.secondSideCardClazz = mage.cards.m.MoonrageBrute.class;
|
||||
|
||||
// When this creature enters the battlefield or transforms into Brutal Cathar, exile target creature an opponent controls until this creature leaves the battlefield.
|
||||
this.addAbility(new BrutalCatharTriggeredAbility());
|
||||
|
||||
// Daybound
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(new DayboundAbility());
|
||||
}
|
||||
|
||||
private BrutalCathar(final BrutalCathar card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrutalCathar copy() {
|
||||
return new BrutalCathar(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BrutalCatharTriggeredAbility extends TriggeredAbilityImpl {
|
||||
|
||||
public BrutalCatharTriggeredAbility() {
|
||||
super(Zone.BATTLEFIELD, new ExileUntilSourceLeavesEffect("creature an opponent controls"), false);
|
||||
this.addTarget(new TargetOpponentsCreaturePermanent());
|
||||
this.addEffect(new CreateDelayedTriggeredAbilityEffect(new OnLeaveReturnExiledToBattlefieldAbility()));
|
||||
}
|
||||
|
||||
public BrutalCatharTriggeredAbility(final BrutalCatharTriggeredAbility ability) {
|
||||
super(ability);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BrutalCatharTriggeredAbility copy() {
|
||||
return new BrutalCatharTriggeredAbility(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkEventType(GameEvent event, Game game) {
|
||||
return event.getType() == GameEvent.EventType.TRANSFORMED
|
||||
|| event.getType() == GameEvent.EventType.ENTERS_THE_BATTLEFIELD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean checkTrigger(GameEvent event, Game game) {
|
||||
if (!event.getTargetId().equals(this.getSourceId())) {
|
||||
return false;
|
||||
}
|
||||
switch (event.getType()) {
|
||||
case TRANSFORMED:
|
||||
Permanent permanent = getSourcePermanentIfItStillExists(game);
|
||||
return permanent != null && !permanent.isTransformed();
|
||||
case ENTERS_THE_BATTLEFIELD:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRule() {
|
||||
return "When this creature enters the battlefield or transforms into {this}, " +
|
||||
"exile target creature an opponent controls until this creature leaves the battlefield.";
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ public final class BumpInTheNight extends CardImpl {
|
|||
this.getSpellAbility().addTarget(new TargetOpponent());
|
||||
|
||||
// Flashback {5}{R}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{5}{R}"), TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{5}{R}")));
|
||||
}
|
||||
|
||||
private BumpInTheNight(final BumpInTheNight card) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ public final class BurlyBreaker extends CardImpl {
|
|||
|
||||
// Daybound
|
||||
this.addAbility(new TransformAbility());
|
||||
this.addAbility(DayboundAbility.getInstance());
|
||||
this.addAbility(new DayboundAbility());
|
||||
}
|
||||
|
||||
private BurlyBreaker(final BurlyBreaker card) {
|
||||
|
|
|
|||
70
Mage.Sets/src/mage/cards/b/BurnTheAccursed.java
Normal file
70
Mage.Sets/src/mage/cards/b/BurnTheAccursed.java
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
package mage.cards.b;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.ExileTargetIfDiesEffect;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.permanent.Permanent;
|
||||
import mage.players.Player;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class BurnTheAccursed extends CardImpl {
|
||||
|
||||
public BurnTheAccursed(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{4}{R}");
|
||||
|
||||
// Burn the Accused deals 5 damage to target creature and 2 damage to that creature's controller. If that creature would die this turn, exile it instead.
|
||||
this.getSpellAbility().addEffect(new BurnTheAccursedEffect());
|
||||
this.getSpellAbility().addEffect(new ExileTargetIfDiesEffect());
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
}
|
||||
|
||||
private BurnTheAccursed(final BurnTheAccursed card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BurnTheAccursed copy() {
|
||||
return new BurnTheAccursed(this);
|
||||
}
|
||||
}
|
||||
|
||||
class BurnTheAccursedEffect extends OneShotEffect {
|
||||
|
||||
BurnTheAccursedEffect() {
|
||||
super(Outcome.Benefit);
|
||||
staticText = "{this} deals 5 damage to target creature and 2 damage to that creature's controller.";
|
||||
}
|
||||
|
||||
private BurnTheAccursedEffect(final BurnTheAccursedEffect effect) {
|
||||
super(effect);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BurnTheAccursedEffect copy() {
|
||||
return new BurnTheAccursedEffect(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Permanent permanent = game.getPermanent(source.getFirstTarget());
|
||||
if (permanent == null) {
|
||||
return false;
|
||||
}
|
||||
permanent.damage(5, source.getSourceId(), source, game);
|
||||
Player player = game.getPlayer(permanent.getControllerId());
|
||||
if (player != null) {
|
||||
player.damage(2, source.getSourceId(), source, game);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -25,7 +25,7 @@ public final class BurningOil extends CardImpl {
|
|||
this.getSpellAbility().addTarget(new TargetAttackingOrBlockingCreature());
|
||||
this.getSpellAbility().addEffect(new DamageTargetEffect(3));
|
||||
// Flashback {3}{W}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{3}{W}"), TimingRule.INSTANT));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{3}{W}")));
|
||||
}
|
||||
|
||||
private BurningOil(final BurningOil card) {
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import mage.cards.CardSetInfo;
|
|||
import mage.cards.Cards;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.TimingRule;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
|
@ -36,9 +35,9 @@ public final class CabalTherapy extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new CabalTherapyEffect());
|
||||
|
||||
// Flashback-Sacrifice a creature.
|
||||
this.addAbility(new FlashbackAbility(
|
||||
new SacrificeTargetCost(new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT, true)),
|
||||
TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new SacrificeTargetCost(
|
||||
new TargetControlledCreaturePermanent(StaticFilters.FILTER_CONTROLLED_CREATURE_SHORT_TEXT)
|
||||
)));
|
||||
}
|
||||
|
||||
private CabalTherapy(final CabalTherapy card) {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public final class CacklingCounterpart extends CardImpl {
|
|||
this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent());
|
||||
|
||||
// Flashback {5}{U}{U}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{5}{U}{U}"), TimingRule.INSTANT));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{5}{U}{U}")));
|
||||
}
|
||||
|
||||
private CacklingCounterpart(final CacklingCounterpart card) {
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ public final class CalibratedBlast extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new CalibratedBlastEffect());
|
||||
|
||||
// Flashback {3}{R}{R}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl<>("{3}{R}{R}"), TimingRule.INSTANT));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{3}{R}{R}")));
|
||||
}
|
||||
|
||||
private CalibratedBlast(final CalibratedBlast card) {
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import java.util.UUID;
|
|||
import java.util.stream.Collectors;
|
||||
|
||||
import static mage.constants.Outcome.Benefit;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
|
|
@ -105,21 +106,24 @@ class CalixDestinysHandExileEffect extends OneShotEffect {
|
|||
|
||||
@Override
|
||||
public boolean apply(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (source.getTargets().size() > 2) {
|
||||
return false;
|
||||
}
|
||||
source.getTargets();
|
||||
Permanent theirPerm = game.getPermanent(source.getTargets().get(0).getFirstTarget());
|
||||
Permanent myPerm = game.getPermanent(source.getTargets().get(1).getFirstTarget());
|
||||
if (player == null || theirPerm == null || myPerm == null) {
|
||||
if (controller == null
|
||||
|| theirPerm == null
|
||||
|| myPerm == null) {
|
||||
return false;
|
||||
}
|
||||
MageObjectReference theirMor = new MageObjectReference(
|
||||
theirPerm.getId(), theirPerm.getZoneChangeCounter(game) + 1, game
|
||||
);
|
||||
MageObjectReference myMor = new MageObjectReference(myPerm, game);
|
||||
player.moveCards(theirPerm, Zone.EXILED, source, game);
|
||||
UUID exileId = CardUtil.getExileZoneId(game, source);
|
||||
controller.moveCardsToExile(theirPerm, source, game, true, exileId, myPerm.getLogName());
|
||||
game.addDelayedTriggeredAbility(new CalixDestinysHandDelayedTriggeredAbility(theirMor, myMor), source);
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public final class CallOfTheHerd extends CardImpl {
|
|||
// Create a 3/3 green Elephant creature token.
|
||||
this.getSpellAbility().addEffect(new CreateTokenEffect(new ElephantToken()));
|
||||
// Flashback {3}{G}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{3}{G}"), TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{3}{G}")));
|
||||
}
|
||||
|
||||
private CallOfTheHerd(final CallOfTheHerd card) {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ public final class CanopyClaws extends CardImpl {
|
|||
this.getSpellAbility().addEffect(new LoseAbilityTargetEffect(FlyingAbility.getInstance(), Duration.EndOfTurn));
|
||||
this.getSpellAbility().addTarget(new TargetCreaturePermanent());
|
||||
// Flashback {G}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl("{G}"), TimingRule.INSTANT));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl("{G}")));
|
||||
}
|
||||
|
||||
private CanopyClaws(final CanopyClaws card) {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public final class CantStayAway extends CardImpl {
|
|||
this.getSpellAbility().addTarget(new TargetCardInYourGraveyard(filter));
|
||||
|
||||
// Flashback {3}{W}{B}
|
||||
this.addAbility(new FlashbackAbility(new ManaCostsImpl<>("{3}{W}{B}"), TimingRule.SORCERY));
|
||||
this.addAbility(new FlashbackAbility(this, new ManaCostsImpl<>("{3}{W}{B}")));
|
||||
}
|
||||
|
||||
private CantStayAway(final CantStayAway card) {
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ public final class CatharCommando extends CardImpl {
|
|||
this.addAbility(FlashAbility.getInstance());
|
||||
|
||||
// {1}, Sacrifice Cathar Commando: Destroy target artifact or enchantment.
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{1}{G}"));
|
||||
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new DestroyTargetEffect(), new ManaCostsImpl("{1}"));
|
||||
ability.addCost(new SacrificeSourceCost());
|
||||
ability.addTarget(new TargetPermanent(StaticFilters.FILTER_PERMANENT_ARTIFACT_OR_ENCHANTMENT));
|
||||
this.addAbility(ability);
|
||||
|
|
|
|||
56
Mage.Sets/src/mage/cards/c/CatharsCall.java
Normal file
56
Mage.Sets/src/mage/cards/c/CatharsCall.java
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
package mage.cards.c;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
|
||||
import mage.abilities.common.SimpleStaticAbility;
|
||||
import mage.abilities.effects.common.AttachEffect;
|
||||
import mage.abilities.effects.common.CreateTokenEffect;
|
||||
import mage.abilities.effects.common.continuous.GainAbilityAttachedEffect;
|
||||
import mage.abilities.keyword.EnchantAbility;
|
||||
import mage.abilities.keyword.VigilanceAbility;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.constants.*;
|
||||
import mage.game.permanent.token.HumanToken;
|
||||
import mage.target.TargetPermanent;
|
||||
import mage.target.common.TargetCreaturePermanent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* @author TheElk801
|
||||
*/
|
||||
public final class CatharsCall extends CardImpl {
|
||||
|
||||
public CatharsCall(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{W}");
|
||||
|
||||
this.subtype.add(SubType.AURA);
|
||||
|
||||
// Enchant creature
|
||||
TargetPermanent auraTarget = new TargetCreaturePermanent();
|
||||
this.getSpellAbility().addTarget(auraTarget);
|
||||
this.getSpellAbility().addEffect(new AttachEffect(Outcome.BoostCreature));
|
||||
Ability ability = new EnchantAbility(auraTarget.getTargetName());
|
||||
this.addAbility(ability);
|
||||
|
||||
// Enchanted creature has vigilance and "At the beginning of your end step, create a 1/1 white Human creature token."
|
||||
ability = new SimpleStaticAbility(new GainAbilityAttachedEffect(VigilanceAbility.getInstance(), AttachmentType.AURA));
|
||||
ability.addEffect(new GainAbilityAttachedEffect(
|
||||
new BeginningOfEndStepTriggeredAbility(
|
||||
new CreateTokenEffect(new HumanToken()),
|
||||
TargetController.YOU, false
|
||||
), AttachmentType.AURA
|
||||
).setText("and \"At the beginning of your end step, create a 1/1 white Human creature token.\""));
|
||||
this.addAbility(ability);
|
||||
}
|
||||
|
||||
private CatharsCall(final CatharsCall card) {
|
||||
super(card);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CatharsCall copy() {
|
||||
return new CatharsCall(this);
|
||||
}
|
||||
}
|
||||
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