Merge pull request #27 from magefree/master

Merge https://github.com/magefree/mage
This commit is contained in:
Zzooouhh 2017-12-30 01:17:26 +01:00 committed by GitHub
commit c2b6a8f559
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
109 changed files with 3665 additions and 785 deletions

View file

@ -204,6 +204,24 @@
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="btnExpansionSearch">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/buttons/search_32.png"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="Fast search set or expansion"/>
<Property name="alignmentX" type="float" value="1.0"/>
<Property name="focusable" type="boolean" value="false"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[23, 23]"/>
</Property>
<Property name="verticalTextPosition" type="int" value="3"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnExpansionSearchActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JToolBar$Separator" name="jSeparator2">
</Component>
<Component class="javax.swing.JCheckBox" name="chkPennyDreadful">

View file

@ -47,10 +47,12 @@ import mage.cards.Sets;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.choices.ChoiceImpl;
import mage.client.MageFrame;
import mage.client.cards.*;
import mage.client.constants.Constants.SortBy;
import mage.client.deckeditor.table.TableModel;
import mage.client.dialog.PickChoiceDialog;
import mage.client.util.GUISizeHelper;
import mage.client.util.sets.ConstructedFormats;
import mage.constants.CardType;
@ -206,6 +208,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
this.btnBooster.setVisible(false);
this.btnClear.setVisible(false);
this.cbExpansionSet.setVisible(false);
this.btnExpansionSearch.setVisible(false);
this.limited = true;
this.cards.clear();
for (Card card : sideboard) {
@ -219,10 +222,44 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
this.btnBooster.setVisible(true);
this.btnClear.setVisible(true);
this.cbExpansionSet.setVisible(true);
this.btnExpansionSearch.setVisible(true);
// cbExpansionSet.setModel(new DefaultComboBoxModel<>(ConstructedFormats.getTypes()));
// Action event on Expansion set triggers loadCards method
cbExpansionSet.setSelectedIndex(0);
}
public void doFastExpansionSearch(){
mage.choices.Choice choice = new ChoiceImpl(false);
// collect data from expansion combobox (String)
DefaultComboBoxModel comboModel = (DefaultComboBoxModel)cbExpansionSet.getModel();
Map<String, String> choiceItems = new HashMap<>(comboModel.getSize());
Map<String, Integer> choiceSorting = new HashMap<>(comboModel.getSize());
String item;
for(int i = 0; i < comboModel.getSize() - 1; i++){
item = (String)comboModel.getElementAt(i);
choiceItems.put(item, item);
choiceSorting.put(item, i); // need so sorting
}
choice.setKeyChoices(choiceItems);
choice.setSortData(choiceSorting);
choice.setMessage("Select set or expansion");
// current selection value restore
String needSelectValue;
needSelectValue = (String)comboModel.getSelectedItem();
// ask for new value
PickChoiceDialog dlg = new PickChoiceDialog();
dlg.setWindowSize(300, 500);
dlg.showDialog(choice, needSelectValue);
if(choice.isChosen()){
item = choice.getChoiceKey();
comboModel.setSelectedItem(item);
}
}
private FilterCard buildFilter() {
FilterCard filter = new FilterCard();
@ -471,6 +508,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
tbColorless = new javax.swing.JToggleButton();
jSeparator1 = new javax.swing.JToolBar.Separator();
cbExpansionSet = new javax.swing.JComboBox<>();
btnExpansionSearch = new javax.swing.JButton();
jSeparator2 = new javax.swing.JToolBar.Separator();
chkPennyDreadful = new javax.swing.JCheckBox();
btnBooster = new javax.swing.JButton();
@ -615,10 +653,23 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
}
});
tbColor.add(cbExpansionSet);
btnExpansionSearch.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/search_32.png"))); // NOI18N
btnExpansionSearch.setToolTipText("Fast search set or expansion");
btnExpansionSearch.setAlignmentX(1.0F);
btnExpansionSearch.setFocusable(false);
btnExpansionSearch.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
btnExpansionSearch.setPreferredSize(new java.awt.Dimension(23, 23));
btnExpansionSearch.setVerticalTextPosition(javax.swing.SwingConstants.BOTTOM);
btnExpansionSearch.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnExpansionSearchActionPerformed(evt);
}
});
tbColor.add(btnExpansionSearch);
tbColor.add(jSeparator2);
chkPennyDreadful.setText("Penny Dreadful");
chkPennyDreadful.setText("Penny Dreadful Only");
chkPennyDreadful.setToolTipText("Will only allow Penny Dreadful legal cards to be shown.");
chkPennyDreadful.setFocusable(false);
chkPennyDreadful.setHorizontalTextPosition(javax.swing.SwingConstants.RIGHT);
@ -628,15 +679,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
chkPilesActionPerformed(evt);
}
});
JPopupMenu filterByFormatPopup = new JPopupMenu();
filterByFormatPopup.add(chkPennyDreadful);
filterByFormatPopup.setLayout(new GridBagLayout());
ButtonGroup selectByTypeModeGroup = new ButtonGroup();
JButton filterByFormatButton = new JButton ("Filter by Format");
makeButtonPopup(filterByFormatButton, filterByFormatPopup);
tbColor.add(filterByFormatButton);
tbColor.add(chkPennyDreadful);
btnBooster.setText("Open Booster");
btnBooster.setToolTipText("(CURRENTLY NOT WORKING) Generates a booster of the selected set and adds the cards to the card selector.");
@ -1211,6 +1254,10 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
// TODO add your handling code here:
}//GEN-LAST:event_chkRulesActionPerformed
private void btnExpansionSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnExpansionSearchActionPerformed
doFastExpansionSearch();
}//GEN-LAST:event_btnExpansionSearchActionPerformed
private void toggleViewMode() {
if (currentView instanceof CardGrid) {
jToggleListView.setSelected(true);
@ -1253,6 +1300,7 @@ public class CardSelector extends javax.swing.JPanel implements ComponentListene
private javax.swing.ButtonGroup bgView;
private javax.swing.JButton btnBooster;
private javax.swing.JButton btnClear;
private javax.swing.JButton btnExpansionSearch;
private javax.swing.JLabel cardCount;
private javax.swing.JLabel cardCountLabel;
private javax.swing.JPanel cardSelectorBottomPanel;

View file

@ -85,9 +85,8 @@ public class AboutDialog extends MageDialog {
jLabel2.setText("Courtesy: BetaSteward@googlemail.com. Site: http://XMage.de/");
jLabel3.setText("Devs: BetaSteward, Noxx, Eugen.Rivniy, North, LevelX2, Jeff, Plopman, dustinconrad, emerald000.,");
jLabel4.setText("fireshoes, lunaskyrise, mnapoleon, jgod, LoneFox, drmDev, spjspj.");
jLabel3.setText("Devs: BetaSteward, Noxx, Eugen.Rivniy, North, LevelX2, Jeff, Plopman, dustinconrad, emerald000,");
jLabel4.setText("fireshoes, lunaskyrise, mnapoleon, jgod, LoneFox, drmDev, spjspj, L_J, JayDi85.");
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);

View file

@ -26,68 +26,76 @@
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="29" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="lblPort" alignment="1" min="-2" max="-2" attributes="0"/>
<Component id="lblServer" min="-2" max="-2" attributes="0"/>
<Component id="lblUserName" min="-2" max="-2" attributes="0"/>
<Component id="lblPassword" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lblFlag" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lblStatus" alignment="1" max="32767" attributes="0"/>
<Component id="chkForceUpdateDB" alignment="0" max="32767" attributes="0"/>
<Component id="chkAutoConnect" alignment="0" pref="386" max="32767" attributes="0"/>
<Component id="jProxySettingsButton" min="-2" max="-2" attributes="0"/>
<Component id="cbFlag" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="txtServer" alignment="1" pref="286" max="32767" attributes="0"/>
<Component id="txtUserName" alignment="1" max="32767" attributes="0"/>
<Component id="txtPassword" alignment="1" max="32767" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="txtPort" min="-2" pref="71" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnFind1" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnFind3" min="-2" pref="42" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="btnFind2" min="-2" pref="42" max="-2" attributes="0"/>
<EmptySpace min="69" pref="69" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<Component id="btnFind" pref="92" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="lblFlag" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="btnRegister" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Component id="btnConnect" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnForgotPassword" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnCancel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="lblUserName" min="-2" max="-2" attributes="0"/>
<Component id="lblPassword" alignment="1" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="26" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="btnConnect" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="btnRegister" max="32767" attributes="0"/>
<Component id="btnForgotPassword" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="btnCancel" min="-2" pref="77" max="-2" attributes="0"/>
</Group>
<Component id="lblStatus" alignment="1" max="32767" attributes="0"/>
<Component id="chkForceUpdateDB" alignment="0" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="jProxySettingsButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Component id="chkAutoConnect" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Component id="panelFlag" alignment="0" max="32767" attributes="0"/>
<Component id="txtServer" alignment="0" max="32767" attributes="0"/>
<Component id="txtUserName" alignment="0" max="32767" attributes="0"/>
<Component id="txtPassword" alignment="0" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Component id="txtPort" min="-2" pref="71" max="-2" attributes="0"/>
<EmptySpace pref="11" max="32767" attributes="0"/>
<Component id="lblFastConnect" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnFind1" min="-2" pref="70" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnFind3" min="-2" pref="70" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnFind2" min="-2" pref="40" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="btnFind" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblServer" alignment="3" min="-2" max="-2" attributes="0"/>
@ -101,39 +109,42 @@
<Component id="btnFind1" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnFind2" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnFind3" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblFastConnect" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="txtUserName" min="-2" max="-2" attributes="0"/>
<Component id="lblUserName" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="txtPassword" min="-2" max="-2" attributes="0"/>
<Component id="lblPassword" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="lblFlag" max="32767" attributes="0"/>
<Component id="cbFlag" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="5" max="-2" attributes="0"/>
<Component id="chkAutoConnect" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="chkForceUpdateDB" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jProxySettingsButton" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="47" max="32767" attributes="0"/>
<Component id="lblStatus" min="-2" pref="24" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btnConnect" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnCancel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnForgotPassword" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="txtUserName" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblUserName" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="txtPassword" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="lblPassword" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Component id="btnRegister" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="panelFlag" min="-2" pref="20" max="-2" attributes="0"/>
<Component id="lblFlag" min="-2" pref="18" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="chkAutoConnect" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="chkForceUpdateDB" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Component id="jProxySettingsButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="lblStatus" min="-2" pref="24" max="-2" attributes="0"/>
<EmptySpace type="unrelated" min="-2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" max="-2" attributes="0">
<Group type="102" attributes="0">
<Component id="btnRegister" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnForgotPassword" max="32767" attributes="0"/>
</Group>
<Component id="btnConnect" max="32767" attributes="0"/>
<Component id="btnCancel" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="23" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -200,11 +211,6 @@
<Property name="text" type="java.lang.String" value="User flag:"/>
</Properties>
</Component>
<Component class="mage.client.util.gui.countryBox.CountryComboBox" name="cbFlag">
<Properties>
<Property name="editable" type="boolean" value="true"/>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="chkAutoConnect">
<Properties>
<Property name="text" type="java.lang.String" value="Automatically connect to this server next time"/>
@ -233,7 +239,13 @@
</Component>
<Component class="javax.swing.JButton" name="btnConnect">
<Properties>
<Property name="text" type="java.lang.String" value="Connect"/>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="11" style="1"/>
</Property>
<Property name="text" type="java.lang.String" value="Connect to server"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnConnectActionPerformed"/>
@ -242,6 +254,10 @@
<Component class="javax.swing.JButton" name="btnCancel">
<Properties>
<Property name="text" type="java.lang.String" value="Cancel"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnCancelActionPerformed"/>
@ -251,8 +267,11 @@
</Component>
<Component class="javax.swing.JButton" name="btnRegister">
<Properties>
<Property name="text" type="java.lang.String" value="Register new user"/>
<Property name="text" type="java.lang.String" value="Register new user..."/>
<Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;XMage now supports user authentication.&lt;br&gt;Register your account before you log in.&lt;html&gt;"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnRegisterActionPerformed"/>
@ -260,8 +279,11 @@
</Component>
<Component class="javax.swing.JButton" name="btnForgotPassword">
<Properties>
<Property name="text" type="java.lang.String" value="Forgot password"/>
<Property name="text" type="java.lang.String" value="Forgot password..."/>
<Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;You can reset your password if you have registered&lt;br&gt;your account with an email address.&lt;/html&gt;"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnForgotPasswordActionPerformed"/>
@ -269,12 +291,16 @@
</Component>
<Component class="javax.swing.JButton" name="btnFind1">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/flags/de.png"/>
</Property>
<Property name="text" type="java.lang.String" value="X"/>
<Property name="toolTipText" type="java.lang.String" value="Connect to xmage.de"/>
<Property name="toolTipText" type="java.lang.String" value="Connect to xmage.de (Europe, most popular)"/>
<Property name="actionCommand" type="java.lang.String" value="connectXmageDe"/>
<Property name="alignmentY" type="float" value="0.0"/>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="horizontalTextPosition" type="int" value="2"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[42, 23]"/>
</Property>
@ -293,11 +319,13 @@
<Component class="javax.swing.JButton" name="btnFind2">
<Properties>
<Property name="text" type="java.lang.String" value="L"/>
<Property name="toolTipText" type="java.lang.String" value="Connect to localhost"/>
<Property name="toolTipText" type="java.lang.String" value="Connect to localhost (local server)"/>
<Property name="actionCommand" type="java.lang.String" value="connectLocalhost"/>
<Property name="alignmentY" type="float" value="0.0"/>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="horizontalTextPosition" type="int" value="2"/>
<Property name="horizontalTextPosition" type="int" value="0"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
<Property name="name" type="java.lang.String" value="connectLocalhostBtn" noResource="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[23, 23]"/>
@ -309,12 +337,16 @@
</Component>
<Component class="javax.swing.JButton" name="btnFind3">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/flags/us.png"/>
</Property>
<Property name="text" type="java.lang.String" value="W"/>
<Property name="toolTipText" type="java.lang.String" value="Connect to woogerworks"/>
<Property name="toolTipText" type="java.lang.String" value="Connect to Woogerworks (USA)"/>
<Property name="actionCommand" type="java.lang.String" value="connectWoogerworks"/>
<Property name="alignmentY" type="float" value="0.0"/>
<Property name="horizontalAlignment" type="int" value="2"/>
<Property name="horizontalTextPosition" type="int" value="2"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
<Property name="name" type="java.lang.String" value="connectWoogerworksBtn" noResource="true"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[23, 23]"/>
@ -324,5 +356,69 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="connectWoogerworks"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="lblFastConnect">
<Properties>
<Property name="labelFor" type="java.awt.Component" editor="org.netbeans.modules.form.ComponentChooserEditor">
<ComponentRef name="btnFind1"/>
</Property>
<Property name="text" type="java.lang.String" value="Fast connect to:"/>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="panelFlag">
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[189, 30]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout"/>
<SubComponents>
<Component class="mage.client.util.gui.countryBox.CountryComboBox" name="cbFlag">
<Properties>
<Property name="editable" type="boolean" value="true"/>
<Property name="maximumRowCount" type="int" value="16"/>
<Property name="alignmentX" type="float" value="0.0"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[50, 18]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[278, 15]"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.Box$Filler" name="filler1">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[5, 32767]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[5, 0]"/>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[4, 0]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="classDetails" type="java.lang.String" value="Box.Filler.HorizontalStrut"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="btnFlagSearch">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/buttons/search_24.png"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="Fast search your flag"/>
<Property name="alignmentX" type="float" value="1.0"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[23, 23]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnFlagSearchActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View file

@ -51,14 +51,21 @@ import java.net.URL;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.SwingWorker;
import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.client.MageFrame;
import static mage.client.dialog.PreferencesDialog.KEY_CONNECTION_URL_SERVER_LIST;
import static mage.client.dialog.PreferencesDialog.KEY_CONNECT_AUTO_CONNECT;
@ -152,7 +159,6 @@ public class ConnectDialog extends MageDialog {
lblPassword = new javax.swing.JLabel();
txtPassword = new javax.swing.JPasswordField();
lblFlag = new javax.swing.JLabel();
cbFlag = new mage.client.util.gui.countryBox.CountryComboBox();
chkAutoConnect = new javax.swing.JCheckBox();
chkForceUpdateDB = new javax.swing.JCheckBox();
jProxySettingsButton = new javax.swing.JButton();
@ -164,6 +170,11 @@ public class ConnectDialog extends MageDialog {
btnFind1 = new javax.swing.JButton();
btnFind2 = new javax.swing.JButton();
btnFind3 = new javax.swing.JButton();
lblFastConnect = new javax.swing.JLabel();
panelFlag = new javax.swing.JPanel();
cbFlag = new mage.client.util.gui.countryBox.CountryComboBox();
filler1 = new javax.swing.Box.Filler(new java.awt.Dimension(5, 0), new java.awt.Dimension(4, 0), new java.awt.Dimension(5, 32767));
btnFlagSearch = new javax.swing.JButton();
setTitle("Connect to server");
setNormalBounds(new java.awt.Rectangle(100, 100, 410, 307));
@ -174,7 +185,11 @@ public class ConnectDialog extends MageDialog {
btnFind.setText("Find...");
btnFind.setToolTipText("Shows the list of public servers");
btnFind.setName("findServerBtn"); // NOI18N
btnFind.addActionListener(evt -> findPublicServerActionPerformed(evt));
btnFind.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
findPublicServerActionPerformed(evt);
}
});
lblPort.setLabelFor(txtPort);
lblPort.setText("Port:");
@ -194,119 +209,194 @@ public class ConnectDialog extends MageDialog {
lblFlag.setLabelFor(txtUserName);
lblFlag.setText("User flag:");
cbFlag.setEditable(true);
chkAutoConnect.setText("Automatically connect to this server next time");
chkAutoConnect.setToolTipText("<HTML>If active this connect dialog will not be shown if you choose to connect.<br>\nInstead XMage tries to connect to the last server you were connected to.");
chkAutoConnect.addActionListener(evt -> chkAutoConnectActionPerformed(evt));
chkAutoConnect.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
chkAutoConnectActionPerformed(evt);
}
});
chkForceUpdateDB.setText("Force update of card database");
chkForceUpdateDB.setToolTipText("<HTML>If active the comparison of the server cards database to the client database will be enforced.<br>If not, the comparison will only done if the database version of the client is lower than the version of the server.");
chkForceUpdateDB.addActionListener(evt -> chkForceUpdateDBActionPerformed(evt));
chkForceUpdateDB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
chkForceUpdateDBActionPerformed(evt);
}
});
jProxySettingsButton.setText("Proxy Settings...");
jProxySettingsButton.addActionListener(evt -> jProxySettingsButtonActionPerformed(evt));
jProxySettingsButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
jProxySettingsButtonActionPerformed(evt);
}
});
btnConnect.setText("Connect");
btnConnect.addActionListener(evt -> btnConnectActionPerformed(evt));
btnConnect.setFont(new java.awt.Font("Tahoma", 1, 11)); // NOI18N
btnConnect.setText("Connect to server");
btnConnect.setMargin(new java.awt.Insets(2, 2, 2, 2));
btnConnect.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnConnectActionPerformed(evt);
}
});
btnCancel.setText("Cancel");
btnCancel.addActionListener(evt -> btnCancelActionPerformed(evt));
btnCancel.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
btnCancel.setMargin(new java.awt.Insets(2, 2, 2, 2));
btnCancel.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnCancelActionPerformed(evt);
}
});
btnRegister.setText("Register new user");
btnRegister.setText("Register new user...");
btnRegister.setToolTipText("<html>XMage now supports user authentication.<br>Register your account before you log in.<html>");
btnRegister.addActionListener(evt -> btnRegisterActionPerformed(evt));
btnRegister.setMargin(new java.awt.Insets(2, 2, 2, 2));
btnRegister.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnRegisterActionPerformed(evt);
}
});
btnForgotPassword.setText("Forgot password");
btnForgotPassword.setText("Forgot password...");
btnForgotPassword.setToolTipText("<html>You can reset your password if you have registered<br>your account with an email address.</html>");
btnForgotPassword.addActionListener(evt -> btnForgotPasswordActionPerformed(evt));
btnForgotPassword.setMargin(new java.awt.Insets(2, 2, 2, 2));
btnForgotPassword.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnForgotPasswordActionPerformed(evt);
}
});
btnFind1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/flags/de.png"))); // NOI18N
btnFind1.setText("X");
btnFind1.setToolTipText("Connect to xmage.de");
btnFind1.setToolTipText("Connect to xmage.de (Europe, most popular)");
btnFind1.setActionCommand("connectXmageDe");
btnFind1.setAlignmentY(0.0F);
btnFind1.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
btnFind1.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
btnFind1.setMargin(new java.awt.Insets(2, 2, 2, 2));
btnFind1.setMaximumSize(new java.awt.Dimension(42, 23));
btnFind1.setMinimumSize(new java.awt.Dimension(42, 23));
btnFind1.setName("connectXmageDeBtn"); // NOI18N
btnFind1.setPreferredSize(new java.awt.Dimension(23, 23));
btnFind1.addActionListener(evt -> connectXmageDe(evt));
btnFind1.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
connectXmageDe(evt);
}
});
btnFind2.setText("L");
btnFind2.setToolTipText("Connect to localhost");
btnFind2.setToolTipText("Connect to localhost (local server)");
btnFind2.setActionCommand("connectLocalhost");
btnFind2.setAlignmentY(0.0F);
btnFind2.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
btnFind2.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
btnFind2.setHorizontalTextPosition(javax.swing.SwingConstants.CENTER);
btnFind2.setMargin(new java.awt.Insets(2, 2, 2, 2));
btnFind2.setName("connectLocalhostBtn"); // NOI18N
btnFind2.setPreferredSize(new java.awt.Dimension(23, 23));
btnFind2.addActionListener(evt -> connectLocalhost(evt));
btnFind2.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
connectLocalhost(evt);
}
});
btnFind3.setIcon(new javax.swing.ImageIcon(getClass().getResource("/flags/us.png"))); // NOI18N
btnFind3.setText("W");
btnFind3.setToolTipText("Connect to woogerworks");
btnFind3.setToolTipText("Connect to Woogerworks (USA)");
btnFind3.setActionCommand("connectWoogerworks");
btnFind3.setAlignmentY(0.0F);
btnFind3.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
btnFind3.setHorizontalTextPosition(javax.swing.SwingConstants.LEFT);
btnFind3.setMargin(new java.awt.Insets(2, 2, 2, 2));
btnFind3.setName("connectWoogerworksBtn"); // NOI18N
btnFind3.setPreferredSize(new java.awt.Dimension(23, 23));
btnFind3.addActionListener(evt -> connectWoogerworks(evt));
btnFind3.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
connectWoogerworks(evt);
}
});
lblFastConnect.setLabelFor(btnFind1);
lblFastConnect.setText("Fast connect to:");
lblFastConnect.setName(""); // NOI18N
panelFlag.setPreferredSize(new java.awt.Dimension(189, 30));
panelFlag.setLayout(new javax.swing.BoxLayout(panelFlag, javax.swing.BoxLayout.LINE_AXIS));
cbFlag.setEditable(true);
cbFlag.setMaximumRowCount(16);
cbFlag.setAlignmentX(0.0F);
cbFlag.setMinimumSize(new java.awt.Dimension(50, 18));
cbFlag.setPreferredSize(new java.awt.Dimension(278, 15));
panelFlag.add(cbFlag);
panelFlag.add(filler1);
btnFlagSearch.setIcon(new javax.swing.ImageIcon(getClass().getResource("/buttons/search_24.png"))); // NOI18N
btnFlagSearch.setToolTipText("Fast search your flag");
btnFlagSearch.setAlignmentX(1.0F);
btnFlagSearch.setPreferredSize(new java.awt.Dimension(23, 23));
btnFlagSearch.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btnFlagSearchActionPerformed(evt);
}
});
panelFlag.add(btnFlagSearch);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(29, 29, 29)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(lblPort)
.addComponent(lblServer)
.addComponent(lblUserName)
.addComponent(lblPassword))
.addComponent(lblFlag, javax.swing.GroupLayout.Alignment.TRAILING))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(lblServer)))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addComponent(lblFlag)))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(lblStatus, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(chkForceUpdateDB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(chkAutoConnect, javax.swing.GroupLayout.DEFAULT_SIZE, 386, Short.MAX_VALUE)
.addComponent(jProxySettingsButton)
.addComponent(cbFlag, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(txtServer, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 286, Short.MAX_VALUE)
.addComponent(txtUserName, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(txtPassword, javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(layout.createSequentialGroup()
.addComponent(txtPort, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnFind1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnFind3, javax.swing.GroupLayout.PREFERRED_SIZE, 42, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnFind2, javax.swing.GroupLayout.PREFERRED_SIZE, 42, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(69, 69, 69)))
.addGap(8, 8, 8)
.addComponent(btnFind, javax.swing.GroupLayout.DEFAULT_SIZE, 92, Short.MAX_VALUE))))
.addComponent(lblUserName)
.addComponent(lblPassword, javax.swing.GroupLayout.Alignment.TRAILING))))
.addGap(0, 0, 0)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(btnConnect, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(btnRegister, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btnForgotPassword, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 77, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(lblStatus, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(chkForceUpdateDB, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(jProxySettingsButton)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(chkAutoConnect, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(btnRegister)
.addComponent(panelFlag, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(txtServer, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(txtUserName, javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(txtPassword, javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(btnConnect)
.addComponent(txtPort, javax.swing.GroupLayout.PREFERRED_SIZE, 71, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 11, Short.MAX_VALUE)
.addComponent(lblFastConnect)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnForgotPassword)
.addComponent(btnFind1, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCancel)))
.addGap(26, 26, 26)))
.addComponent(btnFind3, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnFind2, javax.swing.GroupLayout.PREFERRED_SIZE, 40, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGap(0, 0, 0)
.addComponent(btnFind)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(lblServer)
@ -318,35 +408,37 @@ public class ConnectDialog extends MageDialog {
.addComponent(lblPort)
.addComponent(btnFind1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnFind2, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(btnFind3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(btnFind3, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblFastConnect))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(txtUserName, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblUserName))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(txtPassword, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblPassword))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(lblFlag, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(cbFlag, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(5, 5, 5)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(panelFlag, javax.swing.GroupLayout.PREFERRED_SIZE, 20, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(lblFlag, javax.swing.GroupLayout.PREFERRED_SIZE, 18, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(chkAutoConnect)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(chkForceUpdateDB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(jProxySettingsButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 47, Short.MAX_VALUE)
.addComponent(lblStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnConnect)
.addComponent(btnCancel)
.addComponent(btnForgotPassword))
.addGap(3, 3, 3)
.addComponent(btnRegister)
.addContainerGap())
.addComponent(lblStatus, javax.swing.GroupLayout.PREFERRED_SIZE, 24, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING, false)
.addGroup(layout.createSequentialGroup()
.addComponent(btnRegister, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnForgotPassword, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(btnConnect, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btnCancel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(23, 23, 23))
);
pack();
@ -640,6 +732,45 @@ public class ConnectDialog extends MageDialog {
this.txtPassword.setText(MagePreferences.getPassword(serverAddress));
}//GEN-LAST:event_connectWoogerworks
private void btnFlagSearchActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnFlagSearchActionPerformed
doFastFlagSearch();
}//GEN-LAST:event_btnFlagSearchActionPerformed
private void doFastFlagSearch(){
Choice choice = new ChoiceImpl(false);
// collect data from country combobox String[name][code]
Map<String, String> choiceItems = new LinkedHashMap<>();
DefaultComboBoxModel flagModel = (DefaultComboBoxModel)cbFlag.getModel();
String[] flagItem;
for(int i = 0; i < flagModel.getSize() - 1; i++){
flagItem = (String[])flagModel.getElementAt(i);
choiceItems.put(flagItem[1], flagItem[0]);
}
choice.setKeyChoices(choiceItems);
choice.setMessage("Select your coutry");
// current selection value restore
String needSelectValue = null;
flagItem = (String[])flagModel.getSelectedItem();
if (flagItem != null){
needSelectValue = flagItem[1];
}
// ask for new value
PickChoiceDialog dlg = new PickChoiceDialog();
dlg.setWindowSize(300, 500);
dlg.showDialog(choice, needSelectValue);
if(choice.isChosen()){
flagItem = new String[2];
flagItem[0] = choice.getChoiceValue();
flagItem[1] = choice.getChoiceKey();
flagModel.setSelectedItem(flagItem);
}
}
public String getServer() {
return this.txtServer.getText();
}
@ -655,18 +786,22 @@ public class ConnectDialog extends MageDialog {
private javax.swing.JButton btnFind1;
private javax.swing.JButton btnFind2;
private javax.swing.JButton btnFind3;
private javax.swing.JButton btnFlagSearch;
private javax.swing.JButton btnForgotPassword;
private javax.swing.JButton btnRegister;
private mage.client.util.gui.countryBox.CountryComboBox cbFlag;
private javax.swing.JCheckBox chkAutoConnect;
private javax.swing.JCheckBox chkForceUpdateDB;
private javax.swing.Box.Filler filler1;
private javax.swing.JButton jProxySettingsButton;
private javax.swing.JLabel lblFastConnect;
private javax.swing.JLabel lblFlag;
private javax.swing.JLabel lblPassword;
private javax.swing.JLabel lblPort;
private javax.swing.JLabel lblServer;
private javax.swing.JLabel lblStatus;
private javax.swing.JLabel lblUserName;
private javax.swing.JPanel panelFlag;
private javax.swing.JPasswordField txtPassword;
private javax.swing.JTextField txtPort;
private javax.swing.JTextField txtServer;

View file

@ -1,13 +1,6 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JInternalFrameFormInfo">
<Properties>
<Property name="resizable" type="boolean" value="true"/>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[280, 200]"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
</Properties>
<Form version="1.9" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JInternalFrameFormInfo">
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
</SyntheticProperties>
@ -26,19 +19,13 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Component id="jScrollPane1" alignment="0" pref="335" max="32767" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Component id="btnAutoSelect" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnOk" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btnCancel" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="lblMessage" alignment="0" pref="335" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="scrollList" alignment="1" max="32767" attributes="0"/>
<Component id="panelCommands" alignment="0" max="32767" attributes="0"/>
<Component id="panelHeader" alignment="0" max="32767" attributes="0"/>
<Component id="panelSearch" alignment="1" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -47,74 +34,173 @@
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="lblMessage" min="-2" pref="37" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
<Component id="panelHeader" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jScrollPane1" pref="158" max="32767" attributes="0"/>
<Component id="panelSearch" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btnCancel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnOk" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btnAutoSelect" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="10" max="-2" attributes="0"/>
<Component id="scrollList" pref="246" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="panelCommands" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="btnAutoSelect">
<Properties>
<Property name="text" type="java.lang.String" value="Auto select"/>
<Property name="toolTipText" type="java.lang.String" value="If you select an effect with &quot;Auto select&quot;, this effect will be selected the next time automatically first."/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnAutoSelectActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnCancel">
<Properties>
<Property name="text" type="java.lang.String" value="Cancel"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnCancelActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="btnOk">
<Properties>
<Property name="text" type="java.lang.String" value="OK"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btnOkActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="jScrollPane1">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Container class="javax.swing.JPanel" name="panelHeader">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="labelMessage" alignment="1" pref="210" max="32767" attributes="0"/>
<Component id="labelSubMessage" alignment="1" pref="210" max="32767" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="labelMessage" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="labelSubMessage" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JList" name="lstChoices">
<Component class="javax.swing.JLabel" name="labelMessage">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="5">
<StringItem index="0" value="Item 1"/>
<StringItem index="1" value="Item 2"/>
<StringItem index="2" value="Item 3"/>
<StringItem index="3" value="Item 4"/>
<StringItem index="4" value="Item 5"/>
</StringArray>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="text" type="java.lang.String" value="&lt;html&gt;&lt;div style=&apos;text-align: center;&apos;&gt;example long message example long message example long message example long message example long message&lt;/div&gt;&lt;/html&gt;"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="labelSubMessage">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.modules.form.editors2.FontEditor">
<FontInfo relative="true">
<Font bold="true" component="labelSubMessage" italic="true" property="font" relativeSize="true" size="0"/>
</FontInfo>
</Property>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="text" type="java.lang.String" value="&lt;html&gt;&lt;div style=&apos;text-align: center;&apos;&gt;example long message example long&lt;/div&gt;&lt;/html&gt;"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JLabel" name="lblMessage">
<Properties>
<Property name="horizontalAlignment" type="int" value="0"/>
<Property name="text" type="java.lang.String" value="message"/>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="panelSearch">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
<Component id="labelSearch" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="editSearch" max="32767" attributes="0"/>
<EmptySpace min="0" pref="0" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="labelSearch" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="editSearch" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace min="-2" pref="3" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="labelSearch">
<Properties>
<Property name="text" type="java.lang.String" value="Search:"/>
</Properties>
</Component>
<Component class="javax.swing.JTextField" name="editSearch">
<Properties>
<Property name="text" type="java.lang.String" value="sample search text"/>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JScrollPane" name="scrollList">
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JList" name="listChoices">
<Properties>
<Property name="model" type="javax.swing.ListModel" editor="org.netbeans.modules.form.editors2.ListModelEditor">
<StringArray count="3">
<StringItem index="0" value="item1"/>
<StringItem index="1" value="item2"/>
<StringItem index="2" value="item3"/>
</StringArray>
</Property>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value=""/>
</AuxValues>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="panelCommands">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Component id="btOK" linkSize="3" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="btCancel" linkSize="3" min="-2" pref="70" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btCancel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btOK" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JButton" name="btOK">
<Properties>
<Property name="text" type="java.lang.String" value="Choose"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btOKActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_AddingCodePost" type="java.lang.String" value="getRootPane().setDefaultButton(btOK);"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="btCancel">
<Properties>
<Property name="text" type="java.lang.String" value="Cancel"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btCancelActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View file

@ -1,43 +1,28 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
/*
* PickNumberDialog.java
*
* Created on Feb 25, 2010, 12:03:39 PM
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mage.client.dialog;
import java.awt.Dimension;
import java.awt.Point;
import java.util.Map;
import java.util.UUID;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.*;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.DefaultListModel;
import javax.swing.InputMap;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.KeyStroke;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import mage.choices.Choice;
import mage.client.MageFrame;
import mage.client.util.SettingsManager;
@ -46,37 +31,140 @@ import mage.client.util.gui.MageDialogState;
/**
*
* @author BetaSteward_at_googlemail.com
* @author JayDi85
*/
public class PickChoiceDialog extends MageDialog {
/** Creates new form PickNumberDialog */
public PickChoiceDialog() {
initComponents();
this.setModal(true);
}
Choice choice;
boolean autoSelect;
ArrayList<KeyValueItem> allItems = new ArrayList<>();
DefaultListModel<KeyValueItem> dataModel = new DefaultListModel();
final private static String HTML_TEMPLATE = "<html><div style='text-align: center;'>%s</div></html>";
public void showDialog(Choice choice) {
showDialog(choice, null, null, null);
}
public void showDialog(Choice choice, String startSelectionValue) {
showDialog(choice, null, null, startSelectionValue);
}
public void showDialog(Choice choice, UUID objectId, MageDialogState mageDialogState) {
this.lblMessage.setText("<html>" + choice.getMessage());
showDialog(choice, objectId, mageDialogState, null);
}
public void showDialog(Choice choice, UUID objectId, MageDialogState mageDialogState, String startSelectionValue) {
this.choice = choice;
this.autoSelect = false;
btnAutoSelect.setVisible(choice.isKeyChoice());
if (choice.isKeyChoice()){
ComboItem[] comboItems = new ComboItem[choice.getKeyChoices().size()];
int count = 0;
for (Map.Entry<String, String> entry : choice.getKeyChoices().entrySet()) {
comboItems[count] = new ComboItem(entry.getKey(), entry.getValue());
count++;
setLabelText(this.labelMessage, choice.getMessage());
setLabelText(this.labelSubMessage, choice.getSubMessage());
btCancel.setEnabled(!choice.isRequired());
// 2 modes: string or key-values
// sore data in allItems for inremental filtering
// http://logicbig.com/tutorials/core-java-tutorial/swing/list-filter/
this.allItems.clear();
if (choice.isKeyChoice()){
for (Map.Entry<String, String> entry: choice.getKeyChoices().entrySet()) {
this.allItems.add(new KeyValueItem(entry.getKey(), entry.getValue()));
}
this.lstChoices.setListData(comboItems);
} else {
this.lstChoices.setListData(choice.getChoices().toArray());
for (String value: choice.getChoices()){
this.allItems.add(new KeyValueItem(value, value));
}
}
// sorting
if(choice.isSortEnabled()){
Collections.sort(this.allItems, new Comparator<KeyValueItem>() {
@Override
public int compare(KeyValueItem o1, KeyValueItem o2) {
Integer n1 = choice.getSortData().get(o1.Key);
Integer n2 = choice.getSortData().get(o2.Key);
return n1.compareTo(n2);
}
});
}
// search
if(choice.isSearchEnabled())
{
panelSearch.setVisible(true);
this.editSearch.setText(choice.getSearchText());
}else{
panelSearch.setVisible(false);
this.editSearch.setText("");
}
// listeners for inremental filtering
editSearch.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void insertUpdate(DocumentEvent e) {
choice.setSearchText(editSearch.getText());
loadData();
}
@Override
public void removeUpdate(DocumentEvent e) {
choice.setSearchText(editSearch.getText());
loadData();
}
@Override
public void changedUpdate(DocumentEvent e) {
choice.setSearchText(editSearch.getText());
loadData();
}
});
// listeners for select up and down without edit focus lost
editSearch.addKeyListener(new KeyListener() {
@Override
public void keyTyped(KeyEvent e) {
//System.out.println("types");
}
@Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_UP){
doPrevSelect();
}else if(e.getKeyCode() == KeyEvent.VK_DOWN){
doNextSelect();
}
}
@Override
public void keyReleased(KeyEvent e) {
//System.out.println("released");
}
});
// listeners double click choose
listChoices.addMouseListener(new MouseAdapter() {
@Override
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 2){
doChoose();
}
}
});
// listeners for ESC close
if(!choice.isRequired()){
String cancelName = "cancel";
InputMap inputMap = getRootPane().getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT);
inputMap.put(KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), cancelName);
ActionMap actionMap = getRootPane().getActionMap();
actionMap.put(cancelName, new AbstractAction() {
public void actionPerformed(ActionEvent e) {
doCancel();
}
});
}
// window settings
MageFrame.getDesktop().add(this, JLayeredPane.PALETTE_LAYER);
if (mageDialogState != null) {
mageDialogState.setStateToDialog(this);
@ -87,154 +175,316 @@ public class PickChoiceDialog extends MageDialog {
GuiDisplayUtil.keepComponentInsideScreen(centered.x, centered.y, this);
}
// final load
loadData();
// start selection
if((startSelectionValue != null)){
int selectIndex = -1;
for(int i = 0; i < this.listChoices.getModel().getSize() - 1; i++){
KeyValueItem listItem = (KeyValueItem)this.listChoices.getModel().getElementAt(i);
if (listItem.Key.equals(startSelectionValue)){
selectIndex = i;
break;
}
}
if(selectIndex >= 0){
this.listChoices.setSelectedIndex(selectIndex);
this.listChoices.ensureIndexIsVisible(selectIndex);
}
}
this.setVisible(true);
}
public boolean isAutoSelect() {
return autoSelect;
public void setWindowSize(int width, int heigth){
this.setSize(new Dimension(width, heigth));
}
private void loadData(){
// load data to datamodel after filter or on startup
String filter = choice.getSearchText();
if (filter == null){ filter = ""; }
filter = filter.toLowerCase();
this.dataModel.clear();
for(KeyValueItem item: this.allItems){
if(!choice.isSearchEnabled() || item.Value.toLowerCase().contains(filter)){
this.dataModel.addElement(item);
}
}
}
private void setLabelText(JLabel label, String text){
if ((text != null) && !text.equals("")){
label.setText(String.format(HTML_TEMPLATE, text));
label.setVisible(true);
}else{
label.setText("");
label.setVisible(false);
}
}
private void doNextSelect(){
int newSel = this.listChoices.getSelectedIndex() + 1;
int maxSel = this.listChoices.getModel().getSize() - 1;
if(newSel <= maxSel){
this.listChoices.setSelectedIndex(newSel);
this.listChoices.ensureIndexIsVisible(newSel);
}
}
private void doPrevSelect(){
int newSel = this.listChoices.getSelectedIndex() - 1;
if(newSel >= 0){
this.listChoices.setSelectedIndex(newSel);
this.listChoices.ensureIndexIsVisible(newSel);
}
}
public void setChoice() {
if (this.lstChoices.getSelectedValue() == null) {
choice.clearChoice();
private void doChoose(){
if(setChoice()){
this.hideDialog();
}
}
private void doCancel(){
this.listChoices.clearSelection();
this.choice.clearChoice();
hideDialog();
}
/**
* Creates new form PickChoiceDialog
*/
public PickChoiceDialog() {
initComponents();
this.listChoices.setModel(dataModel);
this.setModal(true);
}
public boolean setChoice() {
KeyValueItem item = (KeyValueItem)this.listChoices.getSelectedValue();
// auto select one item (after incemental filtering)
if((item == null) && (this.listChoices.getModel().getSize() == 1)){
this.listChoices.setSelectedIndex(0);
item = (KeyValueItem)this.listChoices.getSelectedValue();
}
if (choice.isKeyChoice()) {
ComboItem item = (ComboItem)this.lstChoices.getSelectedValue();
if (item != null) {
choice.setChoiceByKey(item.getValue());
} else {
choice.clearChoice();
if(item != null){
if(choice.isKeyChoice()){
choice.setChoiceByKey(item.getKey());
}else{
choice.setChoice(item.getKey());
}
} else {
choice.setChoice((String)this.lstChoices.getSelectedValue());
return true;
}else{
choice.clearChoice();
return false;
}
}
class KeyValueItem
{
private final String Key;
private final String Value;
public KeyValueItem(String value, String label) {
this.Key = value;
this.Value = label;
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
public String getKey() {
return this.Key;
}
public String getValue() {
return this.Value;
}
@Override
public String toString() {
return this.Value;
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
btnAutoSelect = new javax.swing.JButton();
btnCancel = new javax.swing.JButton();
btnOk = new javax.swing.JButton();
jScrollPane1 = new javax.swing.JScrollPane();
lstChoices = new javax.swing.JList();
lblMessage = new javax.swing.JLabel();
panelHeader = new javax.swing.JPanel();
labelMessage = new javax.swing.JLabel();
labelSubMessage = new javax.swing.JLabel();
panelSearch = new javax.swing.JPanel();
labelSearch = new javax.swing.JLabel();
editSearch = new javax.swing.JTextField();
scrollList = new javax.swing.JScrollPane();
listChoices = new javax.swing.JList();
panelCommands = new javax.swing.JPanel();
btOK = new javax.swing.JButton();
btCancel = new javax.swing.JButton();
setResizable(true);
setMinimumSize(new java.awt.Dimension(280, 200));
setName(""); // NOI18N
labelMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
labelMessage.setText("<html><div style='text-align: center;'>example long message example long message example long message example long message example long message</div></html>");
btnAutoSelect.setText("Auto select");
btnAutoSelect.setToolTipText("If you select an effect with \"Auto select\", this effect will be selected the next time automatically first.");
btnAutoSelect.addActionListener(evt -> btnAutoSelectActionPerformed(evt));
labelSubMessage.setFont(labelSubMessage.getFont().deriveFont((labelSubMessage.getFont().getStyle() | java.awt.Font.ITALIC) | java.awt.Font.BOLD));
labelSubMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
labelSubMessage.setText("<html><div style='text-align: center;'>example long message example long</div></html>");
btnCancel.setText("Cancel");
btnCancel.addActionListener(evt -> btnCancelActionPerformed(evt));
javax.swing.GroupLayout panelHeaderLayout = new javax.swing.GroupLayout(panelHeader);
panelHeader.setLayout(panelHeaderLayout);
panelHeaderLayout.setHorizontalGroup(
panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelHeaderLayout.createSequentialGroup()
.addGroup(panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(labelMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 210, Short.MAX_VALUE)
.addComponent(labelSubMessage, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 210, Short.MAX_VALUE))
.addGap(0, 0, 0))
);
panelHeaderLayout.setVerticalGroup(
panelHeaderLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelHeaderLayout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(labelMessage)
.addGap(0, 0, 0)
.addComponent(labelSubMessage))
);
btnOk.setText("OK");
btnOk.addActionListener(evt -> btnOkActionPerformed(evt));
labelSearch.setText("Search:");
lstChoices.setModel(new javax.swing.AbstractListModel() {
final String[] strings = { "Item 1", "Item 2", "Item 3", "Item 4", "Item 5" };
editSearch.setText("sample search text");
javax.swing.GroupLayout panelSearchLayout = new javax.swing.GroupLayout(panelSearch);
panelSearch.setLayout(panelSearchLayout);
panelSearchLayout.setHorizontalGroup(
panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelSearchLayout.createSequentialGroup()
.addGap(0, 0, 0)
.addComponent(labelSearch)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(editSearch)
.addGap(0, 0, 0))
);
panelSearchLayout.setVerticalGroup(
panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelSearchLayout.createSequentialGroup()
.addGap(3, 3, 3)
.addGroup(panelSearchLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(labelSearch)
.addComponent(editSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGap(3, 3, 3))
);
listChoices.setModel(new javax.swing.AbstractListModel() {
String[] strings = { "item1", "item2", "item3" };
public int getSize() { return strings.length; }
public Object getElementAt(int i) { return strings[i]; }
});
jScrollPane1.setViewportView(lstChoices);
scrollList.setViewportView(listChoices);
lblMessage.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
lblMessage.setText("message");
btOK.setText("Choose");
btOK.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btOKActionPerformed(evt);
}
});
btCancel.setText("Cancel");
btCancel.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
btCancelActionPerformed(evt);
}
});
javax.swing.GroupLayout panelCommandsLayout = new javax.swing.GroupLayout(panelCommands);
panelCommands.setLayout(panelCommandsLayout);
panelCommandsLayout.setHorizontalGroup(
panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelCommandsLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btOK)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
panelCommandsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btCancel, btOK});
panelCommandsLayout.setVerticalGroup(
panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelCommandsLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btCancel)
.addComponent(btOK))
.addContainerGap())
);
getRootPane().setDefaultButton(btOK);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 335, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addComponent(btnAutoSelect)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnOk)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btnCancel))
.addComponent(lblMessage, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 335, Short.MAX_VALUE))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(scrollList, javax.swing.GroupLayout.Alignment.TRAILING)
.addComponent(panelCommands, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(panelHeader, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(panelSearch, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(lblMessage, javax.swing.GroupLayout.PREFERRED_SIZE, 37, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap()
.addComponent(panelHeader, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 158, Short.MAX_VALUE)
.addComponent(panelSearch, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btnCancel)
.addComponent(btnOk)
.addComponent(btnAutoSelect))
.addGap(10, 10, 10))
.addComponent(scrollList, javax.swing.GroupLayout.DEFAULT_SIZE, 246, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(panelCommands, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void btnOkActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnOkActionPerformed
setChoice();
this.hideDialog();
}//GEN-LAST:event_btnOkActionPerformed
private void btOKActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btOKActionPerformed
doChoose();
}//GEN-LAST:event_btOKActionPerformed
private void btnCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnCancelActionPerformed
this.lstChoices.clearSelection();
this.choice.clearChoice();
this.hideDialog();
}//GEN-LAST:event_btnCancelActionPerformed
private void btCancelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btCancelActionPerformed
doCancel();
}//GEN-LAST:event_btCancelActionPerformed
private void btnAutoSelectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_btnAutoSelectActionPerformed
this.autoSelect = true;
setChoice();
this.hideDialog();
}//GEN-LAST:event_btnAutoSelectActionPerformed
/**
* Closes the dialog
*/
private void closeDialog(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_closeDialog
doCancel();
}//GEN-LAST:event_closeDialog
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btnAutoSelect;
private javax.swing.JButton btnCancel;
private javax.swing.JButton btnOk;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JLabel lblMessage;
private javax.swing.JList lstChoices;
private javax.swing.JButton btCancel;
private javax.swing.JButton btOK;
private javax.swing.JTextField editSearch;
private javax.swing.JLabel labelMessage;
private javax.swing.JLabel labelSearch;
private javax.swing.JLabel labelSubMessage;
private javax.swing.JList listChoices;
private javax.swing.JPanel panelCommands;
private javax.swing.JPanel panelHeader;
private javax.swing.JPanel panelSearch;
private javax.swing.JScrollPane scrollList;
// End of variables declaration//GEN-END:variables
}
class ComboItem {
private final String value;
private final String label;
public ComboItem(String value, String label) {
this.value = value;
this.label = label;
}
public String getValue() {
return this.value;
}
public String getLabel() {
return this.label;
}
@Override
public String toString() {
return label;
}
}

View file

@ -1220,14 +1220,17 @@ public final class GamePanel extends javax.swing.JPanel {
public void getChoice(Choice choice, UUID objectId) {
hideAll();
// TODO: remember last choices and search incremental for same events?
PickChoiceDialog pickChoice = new PickChoiceDialog();
pickChoice.showDialog(choice, objectId, choiceWindowState);
if (choice.isKeyChoice()) {
SessionHandler.sendPlayerString(gameId, choice.getChoiceKey());
/* // old code, auto complete was for auto scripting?
if (pickChoice.isAutoSelect()) {
SessionHandler.sendPlayerString(gameId, '#' + choice.getChoiceKey());
} else {
SessionHandler.sendPlayerString(gameId, choice.getChoiceKey());
}
}*/
} else {
SessionHandler.sendPlayerString(gameId, choice.getChoice());
}

View file

@ -27,44 +27,34 @@
*/
package mage.client.util.gui.countryBox;
import java.awt.Color;
import java.awt.Component;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import java.awt.*;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
import javax.swing.plaf.basic.BasicComboBoxEditor;
/**
* Editor for JComboBox
* @author wwww.codejava.net
* @author wwww.codejava.net, JayDi85
*
*/
public class CountryItemEditor extends BasicComboBoxEditor {
private final JPanel panel = new JPanel();
private final JLabel labelItem = new JLabel();
private String selectedValue;
private String selectedImage;
private String[] editValue = new String[2];
public CountryItemEditor() {
panel.setLayout(new GridBagLayout());
GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.HORIZONTAL;
constraints.weightx = 1.0;
// constraints.insets = new Insets(2, 5, 2, 2);
constraints.insets = new Insets(0, 5, 0, 0);
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
panel.setBackground(new Color(0, 100,190, 255));
panel.add(labelItem);
labelItem.setAlignmentX(Component.LEFT_ALIGNMENT);
labelItem.setMaximumSize(new Dimension(Short.MAX_VALUE, Short.MAX_VALUE));
labelItem.setBorder(new EmptyBorder(0, 5, 0, 0));
labelItem.setOpaque(false);
labelItem.setHorizontalAlignment(JLabel.LEFT);
labelItem.setForeground(Color.WHITE);
panel.add(labelItem, constraints);
// panel.setBackground(Color.WHITE);
panel.setBackground(new Color(0, 100,190, 255));
selectedValue = null;
editValue = null;
}
@Override
@ -74,21 +64,26 @@ public class CountryItemEditor extends BasicComboBoxEditor {
@Override
public Object getItem() {
return this.selectedValue;
return this.editValue;
}
public String getImageItem() {
return this.selectedImage;
}
return this.editValue[1];
}
@Override
public void setItem(Object item) {
if (item == null || !(item instanceof String[])) {
return;
}
String[] countryItem = (String[]) item;
selectedValue = countryItem[0];
selectedImage = countryItem[1];
labelItem.setText(selectedValue);
labelItem.setIcon(new ImageIcon(getClass().getResource("/flags/"+ countryItem[1] + ".png")));
String[] newItem = (String[])item;
editValue = new String[2];
editValue[0] = newItem[0];
editValue[1] = newItem[1];
labelItem.setText(editValue[0]);
labelItem.setIcon(new ImageIcon(getClass().getResource("/flags/"+ editValue[1] + ".png")));
}
}

View file

@ -50,8 +50,10 @@ public class MyComboBoxRenderer extends JLabel implements ListCellRenderer {
@Override
public Component getListCellRendererComponent(JList list, Object value,
int index, boolean isSelected, boolean cellHasFocus) {
setText(value.toString());
String[] val = (String[]) value;
setText(val[0]);
return this;
}
}

View file

@ -6,22 +6,20 @@ import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.client.constants.Constants;
import mage.constants.Rarity;
import org.apache.log4j.Logger;
import org.mage.plugins.card.dl.DownloadJob;
import static org.mage.plugins.card.dl.DownloadJob.fromURL;
import static org.mage.plugins.card.dl.DownloadJob.toFile;
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
import org.apache.log4j.Logger;
public class GathererSets implements Iterable<DownloadJob> {
private class CheckResult {
String code;
ExpansionSet set;
boolean haveCommon;
@ -69,12 +67,12 @@ public class GathererSets implements Iterable<DownloadJob> {
//"ARENA" -- is't many set with different codes, not one
"CLASH", "CP", "DD3GVL", "DPA", "EURO", "FNMP", "GPX", "GRC", "GUR", "H17", "JR", "MBP", "MGDC", "MLP", "MPRP", "MPS-AKH", "PTC", "S00", "S99", "SUS", "SWS", "UGIN", "UGL", "V10", "V17", "WMCQ", // need to fix
"H09", "PD2", "PD3", "UNH", "CM1", "E02", "V11", "M25", "UST", "IMA", "DD2", "EVG", "DDC", "DDE", "DDD", "DDT", "8EB", "9EB", "CHR" // ok
// current testing
// current testing
};
private static final String[] symbolsBasicWithMyth = {"M10", "M11", "M12", "M13", "M14", "M15", "ORI",
"DDF", "DDG", "DDH", "DDI", "DDJ", "DDK", "DDL", "DDM", "DDN",
"DD3DVD", "DD3JVC", "DDO", "DDP", "DDQ", "DDR", "DDS",
"DD3DVD", "DD3JVC", "DDO", "DDP", "DDQ", "DDR", "DDS", "DDT",
"ALA", "CON", "ARB",
"ZEN", "WWK", "ROE",
"SOM", "MBS", "NPH",
@ -155,7 +153,7 @@ public class GathererSets implements Iterable<DownloadJob> {
outDir = new File(getImagesDir() + Constants.RESOURCE_PATH_SYMBOLS);
if (!outDir.exists()){
if (!outDir.exists()) {
outDir.mkdirs();
}
}
@ -163,6 +161,7 @@ public class GathererSets implements Iterable<DownloadJob> {
// checks for wrong card settings and support (easy to control what all good)
private static final HashMap<String, CheckResult> setsToDownload = new HashMap<>();
private static final HashMap<String, String> codesToIgnoreCheck = new HashMap<>();
static {
// xMage have inner sets for 8th and 9th Edition for booster workaround (cards from core game do not include in boosters)
// see https://mtg.gamepedia.com/8th_Edition/Core_Game
@ -172,7 +171,7 @@ public class GathererSets implements Iterable<DownloadJob> {
}
private void CheckSearchResult(String searchCode, ExpansionSet foundedExp, boolean canDownloadTask,
boolean haveCommon, boolean haveUncommon, boolean haveRare, boolean haveMyth){
boolean haveCommon, boolean haveUncommon, boolean haveRare, boolean haveMyth) {
// duplicated in settings
CheckResult res = setsToDownload.get(searchCode);
@ -190,9 +189,8 @@ public class GathererSets implements Iterable<DownloadJob> {
}
// checks for founded sets only
// to early to download
if (!canDownloadTask){
if (!canDownloadTask) {
Calendar c = Calendar.getInstance();
c.setTime(foundedExp.getReleaseDate());
c.add(Calendar.DATE, -1 * DAYS_BEFORE_RELEASE_TO_DOWNLOAD);
@ -201,14 +199,14 @@ public class GathererSets implements Iterable<DownloadJob> {
}
}
private void AnalyseSearchResult(){
private void AnalyseSearchResult() {
// analyze supported sets and show wrong settings
Date startedDate = new Date();
for (ExpansionSet set : Sets.getInstance().values()) {
// ignore some inner sets
if (codesToIgnoreCheck.get(set.getCode()) != null){
if (codesToIgnoreCheck.get(set.getCode()) != null) {
continue;
}
@ -239,21 +237,20 @@ public class GathererSets implements Iterable<DownloadJob> {
//*/
// 3. info: sets with alternative numbers
for(ExpansionSet.SetCardInfo card: set.getSetCardInfo()){
if (String.valueOf(card.getCardNumberAsInt()).length() != card.getCardNumber().length()){
for (ExpansionSet.SetCardInfo card : set.getSetCardInfo()) {
if (String.valueOf(card.getCardNumberAsInt()).length() != card.getCardNumber().length()) {
logger.info(String.format("Symbols: set have alternative card but do not config to it: %s (%s)", set.getCode(), set.getName()));
break;
}
}
// 4. info: sets with missing cards for boosters (todo: what about +20 number for alternative land arts?)
if (set.getMaxCardNumberInBooster() != Integer.MAX_VALUE)
{
for(ExpansionSet.SetCardInfo card: set.getSetCardInfo()){
if (card.getCardNumberAsInt() > set.getMaxCardNumberInBooster()){
if (set.getMaxCardNumberInBooster() != Integer.MAX_VALUE) {
for (ExpansionSet.SetCardInfo card : set.getSetCardInfo()) {
if (card.getCardNumberAsInt() > set.getMaxCardNumberInBooster()) {
if (card.getRarity() == Rarity.LAND) {
logger.info(String.format("Symbols: set's booster have land above max card number: %s (%s), %s - %s", set.getCode(), set.getName(), card.getCardNumber(), card.getName()));
}else {
} else {
logger.info(String.format("Symbols: set's booster missing nonland card:: %s (%s), %s - %s", set.getCode(), set.getName(), card.getCardNumber(), card.getName()));
}
}
@ -337,4 +334,4 @@ public class GathererSets implements Iterable<DownloadJob> {
String url = "http://gatherer.wizards.com/Handlers/Image.ashx?type=symbol&set=" + set + "&size=small&rarity=" + urlRarity;
return new DownloadJob(set + '-' + rarity, fromURL(url), toFile(dst));
}
}
}

View file

@ -262,6 +262,7 @@ public enum MagicCardsImageSource implements CardImageSource {
put("DDQ", "duel-decks-blessed-vs-cursed");
put("DDR", "duel-decks-nissa-vs-ob-nixilis");
put("DDS", "duel-decks-mind-vs-might");
put("DDS", "duel-decks-merfolk-vs-goblin");
put("DGM", "dragons-maze");
put("DKA", "dark-ascension");
put("DRB", "from-the-vault-dragons");

View file

@ -45,6 +45,9 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.prefs.Preferences;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.client.MageFrame;
import mage.client.dialog.PreferencesDialog;
import mage.remote.Connection;
@ -332,6 +335,7 @@ public enum WizardCardsImageSource implements CardImageSource {
setsAliases.put("DDQ", "Duel Decks: Blessed vs. Cursed");
setsAliases.put("DDR", "Duel Decks: Nissa vs. Ob Nixilis");
setsAliases.put("DDS", "Duel Decks: Mind vs. Might");
setsAliases.put("DDT", "Duel Decks: Merfolk vs. Goblins");
setsAliases.put("DGM", "Dragon's Maze");
setsAliases.put("DIS", "Dissension");
setsAliases.put("DKA", "Dark Ascension");
@ -473,6 +477,56 @@ public enum WizardCardsImageSource implements CardImageSource {
return null;
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
String collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet);
}
if (card.isFlippedSide()) { //doesn't support rotated images
return null;
}
String setNames = setsAliases.get(cardSet);
if (setNames != null) {
Map<String, String> setLinks = sets.computeIfAbsent(cardSet, k -> getSetLinks(cardSet));
if (setLinks == null || setLinks.isEmpty()) {
return null;
}
String link = setLinks.get(card.getDownloadName().toLowerCase());
if (link == null) {
int length = collectorId.length();
if (Character.isLetter(collectorId.charAt(length - 1))) {
length -= 1;
}
int number = Integer.parseInt(collectorId.substring(0, length));
if (number > 0) {
String key = card.getDownloadName().toLowerCase() + number;
link = setLinks.get(key);
}
if (link == null) {
List<String> l = new ArrayList<>(setLinks.values());
if (l.size() >= number) {
link = l.get(number - 1);
} else {;
link = l.get(number - 21);
if (link != null) {
link = link.replace(Integer.toString(number - 20), (Integer.toString(number - 20) + 'a'));
}
}
}
}
if (link != null && !link.startsWith("http://")) {
link = "http://gatherer.wizards.com" + link;
}
return link;
}
return null;
}
private Map<String, String> getSetLinks(String cardSet) {
LinkedHashMap<String, String> setLinks = new LinkedHashMap<>();
ExecutorService executor = Executors.newFixedThreadPool(10);
@ -503,8 +557,12 @@ public enum WizardCardsImageSource implements CardImageSource {
}
String cardName = normalizeName(cardsImages.get(i).attr("alt"));
if (cardName != null && !cardName.isEmpty()) {
Runnable task = new GetImageLinkTask(multiverseId, cardName, preferedLanguage, setLinks);
executor.execute(task);
if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) {
getLandVariations(setLinks, cardSet, multiverseId, cardName);
} else {
Integer preferedMultiverseId = getLocalizedMultiverseId(preferedLanguage, multiverseId);
setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId));
}
}
}
page++;
@ -553,23 +611,38 @@ public enum WizardCardsImageSource implements CardImageSource {
return doc;
}
private Map<String, String> getLandVariations(int multiverseId, String cardName) throws IOException, NumberFormatException {
private void getLandVariations(LinkedHashMap<String, String> setLinks, String cardSet, int multiverseId, String cardName) throws IOException, NumberFormatException {
CardCriteria criteria = new CardCriteria();
criteria.name(cardName);
criteria.setCodes(cardSet);
List<CardInfo> cards = CardRepository.instance.findCards(criteria);
String urlLandDocument = "http://gatherer.wizards.com/Pages/Card/Details.aspx?multiverseid=" + multiverseId;
Document landDoc = getDocument(urlLandDocument);
Elements variations = landDoc.select("a.variationlink");
Map<String, String> links = new HashMap<>();
if (!variations.isEmpty()) {
int landNumber = 1;
if (variations.size() > cards.size()) {
logger.warn("More links for lands than cards in DB found for set: " + cardSet + " Name: " + cardName);
}
if (variations.size() < cards.size()) {
logger.warn("Less links for lands than cards in DB found for set: " + cardSet + " Name: " + cardName);
}
int iteration = 0;
for (Element variation : variations) {
String colNumb = String.valueOf(iteration);
if (cards.size() > iteration) {
CardInfo cardInfo = cards.get(iteration);
if (cardInfo != null) {
colNumb = cardInfo.getCardNumber();
}
}
Integer landMultiverseId = Integer.parseInt(variation.attr("href").replaceAll("[^\\d]", ""));
links.put((cardName + landNumber).toLowerCase(), generateLink(landMultiverseId));
landNumber++;
setLinks.put((cardName).toLowerCase() + colNumb, generateLink(landMultiverseId));
iteration++;
}
} else {
links.put(cardName.toLowerCase(), generateLink(multiverseId));
setLinks.put(cardName.toLowerCase(), generateLink(multiverseId));
}
return links;
}
private static String generateLink(int landMultiverseId) {
@ -627,50 +700,6 @@ public enum WizardCardsImageSource implements CardImageSource {
.replace("Hintreland Scourge", "Hinterland Scourge");
}
@Override
public String generateURL(CardDownloadData card) throws Exception {
String collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet);
}
if (card.isFlippedSide()) { //doesn't support rotated images
return null;
}
String setNames = setsAliases.get(cardSet);
if (setNames != null) {
Map<String, String> setLinks = sets.computeIfAbsent(cardSet, k -> getSetLinks(cardSet));
if (setLinks == null || setLinks.isEmpty()) {
return null;
}
String link = setLinks.get(card.getDownloadName().toLowerCase());
if (link == null) {
int length = collectorId.length();
if (Character.isLetter(collectorId.charAt(length - 1))) {
length -= 1;
}
int number = Integer.parseInt(collectorId.substring(0, length));
List<String> l = new ArrayList<>(setLinks.values());
if (l.size() >= number) {
link = l.get(number - 1);
} else {;
link = l.get(number - 21);
if (link != null) {
link = link.replace(Integer.toString(number - 20), (Integer.toString(number - 20) + 'a'));
}
}
}
if (link != null && !link.startsWith("http://")) {
link = "http://gatherer.wizards.com" + link;
}
return link;
}
return null;
}
@Override
public String generateTokenUrl(CardDownloadData card) {
return null;
@ -681,44 +710,43 @@ public enum WizardCardsImageSource implements CardImageSource {
return 60.0f;
}
private final class GetImageLinkTask implements Runnable {
private int multiverseId;
private String cardName;
private String preferedLanguage;
private LinkedHashMap setLinks;
public GetImageLinkTask(int multiverseId, String cardName, String preferedLanguage, LinkedHashMap setLinks) {
try {
this.multiverseId = multiverseId;
this.cardName = cardName;
this.preferedLanguage = preferedLanguage;
this.setLinks = setLinks;
} catch (Exception ex) {
logger.error(ex.getMessage());
logger.error("multiverseId: " + multiverseId);
logger.error("cardName: " + cardName);
logger.error("preferedLanguage: " + preferedLanguage);
logger.error("setLinks: " + setLinks.toString());
}
}
@Override
public void run() {
try {
if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) {
setLinks.putAll(getLandVariations(multiverseId, cardName));
} else {
Integer preferedMultiverseId = getLocalizedMultiverseId(preferedLanguage, multiverseId);
setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId));
}
} catch (IOException | NumberFormatException ex) {
logger.error("Exception when parsing the wizards page: " + ex.getMessage());
}
}
}
// private final class GetImageLinkTask implements Runnable {
//
// private int multiverseId;
// private String cardName;
// private String preferedLanguage;
// private LinkedHashMap setLinks;
//
// public GetImageLinkTask(int multiverseId, String cardName, String preferedLanguage, LinkedHashMap setLinks) {
// try {
// this.multiverseId = multiverseId;
// this.cardName = cardName;
// this.preferedLanguage = preferedLanguage;
// this.setLinks = setLinks;
// } catch (Exception ex) {
// logger.error(ex.getMessage());
// logger.error("multiverseId: " + multiverseId);
// logger.error("cardName: " + cardName);
// logger.error("preferedLanguage: " + preferedLanguage);
// logger.error("setLinks: " + setLinks.toString());
// }
// }
//
// @Override
// public void run() {
// try {
// if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) {
// setLinks.putAll(getLandVariations(multiverseId, cardName));
// } else {
// Integer preferedMultiverseId = getLocalizedMultiverseId(preferedLanguage, multiverseId);
// setLinks.put(cardName.toLowerCase(), generateLink(preferedMultiverseId));
// }
// } catch (IOException | NumberFormatException ex) {
// logger.error("Exception when parsing the wizards page: " + ex.getMessage());
// }
// }
//
// }
@Override
public int getTotalImages() {
return -1;

View file

@ -327,7 +327,7 @@ public final class ImageCache {
BufferedImage cornerImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
// corner
float ROUNDED_CORNER_SIZE = 0.15f; // see CardPanelComponentImpl
float ROUNDED_CORNER_SIZE = 0.11f; // see CardPanelComponentImpl
int cornerSizeBorder = Math.max(4, Math.round(image.getWidth() * ROUNDED_CORNER_SIZE));
// corner mask

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 595 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 831 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2 KiB

View file

@ -0,0 +1,43 @@
/*
* Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.deck;
import mage.cards.decks.Constructed;
/**
*
* @author LevelX2
*/
public class IxalanBlock extends Constructed {
public IxalanBlock() {
super("Constructed - Ixalan Block");
setCodes.add("XLN");
setCodes.add("RIX");
}
}

View file

@ -0,0 +1,43 @@
/*
* Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.deck;
import mage.cards.decks.Constructed;
/**
*
* @author LevelX2
*/
public class LorwynBlock extends Constructed {
public LorwynBlock() {
super("Constructed - Lorwyn Block");
setCodes.add("LRW");
setCodes.add("MOR");
}
}

View file

@ -78,7 +78,7 @@
<gameType name="Penny Dreadful Commander Free For All" jar="mage-game-pennydreadfulcommanderfreeforall.jar" className="mage.game.PennyDreadfulCommanderFreeForAllMatch" typeName="mage.game.PennyDreadfulCommanderFreeForAllType"/>
<gameType name="Freeform Commander Free For All" jar="mage-game-freeformcommanderfreeforall.jar" className="mage.game.FreeformCommanderFreeForAllMatch" typeName="mage.game.FreeformCommanderFreeForAllType"/>
<gameType name="Momir Basic Two Player Duel" jar="mage-game-momirduel.jar" className="mage.game.MomirDuelMatch" typeName="mage.game.MomirDuelType"/>
<gameType name="Momir Basic Free For All" jar="mage-game-momir.jar" className="mage.game.MomirFreeForAllMatch" typeName="mage.game.MomirFreeForAllType"/>
<gameType name="Momir Basic Free For All" jar="mage-game-momir.jar" className="mage.game.MomirFreeForAllMatch" typeName="mage.game.MomirFreeForAllType"/>
</gameTypes>
<tournamentTypes>
<tournamentType name="Constructed Elimination" jar="mage-tournament-constructed.jar" className="mage.tournament.ConstructedEliminationTournament" typeName="mage.tournament.ConstructedEliminationTournamentType"/>
@ -156,9 +156,11 @@
<deckType name="Block Constructed - Amonkhet" jar="mage-deck-constructed.jar" className="mage.deck.AmonkhetBlock"/>
<deckType name="Block Constructed - Battle for Zendikar" jar="mage-deck-constructed.jar" className="mage.deck.BattleForZendikarBlock"/>
<deckType name="Block Constructed - Innistrad" jar="mage-deck-constructed.jar" className="mage.deck.InnistradBlock"/>
<deckType name="Block Constructed - Ixalan" jar="mage-deck-constructed.jar" className="mage.deck.IxalanBlock"/>
<deckType name="Block Constructed - Kaladesh" jar="mage-deck-constructed.jar" className="mage.deck.KaladeshBlock"/>
<deckType name="Block Constructed - Kamigawa" jar="mage-deck-constructed.jar" className="mage.deck.KamigawaBlock"/>
<deckType name="Block Constructed - Khans of Tarkir" jar="mage-deck-constructed.jar" className="mage.deck.KhansOfTarkirBlock"/>
<deckType name="Block Constructed - Lorwyn" jar="mage-deck-constructed.jar" className="mage.deck.LorwynBlock"/>
<deckType name="Block Constructed - Return to Ravnica" jar="mage-deck-constructed.jar" className="mage.deck.ReturnToRavnicaBlock"/>
<deckType name="Block Constructed - Scars of Mirrodin" jar="mage-deck-constructed.jar" className="mage.deck.ScarsOfMirrodinBlock"/>
<deckType name="Block Constructed - Shadowmoor" jar="mage-deck-constructed.jar" className="mage.deck.ShadowmoorBlock"/>

View file

@ -149,18 +149,20 @@
<deckType name="Variant Magic - Momir Basic" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.Momir"/>
<deckType name="Variant Magic - Penny Dreadful Commander" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.PennyDreadfulCommander"/>
<deckType name="Variant Magic - Freeform Commander" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.FreeformCommander"/>
<deckType name="Block Constructed - Amonkhet" jar="mage-deck-constructed.jar" className="mage.deck.AmonkhetBlock"/>
<deckType name="Block Constructed - Battle for Zendikar" jar="mage-deck-constructed.jar" className="mage.deck.BattleForZendikarBlock"/>
<deckType name="Block Constructed - Amonkhet" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.AmonkhetBlock"/>
<deckType name="Block Constructed - Battle for Zendikar" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.BattleForZendikarBlock"/>
<deckType name="Block Constructed - Innistrad" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.InnistradBlock"/>
<deckType name="Block Constructed - Ixalan" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.IxalanBlock"/>
<deckType name="Block Constructed - Kaladesh" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.KaladeshBlock"/>
<deckType name="Block Constructed - Kamigawa" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.KamigawaBlock"/>
<deckType name="Block Constructed - Khans of Tarkir" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.KhansOfTarkirBlock"/>
<deckType name="Block Constructed - Lorwyn" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.LorwynBlock"/>
<deckType name="Block Constructed - Return to Ravnica" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.ReturnToRavnicaBlock"/>
<deckType name="Block Constructed - Scars of Mirrodin" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.ScarsOfMirrodinBlock"/>
<deckType name="Block Constructed - Shadowmoor" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.ShadowmoorBlock"/>
<deckType name="Block Constructed - Shadows over Innistrad" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.ShadowsOverInnistradBlock"/>
<deckType name="Block Constructed - Shards of Alara" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.ShardsOfAlaraBlock"/>
<deckType name="Block Constructed - Theros" jar="mage-deck-constructed.jar" className="mage.deck.TherosBlock"/>
<deckType name="Block Constructed - Theros" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.TherosBlock"/>
<deckType name="Block Constructed - Zendikar" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.ZendikarBlock"/>
<deckType name="Block Constructed Custom - Star Wars" jar="mage-deck-constructed-${project.version}.jar" className="mage.deck.StarWarsBlock"/>
<deckType name="Limited" jar="mage-deck-limited-${project.version}.jar" className="mage.deck.Limited"/>

View file

@ -65,7 +65,7 @@ public class AphettoDredging extends CardImpl {
if (ability instanceof SpellAbility) {
Player controller = game.getPlayer(ability.getControllerId());
if (controller != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(game.getObject(ability.getSourceId()));
while (!controller.choose(Outcome.PutCreatureInPlay, typeChoice, game)) {
if (!controller.canRespond()) {
return;

View file

@ -98,7 +98,7 @@ class BloodlineShamanEffect extends OneShotEffect {
MageObject sourceObject = game.getObject(source.getSourceId());
if (controller != null) {
// Choose a creature type.
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
while (!controller.choose(outcome, typeChoice, game)) {
if (!controller.canRespond()) {
return false;

View file

@ -129,7 +129,7 @@ class ChooseCreatureTypeEffect extends OneShotEffect { // code by LevelX2, but t
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = game.getObject(source.getSourceId());
if (controller != null && mageObject != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(mageObject);
while (!controller.choose(outcome, typeChoice, game)) {
if (!controller.canRespond()) {
return false;

View file

@ -44,7 +44,7 @@ import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.choices.ChoiceCreatureType;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
@ -154,9 +154,8 @@ class CallousOppressorChooseCreatureTypeEffect extends OneShotEffect {
}
Player opponent = game.getPlayer(target.getFirstTarget());
if (opponent != null && mageObject != null) {
Choice typeChoice = new ChoiceImpl(true);
Choice typeChoice = new ChoiceCreatureType(mageObject);
typeChoice.setMessage("Choose creature type");
typeChoice.setChoices(SubType.getCreatureTypes(false).stream().map(SubType::toString).collect(Collectors.toCollection(LinkedHashSet::new)));
while (!opponent.choose(outcome, typeChoice, game)) {
if (!opponent.canRespond()) {
return false;

View file

@ -68,7 +68,7 @@ public class ChargingCinderhorn extends CardImpl {
// At the beginning of each player's end step, if no creatures attacked this turn, put a fury counter on Charging Cinderhorn. Then Charging Cinderhorn deals damage equal to the number of fury counters on it to that player.
ChargingCinderhornDamageTargetEffect effect = new ChargingCinderhornDamageTargetEffect();
effect.setText("if no creatures attacked this turn, put a fury counter on {this}. Then {this} deals damage equal to the number of fury counters on it to that player");
effect.setText("put a fury counter on {this}. Then {this} deals damage equal to the number of fury counters on it to that player");
BeginningOfEndStepTriggeredAbility ability
= new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.ANY, new ChargingCinderhornCondition(), false);
this.addAbility(ability, new AttackedThisTurnWatcher());
@ -97,7 +97,7 @@ class ChargingCinderhornCondition implements Condition {
@Override
public String toString() {
return "no creatures attacked this turn";
return "if no creatures attacked this turn";
}
}

View file

@ -89,7 +89,7 @@ class CoordinatedBarrageEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Choice choice = new ChoiceCreatureType();
Choice choice = new ChoiceCreatureType(game.getObject(source.getSourceId()));
if (controller.choose(Outcome.Damage, choice, game)) {
String chosenType = choice.getChoice();
FilterControlledPermanent filter = new FilterControlledPermanent();

View file

@ -0,0 +1,146 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.c;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.ContinuousEffect;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.continuous.GainControlTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.players.Player;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author wetterlicht & L_J
*/
public class CragSaurian extends CardImpl {
public CragSaurian(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{R}{R}{R}");
this.subtype.add(SubType.LIZARD);
this.power = new MageInt(4);
this.toughness = new MageInt(4);
// Whenever a source deals damage to Crag Saurian, that source's controller gains control of Crag Saurian.
this.addAbility(new CragSaurianTriggeredAbility());
}
public CragSaurian(final CragSaurian card) {
super(card);
}
@Override
public CragSaurian copy() {
return new CragSaurian(this);
}
private static class CragSaurianEffect extends OneShotEffect {
public CragSaurianEffect() {
super(Outcome.GainControl);
this.staticText = "that source's controller gains control of {this}";
}
private CragSaurianEffect(CragSaurianEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Player newController = game.getPlayer(this.getTargetPointer().getFirst(game, source));
if (newController != null && controller != null && !controller.equals(newController)) {
ContinuousEffect effect = new GainControlTargetEffect(Duration.Custom, newController.getId());
effect.setTargetPointer(new FixedTarget(source.getSourceId()));
game.addEffect(effect, source);
return true;
}
return false;
}
@Override
public Effect copy() {
return new CragSaurianEffect(this);
}
}
class CragSaurianTriggeredAbility extends TriggeredAbilityImpl {
CragSaurianTriggeredAbility() {
super(Zone.BATTLEFIELD, new CragSaurianEffect());
}
CragSaurianTriggeredAbility(final CragSaurianTriggeredAbility ability) {
super(ability);
}
@Override
public CragSaurianTriggeredAbility copy() {
return new CragSaurianTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == EventType.DAMAGED_CREATURE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
if (event.getTargetId().equals(this.sourceId)) {
UUID controller = game.getControllerId(event.getSourceId());
if (controller != null) {
Player player = game.getPlayer(controller);
if (player != null) {
getEffects().get(0).setTargetPointer(new FixedTarget(player.getId()));
return true;
}
}
}
return false;
}
@Override
public String getRule() {
return "Whenever a source deals damage to {this}, " + super.getRule();
}
}
}

View file

@ -27,7 +27,6 @@
*/
package mage.cards.c;
import java.util.List;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.effects.Effect;
@ -37,7 +36,7 @@ import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
@ -50,7 +49,7 @@ import mage.target.common.TargetControlledCreaturePermanent;
public class Curfew extends CardImpl {
public Curfew(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{U}");
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{U}");
// Each player returns a creature he or she controls to its owner's hand.
this.getSpellAbility().addEffect(new CurfewEffect());
@ -66,7 +65,7 @@ public class Curfew extends CardImpl {
}
}
class CurfewEffect extends OneShotEffect{
class CurfewEffect extends OneShotEffect {
public CurfewEffect() {
super(Outcome.ReturnToHand);
@ -78,26 +77,20 @@ class CurfewEffect extends OneShotEffect{
game.informPlayers("Each player returns a creature he or she controls to its owner's hand");
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
Player player = game.getPlayer(playerId);
if (player != null) {
TargetControlledCreaturePermanent target = new TargetControlledCreaturePermanent();
List<Permanent> liste = game.getBattlefield().getActivePermanents(new FilterControlledCreaturePermanent(), playerId, game);
if(!liste.isEmpty()){
player.choose(Outcome.ReturnToHand, target, source.getSourceId(), game);
Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent != null) {
permanent.moveToZone(Zone.HAND, source.getSourceId(), game, false);
}
if (player != null && game.getBattlefield().countAll(StaticFilters.FILTER_PERMANENT_CREATURE, playerId, game) > 0) {
TargetControlledCreaturePermanent target = new TargetControlledCreaturePermanent(1, 1, StaticFilters.FILTER_CONTROLLED_CREATURE, true);
player.choose(Outcome.ReturnToHand, target, source.getSourceId(), game);
Permanent permanent = game.getPermanent(target.getFirstTarget());
if (permanent != null) {
player.moveCards(permanent, Zone.HAND, source, game);
}
}
}
return true;
}
@Override
public Effect copy() {
return new CurfewEffect();
}
}

View file

@ -89,7 +89,7 @@ class DistantMelodyEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(game.getObject(source.getSourceId()));
while (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -96,7 +96,7 @@ class ElvishSoultillerEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
MageObject mageObject = game.getObject(source.getSourceId());
if (controller != null && mageObject != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(mageObject);
while (!controller.choose(outcome, typeChoice, game)) {
if (!controller.canRespond()) {
return false;

View file

@ -83,7 +83,7 @@ class ExtinctionEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
while (!player.choose(outcome, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -0,0 +1,107 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.f;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.DiesCreatureTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.StaticFilters;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.players.Player;
/**
*
* @author LevelX2 & L_J
*/
public class FacesOfThePast extends CardImpl {
public FacesOfThePast(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}");
// Whenever a creature dies, tap all untapped creatures that share a creature type with it or untap all tapped creatures that share a creature type with it.
this.addAbility(new DiesCreatureTriggeredAbility(new FacesOfThePastEffect(), false, false, true));
}
public FacesOfThePast(final FacesOfThePast card) {
super(card);
}
@Override
public FacesOfThePast copy() {
return new FacesOfThePast(this);
}
}
class FacesOfThePastEffect extends OneShotEffect {
public FacesOfThePastEffect() {
super(Outcome.Benefit);
this.staticText = "tap all untapped creatures that share a creature type with it or untap all tapped creatures that share a creature type with it";
}
public FacesOfThePastEffect(final FacesOfThePastEffect effect) {
super(effect);
}
@Override
public FacesOfThePastEffect copy() {
return new FacesOfThePastEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Permanent targetPermanent = (Permanent) game.getLastKnownInformation(this.getTargetPointer().getFirst(game, source), Zone.BATTLEFIELD);
if (targetPermanent != null) {
Player controller = game.getPlayer(targetPermanent.getControllerId());
if (controller != null) {
if (controller.chooseUse(outcome, "Tap all untapped creatures that share a creature type with " + targetPermanent.getLogName() + "? (Otherwise, untaps all tapped)", source, game)) {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, game)) {
if (!permanent.isTapped() && targetPermanent.shareSubtypes(permanent, game)) {
permanent.tap(game);
}
}
} else {
for (Permanent permanent : game.getBattlefield().getAllActivePermanents(StaticFilters.FILTER_PERMANENT_CREATURE, game)) {
if (permanent.isTapped() && targetPermanent.shareSubtypes(permanent, game)) {
permanent.untap(game);
}
}
}
return true;
}
}
return false;
}
}

View file

@ -0,0 +1,141 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.f;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.StateTriggeredAbility;
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.effects.ReplacementEffectImpl;
import mage.abilities.effects.common.RemoveAllCountersSourceEffect;
import mage.abilities.effects.common.SacrificeSourceEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.game.Game;
import mage.game.events.DamageEvent;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.permanent.Permanent;
/**
*
* @author emerald000 & L_J
*/
public class ForceBubble extends CardImpl {
public ForceBubble(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{W}{W}");
// If damage would be dealt to you, put that many depletion counters on Force Bubble instead.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ForceBubbleReplacementEffect()));
// When there are four or more depletion counters on Force Bubble, sacrifice it.
this.addAbility(new ForceBubbleStateTriggeredAbility());
// At the beginning of each end step, remove all depletion counters from Force Bubble.
this.addAbility(new BeginningOfEndStepTriggeredAbility(new RemoveAllCountersSourceEffect(CounterType.DEPLETION), TargetController.ANY, false));
}
public ForceBubble(final ForceBubble card) {
super(card);
}
@Override
public ForceBubble copy() {
return new ForceBubble(this);
}
}
class ForceBubbleReplacementEffect extends ReplacementEffectImpl {
ForceBubbleReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.PreventDamage);
staticText = "If damage would be dealt to you, put that many depletion counters on {this} instead";
}
ForceBubbleReplacementEffect(final ForceBubbleReplacementEffect effect) {
super(effect);
}
@Override
public boolean replaceEvent(GameEvent event, Ability source, Game game) {
DamageEvent damageEvent = (DamageEvent) event;
new AddCountersSourceEffect(CounterType.DEPLETION.createInstance(damageEvent.getAmount()), true).apply(game, source);
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == EventType.DAMAGE_PLAYER;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
return event.getTargetId().equals(source.getControllerId());
}
@Override
public ForceBubbleReplacementEffect copy() {
return new ForceBubbleReplacementEffect(this);
}
}
class ForceBubbleStateTriggeredAbility extends StateTriggeredAbility {
public ForceBubbleStateTriggeredAbility() {
super(Zone.BATTLEFIELD, new SacrificeSourceEffect());
}
public ForceBubbleStateTriggeredAbility(final ForceBubbleStateTriggeredAbility ability) {
super(ability);
}
@Override
public ForceBubbleStateTriggeredAbility copy() {
return new ForceBubbleStateTriggeredAbility(this);
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent permanent = game.getPermanent(getSourceId());
return permanent != null && permanent.getCounters(game).getCount(CounterType.DEPLETION) >= 4;
}
@Override
public String getRule() {
return "When there are four or more depletion counters on {this}, sacrifice it.";
}
}

View file

@ -0,0 +1,78 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.f;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.TurnedFaceUpSourceTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.PreventAllDamageByAllPermanentsEffect;
import mage.abilities.keyword.MorphAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SubtypePredicate;
/**
*
* @author L_J
*/
public class FrontlineStrategist extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("non-Soldier creatures");
static {
filter.add(Predicates.not(new SubtypePredicate(SubType.SOLDIER)));
}
public FrontlineStrategist(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{W}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.SOLDIER);
this.power = new MageInt(1);
this.toughness = new MageInt(1);
// Morph {W}
this.addAbility(new MorphAbility(this, new ManaCostsImpl("{W}")));
// When Frontline Strategist is turned face up, prevent all combat damage non-Soldier creatures would deal this turn.
this.addAbility(new TurnedFaceUpSourceTriggeredAbility(new PreventAllDamageByAllPermanentsEffect(filter, Duration.EndOfTurn, true).setText("prevent all combat damage non-Soldier creatures would deal this turn")));
}
public FrontlineStrategist(final FrontlineStrategist card) {
super(card);
}
@Override
public FrontlineStrategist copy() {
return new FrontlineStrategist(this);
}
}

View file

@ -0,0 +1,211 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.g;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.Ability;
import mage.abilities.condition.common.IsStepCondition;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.decorator.ConditionalActivatedAbility;
import mage.abilities.effects.OneShotEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.SuperType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.combat.CombatGroup;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetAttackingCreature;
/**
*
* @author L_J
*/
public class GeneralJarkeld extends CardImpl {
public GeneralJarkeld(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{W}");
addSuperType(SuperType.LEGENDARY);
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.SOLDIER);
this.power = new MageInt(1);
this.toughness = new MageInt(2);
// {T}: Switch the blocking creatures of two target attacking creatures. Activate this ability only during the declare blockers step.
Ability ability = new ConditionalActivatedAbility(Zone.BATTLEFIELD, new GeneralJarkeldSwitchBlockersEffect(), new TapSourceCost(), new IsStepCondition(PhaseStep.DECLARE_BLOCKERS, false));
ability.addTarget(new TargetAttackingCreature(2));
this.addAbility(ability);
}
public GeneralJarkeld(final GeneralJarkeld card) {
super(card);
}
@Override
public GeneralJarkeld copy() {
return new GeneralJarkeld(this);
}
}
class GeneralJarkeldSwitchBlockersEffect extends OneShotEffect {
public GeneralJarkeldSwitchBlockersEffect() {
super(Outcome.Benefit);
this.staticText = "Switch the blocking creatures of two target attacking creatures";
}
public GeneralJarkeldSwitchBlockersEffect(final GeneralJarkeldSwitchBlockersEffect effect) {
super(effect);
}
@Override
public GeneralJarkeldSwitchBlockersEffect copy() {
return new GeneralJarkeldSwitchBlockersEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
List<UUID> targets = source.getTargets().get(0).getTargets();
if (controller != null && targets != null) {
Permanent attacker1 = game.getPermanent(targets.get(0));
Permanent attacker2 = game.getPermanent(targets.get(1));
if (attacker1 != null && attacker2 != null) {
CombatGroup chosenGroup1 = game.getCombat().findGroup(attacker1.getId());
CombatGroup chosenGroup2 = game.getCombat().findGroup(attacker2.getId());
if (chosenGroup1 != null && chosenGroup2 != null) {
Set<Permanent> blockers1 = new HashSet<>();
Set<Permanent> blockers2 = new HashSet<>();
Set<Permanent> multiBlockers = new HashSet<>();
for (UUID blockerId : chosenGroup1.getBlockers()) {
Permanent blocker = game.getPermanent(blockerId);
if (blocker != null) {
if (blocker.getBlocking() > 1) {
multiBlockers.add(blocker);
} else {
blockers1.add(blocker);
}
}
}
for (UUID blockerId : chosenGroup2.getBlockers()) {
Permanent blocker = game.getPermanent(blockerId);
if (blocker != null) {
if (blocker.getBlocking() > 1) {
multiBlockers.add(blocker);
} else {
blockers2.add(blocker);
}
}
}
handleSingleBlockers(blockers1, chosenGroup1, chosenGroup2, controller, game);
handleSingleBlockers(blockers2, chosenGroup2, chosenGroup1, controller, game);
handleMultiBlockers(multiBlockers, chosenGroup1, chosenGroup2, controller, game);
// the ability doesn't unblock a group that loses all blockers, however it will newly block a previously unblocked group if it gains a blocker this way
if (!(chosenGroup1.getBlockers().isEmpty())) {
chosenGroup1.setBlocked(true);
chosenGroup1.pickBlockerOrder(attacker1.getControllerId(), game);
}
if (!(chosenGroup2.getBlockers().isEmpty())) {
chosenGroup2.setBlocked(true);
chosenGroup2.pickBlockerOrder(attacker2.getControllerId(), game);
}
return true;
}
}
}
return false;
}
private void handleSingleBlockers(Set<Permanent> blockers, CombatGroup chosenGroup, CombatGroup otherGroup, Player controller, Game game) {
for (Permanent blocker : blockers) {
chosenGroup.remove(blocker.getId());
blocker.setBlocking(0);
// 10/4/2004 The new blocker does not trigger any abilities which trigger on creatures becoming blockers, because the creatures were already blockers and the simple change of who is blocking does not trigger such abilities.
otherGroup.addBlockerToGroup(blocker.getId(), controller.getId(), game);
}
}
private void handleMultiBlockers(Set<Permanent> blockers, CombatGroup chosenGroup1, CombatGroup chosenGroup2, Player controller, Game game) {
// for handling multi-blockers (Two Headed Giant of Foriys, etc.)
blockerIteration:
for (Permanent blocker : blockers) {
if (blocker.getBlocking() > 1) {
CombatGroup blockGroup = null;
for (CombatGroup group : game.getCombat().getBlockingGroups()) {
if (group.getBlockers().contains(blocker.getId())) {
blockGroup = group;
break;
}
}
if (blockGroup != null) {
CombatGroup chosenGroup = null;
boolean sameBlocked = false;
for (CombatGroup group : game.getCombat().getGroups()) {
if (group.getBlocked() && group.getBlockers().contains(blocker.getId())) {
if (group == chosenGroup1 || group == chosenGroup2) {
if (sameBlocked) {
continue blockerIteration;
}
sameBlocked = true;
chosenGroup = group;
}
}
}
if (sameBlocked && chosenGroup != null) { // if none (should not happen) or all the blockers correspond to Jarkeld's targets, the blockers remain the same
CombatGroup otherGroup = (chosenGroup.equals(chosenGroup1) ? chosenGroup2 : chosenGroup1);
chosenGroup.remove(blocker.getId());
for (UUID attacker : chosenGroup.getAttackers()) {
blockGroup.remove(attacker);
}
otherGroup.addBlockerToGroup(blocker.getId(), controller.getId(), game);
for (UUID attacker : otherGroup.getAttackers()) {
// 10/4/2004 The new blocker does not trigger any abilities which trigger on creatures becoming blockers, because the creatures were already blockers and the simple change of who is blocking does not trigger such abilities.
game.getCombat().addBlockingGroup(blocker.getId(), attacker, controller.getId(), game);
}
blockGroup.pickAttackerOrder(blocker.getControllerId(), game);
}
}
}
}
}
}

View file

@ -95,7 +95,7 @@ class GraveSifterEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(game.getObject(source.getSourceId()));
typeChoice.setMessage("Choose creature type to return cards from your graveyard");
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {

View file

@ -96,7 +96,7 @@ class HarshMercyEffect extends OneShotEffect {
PlayerIteration:
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
while (!player.choose(Outcome.DestroyPermanent, typeChoice, game)) {
if (!player.canRespond()) {
continue PlayerIteration;

View file

@ -35,17 +35,15 @@ import mage.abilities.common.SimpleStaticAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.cost.CostModificationEffectImpl;
import mage.abilities.keyword.CyclingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.CostModificationType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.SubType;
import mage.constants.Zone;
import mage.game.Game;
import mage.players.Player;
import mage.util.CardUtil;
import mage.watchers.common.CardsCycledOrDiscardedThisTurnWatcher;
@ -94,17 +92,9 @@ class HollowOneReductionEffect extends CostModificationEffectImpl {
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
Player controller = game.getPlayer(source.getControllerId());
CardsCycledOrDiscardedThisTurnWatcher watcher = (CardsCycledOrDiscardedThisTurnWatcher) game.getState().getWatchers().get(CardsCycledOrDiscardedThisTurnWatcher.class.getSimpleName());
int reductionAmount = 0;
if (controller != null
&& watcher != null) {
for (Card card : watcher.getCardsCycledOrDiscardedThisTurn(controller.getId()).getCards(game)) {
if (card.getOwnerId().equals(controller.getId())) {
reductionAmount++;
}
}
CardUtil.reduceCost(abilityToModify, reductionAmount * 2);
if (watcher != null) {
CardUtil.reduceCost(abilityToModify, watcher.getNumberOfCardsCycledOrDiscardedThisTurn(source.getControllerId()) * 2);
return true;
}
return false;

View file

@ -152,7 +152,7 @@ class KaronaFalseGodEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (sourceObject != null && controller != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
while (!controller.choose(Outcome.BoostCreature, typeChoice, game)) {
if (!controller.canRespond()) {
return false;

View file

@ -0,0 +1,95 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.k;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfEndStepTriggeredAbility;
import mage.abilities.condition.Condition;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.SacrificeEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.permanent.ControlledFromStartOfControllerTurnPredicate;
import mage.game.Game;
import mage.watchers.common.AttackedThisTurnWatcher;
/**
*
* @author spjspj & L_J
*/
public class KeldonTwilight extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a creature you controlled since the beginning of the turn");
static {
filter.add(new ControlledFromStartOfControllerTurnPredicate());
}
public KeldonTwilight(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}{R}");
// At the beginning of each player's end step, if no creatures attacked this turn, that player sacrifices a creature he or she controlled since the beginning of the turn.
Effect effect = new SacrificeEffect(filter, 1, "that player ");
effect.setText("that player sacrifices a creature he or she controlled since the beginning of the turn");
BeginningOfEndStepTriggeredAbility ability
= new BeginningOfEndStepTriggeredAbility(Zone.BATTLEFIELD, effect, TargetController.ANY, new KeldonTwilightCondition(), false);
this.addAbility(ability, new AttackedThisTurnWatcher());
}
public KeldonTwilight(final KeldonTwilight card) {
super(card);
}
@Override
public KeldonTwilight copy() {
return new KeldonTwilight(this);
}
}
class KeldonTwilightCondition implements Condition {
@Override
public boolean apply(Game game, Ability source) {
AttackedThisTurnWatcher watcher = (AttackedThisTurnWatcher) game.getState().getWatchers().get(AttackedThisTurnWatcher.class.getSimpleName());
if (watcher != null) {
return watcher.getAttackedThisTurnCreatures().isEmpty();
}
return true;
}
@Override
public String toString() {
return "if no creatures attacked this turn";
}
}

View file

@ -0,0 +1,119 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.l;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.common.AttachEffect;
import mage.abilities.effects.common.SacrificeTargetEffect;
import mage.abilities.keyword.EnchantAbility;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.events.GameEvent;
import mage.target.TargetPermanent;
import mage.target.common.TargetCreaturePermanent;
import mage.target.targetpointer.FixedTarget;
/**
*
* @author L_J
*/
public class LingeringDeath extends CardImpl {
public LingeringDeath(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{1}{B}");
this.subtype.add(SubType.AURA);
// Enchant creature
TargetPermanent auraTarget = new TargetCreaturePermanent();
this.getSpellAbility().addTarget(auraTarget);
this.getSpellAbility().addEffect(new AttachEffect(Outcome.DestroyPermanent));
Ability ability = new EnchantAbility(auraTarget.getTargetName());
this.addAbility(ability);
// At the beginning of the end step of enchanted creature's controller, that player sacrifices that creature.
this.addAbility(new LingeringDeathAbility());
}
public LingeringDeath(final LingeringDeath card) {
super(card);
}
@Override
public LingeringDeath copy() {
return new LingeringDeath(this);
}
}
class LingeringDeathAbility extends TriggeredAbilityImpl {
public LingeringDeathAbility() {
super(Zone.BATTLEFIELD, new SacrificeTargetEffect());
}
public LingeringDeathAbility(final LingeringDeathAbility ability) {
super(ability);
}
@Override
public LingeringDeathAbility copy() {
return new LingeringDeathAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.END_TURN_STEP_PRE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent enchantment = game.getPermanentOrLKIBattlefield(this.getSourceId());
if (enchantment != null && enchantment.getAttachedTo() != null) {
Permanent enchantedCreature = game.getPermanent(enchantment.getAttachedTo());
if (enchantedCreature != null) {
if (event.getPlayerId().equals(enchantedCreature.getControllerId())) {
getEffects().get(0).setTargetPointer(new FixedTarget(enchantment.getAttachedTo()));
return true;
}
}
}
return false;
}
@Override
public String getRule() {
return "At the beginning of the end step of enchanted creature's controller, that player sacrifices that creature.";
}
}

View file

@ -86,9 +86,9 @@ class LuminescentRainEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(game.getObject(source.getSourceId()));
while (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -92,7 +92,7 @@ class MistformSliverEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId());
Permanent permanent = game.getPermanent(source.getSourceId());
if (player != null && permanent != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(permanent);
while (!player.choose(Outcome.Detriment, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -39,7 +39,7 @@ import mage.abilities.text.TextPartSubType;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.choices.Choice;
import mage.choices.ChoiceImpl;
import mage.choices.ChoiceCreatureType;
import mage.constants.*;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.Predicates;
@ -145,9 +145,8 @@ class ChangeCreatureTypeTargetEffect extends ContinuousEffectImpl {
return;
}
if (fromSubType == null) {
Choice typeChoice = new ChoiceImpl(true);
Choice typeChoice = new ChoiceCreatureType(game.getObject(source.getSourceId()));
typeChoice.setMessage("Choose creature type to change to Vampire");
typeChoice.setChoices(SubType.getCreatureTypes(false).stream().map(SubType::toString).collect(Collectors.toCollection(LinkedHashSet::new)));
while (!controller.choose(outcome, typeChoice, game)) {
if (!controller.canRespond()) {
return;

View file

@ -94,7 +94,7 @@ class NykthosShrineToNyxManaAbility extends ActivatedManaAbilityImpl {
public List<Mana> getNetMana(Game game) {
netMana.clear();
if (game != null) {
for (String colorChoice : ChoiceColor.colorChoices) {
for (String colorChoice : ChoiceColor.getBaseColors()) {
netMana.add(((NykthosDynamicManaEffect) this.getEffects().get(0)).computeMana(colorChoice, game, this));
}
}

View file

@ -0,0 +1,84 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.o;
import java.util.UUID;
import mage.abilities.condition.common.PermanentsOnTheBattlefieldCondition;
import mage.abilities.costs.AlternativeCostSourceAbility;
import mage.abilities.costs.Cost;
import mage.abilities.costs.common.TapTargetCost;
import mage.abilities.effects.common.PreventDamageToTargetEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.mageobject.SubtypePredicate;
import mage.filter.predicate.permanent.TappedPredicate;
import mage.target.common.TargetControlledPermanent;
import mage.target.common.TargetCreatureOrPlayer;
/**
*
* @author L_J
*/
public class OrimsCure extends CardImpl {
private static final FilterPermanent filter = new FilterPermanent("If you control a Plains");
private static final FilterControlledCreaturePermanent filterCreature = new FilterControlledCreaturePermanent("untapped creature you control");
static {
filter.add(new SubtypePredicate(SubType.PLAINS));
filterCreature.add(Predicates.not(new TappedPredicate()));
}
public OrimsCure(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{1}{W}");
// If you control a Plains, you may tap an untapped creature you control rather than pay Orim's Cure's mana cost.
Cost cost = new TapTargetCost(new TargetControlledPermanent(1,1,filterCreature,false));
cost.setText(" tap an untapped creature you control");
this.addAbility(new AlternativeCostSourceAbility(cost, new PermanentsOnTheBattlefieldCondition(filter)));
// Prevent the next 4 damage that would be dealt to target creature or player this turn.
this.getSpellAbility().addEffect(new PreventDamageToTargetEffect(Duration.EndOfTurn, 4));
this.getSpellAbility().addTarget(new TargetCreatureOrPlayer());
}
public OrimsCure(final OrimsCure card) {
super(card);
}
@Override
public OrimsCure copy() {
return new OrimsCure(this);
}
}

View file

@ -96,7 +96,7 @@ class OutbreakEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(game.getObject(source.getSourceId()));
while (!player.choose(outcome, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -93,7 +93,7 @@ class PacksDisdainEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(game.getObject(source.getSourceId()));
while (!player.choose(Outcome.UnboostCreature, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -93,7 +93,7 @@ class PatriarchsBiddingEffect extends OneShotEffect {
Set<String> chosenTypes = new HashSet<>();
for (UUID playerId : game.getState().getPlayersInRange(controller.getId(), game)) {
Player player = game.getPlayer(playerId);
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
while (!player.choose(Outcome.PutCreatureInPlay, typeChoice, game)) {
if (!player.canRespond()) {
break;

View file

@ -28,12 +28,12 @@
package mage.cards.p;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.TriggeredAbilityImpl;
import mage.abilities.effects.Effect;
import mage.abilities.effects.common.LoseLifeTargetEffect;
import mage.abilities.keyword.OfferingAbility;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
@ -44,6 +44,7 @@ import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.events.GameEvent.EventType;
import mage.game.events.ZoneChangeEvent;
import mage.game.permanent.Permanent;
import mage.target.targetpointer.FixedTarget;
@ -96,10 +97,9 @@ class PatronOfTheNezumiTriggeredAbility extends TriggeredAbilityImpl {
@Override
public boolean checkTrigger(GameEvent event, Game game) {
ZoneChangeEvent zEvent = (ZoneChangeEvent) event;
if (zEvent.getFromZone() == Zone.BATTLEFIELD
&& zEvent.getToZone() == Zone.GRAVEYARD) {
Card card = game.getCard(zEvent.getTargetId());
if (card != null && game.getOpponents(controllerId).contains(card.getOwnerId())) {
if (zEvent.isDiesEvent()) {
Permanent permanent = game.getPermanentOrLKIBattlefield(zEvent.getTargetId());
if (permanent != null && game.getOpponents(controllerId).contains(permanent.getOwnerId())) {
this.getEffects().get(0).setTargetPointer(new FixedTarget(zEvent.getPlayerId()));
return true;
}

View file

@ -93,7 +93,7 @@ class PeerPressureEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
Choice choice = new ChoiceCreatureType();
Choice choice = new ChoiceCreatureType(game.getObject(source.getSourceId()));
while (!controller.choose(Outcome.GainControl, choice, game)) {
if (!controller.canRespond()) {
return false;

View file

@ -0,0 +1,67 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.r;
import java.util.UUID;
import mage.MageInt;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
import mage.abilities.effects.common.PreventDamageToSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.Duration;
import mage.constants.Zone;
/**
*
* @author L_J
*/
public class ReveredElder extends CardImpl {
public ReveredElder(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{2}{W}");
this.subtype.add(SubType.HUMAN);
this.subtype.add(SubType.CLERIC);
this.power = new MageInt(1);
this.toughness = new MageInt(2);
// {1}: Prevent the next 1 damage that would be dealt to Revered Elder this turn.
this.addAbility(new SimpleActivatedAbility(Zone.BATTLEFIELD, new PreventDamageToSourceEffect(Duration.EndOfTurn, 1), new ManaCostsImpl("{1}")));
}
public ReveredElder(final ReveredElder card) {
super(card);
}
@Override
public ReveredElder copy() {
return new ReveredElder(this);
}
}

View file

@ -94,7 +94,7 @@ class RiptideChronologistEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
while (!player.choose(outcome, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -96,7 +96,7 @@ class RiptideShapeshifterEffect extends OneShotEffect {
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
Choice choice = new ChoiceCreatureType();
Choice choice = new ChoiceCreatureType(sourceObject);
while (!controller.choose(Outcome.BoostCreature, choice, game)) {
if (!controller.canRespond()) {
return false;

View file

@ -89,7 +89,7 @@ class RoarOfTheCrowdEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(game.getObject(source.getSourceId()));
while (!player.choose(Outcome.LoseLife, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -0,0 +1,197 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.s;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BecomesTappedSourceTriggeredAbility;
import mage.abilities.common.SimpleActivatedAbility;
import mage.abilities.costs.common.TapSourceCost;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.DamageAllEffect;
import mage.abilities.effects.common.DamageControllerEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.common.FilterControlledCreaturePermanent;
import mage.filter.common.FilterOpponentsCreaturePermanent;
import mage.filter.predicate.permanent.BlockingPredicate;
import mage.game.Game;
import mage.game.events.GameEvent;
import mage.game.combat.CombatGroup;
import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.target.common.TargetCreaturePermanentSameController;
/**
*
* @author L_J
*/
public class SorrowsPath extends CardImpl {
private static final FilterOpponentsCreaturePermanent filter = new FilterOpponentsCreaturePermanent("blocking creatures an opponent controls");
static {
filter.add(new BlockingPredicate());
}
public SorrowsPath(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.LAND}, "");
// {T}: Choose two target blocking creatures an opponent controls. If each of those creatures could block all creatures that the other is blocking, remove both of them from combat. Each one then blocks all creatures the other was blocking.
Ability ability = new SimpleActivatedAbility(Zone.BATTLEFIELD, new SorrowsPathSwitchBlockersEffect(), new TapSourceCost());
ability.addTarget(new TargetCreaturePermanentSameController(2, 2, filter, false));
this.addAbility(ability);
// Whenever Sorrow's Path becomes tapped, it deals 2 damage to you and each creature you control.
Ability ability2 = new BecomesTappedSourceTriggeredAbility(new DamageControllerEffect(2));
ability2.addEffect(new DamageAllEffect(2, new FilterControlledCreaturePermanent()).setText("and each creature you control"));
this.addAbility(ability2);
}
public SorrowsPath(final SorrowsPath card) {
super(card);
}
@Override
public SorrowsPath copy() {
return new SorrowsPath(this);
}
}
class SorrowsPathSwitchBlockersEffect extends OneShotEffect {
public SorrowsPathSwitchBlockersEffect() {
super(Outcome.Detriment);
this.staticText = "Choose two target blocking creatures an opponent controls. If each of those creatures could block all creatures that the other is blocking, remove both of them from combat. Each one then blocks all creatures the other was blocking";
}
public SorrowsPathSwitchBlockersEffect(final SorrowsPathSwitchBlockersEffect effect) {
super(effect);
}
@Override
public SorrowsPathSwitchBlockersEffect copy() {
return new SorrowsPathSwitchBlockersEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
List<UUID> targets = source.getTargets().get(0).getTargets();
if (controller != null && targets != null) {
Permanent blocker1 = game.getPermanent(targets.get(0));
Permanent blocker2 = game.getPermanent(targets.get(1));
if (blocker1 != null && blocker2 != null) {
Permanent sourcePermanent = game.getPermanentOrLKIBattlefield(source.getSourceId());
// 10/1/2009: When determining whether a creature could block all creatures the other is blocking, take into account evasion abilities (like flying), protection
// abilities, and other blocking restrictions, as well as abilities that allow a creature to block multiple creatures or block as though a certain condition were true.
// Take into account whether those creatures are tapped, but not whether they have costs to block (since those apply only as blockers are declared).
CombatGroup chosenGroup1 = findBlockingGroup(blocker1, game);
CombatGroup chosenGroup2 = findBlockingGroup(blocker2, game);
Set<Permanent> attackers1 = new HashSet<>();
Set<Permanent> attackers2 = new HashSet<>();
boolean blockPossible = false;
if (chosenGroup1 != null && chosenGroup2 != null) {
blockPossible = getRestrictions(chosenGroup1, blocker2, attackers1, sourcePermanent, controller, game) && getRestrictions(chosenGroup2, blocker1, attackers2, sourcePermanent, controller, game);
}
if (!blockPossible) {
return true;
}
// 10/1/2009: When the first ability resolves, if all the creatures that one of the targeted creatures was blocking have left combat, then the other targeted creature
// is considered to be able to block all creatures the first creature is blocking. If the ability has its full effect, the second creature will be removed from combat
// but not returned to combat; it doesn't block anything.
game.getCombat().removeFromCombat(blocker1.getId(), game, false);
game.getCombat().removeFromCombat(blocker2.getId(), game, false);
blocker1.setRemovedFromCombat(attackers2.isEmpty());
blocker2.setRemovedFromCombat(attackers1.isEmpty());
// 10/1/2009: Abilities that trigger whenever one of the targeted creatures blocks will trigger when the first ability resolves, because those creatures will change from
// not blocking (since they're removed from combat) to blocking. It doesn't matter if those abilities triggered when those creatures blocked the first time. Abilities
// that trigger whenever one of the attacking creatures becomes blocked will not trigger again, because they never stopped being blocked creatures. Abilities that
// trigger whenever a creature blocks one of the attacking creatures will trigger again, though; those kinds of abilities trigger once for each creature that blocks.
reassignBlocker(blocker1, attackers2, game);
reassignBlocker(blocker2, attackers1, game);
return true;
}
}
return false;
}
private CombatGroup findBlockingGroup(Permanent blocker, Game game) {
if (blocker.getBlocking() > 1) {
for (CombatGroup group : game.getCombat().getBlockingGroups()) {
if (group.getBlockers().contains(blocker.getId())) {
return group;
}
}
}
return game.getCombat().findGroupOfBlocker(blocker.getId());
}
private boolean getRestrictions(CombatGroup chosenGroup, Permanent blocker, Set<Permanent> attackers, Permanent sourcePermanent, Player controller, Game game) {
for (UUID attackerId : chosenGroup.getAttackers()) {
Permanent attacker = game.getPermanent(attackerId);
if (attacker != null) {
if (blocker.canBlock(attackerId, game) && (blocker.getMaxBlocks() == 0 || chosenGroup.getAttackers().size() <= blocker.getMaxBlocks())) {
attackers.add(attacker);
} else {
game.informPlayer(controller, "Illegal block detected (" + blocker.getName() + "), effect of " + sourcePermanent.getName() + " doesn't apply.");
return false;
}
}
}
return true;
}
private void reassignBlocker(Permanent blocker, Set<Permanent> attackers, Game game) {
for (Permanent attacker : attackers) {
CombatGroup group = game.getCombat().findGroup(attacker.getId());
if (group != null) {
group.addBlockerToGroup(blocker.getId(), blocker.getControllerId(), game);
game.getCombat().addBlockingGroup(blocker.getId(), attacker.getId(), blocker.getControllerId(), game);
// TODO: find an alternate event solution for multi-blockers (as per issue #4285), this will work fine for single blocker creatures though
game.fireEvent(GameEvent.getEvent(GameEvent.EventType.BLOCKER_DECLARED, attacker.getId(), blocker.getId(), blocker.getControllerId()));
group.pickBlockerOrder(attacker.getControllerId(), game);
}
}
CombatGroup blockGroup = findBlockingGroup(blocker, game); // a new blockingGroup is formed, so it's necessary to find it again
if (blockGroup != null) {
blockGroup.pickAttackerOrder(blocker.getControllerId(), game);
}
}
}

View file

@ -85,7 +85,7 @@ class StandardizeEffect extends OneShotEffect {
MageObject sourceObject = game.getObject(source.getSourceId());
String chosenType = "";
if (player != null && sourceObject != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
typeChoice.setMessage("Choose a creature type other than Wall");
typeChoice.getChoices().remove("Wall");
while (!player.choose(Outcome.BoostCreature, typeChoice, game)) {

View file

@ -45,7 +45,7 @@ import mage.filter.common.FilterCreaturePermanent;
public class ToxinSliver extends CardImpl {
public ToxinSliver(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.CREATURE},"{3}{B}");
super(ownerId, setInfo, new CardType[]{CardType.CREATURE}, "{3}{B}");
this.subtype.add(SubType.SLIVER);
this.power = new MageInt(3);
@ -53,9 +53,9 @@ public class ToxinSliver extends CardImpl {
// Whenever a Sliver deals combat damage to a creature, destroy that creature. It can't be regenerated.
this.addAbility(new DealsDamageToACreatureAllTriggeredAbility(
new DestroyTargetEffect(true), false,
new FilterCreaturePermanent(SubType.SLIVER,"a Sliver"),
SetTargetPointer.PERMANENT, true));
new DestroyTargetEffect(true), false,
new FilterCreaturePermanent(SubType.SLIVER, "a Sliver"),
SetTargetPointer.PERMANENT_TARGET, true));
}

View file

@ -91,7 +91,7 @@ class TribalUnityEffect extends OneShotEffect {
MageObject sourceObject = game.getObject(source.getSourceId());
int boost = amount.calculate(game, source, this);
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
while (!player.choose(outcome, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -90,7 +90,7 @@ class TsabosDecreeEffect extends OneShotEffect {
Player targetPlayer = game.getPlayer(targetPointer.getFirst(game, source));
MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
while (!player.choose(outcome, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -91,7 +91,7 @@ class WalkingDesecrationEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId());
MageObject sourceObject = game.getObject(source.getSourceId());
if (player != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(sourceObject);
while (!player.choose(outcome, typeChoice, game)) {
if (!player.canRespond()) {
return false;

View file

@ -0,0 +1,69 @@
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* The views and conclusions contained in the software and documentation are those of the
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
package mage.cards.w;
import java.util.UUID;
import mage.abilities.effects.common.DamageAllEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.BlockedPredicate;
import mage.filter.predicate.permanent.BlockingPredicate;
/**
*
* @author L_J
*/
public class Warpath extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("blocking creature and each blocked creature");
static {
filter.add(Predicates.or(
new BlockingPredicate(),
new BlockedPredicate()));
}
public Warpath(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{R}");
// Warpath deals 3 damage to each blocking creature and each blocked creature.
this.getSpellAbility().addEffect(new DamageAllEffect(3, filter));
}
public Warpath(final Warpath card) {
super(card);
}
@Override
public Warpath copy() {
return new Warpath(this);
}
}

View file

@ -105,7 +105,7 @@ class WarrenWeirdingEffect extends OneShotEffect {
FilterControlledPermanent filter = new FilterControlledPermanent("creature");
filter.add(new CardTypePredicate(CardType.CREATURE));
filter.add(new ControllerIdPredicate(player.getId()));
TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, false);
TargetControlledPermanent target = new TargetControlledPermanent(1, 1, filter, true);
//A spell or ability could have removed the only legal target this player
//had, if thats the case this ability should fizzle.

View file

@ -147,6 +147,7 @@ public class IceAge extends ExpansionSet {
cards.add(new SetCardInfo("Fyndhorn Pollen", 133, Rarity.RARE, mage.cards.f.FyndhornPollen.class));
cards.add(new SetCardInfo("Game of Chaos", 186, Rarity.RARE, mage.cards.g.GameOfChaos.class));
cards.add(new SetCardInfo("Gangrenous Zombies", 15, Rarity.COMMON, mage.cards.g.GangrenousZombies.class));
cards.add(new SetCardInfo("General Jarkeld", 251, Rarity.RARE, mage.cards.g.GeneralJarkeld.class));
cards.add(new SetCardInfo("Giant Growth", 134, Rarity.COMMON, mage.cards.g.GiantGrowth.class));
cards.add(new SetCardInfo("Giant Trap Door Spider", 371, Rarity.UNCOMMON, mage.cards.g.GiantTrapDoorSpider.class));
cards.add(new SetCardInfo("Glacial Chasm", 331, Rarity.UNCOMMON, mage.cards.g.GlacialChasm.class));

View file

@ -220,6 +220,7 @@ public class MastersEditionIII extends ExpansionSet {
cards.add(new SetCardInfo("Sivitri Scarzam", 175, Rarity.COMMON, mage.cards.s.SivitriScarzam.class));
cards.add(new SetCardInfo("Slashing Tiger", 133, Rarity.COMMON, mage.cards.s.SlashingTiger.class));
cards.add(new SetCardInfo("Sol Grail", 201, Rarity.COMMON, mage.cards.s.SolGrail.class));
cards.add(new SetCardInfo("Sorrow's Path", 211, Rarity.RARE, mage.cards.s.SorrowsPath.class));
cards.add(new SetCardInfo("Spirit Shackle", 74, Rarity.COMMON, mage.cards.s.SpiritShackle.class));
cards.add(new SetCardInfo("Spoils of Victory", 134, Rarity.COMMON, mage.cards.s.SpoilsOfVictory.class));
cards.add(new SetCardInfo("Stolen Grain", 75, Rarity.UNCOMMON, mage.cards.s.StolenGrain.class));

View file

@ -111,6 +111,7 @@ public class MercadianMasques extends ExpansionSet {
cards.add(new SetCardInfo("Counterspell", 69, Rarity.COMMON, mage.cards.c.Counterspell.class));
cards.add(new SetCardInfo("Cowardice", 70, Rarity.RARE, mage.cards.c.Cowardice.class));
cards.add(new SetCardInfo("Crackdown", 15, Rarity.RARE, mage.cards.c.Crackdown.class));
cards.add(new SetCardInfo("Crag Saurian", 185, Rarity.RARE, mage.cards.c.CragSaurian.class));
cards.add(new SetCardInfo("Crash", 186, Rarity.COMMON, mage.cards.c.Crash.class));
cards.add(new SetCardInfo("Credit Voucher", 289, Rarity.UNCOMMON, mage.cards.c.CreditVoucher.class));
cards.add(new SetCardInfo("Crenellated Wall", 290, Rarity.UNCOMMON, mage.cards.c.CrenellatedWall.class));
@ -237,6 +238,7 @@ public class MercadianMasques extends ExpansionSet {
cards.add(new SetCardInfo("Noble Purpose", 32, Rarity.UNCOMMON, mage.cards.n.NoblePurpose.class));
cards.add(new SetCardInfo("Notorious Assassin", 150, Rarity.RARE, mage.cards.n.NotoriousAssassin.class));
cards.add(new SetCardInfo("Ogre Taskmaster", 206, Rarity.UNCOMMON, mage.cards.o.OgreTaskmaster.class));
cards.add(new SetCardInfo("Orim's Cure", 33, Rarity.COMMON, mage.cards.o.OrimsCure.class));
cards.add(new SetCardInfo("Overtaker", 89, Rarity.RARE, mage.cards.o.Overtaker.class));
cards.add(new SetCardInfo("Panacea", 308, Rarity.UNCOMMON, mage.cards.p.Panacea.class));
cards.add(new SetCardInfo("Pangosaur", 261, Rarity.RARE, mage.cards.p.Pangosaur.class));
@ -263,6 +265,7 @@ public class MercadianMasques extends ExpansionSet {
cards.add(new SetCardInfo("Rappelling Scouts", 41, Rarity.RARE, mage.cards.r.RappellingScouts.class));
cards.add(new SetCardInfo("Remote Farm", 323, Rarity.COMMON, mage.cards.r.RemoteFarm.class));
cards.add(new SetCardInfo("Renounce", 42, Rarity.UNCOMMON, mage.cards.r.Renounce.class));
cards.add(new SetCardInfo("Revered Elder", 43, Rarity.COMMON, mage.cards.r.ReveredElder.class));
cards.add(new SetCardInfo("Reverent Mantra", 44, Rarity.RARE, mage.cards.r.ReverentMantra.class));
cards.add(new SetCardInfo("Revive", 262, Rarity.UNCOMMON, mage.cards.r.Revive.class));
cards.add(new SetCardInfo("Righteous Aura", 45, Rarity.UNCOMMON, mage.cards.r.RighteousAura.class));
@ -362,6 +365,7 @@ public class MercadianMasques extends ExpansionSet {
cards.add(new SetCardInfo("War Cadence", 224, Rarity.UNCOMMON, mage.cards.w.WarCadence.class));
cards.add(new SetCardInfo("War Tax", 113, Rarity.UNCOMMON, mage.cards.w.WarTax.class));
cards.add(new SetCardInfo("Warmonger", 225, Rarity.UNCOMMON, mage.cards.w.Warmonger.class));
cards.add(new SetCardInfo("Warpath", 226, Rarity.UNCOMMON, mage.cards.w.Warpath.class));
cards.add(new SetCardInfo("Waterfront Bouncer", 114, Rarity.COMMON, mage.cards.w.WaterfrontBouncer.class));
cards.add(new SetCardInfo("Wave of Reckoning", 56, Rarity.RARE, mage.cards.w.WaveOfReckoning.class));
cards.add(new SetCardInfo("Wild Jhovall", 227, Rarity.COMMON, mage.cards.w.WildJhovall.class));

View file

@ -28,6 +28,7 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.constants.Rarity;
import mage.constants.SetType;
/**
@ -46,5 +47,69 @@ public class MerfolkVsGoblins extends ExpansionSet {
super("Duel Decks: Merfolk vs. Goblins", "DDT", ExpansionSet.buildDate(2017, 11, 10), SetType.SUPPLEMENTAL);
this.blockName = "Duel Decks";
this.hasBasicLands = false;
cards.add(new SetCardInfo("Aquitect's Will", 2, Rarity.COMMON, mage.cards.a.AquitectsWill.class));
cards.add(new SetCardInfo("Battle Squadron", 33, Rarity.UNCOMMON, mage.cards.b.BattleSquadron.class));
cards.add(new SetCardInfo("Blighted Cataract", 26, Rarity.UNCOMMON, mage.cards.b.BlightedCataract.class));
cards.add(new SetCardInfo("Blighted Gorge", 58, Rarity.UNCOMMON, mage.cards.b.BlightedGorge.class));
cards.add(new SetCardInfo("Boggart Brute", 34, Rarity.COMMON, mage.cards.b.BoggartBrute.class));
cards.add(new SetCardInfo("Brittle Effigy", 56, Rarity.RARE, mage.cards.b.BrittleEffigy.class));
cards.add(new SetCardInfo("Brute Strength", 35, Rarity.COMMON, mage.cards.b.BruteStrength.class));
cards.add(new SetCardInfo("Claustrophobia", 3, Rarity.COMMON, mage.cards.c.Claustrophobia.class));
cards.add(new SetCardInfo("Cleaver Riot", 36, Rarity.UNCOMMON, mage.cards.c.CleaverRiot.class));
cards.add(new SetCardInfo("Cold-Eyed Selkie", 25, Rarity.RARE, mage.cards.c.ColdEyedSelkie.class));
cards.add(new SetCardInfo("Concentrate", 4, Rarity.UNCOMMON, mage.cards.c.Concentrate.class));
cards.add(new SetCardInfo("Ember Hauler", 37, Rarity.UNCOMMON, mage.cards.e.EmberHauler.class));
cards.add(new SetCardInfo("Engulf the Shore", 5, Rarity.RARE, mage.cards.e.EngulfTheShore.class));
cards.add(new SetCardInfo("Essence Scatter", 6, Rarity.COMMON, mage.cards.e.EssenceScatter.class));
cards.add(new SetCardInfo("Forgotten Cave", 59, Rarity.COMMON, mage.cards.f.ForgottenCave.class));
cards.add(new SetCardInfo("Foundry Street Denizen", 38, Rarity.COMMON, mage.cards.f.FoundryStreetDenizen.class));
cards.add(new SetCardInfo("Gempalm Incinerator", 39, Rarity.UNCOMMON, mage.cards.g.GempalmIncinerator.class));
cards.add(new SetCardInfo("Ghostfire", 40, Rarity.COMMON, mage.cards.g.Ghostfire.class));
cards.add(new SetCardInfo("Goblin Charbelcher", 57, Rarity.RARE, mage.cards.g.GoblinCharbelcher.class));
cards.add(new SetCardInfo("Goblin Chieftain", 41, Rarity.RARE, mage.cards.g.GoblinChieftain.class));
cards.add(new SetCardInfo("Goblin Diplomats", 42, Rarity.RARE, mage.cards.g.GoblinDiplomats.class));
cards.add(new SetCardInfo("Goblin Glory Chaser", 43, Rarity.UNCOMMON, mage.cards.g.GoblinGloryChaser.class));
cards.add(new SetCardInfo("Goblin Goon", 44, Rarity.RARE, mage.cards.g.GoblinGoon.class));
cards.add(new SetCardInfo("Goblin Grenade", 45, Rarity.UNCOMMON, mage.cards.g.GoblinGrenade.class));
cards.add(new SetCardInfo("Goblin Rabblemaster", 46, Rarity.RARE, mage.cards.g.GoblinRabblemaster.class));
cards.add(new SetCardInfo("Goblin Razerunners", 47, Rarity.RARE, mage.cards.g.GoblinRazerunners.class));
cards.add(new SetCardInfo("Goblin Ringleader", 48, Rarity.UNCOMMON, mage.cards.g.GoblinRingleader.class));
cards.add(new SetCardInfo("Goblin Tunneler", 49, Rarity.COMMON, mage.cards.g.GoblinTunneler.class));
cards.add(new SetCardInfo("Goblin Wardriver", 50, Rarity.UNCOMMON, mage.cards.g.GoblinWardriver.class));
cards.add(new SetCardInfo("Harbinger of the Tides", 7, Rarity.RARE, mage.cards.h.HarbingerOfTheTides.class));
cards.add(new SetCardInfo("Hordeling Outburst", 51, Rarity.UNCOMMON, mage.cards.h.HordelingOutburst.class));
cards.add(new SetCardInfo("Inkfathom Divers", 8, Rarity.COMMON, mage.cards.i.InkfathomDivers.class));
cards.add(new SetCardInfo("Island", 28, Rarity.LAND, mage.cards.basiclands.Island.class));
cards.add(new SetCardInfo("Island", 29, Rarity.LAND, mage.cards.basiclands.Island.class));
cards.add(new SetCardInfo("Island", 30, Rarity.LAND, mage.cards.basiclands.Island.class));
cards.add(new SetCardInfo("Island", 31, Rarity.LAND, mage.cards.basiclands.Island.class));
cards.add(new SetCardInfo("Krenko's Command", 53, Rarity.COMMON, mage.cards.k.KrenkosCommand.class));
cards.add(new SetCardInfo("Krenko, Mob Boss", 52, Rarity.RARE, mage.cards.k.KrenkoMobBoss.class));
cards.add(new SetCardInfo("Lonely Sandbar", 27, Rarity.COMMON, mage.cards.l.LonelySandbar.class));
cards.add(new SetCardInfo("Master of Waves", 1, Rarity.MYTHIC, mage.cards.m.MasterOfWaves.class));
cards.add(new SetCardInfo("Master of the Pearl Trident", 9, Rarity.RARE, mage.cards.m.MasterOfThePearlTrident.class));
cards.add(new SetCardInfo("Merfolk Looter", 10, Rarity.UNCOMMON, mage.cards.m.MerfolkLooter.class));
cards.add(new SetCardInfo("Merfolk Sovereign", 11, Rarity.RARE, mage.cards.m.MerfolkSovereign.class));
cards.add(new SetCardInfo("Merfolk Wayfinder", 12, Rarity.UNCOMMON, mage.cards.m.MerfolkWayfinder.class));
cards.add(new SetCardInfo("Merrow Reejerey", 13, Rarity.UNCOMMON, mage.cards.m.MerrowReejerey.class));
cards.add(new SetCardInfo("Mind Spring", 14, Rarity.RARE, mage.cards.m.MindSpring.class));
cards.add(new SetCardInfo("Misdirection", 15, Rarity.RARE, mage.cards.m.Misdirection.class));
cards.add(new SetCardInfo("Mountain", 60, Rarity.LAND, mage.cards.basiclands.Mountain.class));
cards.add(new SetCardInfo("Mountain", 61, Rarity.LAND, mage.cards.basiclands.Mountain.class));
cards.add(new SetCardInfo("Mountain", 62, Rarity.LAND, mage.cards.basiclands.Mountain.class));
cards.add(new SetCardInfo("Mountain", 63, Rarity.LAND, mage.cards.basiclands.Mountain.class));
cards.add(new SetCardInfo("Relentless Assault", 54, Rarity.RARE, mage.cards.r.RelentlessAssault.class));
cards.add(new SetCardInfo("Rootwater Hunter", 16, Rarity.UNCOMMON, mage.cards.r.RootwaterHunter.class));
cards.add(new SetCardInfo("Scroll Thief", 17, Rarity.COMMON, mage.cards.s.ScrollThief.class));
cards.add(new SetCardInfo("Streambed Aquitects", 18, Rarity.COMMON, mage.cards.s.StreambedAquitects.class));
cards.add(new SetCardInfo("Tarfire", 55, Rarity.COMMON, mage.cards.t.Tarfire.class));
cards.add(new SetCardInfo("Tidal Courier", 19, Rarity.UNCOMMON, mage.cards.t.TidalCourier.class));
cards.add(new SetCardInfo("Tidal Warrior", 20, Rarity.COMMON, mage.cards.t.TidalWarrior.class));
cards.add(new SetCardInfo("Tidal Wave", 21, Rarity.COMMON, mage.cards.t.TidalWave.class));
cards.add(new SetCardInfo("Tidebinder Mage", 22, Rarity.RARE, mage.cards.t.TidebinderMage.class));
cards.add(new SetCardInfo("Triton Tactics", 23, Rarity.UNCOMMON, mage.cards.t.TritonTactics.class));
cards.add(new SetCardInfo("Wake Thrasher", 24, Rarity.RARE, mage.cards.w.WakeThrasher.class));
cards.add(new SetCardInfo("Warren Instigator", 32, Rarity.MYTHIC, mage.cards.w.WarrenInstigator.class));
}
}

View file

@ -110,6 +110,7 @@ public class Planeshift extends ExpansionSet {
cards.add(new SetCardInfo("Insolence", 63, Rarity.COMMON, mage.cards.i.Insolence.class));
cards.add(new SetCardInfo("Kavu Recluse", 64, Rarity.COMMON, mage.cards.k.KavuRecluse.class));
cards.add(new SetCardInfo("Keldon Mantle", 65, Rarity.COMMON, mage.cards.k.KeldonMantle.class));
cards.add(new SetCardInfo("Keldon Twilight", 112, Rarity.RARE, mage.cards.k.KeldonTwilight.class));
cards.add(new SetCardInfo("Lashknife Barrier", 9, Rarity.UNCOMMON, mage.cards.l.LashknifeBarrier.class));
cards.add(new SetCardInfo("Lava Zombie", 113, Rarity.COMMON, mage.cards.l.LavaZombie.class));
cards.add(new SetCardInfo("Lord of the Undead", 44, Rarity.RARE, mage.cards.l.LordOfTheUndead.class));

View file

@ -168,6 +168,7 @@ public class PortalSecondAge extends ExpansionSet {
cards.add(new SetCardInfo("Rally the Troops", 139, Rarity.UNCOMMON, mage.cards.r.RallyTheTroops.class));
cards.add(new SetCardInfo("Ravenous Rats", 27, Rarity.COMMON, mage.cards.r.RavenousRats.class));
cards.add(new SetCardInfo("Razorclaw Bear", 82, Rarity.RARE, mage.cards.r.RazorclawBear.class));
cards.add(new SetCardInfo("Relentless Assault", 115, Rarity.RARE, mage.cards.r.RelentlessAssault.class));
cards.add(new SetCardInfo("Remove", 43, Rarity.UNCOMMON, mage.cards.r.Remove.class));
cards.add(new SetCardInfo("Renewing Touch", 83, Rarity.UNCOMMON, mage.cards.r.RenewingTouch.class));
cards.add(new SetCardInfo("Return of the Nightstalkers", 28, Rarity.RARE, mage.cards.r.ReturnOfTheNightstalkers.class));

View file

@ -108,11 +108,14 @@ public class Scourge extends ExpansionSet {
cards.add(new SetCardInfo("Enrage", 91, Rarity.UNCOMMON, mage.cards.e.Enrage.class));
cards.add(new SetCardInfo("Eternal Dragon", 12, Rarity.RARE, mage.cards.e.EternalDragon.class));
cards.add(new SetCardInfo("Extra Arms", 92, Rarity.UNCOMMON, mage.cards.e.ExtraArms.class));
cards.add(new SetCardInfo("Faces of the Past", 35, Rarity.RARE, mage.cards.f.FacesOfThePast.class));
cards.add(new SetCardInfo("Fatal Mutation", 66, Rarity.UNCOMMON, mage.cards.f.FatalMutation.class));
cards.add(new SetCardInfo("Fierce Empath", 119, Rarity.COMMON, mage.cards.f.FierceEmpath.class));
cards.add(new SetCardInfo("Final Punishment", 67, Rarity.RARE, mage.cards.f.FinalPunishment.class));
cards.add(new SetCardInfo("Forgotten Ancient", 120, Rarity.RARE, mage.cards.f.ForgottenAncient.class));
cards.add(new SetCardInfo("Form of the Dragon", 93, Rarity.RARE, mage.cards.f.FormOfTheDragon.class));
cards.add(new SetCardInfo("Force Bubble", 14, Rarity.RARE, mage.cards.f.ForceBubble.class));
cards.add(new SetCardInfo("Frontline Strategist", 15, Rarity.COMMON, mage.cards.f.FrontlineStrategist.class));
cards.add(new SetCardInfo("Frozen Solid", 36, Rarity.COMMON, mage.cards.f.FrozenSolid.class));
cards.add(new SetCardInfo("Gilded Light", 16, Rarity.UNCOMMON, mage.cards.g.GildedLight.class));
cards.add(new SetCardInfo("Goblin Brigand", 94, Rarity.COMMON, mage.cards.g.GoblinBrigand.class));
@ -128,6 +131,7 @@ public class Scourge extends ExpansionSet {
cards.add(new SetCardInfo("Krosan Warchief", 123, Rarity.UNCOMMON, mage.cards.k.KrosanWarchief.class));
cards.add(new SetCardInfo("Kurgadon", 124, Rarity.UNCOMMON, mage.cards.k.Kurgadon.class));
cards.add(new SetCardInfo("Lethal Vapors", 68, Rarity.RARE, mage.cards.l.LethalVapors.class));
cards.add(new SetCardInfo("Lingering Death", 69, Rarity.COMMON, mage.cards.l.LingeringDeath.class));
cards.add(new SetCardInfo("Long-Term Plans", 38, Rarity.UNCOMMON, mage.cards.l.LongTermPlans.class));
cards.add(new SetCardInfo("Mercurial Kite", 39, Rarity.COMMON, mage.cards.m.MercurialKite.class));
cards.add(new SetCardInfo("Metamorphose", 40, Rarity.UNCOMMON, mage.cards.m.Metamorphose.class));

View file

@ -134,6 +134,7 @@ public class TheDark extends ExpansionSet {
cards.add(new SetCardInfo("Season of the Witch", 14, Rarity.RARE, mage.cards.s.SeasonOfTheWitch.class));
cards.add(new SetCardInfo("Sisters of the Flame", 73, Rarity.UNCOMMON, mage.cards.s.SistersOfTheFlame.class));
cards.add(new SetCardInfo("Skull of Orm", 106, Rarity.UNCOMMON, mage.cards.s.SkullOfOrm.class));
cards.add(new SetCardInfo("Sorrow's Path", 116, Rarity.RARE, mage.cards.s.SorrowsPath.class));
cards.add(new SetCardInfo("Squire", 90, Rarity.COMMON, mage.cards.s.Squire.class));
cards.add(new SetCardInfo("Standing Stones", 107, Rarity.UNCOMMON, mage.cards.s.StandingStones.class));
cards.add(new SetCardInfo("Stone Calendar", 108, Rarity.RARE, mage.cards.s.StoneCalendar.class));

View file

@ -30,6 +30,7 @@ package org.mage.test.cards.mana;
import mage.abilities.keyword.FlyingAbility;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
@ -75,9 +76,10 @@ public class ConditionalManaTest extends CardTestPlayerBase {
@Test
public void testWorkingWithReflectingPool() {
addCard(Zone.BATTLEFIELD, playerA, "Cavern of Souls", 1);
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); // can create white mana without restriction from the Cavern
addCard(Zone.HAND, playerA, "Silvercoat Lion", 1);
addCard(Zone.BATTLEFIELD, playerA, "Cavern of Souls", 1); // can give {C] or {any} mana ({any} with restrictions)
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1); // must give {C} or {any} mana from the Cavern, but without restrictions
addCard(Zone.HAND, playerA, "Silvercoat Lion", 1); // white bear
addCard(Zone.BATTLEFIELD, playerA, "Upwelling", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Silvercoat Lion");

View file

@ -33,10 +33,11 @@ import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import static org.mage.test.utils.ManaOptionsTestUtils.*;
/**
*
* @author LevelX2
* @author LevelX2, JayDi85
*/
public class HarvesterDruidTest extends CardTestPlayerBase {
@ -52,8 +53,10 @@ public class HarvesterDruidTest extends CardTestPlayerBase {
execute();
ManaOptions options = playerA.getAvailableManaTest(currentGame);
Assert.assertEquals("Player should be able to create 2 red and 1 blue mana", "{U}{R}{R}", options.get(0).toString());
Assert.assertEquals("Player should be able to create 1 red and 3 blue mana", "{U}{U}{R}", options.get(1).toString());
assertDuplicatedManaOptions(options);
Assert.assertEquals(2, options.size());
assertManaOptions("{U}{R}{R}", options);
assertManaOptions("{U}{U}{R}", options);
}
@Test
@ -68,9 +71,10 @@ public class HarvesterDruidTest extends CardTestPlayerBase {
execute();
ManaOptions options = playerA.getAvailableManaTest(currentGame);
Assert.assertEquals("Player should be able to create 3 red and 1 blue mana", "{U}{R}{R}{R}", options.get(0).toString());
Assert.assertEquals("Player should be able to create 2 red and 2 blue mana", "{U}{U}{R}{R}", options.get(1).toString());
Assert.assertEquals("Player should be able to create 2 red and 2 blue mana", "{U}{U}{R}{R}", options.get(2).toString());
Assert.assertEquals("Player should be able to create 1 red and 3 blue mana", "{U}{U}{U}{R}", options.get(3).toString());
assertDuplicatedManaOptions(options);
Assert.assertEquals(3, options.size());
assertManaOptions("{U}{R}{R}{R}", options);
assertManaOptions("{U}{U}{R}{R}", options);
assertManaOptions("{U}{U}{U}{R}", options);
}
}

View file

@ -6,10 +6,11 @@ import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import static org.mage.test.utils.ManaOptionsTestUtils.manaOptionsContain;
/**
*
* @author escplan9
* @author escplan9, JayDi85
*/
public class NagaVitalistTest extends CardTestPlayerBase {
@ -21,21 +22,112 @@ public class NagaVitalistTest extends CardTestPlayerBase {
private final String nagaVitalist = "Naga Vitalist";
/*
Reported bug (issue #3315)
Naga Vitalist could not produce any color mana with a Gift of Paradise enchanted on a forest. All lands on board were forests.
*/
@Test
public void nagaVitalist_InteractionGiftOfParadise() {
/*
Gift of Paradise 2G
Enchantment - Aura
Enchant - Land
When Gift of Paradise enters the battlefield, you gain 3 life.
Enchanted land has "T: Add two mana of any one color to your mana pool."
*/
String giftParadise = "Gift of Paradise";
*/
private final String giftParadise = "Gift of Paradise";
@Test
public void nagaVitalist_GiftOfParadiseCanAnyColor() {
// Mana pools don't empty as steps and phases end.
addCard(Zone.BATTLEFIELD, playerA, "Upwelling");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
addCard(Zone.HAND, playerA, giftParadise);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
// manual mana cost, cause auto cost can get swamp to pay
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, giftParadise, "Swamp");
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
Assert.assertTrue("playerA must cast {Any}{Any}", manaOptionsContain(playerA.getManaAvailable(currentGame), "{Any}{Any}"));
}
public void nagaVitalist_GiftOfParadisesLandCanGiveAnyColorToNaga_Setup(int giftCastTurn, int nagaManaTapTurn, String nagaManaTapColor) {
// test errors on enchanted ability do not apply for "any mana search" on different steps
addCard(Zone.BATTLEFIELD, playerA, "Upwelling");
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
addCard(Zone.HAND, playerA, giftParadise);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 1);
addCard(Zone.BATTLEFIELD, playerA, nagaVitalist, 1);
// cast and enchant swamp land to any color
activateManaAbility(giftCastTurn, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}");
activateManaAbility(giftCastTurn, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}");
activateManaAbility(giftCastTurn, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {G}");
castSpell(giftCastTurn, PhaseStep.PRECOMBAT_MAIN, playerA, giftParadise, "Swamp");
// activate red mana (by any from enchanted land)
activateManaAbility(nagaManaTapTurn, PhaseStep.POSTCOMBAT_MAIN, playerA, "{T}: Add to your mana pool one mana of any");
setChoice(playerA, nagaManaTapColor);
setStopAt(nagaManaTapTurn, PhaseStep.POSTCOMBAT_MAIN);
execute();
}
@Test
public void nagaVitalist_GiftOfParadisesLandCanGiveAnyColorToNaga_SameStep1() {
nagaVitalist_GiftOfParadisesLandCanGiveAnyColorToNaga_Setup(1, 1, "Red");
//logger.info(playerA.getManaPool().getMana().toString());
//logger.info(playerA.getManaAvailable(currentGame).toString());
//for(Permanent perm: currentGame.getBattlefield().getAllActivePermanents(playerA.getId())){
// logger.info(perm.getIdName() + ": " + perm.getAbilities().toString());
//}
assertTapped("Forest", true);
assertTapped(giftParadise, false);
assertTapped("Swamp", false);
assertTapped(nagaVitalist, true);
Assert.assertEquals(1, playerA.getManaPool().get(ManaType.RED));
}
@Test
public void nagaVitalist_GiftOfParadisesLandCanGiveAnyColorToNaga_DiffStep1() {
nagaVitalist_GiftOfParadisesLandCanGiveAnyColorToNaga_Setup(1, 2, "Red");
assertTapped("Forest", true);
assertTapped(giftParadise, false);
assertTapped("Swamp", false);
assertTapped(nagaVitalist, true);
Assert.assertEquals(1, playerA.getManaPool().get(ManaType.RED));
}
@Test
public void nagaVitalist_GiftOfParadisesLandCanGiveAnyColorToNaga_SameStep3() {
nagaVitalist_GiftOfParadisesLandCanGiveAnyColorToNaga_Setup(3, 3, "Red");
assertTapped("Forest", true);
assertTapped(giftParadise, false);
assertTapped("Swamp", false);
assertTapped(nagaVitalist, true);
Assert.assertEquals(1, playerA.getManaPool().get(ManaType.RED));
}
@Test
public void nagaVitalist_GiftOfParadisesLandCanGiveAnyColorToNaga_DiffStep2() {
nagaVitalist_GiftOfParadisesLandCanGiveAnyColorToNaga_Setup(3, 4, "Red");
assertTapped("Forest", true);
assertTapped(giftParadise, false);
assertTapped("Swamp", false);
assertTapped(nagaVitalist, true);
Assert.assertEquals(1, playerA.getManaPool().get(ManaType.RED));
}
/*
Reported bug (issue #3315)
Naga Vitalist could not produce any color mana with a Gift of Paradise enchanted on a forest. All lands on board were forests.
*/
@Test
public void nagaVitalist_InteractionGiftOfParadise() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
addCard(Zone.BATTLEFIELD, playerA, nagaVitalist);
addCard(Zone.BATTLEFIELD, playerA, "Upwelling"); // mana pools do not empty at the end of phases or turns
@ -43,7 +135,7 @@ public class NagaVitalistTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, giftParadise, "Forest");
activateManaAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add to your mana pool one mana of any type that a land you control could produce");
activateManaAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add to your mana pool one mana of any");
setChoice(playerA, "Red");
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
@ -51,6 +143,8 @@ public class NagaVitalistTest extends CardTestPlayerBase {
assertLife(playerA, 23); // gift of paradise ETB
assertTapped(nagaVitalist, true);
assertTapped(giftParadise, false);
assertTapped("Forest", false);
Assert.assertEquals("one red mana has to be in the mana pool", 1, playerA.getManaPool().get(ManaType.RED));
}
}

View file

@ -28,15 +28,18 @@
package org.mage.test.cards.mana;
import mage.abilities.mana.ManaOptions;
import mage.constants.ManaType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Assert;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import static org.mage.test.utils.ManaOptionsTestUtils.*;
/**
*
* @author LevelX2
* @author LevelX2, JayDi85
*/
public class ReflectingPoolTest extends CardTestPlayerBase {
@ -173,16 +176,122 @@ public class ReflectingPoolTest extends CardTestPlayerBase {
execute();
ManaOptions options = playerA.getAvailableManaTest(currentGame);
Assert.assertEquals("Player A should be able to create the ", "{G}{G}{G}", options.get(0).toString());
Assert.assertEquals("Player A should be able to create the ", "{W}{G}{G}", options.get(1).toString());
Assert.assertEquals("Player A should be able to create the ", "{W}{G}{G}", options.get(2).toString()); // ManaOption type optimzing seems not optimal yet
Assert.assertEquals("Player A should be able to create the ", "{W}{W}{G}", options.get(3).toString());
Assert.assertEquals("Player A should be able to create only 3 different mana options", 4, options.size());
Assert.assertEquals("Player A should be able to create only 3 different mana options", 3, options.size());
assertManaOptions("{G}{G}{G}", options);
assertManaOptions("{W}{G}{G}", options);
assertManaOptions("{W}{W}{G}", options);
options = playerB.getAvailableManaTest(currentGame);
Assert.assertEquals("Player B should be able to create the ", "{W}{G}", options.get(0).toString());
Assert.assertEquals("Player B should be able to create the ", "{W}{W}", options.get(1).toString());
Assert.assertEquals("Player B should be able to create only 3 different mana options", 2, options.size());
Assert.assertEquals("Player B should be able to create only 2 different mana options", 2, options.size());
assertManaOptions("{W}{G}", options);
assertManaOptions("{W}{W}", options);
}
@Test
public void testReflectingPoolGiveNonMana() {
addCard(Zone.HAND, playerA, bear1, 1);
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear1); // do not have any mana
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
Assert.assertEquals(0, playerA.getManaPool().getMana().count());
assertPermanentCount(playerA, bear1, 0);
}
@Test
public void testReflectingPoolGiveNonMana2() {
addCard(Zone.HAND, playerA, bear1, 1);
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear1); // do not have any mana
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
Assert.assertEquals(0, playerA.getManaPool().getMana().count());
assertPermanentCount(playerA, bear1, 0);
}
@Test
public void testReflectingPoolGiveBasicManaNeed() {
addCard(Zone.BATTLEFIELD, playerA, "Forest", 1);
addCard(Zone.HAND, playerA, bear1G, 1);
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear1G); // have {G} mana to cast
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertPermanentCount(playerA, bear1G, 1);
}
@Test
public void testReflectingPoolGiveBasicManaNotNeed() {
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.HAND, playerA, bear1G, 1);
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear1G); // have only {W} mana, can't cast
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertPermanentCount(playerA, bear1G, 0);
}
@Test
public void testReflectingPoolAnyManaNeedWithoutCondition() {
// any mana source without conditions (use any mana at any time)
addCard(Zone.BATTLEFIELD, playerA, "Plains", 2);
addCard(Zone.BATTLEFIELD, playerA, "City of Brass", 1);
String bear2GG = "Razorclaw Bear";
addCard(Zone.HAND, playerA, bear2GG, 1);
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1);
addCard(Zone.BATTLEFIELD, playerA, "Upwelling", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear2GG); // 2 plains + 2 any -- can cast
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertPermanentCount(playerA, bear2GG, 1);
}
@Test
public void testReflectingPoolAnyManaNeedWithCondition() {
// any mana source have condition to use (Reflecting Pool must ignore that condition)
addCard(Zone.BATTLEFIELD, playerA, "Cavern of Souls", 1); // {C} or {any}
addCard(Zone.HAND, playerA, bear1G, 1);
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1);
addCard(Zone.BATTLEFIELD, playerA, "Upwelling", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, bear1G); // {C} from cavern and {any} (green) from reflection
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
assertPermanentCount(playerA, bear1G, 1);
}
@Test
public void testReflectingPoolAnyManaTapped() {
// any mana source with tapped must allow use any too
addCard(Zone.BATTLEFIELD, playerA, "Plains", 1);
addCard(Zone.BATTLEFIELD, playerA, "City of Brass", 1);
addCard(Zone.BATTLEFIELD, playerA, "Reflecting Pool", 1);
addCard(Zone.BATTLEFIELD, playerA, "Upwelling", 1);
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add one mana of any");
activateManaAbility(1, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {W}");
setChoice(playerA,"Black");
setStopAt(1, PhaseStep.PRECOMBAT_MAIN);
execute();
logger.info(playerA.getManaPool().getMana().toString());
logger.info(playerA.getManaAvailable(currentGame).toString());
assertTapped("City of Brass", true);
assertTapped("Plains", true);
assertTapped("Reflecting Pool", false);
Assert.assertEquals(1, playerA.getManaPool().get(ManaType.BLACK));
}
}

View file

@ -27,6 +27,7 @@
*/
package org.mage.test.cards.mana;
import mage.constants.ManaType;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
@ -60,7 +61,29 @@ public class VorinclexVoiceOfHungerTest extends CardTestPlayerBase {
execute();
assertPermanentCount(playerA, "Vedalken Mastermind", 1);
}
/**
* Vorinclex, Voice of Hunger is not mana doubling River of Tears.
*/
@Test
// @Ignore // TODO: need to fix Vorinclex, Voice of Hunger -- it's double fireup mana tap event
public void testVorinclexVoiceofHungerRiverOfTearsManaMultiplier() {
addCard(Zone.BATTLEFIELD, playerA, "Upwelling", 1);
addCard(Zone.HAND, playerA, "River of Tears", 1);
// Trample
// Whenever you tap a land for mana, add one mana to your mana pool of any type that land produced.
// Whenever an opponent taps a land for mana, that land doesn't untap during its controller's next untap step.
addCard(Zone.BATTLEFIELD, playerA, "Vorinclex, Voice of Hunger", 1);
// {T}: Add {U} to your mana pool. If you played a land this turn, add {B} to your mana pool instead.
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "River of Tears");
activateManaAbility(3, PhaseStep.PRECOMBAT_MAIN, playerA, "{T}: Add {U} to your mana pool");
setStopAt(3, PhaseStep.BEGIN_COMBAT);
execute();
assertManaPool(playerA, ManaType.BLUE, 2);
}
/**

View file

@ -0,0 +1,82 @@
package org.mage.test.cards.single;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* @author JRHerlehy Created on 12/27/17.
*/
public class PatronOfTheNezumiTest extends CardTestPlayerBase {
// Rat offering
// Whenever a permanent is put into an opponent's graveyard, that player loses 1 life.
private final String patron = "Patron of the Nezumi";
private final String thopter = "Ornithopter";
private final String elspeth = "Elspeth, Sun's Champion";
private final String elspethAbility = "+1: Create three";
private final String elesh = "Elesh Norn, Grand Cenobite";
private final String sinkhole = "Sinkhole";
@Test
public void testEffectWithTokens() {
this.setupTest();
this.addCard(Zone.BATTLEFIELD, playerB, elspeth);
this.activateAbility(2, PhaseStep.PRECOMBAT_MAIN, playerB, elspethAbility);
this.activateAbility(4, PhaseStep.PRECOMBAT_MAIN, playerB, elspethAbility);
this.castSpell(5, PhaseStep.PRECOMBAT_MAIN, playerA, elesh);
this.setStopAt(5, PhaseStep.POSTCOMBAT_MAIN);
this.execute();
this.assertPermanentCount(playerB, 1);
this.assertLife(playerA, 20);
this.assertLife(playerB, 14);
}
@Test
public void testEffectWithRIP() {
this.setupTest();
this.addCard(Zone.BATTLEFIELD, playerA, "Rest in Peace");
this.addCard(Zone.BATTLEFIELD, playerB, thopter, 5);
this.castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, elesh);
this.setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
this.execute();
this.assertExileCount(playerB, 5);
this.assertLife(playerA, 20);
this.assertLife(playerB, 20);
}
@Test
public void testEffectWithCreatures() {
this.setupTest();
this.addCard(Zone.BATTLEFIELD, playerB, thopter, 5);
this.castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, elesh);
this.setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
this.execute();
this.assertPermanentCount(playerB, 0);
this.assertLife(playerA, 20);
this.assertLife(playerB, 15);
}
private void setupTest() {
this.addCard(Zone.BATTLEFIELD, playerA, patron);
this.addCard(Zone.BATTLEFIELD, playerA, "Swamp", 5);
this.addCard(Zone.BATTLEFIELD, playerA, "Plains", 5);
this.addCard(Zone.HAND, playerA, sinkhole);
this.addCard(Zone.HAND, playerA, elesh);
}
}

View file

@ -66,7 +66,7 @@ public class ZurTheEnchanterTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerB, "Diplomatic Immunity"); // {1}{U}
// Enchant creature
// Enchanted creature gets +1/+1 for each card in your hand.
addCard(Zone.LIBRARY, playerB, "Empyrial Armor");
addCard(Zone.LIBRARY, playerB, "Empyrial Armor", 2);
castSpell(2, PhaseStep.PRECOMBAT_MAIN, playerB, "Diplomatic Immunity", "Zur the Enchanter");

View file

@ -837,22 +837,22 @@ public abstract class CardTestPlayerAPIImpl extends MageTestPlayerBase implement
ManaPool manaPool = currentGame.getPlayer(player.getId()).getManaPool();
switch (color){
case COLORLESS:
Assert.assertEquals(manaPool.getColorless() + manaPool.getConditionalMana().stream().mapToInt(Mana::getColorless).sum(), amount);
Assert.assertEquals(amount,manaPool.getColorless() + manaPool.getConditionalMana().stream().mapToInt(Mana::getColorless).sum());
break;
case RED:
Assert.assertEquals(manaPool.getRed() + manaPool.getConditionalMana().stream().mapToInt(Mana::getRed).sum(), amount);
Assert.assertEquals(amount,manaPool.getRed() + manaPool.getConditionalMana().stream().mapToInt(Mana::getRed).sum());
break;
case BLUE:
Assert.assertEquals(manaPool.getBlue() + manaPool.getConditionalMana().stream().mapToInt(Mana::getBlue).sum(), amount);
Assert.assertEquals(amount,manaPool.getBlue() + manaPool.getConditionalMana().stream().mapToInt(Mana::getBlue).sum());
break;
case WHITE:
Assert.assertEquals(manaPool.getWhite() + manaPool.getConditionalMana().stream().mapToInt(Mana::getWhite).sum(), amount);
Assert.assertEquals(amount,manaPool.getWhite() + manaPool.getConditionalMana().stream().mapToInt(Mana::getWhite).sum());
break;
case GREEN:
Assert.assertEquals(manaPool.getGreen() + manaPool.getConditionalMana().stream().mapToInt(Mana::getGreen).sum(), amount);
Assert.assertEquals(amount,manaPool.getGreen() + manaPool.getConditionalMana().stream().mapToInt(Mana::getGreen).sum());
break;
case BLACK:
Assert.assertEquals(manaPool.getBlack() + manaPool.getConditionalMana().stream().mapToInt(Mana::getBlack).sum(), amount);
Assert.assertEquals(amount,manaPool.getBlack() + manaPool.getConditionalMana().stream().mapToInt(Mana::getBlack).sum());
break;
}
}

View file

@ -35,12 +35,13 @@ import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
import static org.mage.test.utils.ManaOptionsTestUtils.*;
/**
* This test checks if the calculated possible mana options are correct related
* to the given mana sources available.
*
* @author LevelX2
* @author LevelX2, JayDi85
*/
public class ManaOptionsTest extends CardTestPlayerBase {
@ -52,9 +53,10 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
Assert.assertEquals("{G}{G}{G}", getManaOption(0, manaOptions));
assertManaOptions("{G}{G}{G}", manaOptions);
}
@ -69,12 +71,13 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 4, manaOptions.size());
Assert.assertEquals("{G}{G}{G}", getManaOption(0, manaOptions));
Assert.assertEquals("{W}{R}{G}{G}", getManaOption(1, manaOptions));
Assert.assertEquals("{W}{W}{R}{R}{G}", getManaOption(2, manaOptions));
Assert.assertEquals("{W}{W}{W}{R}{R}{R}", getManaOption(3, manaOptions));
assertManaOptions("{G}{G}{G}", manaOptions);
assertManaOptions("{W}{R}{G}{G}", manaOptions);
assertManaOptions("{W}{W}{R}{R}{G}", manaOptions);
assertManaOptions("{W}{W}{W}{R}{R}{R}", manaOptions);
}
@ -89,18 +92,19 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 10, manaOptions.size());
Assert.assertEquals("{C}{C}{C}", getManaOption(0, manaOptions));
Assert.assertEquals("{C}{C}{W}", getManaOption(1, manaOptions));
Assert.assertEquals("{C}{C}{U}", getManaOption(2, manaOptions));
Assert.assertEquals("{C}{W}{W}", getManaOption(3, manaOptions));
Assert.assertEquals("{C}{W}{U}", getManaOption(4, manaOptions));
Assert.assertEquals("{C}{U}{U}", getManaOption(5, manaOptions));
Assert.assertEquals("{W}{W}{W}", getManaOption(6, manaOptions));
Assert.assertEquals("{W}{W}{U}", getManaOption(7, manaOptions));
Assert.assertEquals("{W}{U}{U}", getManaOption(8, manaOptions));
Assert.assertEquals("{U}{U}{U}", getManaOption(9, manaOptions));
assertManaOptions("{C}{C}{C}", manaOptions);
assertManaOptions("{C}{C}{W}", manaOptions);
assertManaOptions("{C}{C}{U}", manaOptions);
assertManaOptions("{C}{W}{W}", manaOptions);
assertManaOptions("{C}{W}{U}", manaOptions);
assertManaOptions("{C}{U}{U}", manaOptions);
assertManaOptions("{W}{W}{W}", manaOptions);
assertManaOptions("{W}{W}{U}", manaOptions);
assertManaOptions("{W}{U}{U}", manaOptions);
assertManaOptions("{U}{U}{U}", manaOptions);
}
// Chromatic Sphere
@ -114,9 +118,10 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
Assert.assertEquals("{Any}{Any}", getManaOption(0, manaOptions));
assertManaOptions("{Any}{Any}", manaOptions);
}
// Orochi Leafcaller
@ -131,9 +136,10 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
Assert.assertEquals("{W}{W}{Any}{Any}", getManaOption(0, manaOptions));
assertManaOptions("{W}{W}{Any}{Any}", manaOptions);
}
// Crystal Quarry
@ -149,9 +155,10 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
Assert.assertEquals("{C}{W}{W}{G}{G}", getManaOption(0, manaOptions));
assertManaOptions("{C}{W}{W}{G}{G}", manaOptions);
}
// Crystal Quarry
@ -167,10 +174,11 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 2, manaOptions.size());
Assert.assertEquals("{C}{W}{W}{G}{G}{G}", getManaOption(0, manaOptions));
Assert.assertEquals("{W}{U}{B}{R}{G}", getManaOption(1, manaOptions));
assertManaOptions("{C}{W}{W}{G}{G}{G}", manaOptions);
assertManaOptions("{W}{U}{B}{R}{G}", manaOptions);
}
// Nykthos, Shrine to Nyx
@ -186,28 +194,30 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 2, manaOptions.size());
Assert.assertEquals("{C}{G}{G}{G}", getManaOption(0, manaOptions));
Assert.assertEquals("{G}{G}{G}{G}{G}", getManaOption(1, manaOptions));
assertManaOptions("{C}{G}{G}{G}", manaOptions);
assertManaOptions("{G}{G}{G}{G}{G}", manaOptions);
}
@Test
public void testNykthos2() {
addCard(Zone.BATTLEFIELD, playerA, "Sedge Scorpion", 4);
addCard(Zone.BATTLEFIELD, playerA, "Akroan Crusader", 3);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3);
addCard(Zone.BATTLEFIELD, playerA, "Nykthos, Shrine to Nyx", 1);
addCard(Zone.BATTLEFIELD, playerA, "Forest", 3); // {G}
addCard(Zone.BATTLEFIELD, playerA, "Nykthos, Shrine to Nyx", 1); // {C}
setStopAt(1, PhaseStep.UPKEEP);
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 3, manaOptions.size());
Assert.assertEquals("{C}{G}{G}{G}", getManaOption(0, manaOptions));
Assert.assertEquals("{G}{G}{G}{G}{G}", getManaOption(1, manaOptions));
Assert.assertEquals("{R}{R}{R}{G}", getManaOption(2, manaOptions));
assertManaOptions("{C}{G}{G}{G}", manaOptions);
assertManaOptions("{G}{G}{G}{G}{G}", manaOptions);
assertManaOptions("{R}{R}{R}{G}", manaOptions);
}
@Test
@ -220,13 +230,46 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
Assert.assertEquals("{C}{G}{Any}", getManaOption(0, manaOptions));
assertManaOptions("{C}{G}{Any}", manaOptions);
}
@Test
public void testMix1() {
public void testDuplicatedDontHave1() {
addCard(Zone.BATTLEFIELD, playerA, "City of Brass", 2); // Any
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
setStopAt(1, PhaseStep.UPKEEP);
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
}
@Test
public void testDuplicatedDontHave3() {
addCard(Zone.BATTLEFIELD, playerA, "Grove of the Burnwillows", 2); // R or G
addCard(Zone.BATTLEFIELD, playerA, "Forest", 2);
setStopAt(1, PhaseStep.UPKEEP);
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
}
@Test
public void testDuplicatedHave() {
// getManaAvailable return any combination of mana variants evailable to player
// if mana ability cost another mana then if replaced in mana cost
// example:
// 1x forest
// 1x Chromatic Star ({1}, {T}, Sacrifice Chromatic Star: Add one mana of any color to your mana pool.)
// give {G}{Any}, but after pay it transform to {Any} (1 green will be pay)
// That's why there are can be duplicated records in getManaAvailable
// {1}, {T}, Sacrifice Chromatic Star: Add one mana of any color to your mana pool.
// When Chromatic Star is put into a graveyard from the battlefield, draw a card.
addCard(Zone.BATTLEFIELD, playerA, "Chromatic Star", 1);
@ -242,10 +285,9 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
Assert.assertEquals("mana variations don't fit", 2, manaOptions.size());
Assert.assertEquals("{Any}{Any}", getManaOption(0, manaOptions));
Assert.assertEquals("{Any}{Any}", getManaOption(1, manaOptions));
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
assertDuplicatedManaOptions(manaOptions);
assertManaOptions("{Any}{Any}", manaOptions);
}
@Test
@ -257,12 +299,13 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 4, manaOptions.size());
Assert.assertEquals("{C}{W}", getManaOption(0, manaOptions));
Assert.assertEquals("{W}{W}", getManaOption(1, manaOptions));
Assert.assertEquals("{W}{B}", getManaOption(2, manaOptions));
Assert.assertEquals("{B}{B}", getManaOption(3, manaOptions));
assertManaOptions("{C}{W}", manaOptions);
assertManaOptions("{W}{W}", manaOptions);
assertManaOptions("{W}{B}", manaOptions);
assertManaOptions("{B}{B}", manaOptions);
}
/**
@ -278,9 +321,10 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
Assert.assertEquals("{W}{B}", getManaOption(0, manaOptions));
assertManaOptions("{W}{B}", manaOptions);
}
@Test
@ -293,10 +337,11 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 2, manaOptions.size());
Assert.assertEquals("{W}{B}{B}", getManaOption(0, manaOptions));
Assert.assertEquals("{B}{B}{B}", getManaOption(1, manaOptions));
assertManaOptions("{W}{B}{B}", manaOptions);
assertManaOptions("{B}{B}{B}", manaOptions);
}
@Test
@ -312,9 +357,10 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
Assert.assertEquals("{C}{W}{B}", getManaOption(0, manaOptions));
assertManaOptions("{C}{W}{B}", manaOptions);
}
@Test
@ -331,9 +377,10 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
Assert.assertEquals("{C}{C}{C}{C}{W}{B}", getManaOption(0, manaOptions));
assertManaOptions("{C}{C}{C}{C}{W}{B}", manaOptions);
}
@Test
@ -349,17 +396,9 @@ public class ManaOptionsTest extends CardTestPlayerBase {
execute();
ManaOptions manaOptions = playerA.getAvailableManaTest(currentGame);
assertDuplicatedManaOptions(manaOptions);
Assert.assertEquals("mana variations don't fit", 1, manaOptions.size());
Assert.assertEquals("{B}{B}", getManaOption(0, manaOptions));
}
// TODO
// Test Calciform Pools combination mana lands
private String getManaOption(int index, ManaOptions manaOptions) {
if (manaOptions.size() < index + 1) {
return "";
}
return manaOptions.get(index).toString();
assertManaOptions("{B}{B}", manaOptions);
}
}

View file

@ -0,0 +1,48 @@
package org.mage.test.utils;
import mage.Mana;
import mage.abilities.mana.ManaOptions;
import org.junit.Assert;
import java.util.HashSet;
import java.util.Set;
public class ManaOptionsTestUtils {
public static String bear1W = "Silvercoat Lion"; // {1}{W}
public static String bearG = "Basking Rootwalla"; // {G}
public static String bear1 = "Augmenting Automaton"; // {1}
public static String bear1G = "Balduvian Bears"; // {1}{G}
public static String bear2C = "Matter Reshaper"; // {2}{C}
//mana info
//logger.info(playerA.getManaPool().getMana().toString());
//logger.info(playerA.getManaAvailable(currentGame).toString());
public static boolean manaOptionsContain(ManaOptions list, String searchMana){
for(Mana mana: list){
if (mana.toString().equals(searchMana)){
return true;
}
}
return false;
}
public static void assertManaOptions(String searchMana, ManaOptions manaList){
if(!manaOptionsContain(manaList, searchMana)){
Assert.fail("Can't find " + searchMana + " in " + manaList.toString());
}
}
public static void assertDuplicatedManaOptions(ManaOptions manaList){
Set<String> list = new HashSet<>();
for(Mana mana: manaList){
String s = mana.toString();
if(list.contains(s)){
Assert.fail("Founded duplicated mana option " + s + " in " + manaList.toString());
}else{
list.add(s);
}
}
}
}

View file

@ -10,6 +10,7 @@ class JsonSet {
public String gathererCode;
public String magicCardsInfoCode;
public String[] magicRaritiesCodes;
public String[] alternativeNames;
public String releaseDate;
public String border;
public String type;

View file

@ -16,6 +16,36 @@ import java.util.Map;
import java.util.zip.ZipInputStream;
public final class MtgJson {
public static Map<String, String> mtgJsonToXMageCodes = new HashMap<>();
public static Map<String, String> xMageToMtgJsonCodes = new HashMap<>();
static {
mtgJsonToXMageCodes.put("pWCQ", "WMCQ");
mtgJsonToXMageCodes.put("pSUS", "SUS");
mtgJsonToXMageCodes.put("pPRE", "PTC");
mtgJsonToXMageCodes.put("pMPR", "MPRP");
mtgJsonToXMageCodes.put("pMEI", "MBP");
mtgJsonToXMageCodes.put("pGTW", "GRC"); // pGTW - Gateway = GRC - WPN Gateway ???
mtgJsonToXMageCodes.put("pGRU", "GUR");
mtgJsonToXMageCodes.put("pGPX", "GPX");
mtgJsonToXMageCodes.put("pFNM", "FNMP");
mtgJsonToXMageCodes.put("pELP", "EURO");
mtgJsonToXMageCodes.put("pARL", "ARENA");
mtgJsonToXMageCodes.put("pALP", "APAC");
mtgJsonToXMageCodes.put("PO2", "P02");
mtgJsonToXMageCodes.put("DD3_JVC", "DD3JVC");
mtgJsonToXMageCodes.put("DD3_GVL", "DDD");
mtgJsonToXMageCodes.put("DD3_EVG", "DD3EVG");
mtgJsonToXMageCodes.put("DD3_DVD", "DDC");
mtgJsonToXMageCodes.put("NMS", "NEM");
// revert search
for(Map.Entry<String, String> entry: mtgJsonToXMageCodes.entrySet()){
xMageToMtgJsonCodes.put(entry.getValue(), entry.getKey());
}
}
private MtgJson() {}
private static final class CardHolder {

View file

@ -152,10 +152,13 @@ public class VerifyCardDataTest {
public void checkWrongCardClasses(){
Collection<String> errorsList = new ArrayList<>();
Map<String, String> classesIndex = new HashMap<>();
int totalCards = 0;
Collection<ExpansionSet> sets = Sets.getInstance().values();
for (ExpansionSet set : sets) {
for (ExpansionSet.SetCardInfo checkCard : set.getSetCardInfo()) {
totalCards = totalCards + 1;
String currentClass = checkCard.getCardClass().toString();
if (classesIndex.containsKey(checkCard.getName())) {
String needClass = classesIndex.get(checkCard.getName());
@ -175,11 +178,45 @@ public class VerifyCardDataTest {
System.out.println(error);
}
// unique cards stats
System.out.println("Total unique cards: " + classesIndex.size() + ", total non unique cards (reprints): " + totalCards);
if (errorsList.size() > 0){
Assert.fail("DB have wrong card classes, founded errors: " + errorsList.size());
}
}
@Test
public void checkMissingSets(){
Collection<String> errorsList = new ArrayList<>();
int totalMissingSets = 0;
int totalMissingCards = 0;
Collection<ExpansionSet> sets = Sets.getInstance().values();
for(Map.Entry<String, JsonSet> refEntry: MtgJson.sets().entrySet()){
JsonSet refSet = refEntry.getValue();
// replace codes for aliases
String searchSet = MtgJson.mtgJsonToXMageCodes.getOrDefault(refSet.code, refSet.code);
ExpansionSet mageSet = Sets.findSet(searchSet);
if(mageSet == null){
totalMissingSets = totalMissingSets + 1;
totalMissingCards = totalMissingCards + refSet.cards.size();
errorsList.add("Warning: missing set " + refSet.code + " - " + refSet.name + " (cards: " + refSet.cards.size() + ")");
}
}
if(errorsList.size() > 0){
errorsList.add("Warning: total missing sets: " + totalMissingSets + ", with missing cards: " + totalMissingCards);
}
// only warnings
for (String error: errorsList) {
System.out.println(error);
}
}
private static final Pattern SHORT_JAVA_STRING = Pattern.compile("(?<=\")[A-Z][a-z]+(?=\")");
private Set<String> findSourceTokens(Class c) throws IOException {

View file

@ -221,6 +221,19 @@ public class Mana implements Comparable<Mana>, Serializable, Copyable<Mana> {
return new Mana(0, 0, 0, 0, 0, 0, 0, notNegative(num, "Colorless"));
}
/**
* Creates a {@link Mana} object with the passed in {@code num} of Any
* mana. {@code num} can not be a negative value. Negative values will be
* logged and set to 0.
*
* @param num value of Any mana to create.
* @return a {@link Mana} object with the passed in {@code num} of Any
* mana.
*/
public static Mana AnyMana(int num) {
return new Mana(0, 0, 0, 0, 0, 0, notNegative(num, "Any"), 0);
}
/**
* Adds mana from the passed in {@link Mana} object to this object.
*

View file

@ -407,7 +407,7 @@ public abstract class AbilityImpl implements Ability {
}
if (!game.isSimulation()) {
// inform about x costs now, so canceled announcements are not shown in the log
if (announceString != null) {
if ((announceString != null) && (!announceString.equals(""))) {
game.informPlayers(announceString);
}
if (variableManaCost != null) {

View file

@ -19,14 +19,29 @@ import mage.target.targetpointer.FixedTarget;
/**
*
* @author Ludwig.Hirth
* @author LevelX2
*/
public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityImpl {
private final boolean combatDamageOnly;
private final FilterPermanent filterPermanent;
private final SetTargetPointer setTargetPointer;
/**
* This ability works only for permanents doing damage.
*
* @param effect
* @param optional
* @param filterPermanent The filter that restricts which permanets have to
* trigger
* @param setTargetPointer The target to be set to target pointer of the
* effect.<br>
* - PLAYER = player controlling the damage source.<br>
* - PERMANENT = source permanent.<br>
* - PERMANENT_TARGET = damaged creature.
* @param combatDamageOnly The flag to determine if only combat damage has
* to trigger
*/
public DealsDamageToACreatureAllTriggeredAbility(Effect effect, boolean optional, FilterPermanent filterPermanent, SetTargetPointer setTargetPointer, boolean combatDamageOnly) {
super(Zone.BATTLEFIELD, effect, optional);
this.combatDamageOnly = combatDamageOnly;
@ -64,7 +79,13 @@ public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityI
effect.setTargetPointer(new FixedTarget(permanent.getControllerId()));
break;
case PERMANENT:
effect.setTargetPointer(new FixedTarget(permanent.getId(), permanent.getZoneChangeCounter(game)));
effect.setTargetPointer(new FixedTarget(permanent, game));
break;
case PERMANENT_TARGET:
Permanent permanent_target = game.getPermanentOrLKIBattlefield(event.getTargetId());
if (permanent_target != null) {
effect.setTargetPointer(new FixedTarget(permanent_target, game));
}
break;
}
@ -77,7 +98,7 @@ public class DealsDamageToACreatureAllTriggeredAbility extends TriggeredAbilityI
@Override
public String getRule() {
return "Whenever " + filterPermanent.getMessage() + " deals "
+ (combatDamageOnly ? "combat ":"") + "damage to a creature, " + super.getRule();
return "Whenever " + filterPermanent.getMessage() + " deals "
+ (combatDamageOnly ? "combat " : "") + "damage to a creature, " + super.getRule();
}
}
}

View file

@ -79,8 +79,10 @@ public class ConditionalManaEffect extends ManaEffect {
otherwiseEffect.setTargetPointer(this.targetPointer);
}
Mana mana = getMana(game, source);
if (mana != null && mana.getAny() > 0) {
if (mana == null) {
return false;
}
if (mana.getAny() > 0) {
int amount = mana.getAny();
ChoiceColor choice = new ChoiceColor(true);
@ -92,13 +94,14 @@ public class ConditionalManaEffect extends ManaEffect {
createdMana = choice.getMana(amount);
}
if (createdMana == null) {
return false;
}
mana = createdMana;
}
if (mana != null) {
// because the mana type is now choosen, fire the event with the mana information
checkToFirePossibleEvents(mana, game, source);
controller.getManaPool().addMana(mana, game, source);
}
controller.getManaPool().addMana(mana, game, source);
return true;
}

View file

@ -98,7 +98,7 @@ public class AuraReplacementEffect extends ReplacementEffectImpl {
// Aura enters the battlefield attached
Object object = game.getState().getValue("attachTo:" + card.getId());
if (object != null) {
if (object instanceof PermanentCard) {
if (object instanceof Permanent) {
// Aura is attached to a permanent on the battlefield
return false;
}

View file

@ -69,11 +69,13 @@ public class AddManaOfAnyColorEffect extends BasicManaEffect {
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
if (controller != null) {
ChoiceColor choice = new ChoiceColor(true);
String mes = String.format("Select color of %d mana to add it to your mana pool", this.amount);
ChoiceColor choice = new ChoiceColor(true, mes, game.getObject(source.getSourceId()));
if (controller.choose(outcome, choice, game)) {
if (choice.getColor() == null) {
return false; // it happens, don't know how
// on user's reconnect choice dialog close and return null even with required settings
return false;
}
Mana createdMana = choice.getMana(amount);
if (createdMana != null) {

View file

@ -33,6 +33,7 @@ import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.choices.Choice;
import mage.choices.ChoiceCreatureType;
import mage.choices.ChoiceImpl;
import mage.constants.Outcome;
import mage.constants.SubType;
@ -63,9 +64,7 @@ public class ChooseCreatureTypeEffect extends OneShotEffect {
mageObject = game.getObject(source.getSourceId());
}
if (controller != null && mageObject != null) {
Choice typeChoice = new ChoiceImpl(true);
typeChoice.setMessage("Choose creature type");
typeChoice.setChoices(SubType.getCreatureTypes(false).stream().map(SubType::toString).collect(Collectors.toCollection(LinkedHashSet::new)));
Choice typeChoice = new ChoiceCreatureType(mageObject);
while (!controller.choose(outcome, typeChoice, game)) {
if (!controller.canRespond()) {
return false;

View file

@ -55,7 +55,7 @@ public class GainProtectionFromColorTargetEffect extends GainAbilityTargetEffect
public GainProtectionFromColorTargetEffect(Duration duration, ObjectColor protectColor) {
super(new ProtectionAbility(new FilterCard()), duration);
choice = new ChoiceColor(true);
choice = new ChoiceColor(true, "Choose a color to gain protection against it");
if (protectColor != null) {
choice.setChoice(protectColor.toString());
}

View file

@ -225,6 +225,9 @@ class AnyColorLandsProduceManaEffect extends ManaEffect {
if (types.getColorless() > 0) {
netManas.add(Mana.ColorlessMana(1));
}
if (types.getAny() > 0) {
netManas.add(Mana.AnyMana(1));
}
return netManas;
}

View file

@ -28,7 +28,10 @@
package mage.abilities.mana;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import mage.Mana;
import mage.game.Game;
@ -326,4 +329,19 @@ public class ManaOptions extends ArrayList<Mana> {
payCombinations.add(newMana);
payCombinationsStrings.add(newMana.toString());
}
public void removeDuplicated(){
Set<String> list = new HashSet<>();
for(int i = this.size() - 1; i >= 0; i--){
String s = this.get(i).toString();
if (list.contains(s)){
// remove duplicated
this.remove(i);
}else{
list.add(s);
}
}
}
}

View file

@ -37,19 +37,40 @@ import java.util.Set;
*/
public interface Choice {
boolean isChosen();
boolean isRequired();
void clearChoice();
String getMessage();
void setMessage(String message);
void setChoice(String choice);
void setChoiceByKey(String choiceKey);
Set<String> getChoices();
Map<String,String> getKeyChoices();
void setChoices(Set<String> choices);
void setKeyChoices(Map<String, String> choices);
String getChoice();
String getChoiceKey();
boolean isKeyChoice();
String getSubMessage();
void setSubMessage(String subMessage);
void clearChoice();
boolean isChosen();
boolean isRequired();
Choice copy();
// string choice
void setChoices(Set<String> choices);
Set<String> getChoices();
void setChoice(String choice);
String getChoice();
// key-value choice
boolean isKeyChoice();
void setKeyChoices(Map<String, String> choices);
Map<String,String> getKeyChoices();
void setChoiceByKey(String choiceKey);
String getChoiceKey();
String getChoiceValue();
// search
boolean isSearchEnabled();
void setSearchEnabled(boolean isEnabled);
void setSearchText(String searchText);
String getSearchText();
// sorting
boolean isSortEnabled();
void setSortData(Map<String, Integer> sortData);
Map<String, Integer> getSortData();
}

View file

@ -27,24 +27,30 @@
*/
package mage.choices;
import java.util.ArrayList;
import mage.MageObject;
import mage.Mana;
import mage.ObjectColor;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author BetaSteward_at_googlemail.com
* @author BetaSteward_at_googlemail.com, JayDi85
*/
public class ChoiceColor extends ChoiceImpl {
public static final ArrayList<String> colorChoices = new ArrayList<>();
private static final ArrayList<String> colorChoices = getBaseColors();
static {
colorChoices.add("Green");
colorChoices.add("Blue");
colorChoices.add("Black");
colorChoices.add("Red");
colorChoices.add("White");
public static ArrayList<String> getBaseColors(){
ArrayList<String> arr = new ArrayList<>();
arr.add("Green");
arr.add("Blue");
arr.add("Black");
arr.add("Red");
arr.add("White");
return arr;
}
public ChoiceColor() {
@ -52,9 +58,24 @@ public class ChoiceColor extends ChoiceImpl {
}
public ChoiceColor(boolean required) {
this(required, "Choose color");
}
public ChoiceColor(boolean required, String chooseMessage){
this(required, chooseMessage, "");
}
public ChoiceColor(boolean required, String chooseMessage, MageObject source){
this(required, chooseMessage, source.getIdName());
}
public ChoiceColor(boolean required, String chooseMessage, String chooseSubMessage){
super(required);
this.choices.addAll(colorChoices);
this.message = "Choose color";
this.setMessage(chooseMessage);
this.setSubMessage(chooseSubMessage);
}
public ChoiceColor(final ChoiceColor choice) {

View file

@ -1,5 +1,6 @@
package mage.choices;
import mage.MageObject;
import mage.constants.SubType;
import java.util.LinkedHashSet;
@ -7,10 +8,28 @@ import java.util.stream.Collectors;
public class ChoiceCreatureType extends ChoiceImpl {
private static String DEFAULT_MESSAGE = "Choose a creature type";
public ChoiceCreatureType() {
super(true);
this(true, DEFAULT_MESSAGE, null);
}
public ChoiceCreatureType(MageObject source) {
this(true, DEFAULT_MESSAGE, source);
}
public ChoiceCreatureType(String chooseMessage, MageObject source) {
this(true, chooseMessage, source);
}
public ChoiceCreatureType(boolean required, String chooseMessage, MageObject source){
super(required);
this.setChoices(SubType.getCreatureTypes(false).stream().map(SubType::toString).collect(Collectors.toCollection(LinkedHashSet::new)));
this.message = "Choose a creature type:";
this.setMessage(chooseMessage);
if(source != null) {
this.setSubMessage(source.getIdName());
}
this.setSearchEnabled(true);
}
public ChoiceCreatureType(final ChoiceCreatureType choice) {

View file

@ -40,13 +40,18 @@ import java.util.Set;
*/
public class ChoiceImpl implements Choice, Serializable {
// TODO: add sorting to items
protected boolean chosen;
protected final boolean required;
protected String choice;
protected String choiceKey;
protected Set<String> choices = new LinkedHashSet<>();
protected Map<String, String> keyChoices = new LinkedHashMap<>();
protected Map<String, Integer> sortData = new LinkedHashMap<>();
protected String message;
protected String subMessage;
protected boolean searchEnabled = true; // enable for all windows by default
protected String searchText;
public ChoiceImpl() {
this(false);
@ -61,9 +66,13 @@ public class ChoiceImpl implements Choice, Serializable {
this.chosen = choice.chosen;
this.required = choice.required;
this.message = choice.message;
this.subMessage = choice.subMessage;
this.searchEnabled = choice.searchEnabled;
this.searchText = choice.searchText;
this.choices.addAll(choice.choices);
this.choiceKey = choice.choiceKey;
this.keyChoices = choice.keyChoices; // list should never change for the same object so copy by reference
this.keyChoices = choice.keyChoices; // list should never change for the same object so copy by reference TODO: check errors with that, it that ok? Color list is static
this.sortData = choice.sortData;
}
@Override
@ -88,6 +97,12 @@ public class ChoiceImpl implements Choice, Serializable {
this.message = message;
}
@Override
public String getSubMessage(){ return subMessage; }
@Override
public void setSubMessage(String subMessage){ this.subMessage = subMessage; }
@Override
public Set<String> getChoices() {
return choices;
@ -136,12 +151,22 @@ public class ChoiceImpl implements Choice, Serializable {
return choiceKey;
}
@Override
public String getChoiceValue() {
if ((keyChoices != null) && (keyChoices.containsKey(choiceKey))){
return keyChoices.get(choiceKey);
}else{
return null;
}
}
@Override
public void setChoiceByKey(String choiceKey) {
String choiceToSet = keyChoices.get(choiceKey);
if (choiceToSet != null) {
this.choice = choiceToSet;
this.choiceKey = choiceKey;
this.chosen = true;
}
}
@ -150,4 +175,39 @@ public class ChoiceImpl implements Choice, Serializable {
return !keyChoices.isEmpty();
}
}
@Override
public boolean isSearchEnabled(){
return this.searchEnabled;
};
@Override
public void setSearchEnabled(boolean isEnabled){
this.searchEnabled = isEnabled;
};
@Override
public void setSearchText(String searchText){
this.searchText = searchText;
};
@Override
public String getSearchText(){
return this.searchText;
};
@Override
public boolean isSortEnabled(){
return (this.sortData != null) && !this.sortData.isEmpty();
};
@Override
public void setSortData(Map<String, Integer> sortData){
this.sortData = sortData;
};
@Override
public Map<String, Integer> getSortData(){
return this.sortData;
};
}

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