* GUI: added auto-choose for replacement effects (remember answer in dialog + reset answer in popup menu + new option in preferences; #4360, #328, #4219, #6676, #7914);

This commit is contained in:
Oleg Agafonov 2021-08-09 11:25:51 +04:00
parent c081d3fa33
commit c9ab896d24
11 changed files with 311 additions and 77 deletions

View file

@ -324,9 +324,9 @@ public class PickCheckBoxDialog extends MageDialog {
if (item != null) {
if (choice.isKeyChoice()) {
choice.setChoiceByKey(item.getKey());
choice.setChoiceByKey(item.getKey(), false);
} else {
choice.setChoice(item.getKey());
choice.setChoice(item.getKey(), false);
}
return true;
} else {

View file

@ -161,7 +161,8 @@
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="cbSpecial" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="btOK" linkSize="3" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
@ -177,6 +178,7 @@
<Group type="103" groupAlignment="3" attributes="0">
<Component id="btCancel" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="btOK" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="cbSpecial" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
@ -203,6 +205,11 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="btCancelActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="cbSpecial">
<Properties>
<Property name="text" type="java.lang.String" value="Remember choose"/>
</Properties>
</Component>
</SubComponents>
</Container>
</SubComponents>

View file

@ -18,10 +18,11 @@ import mage.client.util.gui.MageDialogState;
public class PickChoiceDialog extends MageDialog {
Choice choice;
java.util.List<KeyValueItem> allItems = new ArrayList<>();
DefaultListModel<KeyValueItem> dataModel = new DefaultListModel<>();
final private static String HTML_TEMPLATE = "<html><div style='text-align: center;'>%s</div></html>";
final private static String HTML_HEADERS_TEMPLATE = "<html><div style='text-align: center;'>%s</div></html>";
public void showDialog(Choice choice) {
showDialog(choice, null, null, null);
@ -40,9 +41,13 @@ public class PickChoiceDialog extends MageDialog {
setLabelText(this.labelMessage, choice.getMessage());
setLabelText(this.labelSubMessage, choice.getSubMessage());
btCancel.setEnabled(!choice.isRequired());
// special choice (example: auto-choose answer next time)
cbSpecial.setVisible(choice.isSpecialEnabled());
cbSpecial.setText(choice.getSpecialText());
cbSpecial.setToolTipText(choice.getSpecialHint());
// 2 modes: string or key-values
// sore data in allItems for inremental filtering
// http://logicbig.com/tutorials/core-java-tutorial/swing/list-filter/
@ -199,7 +204,7 @@ public class PickChoiceDialog extends MageDialog {
private void setLabelText(JLabel label, String text) {
if ((text != null) && !text.equals("")) {
label.setText(String.format(HTML_TEMPLATE, text));
label.setText(String.format(HTML_HEADERS_TEMPLATE, text));
label.setVisible(true);
} else {
label.setText("");
@ -247,6 +252,7 @@ public class PickChoiceDialog extends MageDialog {
public boolean setChoice() {
KeyValueItem item = (KeyValueItem) this.listChoices.getSelectedValue();
boolean isSpecial = choice.isSpecialEnabled() && cbSpecial.isSelected();
// auto select one item (after incemental filtering)
if ((item == null) && (this.listChoices.getModel().getSize() == 1)) {
@ -256,12 +262,23 @@ public class PickChoiceDialog extends MageDialog {
if (item != null) {
if (choice.isKeyChoice()) {
choice.setChoiceByKey(item.getKey());
choice.setChoiceByKey(item.getKey(), isSpecial);
} else {
choice.setChoice(item.getKey());
choice.setChoice(item.getKey(), isSpecial);
}
return true;
} else {
// special choice can be empty
if (choice.isSpecialEnabled() && choice.isSpecialCanBeEmpty()) {
if (choice.isKeyChoice()) {
choice.setChoiceByKey(null, isSpecial);
} else {
choice.setChoice(null, isSpecial);
}
return true;
}
// nothing to choose
choice.clearChoice();
return false;
}
@ -311,6 +328,7 @@ public class PickChoiceDialog extends MageDialog {
panelCommands = new javax.swing.JPanel();
btOK = new javax.swing.JButton();
btCancel = new javax.swing.JButton();
cbSpecial = new javax.swing.JCheckBox();
setResizable(true);
@ -386,19 +404,22 @@ public class PickChoiceDialog extends MageDialog {
}
});
cbSpecial.setText("Remember choose");
javax.swing.GroupLayout panelCommandsLayout = new javax.swing.GroupLayout(panelCommands);
panelCommands.setLayout(panelCommandsLayout);
panelCommandsLayout.setHorizontalGroup(
panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(panelCommandsLayout.createSequentialGroup()
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(cbSpecial)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(btOK)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(btCancel, javax.swing.GroupLayout.PREFERRED_SIZE, 70, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
panelCommandsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, btCancel, btOK);
panelCommandsLayout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {btCancel, btOK});
panelCommandsLayout.setVerticalGroup(
panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
@ -406,7 +427,8 @@ public class PickChoiceDialog extends MageDialog {
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(panelCommandsLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(btCancel)
.addComponent(btOK))
.addComponent(btOK)
.addComponent(cbSpecial))
.addContainerGap())
);
@ -460,6 +482,7 @@ public class PickChoiceDialog extends MageDialog {
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton btCancel;
private javax.swing.JButton btOK;
private javax.swing.JCheckBox cbSpecial;
private javax.swing.JTextField editSearch;
private javax.swing.JLabel labelMessage;
private javax.swing.JLabel labelSearch;

View file

@ -4082,8 +4082,8 @@
<Component id="checkBoxEndTurnOthers" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="phases_stopSettings" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="phases_stopSettings" pref="291" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
@ -4182,7 +4182,7 @@
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignGridLayout">
<Property name="columns" type="int" value="1"/>
<Property name="rows" type="int" value="9"/>
<Property name="rows" type="int" value="10"/>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="cbStopAttack">
@ -4281,8 +4281,8 @@
</Component>
<Component class="javax.swing.JCheckBox" name="cbAutoOrderTrigger">
<Properties>
<Property name="text" type="java.lang.String" value="Set order for your triggers automatically if all have the same text"/>
<Property name="toolTipText" type="java.lang.String" value="&lt;HTML&gt;If activated the order to put on the stack your triggers that trigger at the same time&lt;br/&gt;&#xa;is set automatically if all have the same text."/>
<Property name="text" type="java.lang.String" value="TRIGGERS: auto-choose triggers order for same rule texts (put same triggers to the stack at default order)"/>
<Property name="toolTipText" type="java.lang.String" value="&lt;HTML&gt;If you put same triggers with same texts on the stack then auto-choose their order.&lt;br/&gt;&#xa;You can change that settings anytime at the game."/>
<Property name="actionCommand" type="java.lang.String" value=""/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 25]"/>
@ -4292,6 +4292,19 @@
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbAutoOrderTriggerActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="cbUseSameSettingsForReplacementEffect">
<Properties>
<Property name="text" type="java.lang.String" value="REPLACEMENT EFFECTS: use same auto-choose settings for same cards (choose replacement effects order dialog)"/>
<Property name="toolTipText" type="java.lang.String" value="&lt;HTML&gt;If you setup auto-choose for one object/card then it will be applied for all other objects with same name.&lt;br/&gt;&#xa;You can change that settings anytime at the game."/>
<Property name="actionCommand" type="java.lang.String" value=""/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[300, 25]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="cbUseSameSettingsForReplacementEffectActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
</SubComponents>

View file

@ -134,6 +134,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
public static final String KEY_PASS_PRIORITY_CAST = "passPriorityCast";
public static final String KEY_PASS_PRIORITY_ACTIVATION = "passPriorityActivation";
public static final String KEY_AUTO_ORDER_TRIGGER = "autoOrderTrigger";
public static final String KEY_USE_SAME_SETTINGS_FOR_SAME_REPLACEMENT_EFFECTS = "useSameSettingsForSameReplacementEffects";
public static final String KEY_USE_FIRST_MANA_ABILITY = "useFirstManaAbility";
// mana auto payment
@ -501,6 +502,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
cbPassPriorityCast = new javax.swing.JCheckBox();
cbPassPriorityActivation = new javax.swing.JCheckBox();
cbAutoOrderTrigger = new javax.swing.JCheckBox();
cbUseSameSettingsForReplacementEffect = new javax.swing.JCheckBox();
tabImages = new javax.swing.JPanel();
panelCardImages = new javax.swing.JPanel();
cbUseDefaultImageFolder = new javax.swing.JCheckBox();
@ -1405,7 +1407,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
jLabelEndOfTurn.setText("End of turn:");
phases_stopSettings.setBorder(javax.swing.BorderFactory.createTitledBorder(javax.swing.BorderFactory.createEtchedBorder(), "SKIP settings"));
phases_stopSettings.setLayout(new java.awt.GridLayout(9, 1));
phases_stopSettings.setLayout(new java.awt.GridLayout(10, 1));
cbStopAttack.setSelected(true);
cbStopAttack.setText("STOP skips on declare attackers if attackers are available");
@ -1493,8 +1495,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
});
phases_stopSettings.add(cbPassPriorityActivation);
cbAutoOrderTrigger.setText("Set order for your triggers automatically if all have the same text");
cbAutoOrderTrigger.setToolTipText("<HTML>If activated the order to put on the stack your triggers that trigger at the same time<br/>\nis set automatically if all have the same text.");
cbAutoOrderTrigger.setText("TRIGGERS: auto-choose triggers order for same rule texts (put same triggers to the stack at default order)");
cbAutoOrderTrigger.setToolTipText("<HTML>If you put same triggers with same texts on the stack then auto-choose their order.<br/>\nYou can change that settings anytime at the game.");
cbAutoOrderTrigger.setActionCommand("");
cbAutoOrderTrigger.setPreferredSize(new java.awt.Dimension(300, 25));
cbAutoOrderTrigger.addActionListener(new java.awt.event.ActionListener() {
@ -1504,6 +1506,17 @@ public class PreferencesDialog extends javax.swing.JDialog {
});
phases_stopSettings.add(cbAutoOrderTrigger);
cbUseSameSettingsForReplacementEffect.setText("REPLACEMENT EFFECTS: use same auto-choose settings for same cards (choose replacement effects order dialog)");
cbUseSameSettingsForReplacementEffect.setToolTipText("<HTML>If you setup auto-choose for one object/card then it will be applied for all other objects with same name.<br/>\nYou can change that settings anytime at the game.");
cbUseSameSettingsForReplacementEffect.setActionCommand("");
cbUseSameSettingsForReplacementEffect.setPreferredSize(new java.awt.Dimension(300, 25));
cbUseSameSettingsForReplacementEffect.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
cbUseSameSettingsForReplacementEffectActionPerformed(evt);
}
});
phases_stopSettings.add(cbUseSameSettingsForReplacementEffect);
org.jdesktop.layout.GroupLayout tabPhasesLayout = new org.jdesktop.layout.GroupLayout(tabPhases);
tabPhases.setLayout(tabPhasesLayout);
tabPhasesLayout.setHorizontalGroup(
@ -1614,8 +1627,8 @@ public class PreferencesDialog extends javax.swing.JDialog {
.add(jLabelEndOfTurn)
.add(checkBoxEndTurnOthers))
.addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED)
.add(phases_stopSettings, 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(phases_stopSettings, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 291, Short.MAX_VALUE)
.addContainerGap())
);
tabsPanel.addTab("Phases & Priority", tabPhases);
@ -2925,6 +2938,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
save(prefs, dialog.cbPassPriorityCast, KEY_PASS_PRIORITY_CAST, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.cbPassPriorityActivation, KEY_PASS_PRIORITY_ACTIVATION, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.cbAutoOrderTrigger, KEY_AUTO_ORDER_TRIGGER, "true", "false", UPDATE_CACHE_POLICY);
save(prefs, dialog.cbUseSameSettingsForReplacementEffect, KEY_USE_SAME_SETTINGS_FOR_SAME_REPLACEMENT_EFFECTS, "true", "false", UPDATE_CACHE_POLICY);
// images
save(prefs, dialog.cbUseDefaultImageFolder, KEY_CARD_IMAGES_USE_DEFAULT, "true", "false", UPDATE_CACHE_POLICY);
@ -3309,6 +3323,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
}
}//GEN-LAST:event_sliderGUISizeStateChanged
private void cbUseSameSettingsForReplacementEffectActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_cbUseSameSettingsForReplacementEffectActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_cbUseSameSettingsForReplacementEffectActionPerformed
private void showProxySettings() {
Connection.ProxyType proxyType = (Connection.ProxyType) cbProxyType.getSelectedItem();
switch (proxyType) {
@ -3465,7 +3483,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
load(prefs, dialog.cbPassPriorityCast, KEY_PASS_PRIORITY_CAST, "true", "false");
load(prefs, dialog.cbPassPriorityActivation, KEY_PASS_PRIORITY_ACTIVATION, "true", "false");
load(prefs, dialog.cbAutoOrderTrigger, KEY_AUTO_ORDER_TRIGGER, "true", "true");
load(prefs, dialog.cbUseSameSettingsForReplacementEffect, KEY_USE_SAME_SETTINGS_FOR_SAME_REPLACEMENT_EFFECTS, "true", "true");
}
private static void loadGuiSize(Preferences prefs) {
@ -3996,6 +4014,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PASS_PRIORITY_CAST, "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_USE_SAME_SETTINGS_FOR_SAME_REPLACEMENT_EFFECTS, "true").equals("true"),
PreferencesDialog.getCachedValue(PreferencesDialog.KEY_USE_FIRST_MANA_ABILITY, "false").equals("true"),
userStrId
);
@ -4069,6 +4088,7 @@ public class PreferencesDialog extends javax.swing.JDialog {
private javax.swing.JCheckBox cbUseDefaultBattleImage;
private javax.swing.JCheckBox cbUseDefaultImageFolder;
private javax.swing.JCheckBox cbUseRandomBattleImage;
private javax.swing.JCheckBox cbUseSameSettingsForReplacementEffect;
private javax.swing.JLabel chatFontSizeLabel;
private javax.swing.JCheckBox checkBoxBeforeCOthers;
private javax.swing.JCheckBox checkBoxBeforeCYou;

View file

@ -108,7 +108,7 @@ public final class GamePanel extends javax.swing.JPanel {
private boolean initComponents;
private Timer resizeTimer; // can't be final
private final Timer resizeTimer; // can't be final
private enum PopUpMenuType {
TRIGGER_ORDER
@ -128,6 +128,7 @@ public final class GamePanel extends javax.swing.JPanel {
Map<String, Serializable> options;
Set<UUID> targets;
}
private final LastGameData lastGameData = new LastGameData();
@ -1746,18 +1747,22 @@ public final class GamePanel extends javax.swing.JPanel {
// TODO: remember last choices and search incremental for same events?
PickChoiceDialog pickChoice = new PickChoiceDialog();
pickChoice.showDialog(choice, objectId, choiceWindowState);
// special mode adds # to the answer (server side code must process that prefix, see replacementEffectChoice)
String specialPrefix = choice.isChosenSpecial() ? "#" : "";
String valueToSend;
if (choice.isKeyChoice()) {
SessionHandler.sendPlayerString(gameId, choice.getChoiceKey());
/* // old code, auto complete was for auto scripting?
if (pickChoice.isAutoSelect()) {
SessionHandler.sendPlayerString(gameId, '#' + choice.getChoiceKey());
} else {
SessionHandler.sendPlayerString(gameId, choice.getChoiceKey());
}*/
valueToSend = choice.getChoiceKey();
} else {
valueToSend = choice.getChoice();
SessionHandler.sendPlayerString(gameId, choice.getChoice());
}
SessionHandler.sendPlayerString(gameId, valueToSend == null ? null : specialPrefix + valueToSend);
// keep dialog position
choiceWindowState = new MageDialogState(pickChoice);
pickChoice.removeDialog();
}

View file

@ -74,7 +74,7 @@ public class HumanPlayer extends PlayerImpl {
protected final Choice replacementEffectChoice;
private static final Logger logger = Logger.getLogger(HumanPlayer.class);
protected HashSet<String> autoSelectReplacementEffects = new HashSet<>();
protected HashSet<String> autoSelectReplacementEffects = new LinkedHashSet<>(); // must be sorted
protected ManaCost currentlyUnpaidMana;
protected Set<UUID> triggerAutoOrderAbilityFirst = new HashSet<>();
@ -96,9 +96,16 @@ public class HumanPlayer extends PlayerImpl {
public HumanPlayer(String name, RangeOfInfluence range, int skill) {
super(name, range);
human = true;
replacementEffectChoice = new ChoiceImpl(true);
replacementEffectChoice.setMessage("Choose replacement effect to resolve first");
human = true;
replacementEffectChoice.setSpecial(
true,
false,
"Remember answer",
"Choose same answer next time (you can reset saved answers by battlefield popup menu)"
);
}
public HumanPlayer(final HumanPlayer player) {
@ -289,6 +296,8 @@ public class HumanPlayer extends PlayerImpl {
if (falseText != null) {
options.put("UI.right.btn.text", falseText);
}
// auto-answer
if (source != null) {
Boolean answer = requestAutoAnswerId.get(source.getOriginalId() + "#" + message);
if (answer != null) {
@ -359,11 +368,20 @@ public class HumanPlayer extends PlayerImpl {
return 0;
}
// use auto-choice:
// - uses "always first" logic (choose in same order as user answer saves)
// - search same effects by text (object name [id]: rules)
// - autoSelectReplacementEffects is sorted set
// - must get "same settings" option between cycle/response (user can change it by preferences)
boolean useSameSettings = getControllingPlayersUserData(game).isUseSameSettingsForReplacementEffects();
if (!autoSelectReplacementEffects.isEmpty()) {
for (String autoKey : autoSelectReplacementEffects) {
for (String autoText : autoSelectReplacementEffects) {
int count = 0;
// find effect with same saved text
for (String effectKey : rEffects.keySet()) {
if (effectKey.equals(autoKey)) {
String currentText = prepareReplacementText(rEffects.get(effectKey), useSameSettings);
if (currentText.equals(autoText)) {
return count;
}
count++;
@ -371,10 +389,11 @@ public class HumanPlayer extends PlayerImpl {
}
}
replacementEffectChoice.clearChoice();
replacementEffectChoice.getChoices().clear();
replacementEffectChoice.setKeyChoices(rEffects);
// Check if there are different ones
// if same choices then select first
int differentChoices = 0;
String lastChoice = "";
for (String value : replacementEffectChoice.getKeyChoices().values()) {
@ -397,11 +416,18 @@ public class HumanPlayer extends PlayerImpl {
logger.debug("Choose effect: " + response.getString());
if (response.getString() != null) {
// save auto-choice (effect's text)
if (response.getString().startsWith("#")) {
autoSelectReplacementEffects.add(response.getString().substring(1));
replacementEffectChoice.setChoiceByKey(response.getString().substring(1));
replacementEffectChoice.setChoiceByKey(response.getString().substring(1), true);
if (replacementEffectChoice.isChosen()) {
// put auto-choice to the end
useSameSettings = getControllingPlayersUserData(game).isUseSameSettingsForReplacementEffects();
String effectText = prepareReplacementText(replacementEffectChoice.getChoiceValue(), useSameSettings);
autoSelectReplacementEffects.remove(effectText);
autoSelectReplacementEffects.add(effectText);
}
} else {
replacementEffectChoice.setChoiceByKey(response.getString());
replacementEffectChoice.setChoiceByKey(response.getString(), false);
}
if (replacementEffectChoice.getChoiceKey() != null) {
@ -419,6 +445,14 @@ public class HumanPlayer extends PlayerImpl {
return 0;
}
private String prepareReplacementText(String fullText, boolean useSameSettingsForDifferentObjects) {
// remove object id from the rules text (example: object [abd]: rules -> object : rules)
if (useSameSettingsForDifferentObjects) {
fullText = fullText.replaceAll("\\[\\w+\\]", "");
}
return fullText;
}
@Override
public boolean choose(Outcome outcome, Choice choice, Game game) {
if (gameInCheckPlayableState(game)) {

View file

@ -189,7 +189,7 @@ public class ContinuousEffects implements Serializable {
}
}
} else {
logger.error("No abilities for continuous effect: " + effect.toString());
logger.error("No abilities for continuous effect: " + effect);
}
break;
default:
@ -1282,7 +1282,7 @@ public class ContinuousEffects implements Serializable {
logger.error("Effect is null: " + source.toString());
return;
} else if (source == null) {
logger.warn("Adding effect without ability : " + effect.toString());
logger.warn("Adding effect without ability : " + effect);
}
switch (effect.getEffectType()) {
case REPLACEMENT:
@ -1369,6 +1369,8 @@ public class ContinuousEffects implements Serializable {
}
public Map<String, String> getReplacementEffectsTexts(Map<ReplacementEffect, Set<Ability>> rEffects, Game game) {
// warning, autoSelectReplacementEffects uses [object id] in texts as different settings,
// so if you change keys or texts logic then don't forget to change auto-choose too
Map<String, String> texts = new LinkedHashMap<>();
for (Map.Entry<ReplacementEffect, Set<Ability>> entry : rEffects.entrySet()) {
if (entry.getValue() != null) {

View file

@ -1,55 +1,92 @@
package mage.choices;
import mage.util.Copyable;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
*
* @author BetaSteward_at_googlemail.com, JayDi85
*/
public interface Choice {
public interface Choice extends Serializable, Copyable<Choice> {
String getMessage();
void setMessage(String message);
String getSubMessage();
void setSubMessage(String subMessage);
void clearChoice();
boolean isChosen();
boolean isChosenSpecial();
boolean isRequired();
Choice copy();
// special mode for all choices
void setSpecial(boolean enabled, boolean canBeEmpty, String text, String hint);
boolean isSpecialEnabled();
boolean isSpecialCanBeEmpty();
String getSpecialText();
String getSpecialHint();
// string choice
void setChoices(Set<String> choices);
Set<String> getChoices();
void setChoice(String choice);
void setChoice(String choice, boolean isSpecial);
String getChoice();
default void setChoice(String choice) {
setChoice(choice, false);
}
// key-value choice
boolean isKeyChoice();
void setKeyChoices(Map<String, String> choices);
Map<String, String> getKeyChoices();
void setChoiceByKey(String choiceKey);
void setChoiceByKey(String choiceKey, boolean isSpecial);
String getChoiceKey();
String getChoiceValue();
default void setChoiceByKey(String choiceKey) {
setChoiceByKey(choiceKey, false);
}
// search
boolean isSearchEnabled();
void setSearchEnabled(boolean isEnabled);
void setSearchText(String searchText);
String getSearchText();
// sorting
boolean isSortEnabled();
void setSortData(Map<String, Integer> sortData);
Map<String, Integer> getSortData();
// random choice
// random choice (for AI usage)
void setRandomChoice();
boolean setChoiceByAnswers(List<String> answers, boolean removeSelectAnswerFromList);
}

View file

@ -2,15 +2,15 @@ package mage.choices;
import mage.util.RandomUtil;
import java.io.Serializable;
import java.util.*;
/**
* @author BetaSteward_at_googlemail.com, JayDi85
*/
public class ChoiceImpl implements Choice, Serializable {
public class ChoiceImpl implements Choice {
protected boolean chosen;
protected boolean chosenNormal;
protected boolean chosenSpecial;
protected final boolean required;
protected String choice;
protected String choiceKey;
@ -22,6 +22,13 @@ public class ChoiceImpl implements Choice, Serializable {
protected boolean searchEnabled = true; // enable for all windows by default
protected String searchText;
// special button with #-answer
// warning, only for human's GUI, not AI
protected boolean specialEnabled = false;
protected boolean specialCanBeEmpty = false; // enable if you want to select "nothing", but not cancel
protected String specialText = "";
protected String specialHint = "";
public ChoiceImpl() {
this(false);
}
@ -30,9 +37,10 @@ public class ChoiceImpl implements Choice, Serializable {
this.required = required;
}
public ChoiceImpl(ChoiceImpl choice) {
public ChoiceImpl(final ChoiceImpl choice) {
this.choice = choice.choice;
this.chosen = choice.chosen;
this.chosenNormal = choice.chosenNormal;
this.chosenSpecial = choice.chosenSpecial;
this.required = choice.required;
this.message = choice.message;
this.subMessage = choice.subMessage;
@ -42,18 +50,28 @@ public class ChoiceImpl implements Choice, Serializable {
this.choiceKey = choice.choiceKey;
this.keyChoices = choice.keyChoices; // list should never change for the same object so copy by reference TODO: check errors with that, it that ok? Color list is static
this.sortData = choice.sortData;
this.specialEnabled = choice.specialEnabled;
this.specialCanBeEmpty = choice.specialCanBeEmpty;
this.specialText = choice.specialText;
this.specialHint = choice.specialHint;
}
@Override
public boolean isChosen() {
return chosen;
return chosenNormal || chosenSpecial;
}
@Override
public boolean isChosenSpecial() {
return chosenSpecial;
}
@Override
public void clearChoice() {
choice = null;
choiceKey = null;
chosen = false;
this.choice = null;
this.choiceKey = null;
this.chosenNormal = false;
this.chosenSpecial = false;
}
@Override
@ -92,10 +110,17 @@ public class ChoiceImpl implements Choice, Serializable {
}
@Override
public void setChoice(String choice) {
public void setChoice(String choice, boolean isSpecial) {
if (choices.contains(choice)) {
this.choice = choice;
this.chosen = true;
this.chosenNormal = true;
this.chosenSpecial = isSpecial;
}
if (isSpecial && this.specialCanBeEmpty && (choice == null || choice.isEmpty())) {
clearChoice();
this.chosenNormal = false;
this.chosenSpecial = true;
}
}
@ -134,12 +159,19 @@ public class ChoiceImpl implements Choice, Serializable {
}
@Override
public void setChoiceByKey(String choiceKey) {
public void setChoiceByKey(String choiceKey, boolean isSpecial) {
String choiceToSet = keyChoices.get(choiceKey);
if (choiceToSet != null) {
this.choice = choiceToSet;
this.choiceKey = choiceKey;
this.chosen = true;
this.chosenNormal = true;
this.chosenSpecial = isSpecial;
}
if (isSpecial && this.specialCanBeEmpty && (choiceKey == null || choiceKey.isEmpty())) {
clearChoice();
this.chosenNormal = false;
this.chosenSpecial = true;
}
}
@ -191,14 +223,14 @@ public class ChoiceImpl implements Choice, Serializable {
String[] vals = this.getKeyChoices().keySet().toArray(new String[0]);
if (vals.length > 0) {
int choiceNum = RandomUtil.nextInt(vals.length);
this.setChoiceByKey(vals[choiceNum]);
this.setChoiceByKey(vals[choiceNum], false);
}
} else {
// string mode
String[] vals = this.getChoices().toArray(new String[0]);
if (vals.length > 0) {
int choiceNum = RandomUtil.nextInt(vals.length);
this.setChoice(vals[choiceNum]);
this.setChoice(vals[choiceNum], false);
}
}
}
@ -211,18 +243,18 @@ public class ChoiceImpl implements Choice, Serializable {
for (String needChoice : answers) {
for (Map.Entry<String, String> currentChoice : this.getKeyChoices().entrySet()) {
if (currentChoice.getKey().equals(needChoice)) {
this.setChoiceByKey(needChoice);
this.setChoiceByKey(needChoice, false);
answers.remove(needChoice);
return true;
}
}
}
// no key answer found, try to macht by text starting with
// no key answer found, try to match by text starting with
for (String needChoice : answers) {
for (Map.Entry<String, String> currentChoice : this.getKeyChoices().entrySet()) {
if (currentChoice.getValue().startsWith(needChoice)) {
this.setChoiceByKey(currentChoice.getKey());
this.setChoiceByKey(currentChoice.getKey(), false);
answers.remove(needChoice);
return true;
}
@ -233,7 +265,7 @@ public class ChoiceImpl implements Choice, Serializable {
for (String needChoice : answers) {
for (String currentChoice : this.getChoices()) {
if (currentChoice.equals(needChoice)) {
this.setChoice(needChoice);
this.setChoice(needChoice, false);
answers.remove(needChoice);
return true;
}
@ -242,4 +274,32 @@ public class ChoiceImpl implements Choice, Serializable {
}
return false; // can't find answer
}
@Override
public void setSpecial(boolean enabled, boolean canBeEmpty, String text, String hint) {
this.specialEnabled = enabled;
this.specialCanBeEmpty = canBeEmpty;
this.specialText = text;
this.specialHint = hint;
}
@Override
public boolean isSpecialEnabled() {
return this.specialEnabled;
}
@Override
public boolean isSpecialCanBeEmpty() {
return this.specialCanBeEmpty;
}
@Override
public String getSpecialText() {
return this.specialText;
}
@Override
public String getSpecialHint() {
return this.specialHint;
}
}

View file

@ -23,6 +23,7 @@ public class UserData implements Serializable {
protected boolean passPriorityCast;
protected boolean passPriorityActivation;
protected boolean autoOrderTrigger;
protected boolean useSameSettingsForReplacementEffects;
protected boolean useFirstManaAbility = false;
private String userIdStr;
protected Map<UUID, Set<UUID>> requestedHandPlayersList; // game -> players list
@ -36,10 +37,22 @@ public class UserData implements Serializable {
private int constructedRating;
private int limitedRating;
public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced,
boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps,
String flagName, boolean askMoveToGraveOrder, boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted,
boolean passPriorityCast, boolean passPriorityActivation, boolean autoOrderTrigger, boolean useFirstManaAbility, String userIdStr) {
public UserData(UserGroup userGroup,
int avatarId,
boolean showAbilityPickerForced,
boolean allowRequestShowHandCards,
boolean confirmEmptyManaPool,
UserSkipPrioritySteps userSkipPrioritySteps,
String flagName,
boolean askMoveToGraveOrder,
boolean manaPoolAutomatic,
boolean manaPoolAutomaticRestricted,
boolean passPriorityCast,
boolean passPriorityActivation,
boolean autoOrderTrigger,
boolean useSameSettingsForReplacementEffects,
boolean useFirstManaAbility,
String userIdStr) {
this.groupId = userGroup.getGroupId();
this.avatarId = avatarId;
this.showAbilityPickerForced = showAbilityPickerForced;
@ -53,6 +66,7 @@ public class UserData implements Serializable {
this.passPriorityCast = passPriorityCast;
this.passPriorityActivation = passPriorityActivation;
this.autoOrderTrigger = autoOrderTrigger;
this.useSameSettingsForReplacementEffects = useSameSettingsForReplacementEffects;
this.useFirstManaAbility = useFirstManaAbility;
this.matchHistory = "";
this.matchQuitRatio = 0;
@ -76,13 +90,31 @@ public class UserData implements Serializable {
this.passPriorityCast = userData.passPriorityCast;
this.passPriorityActivation = userData.passPriorityActivation;
this.autoOrderTrigger = userData.autoOrderTrigger;
this.useSameSettingsForReplacementEffects = userData.useSameSettingsForReplacementEffects;
this.useFirstManaAbility = userData.useFirstManaAbility;
this.userIdStr = userData.userIdStr;
// todo: why we don't update user stats here? => can't be updated from client side
}
public static UserData getDefaultUserDataView() {
return new UserData(UserGroup.DEFAULT, 0, false, false, true, new UserSkipPrioritySteps(), getDefaultFlagName(), false, true, true, false, false, false, false, "");
return new UserData(
UserGroup.DEFAULT,
0,
false,
false,
true,
new UserSkipPrioritySteps(),
getDefaultFlagName(),
false,
true,
true,
false,
false,
true,
true,
false,
""
);
}
public void setGroupId(int groupId) {
@ -115,10 +147,7 @@ public class UserData implements Serializable {
public boolean isAllowRequestHandToPlayer(UUID gameId, UUID requesterPlayerId) {
// once per game
boolean allowToPlayer = true;
if (requestedHandPlayersList.containsKey(gameId) && requestedHandPlayersList.get(gameId).contains(requesterPlayerId)) {
allowToPlayer = false;
}
boolean allowToPlayer = !requestedHandPlayersList.containsKey(gameId) || !requestedHandPlayersList.get(gameId).contains(requesterPlayerId);
return isAllowRequestHandToAll() && allowToPlayer;
}
@ -206,6 +235,10 @@ public class UserData implements Serializable {
return autoOrderTrigger;
}
public boolean isUseSameSettingsForReplacementEffects() {
return useSameSettingsForReplacementEffects;
}
public void setAutoOrderTrigger(boolean autoOrderTrigger) {
this.autoOrderTrigger = autoOrderTrigger;
}