GUI: Autochoose targets if choice can be made (#9206)

This commit is contained in:
Alex Vasile 2022-07-08 21:58:42 -04:00 committed by GitHub
parent 1e01efd49d
commit 96f6fbefc8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 492 additions and 213 deletions

View file

@ -93,6 +93,10 @@ public final class Constants {
public static final int BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_ONE_COLOR = 1; public static final int BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_ONE_COLOR = 1;
public static final int BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_MULTICOLOR = 2; public static final int BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_MULTICOLOR = 2;
public static final int AUTO_TARGET_DISABLE = 0;
public static final int AUTO_TARGET_NON_FEEL_BAD = 1;
public static final int AUTO_TARGET_ALL = 2;
public interface IO { public interface IO {
String DEFAULT_IMAGES_DIR = "plugins" + File.separator + "images" + File.separator; String DEFAULT_IMAGES_DIR = "plugins" + File.separator + "images" + File.separator;
} }

View file

@ -42,8 +42,8 @@
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="tabsPanel" min="-2" pref="554" max="-2" attributes="0"/> <Component id="tabsPanel" pref="554" max="32767" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0"> <Group type="103" groupAlignment="3" attributes="0">
<Component id="saveButton" alignment="3" min="-2" pref="30" max="-2" attributes="0"/> <Component id="saveButton" alignment="3" min="-2" pref="30" max="-2" attributes="0"/>
<Component id="exitButton" alignment="3" min="-2" pref="30" max="-2" attributes="0"/> <Component id="exitButton" alignment="3" min="-2" pref="30" max="-2" attributes="0"/>
@ -98,7 +98,7 @@
<Component id="main_gamelog" min="-2" max="-2" attributes="0"/> <Component id="main_gamelog" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="main_battlefield" min="-2" max="-2" attributes="0"/> <Component id="main_battlefield" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/> <EmptySpace pref="22" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -171,18 +171,19 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/> <EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="tooltipDelayLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="383" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="103" alignment="0" groupAlignment="1" max="-2" attributes="0">
<Component id="tooltipDelayLabel" max="32767" attributes="0"/>
<Component id="tooltipDelay" alignment="1" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<Component id="showCardName" min="-2" max="-2" attributes="0"/> <Component id="showCardName" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="showFullImagePath" min="-2" max="-2" attributes="0"/> <Component id="showFullImagePath" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<Component id="tooltipDelay" alignment="0" min="-2" pref="522" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace max="32767" attributes="0"/> <EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -268,22 +269,25 @@
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0"> <Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0"> <Component id="lblTargetAutoChoose" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="showPlayerNamesPermanently" alignment="0" max="32767" attributes="0"/>
<Component id="nonLandPermanentsInOnePile" alignment="0" max="32767" attributes="0"/>
<Component id="cbConfirmEmptyManaPool" alignment="0" max="32767" attributes="0"/>
<Component id="cbAllowRequestToShowHandCards" alignment="0" max="32767" attributes="0"/>
<Component id="cbShowStormCounter" alignment="0" max="32767" attributes="0"/>
<Component id="cbAskMoveToGraveOrder" alignment="0" max="32767" attributes="0"/>
<Component id="showAbilityPickerForced" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Component id="displayLifeOnAvatar" alignment="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="cbTargetAutoChooseLevel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Component id="displayLifeOnAvatar" alignment="0" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="cbAskMoveToGraveOrder" alignment="0" min="-2" pref="596" max="-2" attributes="0"/>
<Group type="103" alignment="0" groupAlignment="0" max="-2" attributes="0">
<Component id="showPlayerNamesPermanently" alignment="0" max="32767" attributes="0"/>
<Component id="nonLandPermanentsInOnePile" alignment="0" max="32767" attributes="0"/>
<Component id="cbConfirmEmptyManaPool" alignment="0" max="32767" attributes="0"/>
<Component id="cbAllowRequestToShowHandCards" alignment="0" max="32767" attributes="0"/>
<Component id="cbShowStormCounter" alignment="0" max="32767" attributes="0"/>
<Component id="showAbilityPickerForced" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="315" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -305,6 +309,11 @@
<Component id="cbConfirmEmptyManaPool" min="-2" max="-2" attributes="0"/> <Component id="cbConfirmEmptyManaPool" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="cbAskMoveToGraveOrder" min="-2" max="-2" attributes="0"/> <Component id="cbAskMoveToGraveOrder" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="lblTargetAutoChoose" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cbTargetAutoChooseLevel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -328,7 +337,6 @@
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" value="Show player names on avatar permanently"/> <Property name="text" type="java.lang.String" value="Show player names on avatar permanently"/>
<Property name="toolTipText" type="java.lang.String" value="Instead showing the names only if you hover over the avatar with the mouse, the name is shown all the time."/> <Property name="toolTipText" type="java.lang.String" value="Instead showing the names only if you hover over the avatar with the mouse, the name is shown all the time."/>
<Property name="horizontalAlignment" type="int" value="2"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showPlayerNamesPermanentlyActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showPlayerNamesPermanentlyActionPerformed"/>
@ -339,7 +347,6 @@
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" value="Display life on avatar image"/> <Property name="text" type="java.lang.String" value="Display life on avatar image"/>
<Property name="toolTipText" type="java.lang.String" value="Display the player&apos;s life over its avatar image."/> <Property name="toolTipText" type="java.lang.String" value="Display the player&apos;s life over its avatar image."/>
<Property name="horizontalAlignment" type="int" value="2"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="displayLifeOnAvatarActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="displayLifeOnAvatarActionPerformed"/>
@ -350,7 +357,6 @@
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" value="Show ability picker for 1 available option (spells without costs, mdf/split side, adventure)"/> <Property name="text" type="java.lang.String" value="Show ability picker for 1 available option (spells without costs, mdf/split side, adventure)"/>
<Property name="toolTipText" type="java.lang.String" value="This prevents you from accidently activating abilities what you don&apos;t want (example: if you haven&apos;t mana to cast main side, but clicks on mdf card and play land instead)"/> <Property name="toolTipText" type="java.lang.String" value="This prevents you from accidently activating abilities what you don&apos;t want (example: if you haven&apos;t mana to cast main side, but clicks on mdf card and play land instead)"/>
<Property name="horizontalAlignment" type="int" value="2"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showAbilityPickerForcedActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="showAbilityPickerForcedActionPerformed"/>
@ -361,7 +367,6 @@
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" value="Allow requests from players and spectators to show your hand cards"/> <Property name="text" type="java.lang.String" value="Allow requests from players and spectators to show your hand cards"/>
<Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;This is the default setting used for your matches. If activated other players or spectators&lt;br&gt;&#xa;of your match can send a request so you can allow them to see your hand cards."/> <Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;This is the default setting used for your matches. If activated other players or spectators&lt;br&gt;&#xa;of your match can send a request so you can allow them to see your hand cards."/>
<Property name="horizontalAlignment" type="int" value="2"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbAllowRequestToShowHandCardsActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbAllowRequestToShowHandCardsActionPerformed"/>
@ -372,7 +377,6 @@
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" value="Show the number of spell casts during the current turn"/> <Property name="text" type="java.lang.String" value="Show the number of spell casts during the current turn"/>
<Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;Adds a little box left to the short keys line with the number&lt;br&gt;&#xa;of spells already cast during the current turn (storm counter)."/> <Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;Adds a little box left to the short keys line with the number&lt;br&gt;&#xa;of spells already cast during the current turn (storm counter)."/>
<Property name="horizontalAlignment" type="int" value="2"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbShowStormCounterActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbShowStormCounterActionPerformed"/>
@ -383,7 +387,6 @@
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" value="Confirm if you want to pass a phase/step but there is still mana in your mana pool"/> <Property name="text" type="java.lang.String" value="Confirm if you want to pass a phase/step but there is still mana in your mana pool"/>
<Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;If activated you get a confirm message if you pass priority while stack is empty&lt;br&gt;&#xa; and you still have mana in your mana pool."/> <Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;If activated you get a confirm message if you pass priority while stack is empty&lt;br&gt;&#xa; and you still have mana in your mana pool."/>
<Property name="horizontalAlignment" type="int" value="2"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbConfirmEmptyManaPoolActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbConfirmEmptyManaPoolActionPerformed"/>
@ -394,12 +397,41 @@
<Property name="selected" type="boolean" value="true"/> <Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" value="Ask player for setting order cards go to graveyard"/> <Property name="text" type="java.lang.String" value="Ask player for setting order cards go to graveyard"/>
<Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;If activated and multiple cards go to the graveyard at the same time&lt;br&gt;&#xa;the player is asked to set the order of the cards."/> <Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;If activated and multiple cards go to the graveyard at the same time&lt;br&gt;&#xa;the player is asked to set the order of the cards."/>
<Property name="horizontalAlignment" type="int" value="2"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbAskMoveToGraveOrderActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbAskMoveToGraveOrderActionPerformed"/>
</Events> </Events>
</Component> </Component>
<Component class="javax.swing.JLabel" name="lblTargetAutoChoose">
<Properties>
<Property name="text" type="java.lang.String" value="Auto-choose targets for player:"/>
<Property name="toolTipText" type="java.lang.String" value="&lt;html&gt;&#xa;When there is only one possible outcome for targeting, the targets can be chosen for you.&#xa;&lt;br&gt;&#xa;&lt;b&gt;None:&lt;/b&gt; All targeting must be done by the player.&#xa;&lt;br&gt;&#xa;&lt;b&gt;Most:&lt;/b&gt; All targeting other than feel-bad effects (discarding, destroy, sacrifice, exile) that target you, a card you own, or a permanent/spell you control.&#xa;&lt;br&gt;&#xa;&lt;b&gt;All:&lt;/b&gt; All targeting that can be automated will be."/>
</Properties>
</Component>
<Component class="javax.swing.JComboBox" name="cbTargetAutoChooseLevel">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="3">
<StringItem index="0" value="Off"/>
<StringItem index="1" value="Most"/>
<StringItem index="2" value="All"/>
</StringArray>
</Property>
<Property name="selectedIndex" type="int" value="1"/>
<Property name="toolTipText" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection component="lblTargetAutoChoose" name="toolTipText" type="property"/>
</Property>
</Properties>
<AccessibilityProperties>
<Property name="AccessibleContext.accessibleName" type="java.lang.String" value="Auto-choose targets for player combo box"/>
</AccessibilityProperties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbTargetAutoChooseLevelActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
</SubComponents> </SubComponents>
</Container> </Container>
<Container class="javax.swing.JPanel" name="main_battlefield"> <Container class="javax.swing.JPanel" name="main_battlefield">
@ -4082,7 +4114,7 @@
<Component id="checkBoxEndTurnOthers" min="-2" max="-2" attributes="0"/> <Component id="checkBoxEndTurnOthers" min="-2" max="-2" attributes="0"/>
</Group> </Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/> <EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="phases_stopSettings" pref="291" max="32767" attributes="0"/> <Component id="phases_stopSettings" pref="354" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
</Group> </Group>
</Group> </Group>
@ -4341,7 +4373,7 @@
<Component id="panelCardImages" min="-2" max="-2" attributes="0"/> <Component id="panelCardImages" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/> <EmptySpace max="-2" attributes="0"/>
<Component id="panelBackgroundImages" min="-2" max="-2" attributes="0"/> <Component id="panelBackgroundImages" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="98" max="32767" attributes="0"/> <EmptySpace pref="142" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</DimensionLayout> </DimensionLayout>
@ -6297,12 +6329,12 @@
</DimensionLayout> </DimensionLayout>
<DimensionLayout dim="1"> <DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0"> <Group type="103" groupAlignment="0" attributes="0">
<EmptySpace min="0" pref="526" max="32767" attributes="0"/> <EmptySpace min="0" pref="623" max="32767" attributes="0"/>
<Group type="103" rootIndex="1" groupAlignment="0" attributes="0"> <Group type="103" rootIndex="1" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0"> <Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="21" max="-2" attributes="0"/> <EmptySpace min="-2" pref="21" max="-2" attributes="0"/>
<Component id="themesCategory" min="-2" max="-2" attributes="0"/> <Component id="themesCategory" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="430" max="32767" attributes="0"/> <EmptySpace pref="523" max="32767" attributes="0"/>
</Group> </Group>
</Group> </Group>
</Group> </Group>
@ -6395,7 +6427,6 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 30]"/> <Dimension value="[100, 30]"/>
</Property> </Property>
<Property name="verticalAlignment" type="int" value="3"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="saveButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="saveButtonActionPerformed"/>
@ -6413,7 +6444,6 @@
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor"> <Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[100, 30]"/> <Dimension value="[100, 30]"/>
</Property> </Property>
<Property name="verticalAlignment" type="int" value="3"/>
</Properties> </Properties>
<Events> <Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exitButtonActionPerformed"/> <EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="exitButtonActionPerformed"/>

View file

@ -31,6 +31,7 @@ import java.util.*;
import java.util.prefs.BackingStoreException; import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences; import java.util.prefs.Preferences;
import static mage.client.constants.Constants.AUTO_TARGET_NON_FEEL_BAD;
import static mage.client.constants.Constants.BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_MULTICOLOR; import static mage.client.constants.Constants.BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_MULTICOLOR;
import static mage.constants.Constants.*; import static mage.constants.Constants.*;
@ -236,6 +237,9 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_NEW_TOURNAMENT_MINIMUM_RATING = "newTournamentMinimumRating"; public static final String KEY_NEW_TOURNAMENT_MINIMUM_RATING = "newTournamentMinimumRating";
public static final String KEY_NEW_TOURNAMENT_RATED = "newTournamentRated"; public static final String KEY_NEW_TOURNAMENT_RATED = "newTournamentRated";
// Settings for auto-choosing targets
public static final String KEY_AUTO_TARGET_LEVEL = "autoTargetLevel";
// pref setting for deck generator // pref setting for deck generator
public static final String KEY_NEW_DECK_GENERATOR_DECK_SIZE = "newDeckGeneratorDeckSize"; public static final String KEY_NEW_DECK_GENERATOR_DECK_SIZE = "newDeckGeneratorDeckSize";
public static final String KEY_NEW_DECK_GENERATOR_SET = "newDeckGeneratorSet"; public static final String KEY_NEW_DECK_GENERATOR_SET = "newDeckGeneratorSet";
@ -435,6 +439,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbShowStormCounter = new javax.swing.JCheckBox(); cbShowStormCounter = new javax.swing.JCheckBox();
cbConfirmEmptyManaPool = new javax.swing.JCheckBox(); cbConfirmEmptyManaPool = new javax.swing.JCheckBox();
cbAskMoveToGraveOrder = new javax.swing.JCheckBox(); cbAskMoveToGraveOrder = new javax.swing.JCheckBox();
lblTargetAutoChoose = new javax.swing.JLabel();
cbTargetAutoChooseLevel = new javax.swing.JComboBox<>();
main_battlefield = new javax.swing.JPanel(); main_battlefield = new javax.swing.JPanel();
cbBattlefieldFeedbackColorizingMode = new javax.swing.JComboBox(); cbBattlefieldFeedbackColorizingMode = new javax.swing.JComboBox();
lblBattlefieldFeedbackColorizingMode = new javax.swing.JLabel(); lblBattlefieldFeedbackColorizingMode = new javax.swing.JLabel();
@ -689,15 +695,16 @@ public class PreferencesDialog extends javax.swing.JDialog {
main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(main_cardLayout.createSequentialGroup() .add(main_cardLayout.createSequentialGroup()
.add(6, 6, 6) .add(6, 6, 6)
.add(tooltipDelayLabel)
.addContainerGap(383, Short.MAX_VALUE))
.add(main_cardLayout.createSequentialGroup()
.add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.TRAILING, false)
.add(tooltipDelayLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(tooltipDelay, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.add(main_cardLayout.createSequentialGroup() .add(main_cardLayout.createSequentialGroup()
.add(showCardName) .add(showCardName)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED) .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(showFullImagePath))) .add(showFullImagePath))
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(tooltipDelay, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 522, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
.add(0, 0, Short.MAX_VALUE))
); );
main_cardLayout.setVerticalGroup( main_cardLayout.setVerticalGroup(
main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) main_cardLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
@ -725,7 +732,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
showPlayerNamesPermanently.setSelected(true); showPlayerNamesPermanently.setSelected(true);
showPlayerNamesPermanently.setText("Show player names on avatar permanently"); showPlayerNamesPermanently.setText("Show player names on avatar permanently");
showPlayerNamesPermanently.setToolTipText("Instead showing the names only if you hover over the avatar with the mouse, the name is shown all the time."); showPlayerNamesPermanently.setToolTipText("Instead showing the names only if you hover over the avatar with the mouse, the name is shown all the time.");
showPlayerNamesPermanently.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
showPlayerNamesPermanently.addActionListener(new java.awt.event.ActionListener() { showPlayerNamesPermanently.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
showPlayerNamesPermanentlyActionPerformed(evt); showPlayerNamesPermanentlyActionPerformed(evt);
@ -735,7 +741,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
displayLifeOnAvatar.setSelected(true); displayLifeOnAvatar.setSelected(true);
displayLifeOnAvatar.setText("Display life on avatar image"); displayLifeOnAvatar.setText("Display life on avatar image");
displayLifeOnAvatar.setToolTipText("Display the player's life over its avatar image."); displayLifeOnAvatar.setToolTipText("Display the player's life over its avatar image.");
displayLifeOnAvatar.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
displayLifeOnAvatar.addActionListener(new java.awt.event.ActionListener() { displayLifeOnAvatar.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
displayLifeOnAvatarActionPerformed(evt); displayLifeOnAvatarActionPerformed(evt);
@ -745,7 +750,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
showAbilityPickerForced.setSelected(true); showAbilityPickerForced.setSelected(true);
showAbilityPickerForced.setText("Show ability picker for 1 available option (spells without costs, mdf/split side, adventure)"); showAbilityPickerForced.setText("Show ability picker for 1 available option (spells without costs, mdf/split side, adventure)");
showAbilityPickerForced.setToolTipText("This prevents you from accidently activating abilities what you don't want (example: if you haven't mana to cast main side, but clicks on mdf card and play land instead)"); showAbilityPickerForced.setToolTipText("This prevents you from accidently activating abilities what you don't want (example: if you haven't mana to cast main side, but clicks on mdf card and play land instead)");
showAbilityPickerForced.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
showAbilityPickerForced.addActionListener(new java.awt.event.ActionListener() { showAbilityPickerForced.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
showAbilityPickerForcedActionPerformed(evt); showAbilityPickerForcedActionPerformed(evt);
@ -755,7 +759,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbAllowRequestToShowHandCards.setSelected(true); cbAllowRequestToShowHandCards.setSelected(true);
cbAllowRequestToShowHandCards.setText("Allow requests from players and spectators to show your hand cards"); cbAllowRequestToShowHandCards.setText("Allow requests from players and spectators to show your hand cards");
cbAllowRequestToShowHandCards.setToolTipText("<html>This is the default setting used for your matches. If activated other players or spectators<br>\nof your match can send a request so you can allow them to see your hand cards."); cbAllowRequestToShowHandCards.setToolTipText("<html>This is the default setting used for your matches. If activated other players or spectators<br>\nof your match can send a request so you can allow them to see your hand cards.");
cbAllowRequestToShowHandCards.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
cbAllowRequestToShowHandCards.addActionListener(new java.awt.event.ActionListener() { cbAllowRequestToShowHandCards.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
cbAllowRequestToShowHandCardsActionPerformed(evt); cbAllowRequestToShowHandCardsActionPerformed(evt);
@ -765,7 +768,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbShowStormCounter.setSelected(true); cbShowStormCounter.setSelected(true);
cbShowStormCounter.setText("Show the number of spell casts during the current turn"); cbShowStormCounter.setText("Show the number of spell casts during the current turn");
cbShowStormCounter.setToolTipText("<html>Adds a little box left to the short keys line with the number<br>\nof spells already cast during the current turn (storm counter)."); cbShowStormCounter.setToolTipText("<html>Adds a little box left to the short keys line with the number<br>\nof spells already cast during the current turn (storm counter).");
cbShowStormCounter.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
cbShowStormCounter.addActionListener(new java.awt.event.ActionListener() { cbShowStormCounter.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
cbShowStormCounterActionPerformed(evt); cbShowStormCounterActionPerformed(evt);
@ -775,7 +777,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbConfirmEmptyManaPool.setSelected(true); cbConfirmEmptyManaPool.setSelected(true);
cbConfirmEmptyManaPool.setText("Confirm if you want to pass a phase/step but there is still mana in your mana pool"); cbConfirmEmptyManaPool.setText("Confirm if you want to pass a phase/step but there is still mana in your mana pool");
cbConfirmEmptyManaPool.setToolTipText("<html>If activated you get a confirm message if you pass priority while stack is empty<br>\n and you still have mana in your mana pool."); cbConfirmEmptyManaPool.setToolTipText("<html>If activated you get a confirm message if you pass priority while stack is empty<br>\n and you still have mana in your mana pool.");
cbConfirmEmptyManaPool.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
cbConfirmEmptyManaPool.addActionListener(new java.awt.event.ActionListener() { cbConfirmEmptyManaPool.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
cbConfirmEmptyManaPoolActionPerformed(evt); cbConfirmEmptyManaPoolActionPerformed(evt);
@ -785,32 +786,46 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbAskMoveToGraveOrder.setSelected(true); cbAskMoveToGraveOrder.setSelected(true);
cbAskMoveToGraveOrder.setText("Ask player for setting order cards go to graveyard"); cbAskMoveToGraveOrder.setText("Ask player for setting order cards go to graveyard");
cbAskMoveToGraveOrder.setToolTipText("<html>If activated and multiple cards go to the graveyard at the same time<br>\nthe player is asked to set the order of the cards."); cbAskMoveToGraveOrder.setToolTipText("<html>If activated and multiple cards go to the graveyard at the same time<br>\nthe player is asked to set the order of the cards.");
cbAskMoveToGraveOrder.setHorizontalAlignment(javax.swing.SwingConstants.LEFT);
cbAskMoveToGraveOrder.addActionListener(new java.awt.event.ActionListener() { cbAskMoveToGraveOrder.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
cbAskMoveToGraveOrderActionPerformed(evt); cbAskMoveToGraveOrderActionPerformed(evt);
} }
}); });
lblTargetAutoChoose.setText("Auto-choose targets for player:");
lblTargetAutoChoose.setToolTipText("<html>\nWhen there is only one possible outcome for targeting, the targets can be chosen for you.\n<br>\n<b>None:</b> All targeting must be done by the player.\n<br>\n<b>Most:</b> All targeting other than feel-bad effects (discarding, destroy, sacrifice, exile) that target you, a card you own, or a permanent/spell you control.\n<br>\n<b>All:</b> All targeting that can be automated will be.");
cbTargetAutoChooseLevel.setModel(new javax.swing.DefaultComboBoxModel<>(new String[] { "Off", "Most", "All" }));
cbTargetAutoChooseLevel.setSelectedIndex(1);
cbTargetAutoChooseLevel.setToolTipText(lblTargetAutoChoose.getToolTipText());
cbTargetAutoChooseLevel.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cbTargetAutoChooseLevelActionPerformed(evt);
}
});
org.jdesktop.layout.GroupLayout main_gameLayout = new org.jdesktop.layout.GroupLayout(main_game); org.jdesktop.layout.GroupLayout main_gameLayout = new org.jdesktop.layout.GroupLayout(main_game);
main_game.setLayout(main_gameLayout); main_game.setLayout(main_gameLayout);
main_gameLayout.setHorizontalGroup( main_gameLayout.setHorizontalGroup(
main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(main_gameLayout.createSequentialGroup() .add(main_gameLayout.createSequentialGroup()
.addContainerGap() .addContainerGap()
.add(lblTargetAutoChoose)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(cbTargetAutoChooseLevel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.add(displayLifeOnAvatar, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(main_gameLayout.createSequentialGroup()
.add(main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(main_gameLayout.createSequentialGroup() .add(cbAskMoveToGraveOrder, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 596, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false) .add(main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING, false)
.add(showPlayerNamesPermanently, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(showPlayerNamesPermanently, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(nonLandPermanentsInOnePile, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(nonLandPermanentsInOnePile, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(cbConfirmEmptyManaPool, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(cbConfirmEmptyManaPool, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(cbAllowRequestToShowHandCards, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(cbAllowRequestToShowHandCards, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(cbShowStormCounter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(cbShowStormCounter, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.add(cbAskMoveToGraveOrder, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(showAbilityPickerForced)))
.add(showAbilityPickerForced, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .add(0, 315, Short.MAX_VALUE))
.add(0, 0, Short.MAX_VALUE))
.add(displayLifeOnAvatar, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addContainerGap())
); );
main_gameLayout.setVerticalGroup( main_gameLayout.setVerticalGroup(
main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
@ -829,10 +844,15 @@ public class PreferencesDialog extends javax.swing.JDialog {
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(cbConfirmEmptyManaPool) .add(cbConfirmEmptyManaPool)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(cbAskMoveToGraveOrder)) .add(cbAskMoveToGraveOrder)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(main_gameLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(lblTargetAutoChoose)
.add(cbTargetAutoChooseLevel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)))
); );
nonLandPermanentsInOnePile.getAccessibleContext().setAccessibleName("nonLandPermanentsInOnePile"); nonLandPermanentsInOnePile.getAccessibleContext().setAccessibleName("nonLandPermanentsInOnePile");
cbTargetAutoChooseLevel.getAccessibleContext().setAccessibleName("Auto-choose targets for player combo box");
main_battlefield.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Battlefield")); main_battlefield.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "Battlefield"));
@ -889,7 +909,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(main_gamelog, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(main_gamelog, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(main_battlefield, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(main_battlefield, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addContainerGap(org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap(22, Short.MAX_VALUE))
); );
main_card.getAccessibleContext().setAccessibleName("Game panel"); main_card.getAccessibleContext().setAccessibleName("Game panel");
@ -1627,7 +1647,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(jLabelEndOfTurn) .add(jLabelEndOfTurn)
.add(checkBoxEndTurnOthers)) .add(checkBoxEndTurnOthers))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED) .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(phases_stopSettings, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 291, Short.MAX_VALUE) .add(phases_stopSettings, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 354, Short.MAX_VALUE)
.addContainerGap()) .addContainerGap())
); );
@ -1852,7 +1872,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(panelCardImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(panelCardImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(panelBackgroundImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(panelBackgroundImages, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addContainerGap(98, Short.MAX_VALUE)) .addContainerGap(142, Short.MAX_VALUE))
); );
tabsPanel.addTab("Images", tabImages); tabsPanel.addTab("Images", tabImages);
@ -2819,12 +2839,12 @@ public class PreferencesDialog extends javax.swing.JDialog {
); );
tabThemesLayout.setVerticalGroup( tabThemesLayout.setVerticalGroup(
tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(0, 526, Short.MAX_VALUE) .add(0, 623, Short.MAX_VALUE)
.add(tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(tabThemesLayout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(tabThemesLayout.createSequentialGroup() .add(tabThemesLayout.createSequentialGroup()
.add(21, 21, 21) .add(21, 21, 21)
.add(themesCategory, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(themesCategory, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.addContainerGap(430, Short.MAX_VALUE))) .addContainerGap(523, Short.MAX_VALUE)))
); );
tabsPanel.addTab("Themes", tabThemes); tabsPanel.addTab("Themes", tabThemes);
@ -2833,7 +2853,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
saveButton.setMaximumSize(new java.awt.Dimension(100, 30)); saveButton.setMaximumSize(new java.awt.Dimension(100, 30));
saveButton.setMinimumSize(new java.awt.Dimension(100, 30)); saveButton.setMinimumSize(new java.awt.Dimension(100, 30));
saveButton.setPreferredSize(new java.awt.Dimension(100, 30)); saveButton.setPreferredSize(new java.awt.Dimension(100, 30));
saveButton.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
saveButton.addActionListener(new java.awt.event.ActionListener() { saveButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
saveButtonActionPerformed(evt); saveButtonActionPerformed(evt);
@ -2844,7 +2863,6 @@ public class PreferencesDialog extends javax.swing.JDialog {
exitButton.setMaximumSize(new java.awt.Dimension(100, 30)); exitButton.setMaximumSize(new java.awt.Dimension(100, 30));
exitButton.setMinimumSize(new java.awt.Dimension(100, 30)); exitButton.setMinimumSize(new java.awt.Dimension(100, 30));
exitButton.setPreferredSize(new java.awt.Dimension(100, 30)); exitButton.setPreferredSize(new java.awt.Dimension(100, 30));
exitButton.setVerticalAlignment(javax.swing.SwingConstants.BOTTOM);
exitButton.addActionListener(new java.awt.event.ActionListener() { exitButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) { public void actionPerformed(java.awt.event.ActionEvent evt) {
exitButtonActionPerformed(evt); exitButtonActionPerformed(evt);
@ -2869,8 +2887,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
layout.setVerticalGroup( layout.setVerticalGroup(
layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING)
.add(layout.createSequentialGroup() .add(layout.createSequentialGroup()
.add(tabsPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 554, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(tabsPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 554, Short.MAX_VALUE)
.addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED)
.add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) .add(layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE)
.add(saveButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 30, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .add(saveButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 30, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)
.add(exitButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 30, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE)) .add(exitButton, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 30, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE))
@ -2910,6 +2928,16 @@ public class PreferencesDialog extends javax.swing.JDialog {
} }
} }
String paramNameAutoTarget = KEY_AUTO_TARGET_LEVEL;
int paramValueAutoTarger = dialog.cbTargetAutoChooseLevel.getSelectedIndex();
int paramDefaultAutoTarget = AUTO_TARGET_NON_FEEL_BAD;
if (getCachedValue(paramNameAutoTarget, paramDefault) != paramValueAutoTarger) {
prefs.putInt(paramNameAutoTarget, paramValueAutoTarger);
if (UPDATE_CACHE_POLICY) {
updateCache(paramNameAutoTarget, Integer.toString(paramValueAutoTarger));
}
}
saveGUISize(); saveGUISize();
// Phases & Priority // Phases & Priority
@ -3327,6 +3355,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
// TODO add your handling code here: // TODO add your handling code here:
}//GEN-LAST:event_cbUseSameSettingsForReplacementEffectActionPerformed }//GEN-LAST:event_cbUseSameSettingsForReplacementEffectActionPerformed
private void cbTargetAutoChooseLevelActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbTargetAutoChooseLevelActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cbTargetAutoChooseLevelActionPerformed
private void showProxySettings() { private void showProxySettings() {
Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem(); Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem();
switch (proxyType) { switch (proxyType) {
@ -3458,6 +3490,17 @@ public class PreferencesDialog extends javax.swing.JDialog {
dialog.cbBattlefieldFeedbackColorizingMode.setSelectedIndex(BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_MULTICOLOR); dialog.cbBattlefieldFeedbackColorizingMode.setSelectedIndex(BATTLEFIELD_FEEDBACK_COLORIZING_MODE_ENABLE_BY_MULTICOLOR);
} }
String autoTargetParam;
try {
autoTargetParam = MageFrame.getPreferences().get(KEY_AUTO_TARGET_LEVEL, "1");
int autoTargetMode = Integer.parseInt(autoTargetParam);
dialog.cbTargetAutoChooseLevel.setSelectedIndex(autoTargetMode);
} catch (Throwable e) {
autoTargetParam = "";
dialog.cbTargetAutoChooseLevel.setSelectedIndex(AUTO_TARGET_NON_FEEL_BAD);
logger.error("Can't Parse and setup param " + KEY_AUTO_TARGET_LEVEL + " = " + autoTargetParam, e);
}
load(prefs, dialog.checkBoxUpkeepYou, UPKEEP_YOU, "on", "on"); load(prefs, dialog.checkBoxUpkeepYou, UPKEEP_YOU, "on", "on");
load(prefs, dialog.checkBoxDrawYou, DRAW_YOU, "on", "on"); load(prefs, dialog.checkBoxDrawYou, DRAW_YOU, "on", "on");
load(prefs, dialog.checkBoxMainYou, MAIN_YOU, "on", "on"); load(prefs, dialog.checkBoxMainYou, MAIN_YOU, "on", "on");
@ -4014,6 +4057,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PASS_PRIORITY_CAST, "true").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PASS_PRIORITY_CAST, "true").equals("true"),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PASS_PRIORITY_ACTIVATION, "true").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PASS_PRIORITY_ACTIVATION, "true").equals("true"),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_AUTO_ORDER_TRIGGER, "true").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_AUTO_ORDER_TRIGGER, "true").equals("true"),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_AUTO_TARGET_LEVEL, 1),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_USE_SAME_SETTINGS_FOR_SAME_REPLACEMENT_EFFECTS, "true").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_USE_SAME_SETTINGS_FOR_SAME_REPLACEMENT_EFFECTS, "true").equals("true"),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_USE_FIRST_MANA_ABILITY, "false").equals("true"), PreferencesDialog.getCachedValue(PreferencesDialog.KEY_USE_FIRST_MANA_ABILITY, "false").equals("true"),
userStrId userStrId
@ -4083,6 +4127,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
private javax.swing.JCheckBox cbStopOnAllEnd; private javax.swing.JCheckBox cbStopOnAllEnd;
private javax.swing.JCheckBox cbStopOnAllMain; private javax.swing.JCheckBox cbStopOnAllMain;
private javax.swing.JCheckBox cbStopOnNewStackObjects; private javax.swing.JCheckBox cbStopOnNewStackObjects;
private javax.swing.JComboBox<String> cbTargetAutoChooseLevel;
private javax.swing.JComboBox<ThemeType> cbTheme; private javax.swing.JComboBox<ThemeType> cbTheme;
private javax.swing.JCheckBox cbUseDefaultBackground; private javax.swing.JCheckBox cbUseDefaultBackground;
private javax.swing.JCheckBox cbUseDefaultBattleImage; private javax.swing.JCheckBox cbUseDefaultBattleImage;
@ -4192,6 +4237,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
private javax.swing.JLabel lblProxyServer; private javax.swing.JLabel lblProxyServer;
private javax.swing.JLabel lblProxyType; private javax.swing.JLabel lblProxyType;
private javax.swing.JLabel lblProxyUserName; private javax.swing.JLabel lblProxyUserName;
private javax.swing.JLabel lblTargetAutoChoose;
private javax.swing.JLabel lblURLServerList; private javax.swing.JLabel lblURLServerList;
private javax.swing.JLabel lebelSkip; private javax.swing.JLabel lebelSkip;
private javax.swing.JPanel main_battlefield; private javax.swing.JPanel main_battlefield;

View file

@ -69,12 +69,12 @@
cardArea.clearCardEventListeners(); cardArea.clearCardEventListeners();
cardArea.loadCards(showCards, bigCard, gameId); cardArea.loadCards(showCards, bigCard, gameId);
if (options != null) { if (options != null) {
if (options.containsKey("chosen")) { if (options.containsKey("chosenTargets")) {
java.util.List<UUID> chosenCards = (java.util.List<UUID>) options.get("chosen"); java.util.List<UUID> chosenCards = (java.util.List<UUID>) options.get("chosenTargets");
cardArea.selectCards(chosenCards); cardArea.selectCards(chosenCards);
} }
if (options.containsKey("choosable")) { if (options.containsKey("possibleTargets")) {
java.util.List<UUID> choosableCards = (java.util.List<UUID>) options.get("choosable"); java.util.List<UUID> choosableCards = (java.util.List<UUID>) options.get("possibleTargets");
cardArea.markCards(choosableCards); cardArea.markCards(choosableCards);
} }
if (options.containsKey("queryType") && options.get("queryType") == QueryType.PICK_ABILITY) { if (options.containsKey("queryType") && options.get("queryType") == QueryType.PICK_ABILITY) {

View file

@ -1382,8 +1382,8 @@ public final class GamePanel extends javax.swing.JPanel {
} }
List<UUID> needChosen; List<UUID> needChosen;
if (lastGameData.options != null && lastGameData.options.containsKey("chosen")) { if (lastGameData.options != null && lastGameData.options.containsKey("chosenTargets")) {
needChosen = (List<UUID>) lastGameData.options.get("chosen"); needChosen = (List<UUID>) lastGameData.options.get("chosenTargets");
} else { } else {
needChosen = new ArrayList<>(); needChosen = new ArrayList<>();
} }

View file

@ -519,8 +519,8 @@ public class HumanPlayer extends PlayerImpl {
} }
while (canRespond()) { while (canRespond()) {
Set<UUID> targetIds = target.possibleTargets(abilityControllerId, source, game); Set<UUID> possibleTargetIds = target.possibleTargets(abilityControllerId, source, game);
if (targetIds == null || targetIds.isEmpty()) { if (possibleTargetIds == null || possibleTargetIds.isEmpty()) {
return target.getTargets().size() >= target.getNumberOfTargets(); return target.getTargets().size() >= target.getNumberOfTargets();
} }
@ -529,17 +529,22 @@ public class HumanPlayer extends PlayerImpl {
required = false; required = false;
} }
java.util.List<UUID> chosen = target.getTargets(); UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game);
options.put("chosen", (Serializable) chosen);
updateGameStatePriority("choose(5)", game); // responseId is null if a choice couldn't be automatically made
prepareForResponse(game); if (responseId == null) {
if (!isExecutingMacro()) { List<UUID> chosenTargets = target.getTargets();
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), targetIds, required, getOptions(target, options)); options.put("chosenTargets", (Serializable) chosenTargets);
updateGameStatePriority("choose(5)", game);
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), possibleTargetIds, required, getOptions(target, options));
}
waitForResponse(game);
responseId = getFixedResponseUUID(game);
} }
waitForResponse(game);
UUID responseId = getFixedResponseUUID(game);
if (responseId != null) { if (responseId != null) {
// selected some target // selected some target
@ -549,7 +554,7 @@ public class HumanPlayer extends PlayerImpl {
continue; continue;
} }
if (!targetIds.contains(responseId)) { if (!possibleTargetIds.contains(responseId)) {
continue; continue;
} }
@ -617,25 +622,32 @@ public class HumanPlayer extends PlayerImpl {
Map<String, Serializable> options = new HashMap<>(); Map<String, Serializable> options = new HashMap<>();
while (canRespond()) { while (canRespond()) {
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game); Set<UUID> possibleTargetIds = target.possibleTargets(abilityControllerId, source, game);
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game); boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
if (possibleTargets.isEmpty() if (possibleTargetIds.isEmpty()
|| target.getTargets().size() >= target.getNumberOfTargets()) { || target.getTargets().size() >= target.getNumberOfTargets()) {
required = false; required = false;
} }
java.util.List<UUID> chosen = target.getTargets(); UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game);
options.put("chosen", (Serializable) chosen);
updateGameStatePriority("chooseTarget", game); // responseId is null if a choice couldn't be automatically made
prepareForResponse(game); if (responseId == null) {
if (!isExecutingMacro()) {
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)),
possibleTargets, required, getOptions(target, options)); List<UUID> chosenTargets = target.getTargets();
options.put("chosenTargets", (Serializable) chosenTargets);
updateGameStatePriority("chooseTarget", game);
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireSelectTargetEvent(getId(), new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)),
possibleTargetIds, required, getOptions(target, options));
}
waitForResponse(game);
responseId = getFixedResponseUUID(game);
} }
waitForResponse(game);
UUID responseId = getFixedResponseUUID(game);
if (responseId != null) { if (responseId != null) {
// remove selected // remove selected
if (target.getTargets().contains(responseId)) { if (target.getTargets().contains(responseId)) {
@ -643,7 +655,7 @@ public class HumanPlayer extends PlayerImpl {
continue; continue;
} }
if (possibleTargets.contains(responseId)) { if (possibleTargetIds.contains(responseId)) {
if (target.canTarget(abilityControllerId, responseId, source, game)) { if (target.canTarget(abilityControllerId, responseId, source, game)) {
target.addTarget(responseId, source, game); target.addTarget(responseId, source, game);
if (target.doneChoosing()) { if (target.doneChoosing()) {
@ -687,10 +699,12 @@ public class HumanPlayer extends PlayerImpl {
return false; return false;
} }
UUID abilityControllerId = playerId; UUID abilityControllerId;
if (target.getTargetController() != null if (target.getTargetController() != null
&& target.getAbilityController() != null) { && target.getAbilityController() != null) {
abilityControllerId = target.getAbilityController(); abilityControllerId = target.getAbilityController();
} else {
abilityControllerId = playerId;
} }
while (canRespond()) { while (canRespond()) {
@ -701,32 +715,37 @@ public class HumanPlayer extends PlayerImpl {
required = false; required = false;
} }
Map<String, Serializable> options = getOptions(target, null); List<UUID> chosenTargets = target.getTargets();
java.util.List<UUID> chosen = target.getTargets(); List<UUID> possibleTargets = new ArrayList<>();
options.put("chosen", (Serializable) chosen);
java.util.List<UUID> choosable = new ArrayList<>();
for (UUID cardId : cards) { for (UUID cardId : cards) {
if (target.canTarget(abilityControllerId, cardId, null, cards, game)) { if (target.canTarget(abilityControllerId, cardId, null, cards, game)) {
choosable.add(cardId); possibleTargets.add(cardId);
} }
} }
if (!choosable.isEmpty()) {
options.put("choosable", (Serializable) choosable);
}
// if nothing to choose then show dialog (user must see non selectable items and click on any of them) // if nothing to choose then show dialog (user must see non selectable items and click on any of them)
if (required && choosable.isEmpty()) { if (required && possibleTargets.isEmpty()) {
required = false; required = false;
} }
updateGameStatePriority("choose(4)", game); UUID responseId = target.tryToAutoChoose(abilityControllerId, null, game, possibleTargets);
prepareForResponse(game);
if (!isExecutingMacro()) { if (responseId == null) {
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage()), cards, required, options); Map<String, Serializable> options = getOptions(target, null);
} options.put("chosenTargets", (Serializable) chosenTargets);
waitForResponse(game); if (!possibleTargets.isEmpty()) {
options.put("possibleTargets", (Serializable) possibleTargets);
}
updateGameStatePriority("choose(4)", game);
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage()), cards, required, options);
}
waitForResponse(game);
responseId = getFixedResponseUUID(game);
}
UUID responseId = getFixedResponseUUID(game);
if (responseId != null) { if (responseId != null) {
if (target.getTargets().contains(responseId)) { // if already included remove it with if (target.getTargets().contains(responseId)) { // if already included remove it with
target.remove(responseId); target.remove(responseId);
@ -762,10 +781,12 @@ public class HumanPlayer extends PlayerImpl {
return false; return false;
} }
UUID abilityControllerId = playerId; UUID abilityControllerId;
if (target.getTargetController() != null if (target.getTargetController() != null
&& target.getAbilityController() != null) { && target.getAbilityController() != null) {
abilityControllerId = target.getAbilityController(); abilityControllerId = target.getAbilityController();
} else {
abilityControllerId = playerId;
} }
while (canRespond()) { while (canRespond()) {
@ -776,32 +797,38 @@ public class HumanPlayer extends PlayerImpl {
required = false; required = false;
} }
Map<String, Serializable> options = getOptions(target, null); List<UUID> possibleTargets = new ArrayList<>();
java.util.List<UUID> chosen = target.getTargets();
options.put("chosen", (Serializable) chosen);
java.util.List<UUID> choosable = new ArrayList<>();
for (UUID cardId : cards) { for (UUID cardId : cards) {
if (target.canTarget(abilityControllerId, cardId, source, cards, game)) { if (target.canTarget(abilityControllerId, cardId, source, cards, game)) {
choosable.add(cardId); possibleTargets.add(cardId);
} }
} }
if (!choosable.isEmpty()) {
options.put("choosable", (Serializable) choosable);
}
// if nothing to choose then show dialog (user must see non selectable items and click on any of them) // if nothing to choose then show dialog (user must see non selectable items and click on any of them)
if (required && choosable.isEmpty()) { if (required && possibleTargets.isEmpty()) {
required = false; required = false;
} }
updateGameStatePriority("chooseTarget(5)", game); UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game, possibleTargets);
prepareForResponse(game);
if (!isExecutingMacro()) { if (responseId == null) {
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), cards, required, options); List<UUID> chosenTargets = target.getTargets();
} Map<String, Serializable> options = getOptions(target, null);
waitForResponse(game); options.put("chosenTargets", (Serializable) chosenTargets);
if (!possibleTargets.isEmpty()) {
options.put("possibleTargets", (Serializable) possibleTargets);
}
updateGameStatePriority("chooseTarget(5)", game);
prepareForResponse(game);
if (!isExecutingMacro()) {
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), cards, required, options);
}
waitForResponse(game);
responseId = getFixedResponseUUID(game);
}
UUID responseId = getFixedResponseUUID(game);
if (responseId != null) { if (responseId != null) {
if (target.getTargets().contains(responseId)) { // if already included remove it if (target.getTargets().contains(responseId)) { // if already included remove it
target.remove(responseId); target.remove(responseId);
@ -849,47 +876,53 @@ public class HumanPlayer extends PlayerImpl {
// 1. Select targets // 1. Select targets
while (canRespond()) { while (canRespond()) {
Set<UUID> possibleTargets = target.possibleTargets(abilityControllerId, source, game); Set<UUID> possibleTargetIds = target.possibleTargets(abilityControllerId, source, game);
boolean required = target.isRequired(source != null ? source.getSourceId() : null, game); boolean required = target.isRequired(source != null ? source.getSourceId() : null, game);
if (possibleTargets.isEmpty() if (possibleTargetIds.isEmpty()
|| target.getSize() >= target.getNumberOfTargets()) { || target.getSize() >= target.getNumberOfTargets()) {
required = false; required = false;
} }
// selected UUID responseId = target.tryToAutoChoose(abilityControllerId, source, game);
Map<String, Serializable> options = getOptions(target, null);
java.util.List<UUID> chosen = target.getTargets(); // responseId is null if a choice couldn't be automatically made
options.put("chosen", (Serializable) chosen); if (responseId == null) {
// selectable List<UUID> chosenTargets = target.getTargets();
java.util.List<UUID> choosable = new ArrayList<>(); List<UUID> possibleTargets = new ArrayList<>();
for (UUID targetId : possibleTargets) { for (UUID targetId : possibleTargetIds) {
if (target.canTarget(abilityControllerId, targetId, source, game)) { if (target.canTarget(abilityControllerId, targetId, source, game)) {
choosable.add(targetId); possibleTargets.add(targetId);
}
} }
} // if nothing to choose then show dialog (user must see non selectable items and click on any of them)
if (!choosable.isEmpty()) { if (required && possibleTargets.isEmpty()) {
options.put("choosable", (Serializable) choosable); required = false;
}
// selected
Map<String, Serializable> options = getOptions(target, null);
options.put("chosenTargets", (Serializable) chosenTargets);
if (!possibleTargets.isEmpty()) {
options.put("possibleTargets", (Serializable) possibleTargets);
}
updateGameStatePriority("chooseTargetAmount", game);
prepareForResponse(game);
if (!isExecutingMacro()) {
// target amount uses for damage only, if you see another use case then message must be changed here and on getMultiAmount call
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), possibleTargetIds, required, options);
}
waitForResponse(game);
responseId = getFixedResponseUUID(game);
} }
// if nothing to choose then show dialog (user must see non selectable items and click on any of them)
if (required && choosable.isEmpty()) {
required = false;
}
updateGameStatePriority("chooseTargetAmount", game);
prepareForResponse(game);
if (!isExecutingMacro()) {
// target amount uses for damage only, if you see another use case then message must be changed here and on getMultiAmount call
game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage(), getRelatedObjectName(source, game)), possibleTargets, required, options);
}
waitForResponse(game);
UUID responseId = getFixedResponseUUID(game);
if (responseId != null) { if (responseId != null) {
if (target.contains(responseId)) { if (target.contains(responseId)) {
// unselect // unselect
target.remove(responseId); target.remove(responseId);
} else if (possibleTargets.contains(responseId) && target.canTarget(abilityControllerId, responseId, source, game)) { } else if (possibleTargetIds.contains(responseId) && target.canTarget(abilityControllerId, responseId, source, game)) {
// select // select
target.addTarget(responseId, source, game); target.addTarget(responseId, source, game);
} }
@ -1892,6 +1925,8 @@ public class HumanPlayer extends PlayerImpl {
return; return;
} }
UUID responseId = null;
updateGameStatePriority("selectCombatGroup", game); updateGameStatePriority("selectCombatGroup", game);
prepareForResponse(game); prepareForResponse(game);
if (!isExecutingMacro()) { if (!isExecutingMacro()) {
@ -1905,12 +1940,19 @@ public class HumanPlayer extends PlayerImpl {
possibleTargets.add(attackerId); possibleTargets.add(attackerId);
} }
} }
game.fireSelectTargetEvent(playerId, new MessageToClient("Select attacker to block", getRelatedObjectName(blockerId, game)), if (possibleTargets.size() == 1) {
possibleTargets, false, getOptions(target, null)); responseId = possibleTargets.stream().iterator().next();
} else {
game.fireSelectTargetEvent(playerId, new MessageToClient("Select attacker to block", getRelatedObjectName(blockerId, game)),
possibleTargets, false, getOptions(target, null));
}
} }
waitForResponse(game); waitForResponse(game);
UUID responseId = getFixedResponseUUID(game); if (responseId == null) {
responseId = getFixedResponseUUID(game);
}
if (response.getBoolean() != null) { if (response.getBoolean() != null) {
// do nothing // do nothing
} else if (responseId != null) { } else if (responseId != null) {

View file

@ -41,7 +41,7 @@ public class EvokeTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shriekmaw"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Shriekmaw");
setChoice(playerA, true); setChoice(playerA, true);
addTarget(playerA, "Silvercoat Lion"); // addTarget(playerA, "Silvercoat Lion"); Autochosen, only target
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Exhume"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Exhume");
setStopAt(1, PhaseStep.END_TURN); setStopAt(1, PhaseStep.END_TURN);
execute(); execute();

View file

@ -59,11 +59,11 @@ public class FiendHunterTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion"); addCard(Zone.BATTLEFIELD, playerB, "Silvercoat Lion");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fiend Hunter"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Fiend Hunter");
addTarget(playerA, "Primeval Titan"); // addTarget(playerA, "Primeval Titan"); Autochosen, only option
// When Restoration Angel enters the battlefield, you may exile target non-Angel creature you control, then return that card to the battlefield under your control // When Restoration Angel enters the battlefield, you may exile target non-Angel creature you control, then return that card to the battlefield under your control
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Restoration Angel"); castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, "Restoration Angel");
addTarget(playerA, "Fiend Hunter"); // addTarget(playerA, "Fiend Hunter"); Autochosen, only option
setStopAt(4, PhaseStep.PRECOMBAT_MAIN); setStopAt(4, PhaseStep.PRECOMBAT_MAIN);
execute(); execute();

View file

@ -32,7 +32,7 @@ public class MerfolkTricksterTest extends CardTestPlayerBase {
attack(1, playerA, "Flying Men"); attack(1, playerA, "Flying Men");
castSpell(1, PhaseStep.DECLARE_BLOCKERS, playerB, mTrickster); castSpell(1, PhaseStep.DECLARE_BLOCKERS, playerB, mTrickster);
addTarget(playerB, "Flying Men"); // addTarget(playerB, "Flying Men"); Autochosen, only option
setStopAt(1, PhaseStep.END_COMBAT); setStopAt(1, PhaseStep.END_COMBAT);
execute(); execute();
@ -60,7 +60,7 @@ public class MerfolkTricksterTest extends CardTestPlayerBase {
attack(1, playerA, "Flying Men"); attack(1, playerA, "Flying Men");
castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerB, mTrickster); castSpell(1, PhaseStep.DECLARE_ATTACKERS, playerB, mTrickster);
addTarget(playerB, "Flying Men"); // addTarget(playerB, "Flying Men"); Autochosen, only target
block(1, playerB, mTrickster, "Flying Men"); block(1, playerB, mTrickster, "Flying Men");
setStopAt(1, PhaseStep.END_COMBAT); setStopAt(1, PhaseStep.END_COMBAT);

View file

@ -105,7 +105,7 @@ public class CleverImpersonatorTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Liliana, Defiant Necromancer", 1); addCard(Zone.BATTLEFIELD, playerB, "Liliana, Defiant Necromancer", 1);
attack(1, playerA, "Alesha, Who Smiles at Death"); attack(1, playerA, "Alesha, Who Smiles at Death");
addTarget(playerA, "Clever Impersonator"); // addTarget(playerA, "Clever Impersonator"); (Autochosen, only target)
setChoice(playerA, "Liliana, Defiant Necromancer"); setChoice(playerA, "Liliana, Defiant Necromancer");
activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "+2: Each player discards a card"); activateAbility(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "+2: Each player discards a card");

View file

@ -59,25 +59,28 @@ public class SharuumTheHegemonTest extends CardTestPlayerBase {
setChoice(playerA, "Whenever {this} or another creature dies"); // blood first setChoice(playerA, "Whenever {this} or another creature dies"); // blood first
addTarget(playerA, playerB); // damage by blood addTarget(playerA, playerB); // damage by blood
setChoice(playerA, true); // return setChoice(playerA, true); // return
addTarget(playerA, "Sharuum the Hegemon"); // return real sharuum // addTarget(playerA, "Sharuum the Hegemon"); // return real sharuum (Autochosen, only target)
addTarget(playerA, "Sharuum the Hegemon[only copy]"); // which legend to keep addTarget(playerA, "Sharuum the Hegemon[only copy]"); // which legend to keep
setChoice(playerA, "Whenever {this} or another creature dies"); // blood first setChoice(playerA, "Whenever {this} or another creature dies"); // blood first
addTarget(playerA, playerB); // damage by blood addTarget(playerA, playerB); // damage by blood
setChoice(playerA, true); // return setChoice(playerA, true); // return
addTarget(playerA, "Sharuum the Hegemon"); // return real sharuum // addTarget(playerA, "Sharuum the Hegemon"); // return real sharuum (Autochosen, only target
addTarget(playerA, "Sharuum the Hegemon[only copy]"); // which legend to keep addTarget(playerA, "Sharuum the Hegemon[only copy]"); // which legend to keep
setChoice(playerA, "Whenever {this} or another creature dies"); // blood first setChoice(playerA, "Whenever {this} or another creature dies"); // blood first
addTarget(playerA, playerB); // damage by blood addTarget(playerA, playerB); // damage by blood
setChoice(playerA, true); // return setChoice(playerA, true); // return
addTarget(playerA, "Sharuum the Hegemon"); // return real sharuum // addTarget(playerA, "Sharuum the Hegemon"); // return real sharuum (Autochosen, only target)
addTarget(playerA, "Sharuum the Hegemon[only copy]"); // which legend to keep addTarget(playerA, "Sharuum the Hegemon[only copy]"); // which legend to keep
setChoice(playerA, "Whenever {this} or another creature dies"); // blood first
addTarget(playerA, playerB); // damage by blood
setChoice(playerA, false); // Don't use it anymore setChoice(playerA, false); // Don't use it anymore
setStopAt(1, PhaseStep.END_TURN); setStopAt(1, PhaseStep.END_TURN);
execute(); execute();
assertAllCommandsUsed();
assertLife(playerA, 24); assertLife(playerA, 24);
assertLife(playerB, 16); assertLife(playerB, 16);

View file

@ -26,8 +26,8 @@ public class SpelltwineTest extends CardTestPlayerBase {
addCard(Zone.GRAVEYARD, playerB, "Shock"); addCard(Zone.GRAVEYARD, playerB, "Shock");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spelltwine"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Spelltwine");
addTarget(playerA, "Lightning Bolt"); // addTarget(playerA, "Lightning Bolt"); Autochosen, only target
addTarget(playerA, "Shock"); // addTarget(playerA, "Shock"); Autochosen, only target
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute(); execute();

View file

@ -80,7 +80,7 @@ public class SweepTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerA, "Plow Through Reito"); addCard(Zone.HAND, playerA, "Plow Through Reito");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plow Through Reito"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plow Through Reito");
addTarget(playerA, "Raging Goblin"); // target to boost // addTarget(playerA, "Raging Goblin"); // Autochosen (target to boost)
addTarget(playerA, TestPlayer.TARGET_SKIP); // targets to sweep (zero) addTarget(playerA, TestPlayer.TARGET_SKIP); // targets to sweep (zero)
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);

View file

@ -24,7 +24,7 @@ public class OathOfLiegesTest extends CardTestPlayerBase {
// turn 1 - A // turn 1 - A
// oath A triggers for A and activates // oath A triggers for A and activates
addTarget(playerA, playerB); // who control more lands // addTarget(playerA, playerB); // who control more lands (Autochosen, only target)
setChoice(playerA, true); // search library setChoice(playerA, true); // search library
addTarget(playerA, "Plains"); // card from library addTarget(playerA, "Plains"); // card from library
@ -56,7 +56,7 @@ public class OathOfLiegesTest extends CardTestPlayerBase {
// turn 2 - B // turn 2 - B
// oath A triggers for B and activates // oath A triggers for B and activates
addTarget(playerB, playerA); // who control more lands // addTarget(playerB, playerA); // who control more lands (Autochosen, only target)
setChoice(playerB, true); // search library setChoice(playerB, true); // search library
addTarget(playerB, "Plains"); // card from library addTarget(playerB, "Plains"); // card from library
@ -80,7 +80,7 @@ public class OathOfLiegesTest extends CardTestPlayerBase {
// turn 1 - A // turn 1 - A
// oath B triggers for A and activates // oath B triggers for A and activates
addTarget(playerA, playerB); // who control more lands // addTarget(playerA, playerB); // who control more lands (Autochosen, only target)
setChoice(playerA, true); // search library setChoice(playerA, true); // search library
addTarget(playerA, "Plains"); // card from library addTarget(playerA, "Plains"); // card from library
@ -105,11 +105,11 @@ public class OathOfLiegesTest extends CardTestPlayerBase {
// oath A triggers for A and activates // oath A triggers for A and activates
// oath B triggers for A and activates // oath B triggers for A and activates
// 1 // 1
addTarget(playerA, playerB); // who control more lands // addTarget(playerA, playerB); // who control more lands (Autochosen, only target)
setChoice(playerA, true); // search library setChoice(playerA, true); // search library
addTarget(playerA, "Plains"); // card from library addTarget(playerA, "Plains"); // card from library
// 2 // 2
addTarget(playerA, playerB); // who control more lands // addTarget(playerA, playerB); // who control more lands (Autochosen, only target)
setChoice(playerA, true); // search library setChoice(playerA, true); // search library
addTarget(playerA, "Plains"); // card from library addTarget(playerA, "Plains"); // card from library
@ -212,11 +212,11 @@ public class OathOfLiegesTest extends CardTestPlayerBase {
// oath A triggers for A and activates // oath A triggers for A and activates
// copy oath B triggers for A and activates // copy oath B triggers for A and activates
// 1 // 1
addTarget(playerA, playerB); // who control more lands // addTarget(playerA, playerB); // who control more lands (Autochosen, only target)
setChoice(playerA, true); // search library setChoice(playerA, true); // search library
addTarget(playerA, "Plains"); // card from library addTarget(playerA, "Plains"); // card from library
// 2 // 2
addTarget(playerA, playerB); // who control more lands // addTarget(playerA, playerB); // who control more lands (Autochosen, only target)
setChoice(playerA, true); // search library setChoice(playerA, true); // search library
addTarget(playerA, "Plains"); // card from library addTarget(playerA, "Plains"); // card from library

View file

@ -45,7 +45,7 @@ public class StarfieldOfNyxTest extends CardTestPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Starfield of Nyx"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Starfield of Nyx");
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudform"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Cloudform");
addTarget(playerA, "Cloudform"); // addTarget(playerA, "Cloudform"); Autochosen, only target
setStopAt(3, PhaseStep.PRECOMBAT_MAIN); setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
execute(); execute();

View file

@ -55,7 +55,9 @@ public class LilianaTest extends CardTestPlayerBase {
addTarget(playerA, yOx); // tap the ox addTarget(playerA, yOx); // tap the ox
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
setStrictChooseMode(true);
execute(); execute();
assertAllCommandsUsed();
assertPermanentCount(playerA, bMummy, 1); assertPermanentCount(playerA, bMummy, 1);
assertPermanentCount(playerA, liliannaDM, 1); assertPermanentCount(playerA, liliannaDM, 1);

View file

@ -27,7 +27,7 @@ public class EonFrolickerTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5); addCard(Zone.BATTLEFIELD, playerB, "Mountain", 5);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Eon Frolicker"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Eon Frolicker");
addTarget(playerA, playerB); // addTarget(playerA, playerB); Autochosen, only target
// AI can targets only Eon Frolicker (cause A protected from B) // AI can targets only Eon Frolicker (cause A protected from B)
checkPlayableAbility("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Cast Chandra's Fury", true); checkPlayableAbility("after", 1, PhaseStep.POSTCOMBAT_MAIN, playerB, "Cast Chandra's Fury", true);

View file

@ -71,20 +71,13 @@ public class SimpleDominariaCards extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Avatar of Woe"); addCard(Zone.BATTLEFIELD, playerB, "Avatar of Woe");
activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}"); activateAbility(1, PhaseStep.PRECOMBAT_MAIN, playerB, "{T}");
addTarget(playerB, "Knight of Grace");
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN); setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
try { execute();
execute(); assertAllCommandsUsed();
Assert.fail("must throw exception on execute");
} catch (Throwable e) {
if (!e.getMessage().contains("setup good targets")) {
Assert.fail("must throw error about bad targets, but got:\n" + e.getMessage());
}
}
assertTapped("Avatar of Woe", false);
assertGraveyardCount(playerA, "Knight of Grace", 0); assertGraveyardCount(playerA, "Knight of Grace", 0);
assertGraveyardCount(playerB, "Avatar of Woe", 1); // Autokills itself since its only valid target
} }
@Test @Test

View file

@ -23,7 +23,7 @@ public class BrainMaggotTest extends CardTestPlayerBase {
addCard(Zone.HAND, playerB, "Bloodflow Connoisseur", 1); addCard(Zone.HAND, playerB, "Bloodflow Connoisseur", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brain Maggot"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brain Maggot");
addTarget(playerA, playerB); // addTarget(playerA, playerB); Autochosen, only target
setChoice(playerA, "Bloodflow Connoisseur"); setChoice(playerA, "Bloodflow Connoisseur");
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);
@ -46,7 +46,7 @@ public class BrainMaggotTest extends CardTestPlayerBase {
// exile // exile
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brain Maggot"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Brain Maggot");
addTarget(playerA, playerB); // addTarget(playerA, playerB); Autochosen, only target
setChoice(playerA, "Bloodflow Connoisseur"); setChoice(playerA, "Bloodflow Connoisseur");
// showExile("exile", 1, PhaseStep.BEGIN_COMBAT, playerB); // showExile("exile", 1, PhaseStep.BEGIN_COMBAT, playerB);
checkExileCount("blood must be in exile", 1, PhaseStep.BEGIN_COMBAT, playerB, "Bloodflow Connoisseur", 1); checkExileCount("blood must be in exile", 1, PhaseStep.BEGIN_COMBAT, playerB, "Bloodflow Connoisseur", 1);
@ -77,7 +77,7 @@ public class BrainMaggotTest extends CardTestPlayerBase {
addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2); addCard(Zone.BATTLEFIELD, playerB, "Mountain", 2);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mesmeric Fiend"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Mesmeric Fiend");
addTarget(playerA, playerB); // addTarget(playerA, playerB); Autochosen, only target
setChoice(playerA, "Bloodflow Connoisseur"); setChoice(playerA, "Bloodflow Connoisseur");
// //
castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Lightning Bolt", "Mesmeric Fiend"); castSpell(1, PhaseStep.BEGIN_COMBAT, playerB, "Lightning Bolt", "Mesmeric Fiend");

View file

@ -31,7 +31,7 @@ public class TidehollowScullerTest extends CardTestPlayerBase {
// cast and exile from hand // cast and exile from hand
checkHandCardCount("B hand must have blood", 1, PhaseStep.UPKEEP, playerB, "Bloodflow Connoisseur", 1); checkHandCardCount("B hand must have blood", 1, PhaseStep.UPKEEP, playerB, "Bloodflow Connoisseur", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tidehollow Sculler"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tidehollow Sculler");
addTarget(playerA, playerB); // choose opponent // addTarget(playerA, playerB); // choose opponent (Autochosen, only target)
setChoice(playerA, "Bloodflow Connoisseur"); // card to exile setChoice(playerA, "Bloodflow Connoisseur"); // card to exile
checkHandCardCount("B hand must lost blood", 1, PhaseStep.BEGIN_COMBAT, playerB, "Bloodflow Connoisseur", 0); checkHandCardCount("B hand must lost blood", 1, PhaseStep.BEGIN_COMBAT, playerB, "Bloodflow Connoisseur", 0);
@ -68,13 +68,13 @@ public class TidehollowScullerTest extends CardTestPlayerBase {
// cast 1 and exile from hand // cast 1 and exile from hand
checkHandCardCount("B hand must have blood", 1, PhaseStep.UPKEEP, playerB, "Bloodflow Connoisseur", 1); checkHandCardCount("B hand must have blood", 1, PhaseStep.UPKEEP, playerB, "Bloodflow Connoisseur", 1);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tidehollow Sculler"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Tidehollow Sculler");
addTarget(playerA, playerB); // choose opponent // addTarget(playerA, playerB); // choose opponent (Autochosen, only target)
setChoice(playerA, "Bloodflow Connoisseur"); // card to exile setChoice(playerA, "Bloodflow Connoisseur"); // card to exile
checkHandCardCount("B hand must lost blood", 1, PhaseStep.BEGIN_COMBAT, playerB, "Bloodflow Connoisseur", 0); checkHandCardCount("B hand must lost blood", 1, PhaseStep.BEGIN_COMBAT, playerB, "Bloodflow Connoisseur", 0);
// cast 2 and exile from hand // cast 2 and exile from hand
checkHandCardCount("B hand must have lion", 1, PhaseStep.END_COMBAT, playerB, "Silvercoat Lion", 1); checkHandCardCount("B hand must have lion", 1, PhaseStep.END_COMBAT, playerB, "Silvercoat Lion", 1);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Tidehollow Sculler"); castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, "Tidehollow Sculler");
addTarget(playerA, playerB); // choose opponent // addTarget(playerA, playerB); // choose opponent (Autochosen, only target)
setChoice(playerA, "Silvercoat Lion"); // card to exile setChoice(playerA, "Silvercoat Lion"); // card to exile
checkHandCardCount("B hand must lost lion", 1, PhaseStep.END_TURN, playerB, "Silvercoat Lion", 0); checkHandCardCount("B hand must lost lion", 1, PhaseStep.END_TURN, playerB, "Silvercoat Lion", 0);

View file

@ -75,8 +75,8 @@ public class BlatantThieveryTest extends CardTestMultiPlayerBase {
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blatant Thievery"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Blatant Thievery");
addTarget(playerA, "Silvercoat Lion"); addTarget(playerA, "Silvercoat Lion");
addTarget(playerA, "Walking Corpse"); // addTarget(playerA, "Walking Corpse"); Autochosen, only target
addTarget(playerA, "Pillarfield Ox"); // addTarget(playerA, "Pillarfield Ox"); Autochosen, only target
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Act of Aggression", "Pillarfield Ox", "Blatant Thievery"); castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerB, "Act of Aggression", "Pillarfield Ox", "Blatant Thievery");
setStopAt(1, PhaseStep.BEGIN_COMBAT); setStopAt(1, PhaseStep.BEGIN_COMBAT);

View file

@ -4346,6 +4346,16 @@ public class TestPlayer implements Player {
this.strictChooseMode = enable; this.strictChooseMode = enable;
} }
@Override
public boolean getStrictChooseMode() {
return this.strictChooseMode;
}
@Override
public UserData getControllingPlayersUserData(Game game) {
return null;
}
@Override @Override
public void addPhyrexianToColors(FilterMana colors) { public void addPhyrexianToColors(FilterMana colors) {
computerPlayer.addPhyrexianToColors(colors); computerPlayer.addPhyrexianToColors(colors);

View file

@ -1430,6 +1430,11 @@ public class PlayerStub implements Player {
return (new FilterMana()); return (new FilterMana());
} }
@Override
public UserData getControllingPlayersUserData(Game game) {
return null;
}
@Override @Override
public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) { public SpellAbility chooseAbilityForCast(Card card, Game game, boolean noMana) {
return card.getSpellAbility(); return card.getSpellAbility();

View file

@ -788,7 +788,7 @@ public abstract class AbilityImpl implements Ability {
@Override @Override
public String getRule(boolean all) { public String getRule(boolean all) {
StringBuilder sbRule = threadLocalBuilder.get(); StringBuilder sbRule = threadLocalBuilder.get();
if (all || this.abilityType != AbilityType.SPELL) { if (all || this.abilityType != AbilityType.SPELL) { // TODO: Why the override for non-spells?
if (!manaCosts.isEmpty()) { if (!manaCosts.isEmpty()) {
sbRule.append(manaCosts.getText()); sbRule.append(manaCosts.getText());
} }

View file

@ -1054,4 +1054,16 @@ public interface Player extends MageItem, Copyable<Player> {
* @return * @return
*/ */
FilterMana getPhyrexianColors(); FilterMana getPhyrexianColors();
/**
* Function to query if the player has strictChooseMode enabled. Only the test player can have it.
* Function is added here so that the test suite project does not have to be imported into the client/server project.
*
* @return whether the player has strictChooseMode enabled
*/
public default boolean getStrictChooseMode() {
return false;
}
public UserData getControllingPlayersUserData(Game game);
} }

View file

@ -4268,6 +4268,7 @@ public abstract class PlayerImpl implements Player, Serializable {
return this.userData; return this.userData;
} }
@Override
public UserData getControllingPlayersUserData(Game game) { public UserData getControllingPlayersUserData(Game game) {
if (!isGameUnderControl()) { if (!isGameUnderControl()) {
Player player = game.getPlayer(getTurnControlledBy()); Player player = game.getPlayer(getTurnControlledBy());

View file

@ -23,6 +23,7 @@ public class UserData implements Serializable {
protected boolean passPriorityCast; protected boolean passPriorityCast;
protected boolean passPriorityActivation; protected boolean passPriorityActivation;
protected boolean autoOrderTrigger; protected boolean autoOrderTrigger;
protected int autoTargetLevel;
protected boolean useSameSettingsForReplacementEffects; protected boolean useSameSettingsForReplacementEffects;
protected boolean useFirstManaAbility = false; protected boolean useFirstManaAbility = false;
private String userIdStr; private String userIdStr;
@ -50,6 +51,7 @@ public class UserData implements Serializable {
boolean passPriorityCast, boolean passPriorityCast,
boolean passPriorityActivation, boolean passPriorityActivation,
boolean autoOrderTrigger, boolean autoOrderTrigger,
int autoTargetLevel,
boolean useSameSettingsForReplacementEffects, boolean useSameSettingsForReplacementEffects,
boolean useFirstManaAbility, boolean useFirstManaAbility,
String userIdStr) { String userIdStr) {
@ -66,6 +68,7 @@ public class UserData implements Serializable {
this.passPriorityCast = passPriorityCast; this.passPriorityCast = passPriorityCast;
this.passPriorityActivation = passPriorityActivation; this.passPriorityActivation = passPriorityActivation;
this.autoOrderTrigger = autoOrderTrigger; this.autoOrderTrigger = autoOrderTrigger;
this.autoTargetLevel = autoTargetLevel;
this.useSameSettingsForReplacementEffects = useSameSettingsForReplacementEffects; this.useSameSettingsForReplacementEffects = useSameSettingsForReplacementEffects;
this.useFirstManaAbility = useFirstManaAbility; this.useFirstManaAbility = useFirstManaAbility;
this.matchHistory = ""; this.matchHistory = "";
@ -90,6 +93,7 @@ public class UserData implements Serializable {
this.passPriorityCast = userData.passPriorityCast; this.passPriorityCast = userData.passPriorityCast;
this.passPriorityActivation = userData.passPriorityActivation; this.passPriorityActivation = userData.passPriorityActivation;
this.autoOrderTrigger = userData.autoOrderTrigger; this.autoOrderTrigger = userData.autoOrderTrigger;
this.autoTargetLevel = userData.autoTargetLevel;
this.useSameSettingsForReplacementEffects = userData.useSameSettingsForReplacementEffects; this.useSameSettingsForReplacementEffects = userData.useSameSettingsForReplacementEffects;
this.useFirstManaAbility = userData.useFirstManaAbility; this.useFirstManaAbility = userData.useFirstManaAbility;
this.userIdStr = userData.userIdStr; this.userIdStr = userData.userIdStr;
@ -111,6 +115,7 @@ public class UserData implements Serializable {
false, false,
false, false,
true, true,
1,
true, true,
false, false,
"" ""
@ -235,14 +240,22 @@ public class UserData implements Serializable {
return autoOrderTrigger; return autoOrderTrigger;
} }
public boolean isUseSameSettingsForReplacementEffects() {
return useSameSettingsForReplacementEffects;
}
public void setAutoOrderTrigger(boolean autoOrderTrigger) { public void setAutoOrderTrigger(boolean autoOrderTrigger) {
this.autoOrderTrigger = autoOrderTrigger; this.autoOrderTrigger = autoOrderTrigger;
} }
public int getAutoTargetLevel() {
return autoTargetLevel;
}
public void setAutoTargetLevel(int autoTargetLevel) {
this.autoTargetLevel = autoTargetLevel;
}
public boolean isUseSameSettingsForReplacementEffects() {
return useSameSettingsForReplacementEffects;
}
public boolean isUseFirstManaAbility() { public boolean isUseFirstManaAbility() {
return useFirstManaAbility; return useFirstManaAbility;
} }

View file

@ -1,6 +1,7 @@
package mage.target; package mage.target;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.cards.Cards;
import mage.constants.Outcome; import mage.constants.Outcome;
import mage.constants.Zone; import mage.constants.Zone;
import mage.filter.Filter; import mage.filter.Filter;
@ -8,6 +9,7 @@ import mage.game.Game;
import mage.players.Player; import mage.players.Player;
import java.io.Serializable; import java.io.Serializable;
import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
@ -159,4 +161,35 @@ public interface Target extends Serializable {
int getSize(); int getSize();
boolean contains(UUID targetId); boolean contains(UUID targetId);
/**
* This function tries to auto-choose the next target.
* <p>
* It will NOT add it to the list of targets, it will ony choose the next target
* <p>
* Use this version when the targets is selected from targets.getTargets.
* <p>
* It will auto-choosen if all of the following criteria are met:
* - The minimum and maximum number of targets is the same (i.e. effect does not have "up to" in its name)
* - The number of valid targets is equal to the number of targets still left to be specified
*
*
* @param abilityControllerId
* @param source
* @param game
* @return The UUID of the chosen option, or null if one could not be chosen
*/
UUID tryToAutoChoose(UUID abilityControllerId, Ability source, Game game);
/**
* Use this version when the target is chosen from a specified collection.
* E.g. {@link Player#chooseTarget(Outcome outcome, Cards cards, TargetCard target, Ability source, Game game)}
*
* @param abilityControllerId
* @param source
* @param game
* @param possibleTargets
* @return
*/
UUID tryToAutoChoose(UUID abilityControllerId, Ability source, Game game, Collection<UUID> possibleTargets);
} }

View file

@ -10,6 +10,7 @@ import mage.game.Game;
import mage.game.events.GameEvent; import mage.game.events.GameEvent;
import mage.game.events.TargetEvent; import mage.game.events.TargetEvent;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil; import mage.util.CardUtil;
import mage.util.RandomUtil; import mage.util.RandomUtil;
@ -317,8 +318,14 @@ public abstract class TargetImpl implements Target {
possibleTargets.remove(index); possibleTargets.remove(index);
} }
} }
} else if (!targetController.chooseTarget(outcome, this, source, game)) { } else {
return chosen; // Try to autochoosen
UUID autoChosenId = tryToAutoChoose(playerId, source, game);
if (autoChosenId != null) {
addTarget(autoChosenId, source, game);
} else if (!targetController.chooseTarget(outcome, this, source, game)) { // If couldn't autochoose ask player
return chosen;
}
} }
chosen = targets.size() >= getNumberOfTargets(); chosen = targets.size() >= getNumberOfTargets();
} while (!isChosen() && !doneChoosing()); } while (!isChosen() && !doneChoosing());
@ -595,4 +602,82 @@ public abstract class TargetImpl implements Target {
public boolean contains(UUID targetId) { public boolean contains(UUID targetId) {
return targets.containsKey(targetId); return targets.containsKey(targetId);
} }
@Override
public UUID tryToAutoChoose(UUID abilityControllerId, Ability source, Game game) {
Set<UUID> possibleTargets = possibleTargets(abilityControllerId, source, game);
possibleTargets.removeAll(this.targets.keySet());
return tryToAutoChoose(abilityControllerId, source, game, possibleTargets);
}
@Override
public UUID tryToAutoChoose(UUID abilityControllerId, Ability source, Game game, Collection<UUID> possibleTargets) {
Player player = game.getPlayer(abilityControllerId);
if (player == null) {
return null;
}
int playerAutoTargetLevel;
if (player.isHuman() && player.getControllingPlayersUserData(game) != null) { // Ensure that non-strictChooseMode ComputerPlayer will still use this ability
playerAutoTargetLevel = player.getControllingPlayersUserData(game).getAutoTargetLevel();
} else {
playerAutoTargetLevel = 2;
}
boolean strictModeEnabled = player.getStrictChooseMode();
boolean canAutoChoose = this.getMinNumberOfTargets() == this.getMaxNumberOfTargets() && // Targets must be picked
possibleTargets.size() == this.getNumberOfTargets() - this.getSize() && // Available targets are equal to the number that must be picked
!strictModeEnabled && // Test AI is not set to strictChooseMode(true)
playerAutoTargetLevel > 0; // Human player has enabled auto-choose in settings
if (canAutoChoose) {
boolean autoTargetAll = playerAutoTargetLevel == 2;
for (UUID possibleChooseId : possibleTargets) {
// Don't pick a target that's already been chosen, this will lead to an infinite loop of
// choosen and unchoosing the same target.
if (this.targets.containsKey(possibleChooseId)) {
continue;
}
if (autoTargetAll) { // No need for further checks since all targeting is to be automated
return possibleChooseId;
}
// Check if you control the target (or own the card)
boolean targetingOwnThing;
if (possibleChooseId == abilityControllerId) {
targetingOwnThing = true;
} else {
Permanent targetPermanent = game.getPermanent(possibleChooseId);
Card targetCard = game.getCard(possibleChooseId);
Spell targetSpell = game.getSpell(possibleChooseId);
if (targetPermanent != null) {
targetingOwnThing = abilityControllerId == targetPermanent.getControllerId();
} else if (targetCard != null) {
targetingOwnThing = abilityControllerId == targetCard.getOwnerId();
} else if (targetSpell != null) {
targetingOwnThing = abilityControllerId == targetSpell.getControllerId();
} else {
// No point further checking
continue;
}
}
// If you control (or own the card) the target, check if it's one of the feel-bad effects.
if (targetingOwnThing) {
String abilityText = source.getRule(true).toLowerCase();
if (abilityText.contains("discard")
|| abilityText.contains("sacrifice")
|| abilityText.contains("destroy")
|| abilityText.contains("exile")) {
continue;
}
// Otherwise return the target with the return statement below.
}
// If we get here then it means that the target UUID passes the checks.
return possibleChooseId;
}
}
return null;
}
} }