From 1d20069ddf336506b7f72a117149592fa66c7913 Mon Sep 17 00:00:00 2001 From: Oleg Agafonov Date: Sat, 7 Dec 2019 01:48:39 +0400 Subject: [PATCH] * Fixed wrong selection count in choose ability mode dialog (cards like Planewide Celebration); --- .../src/mage/player/human/HumanPlayer.java | 40 ++++++++++--------- Mage/src/main/java/mage/abilities/Modes.java | 39 ++++++++++++++++-- 2 files changed, 58 insertions(+), 21 deletions(-) diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 415573b89f..e3254b3a02 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -710,9 +710,9 @@ public class HumanPlayer extends PlayerImpl { if (!isExecutingMacro()) { String selectedNames = target.getTargetedName(game); game.fireSelectTargetEvent(playerId, new MessageToClient(target.getMessage() - + "
Amount remaining: " + target.getAmountRemaining() - + (selectedNames.isEmpty() ? "" : ", selected: " + selectedNames), - getRelatedObjectName(source, game)), + + "
Amount remaining: " + target.getAmountRemaining() + + (selectedNames.isEmpty() ? "" : ", selected: " + selectedNames), + getRelatedObjectName(source, game)), target.possibleTargets(source == null ? null : source.getSourceId(), playerId, game), target.isRequired(source), getOptions(target, null)); @@ -725,7 +725,7 @@ public class HumanPlayer extends PlayerImpl { boolean removeMode = target.getTargets().contains(targetId) && chooseUse(outcome, "What do you want to do with " + (targetObject != null ? targetObject.getLogName() : "target") + "?", "", - "Remove from selected", "Add extra amount", source, game); + "Remove from selected", "Add extra amount", source, game); if (removeMode) { target.remove(targetId); @@ -862,9 +862,9 @@ public class HumanPlayer extends PlayerImpl { if (!skippedAtLeastOnce || (playerId.equals(game.getActivePlayerId()) && !controllingPlayer - .getUserData() - .getUserSkipPrioritySteps() - .isStopOnAllEndPhases())) { + .getUserData() + .getUserSkipPrioritySteps() + .isStopOnAllEndPhases())) { skippedAtLeastOnce = true; if (passWithManaPoolCheck(game)) { return false; @@ -896,9 +896,9 @@ public class HumanPlayer extends PlayerImpl { if (haveNewObjectsOnStack && (playerId.equals(game.getActivePlayerId()) && controllingPlayer - .getUserData() - .getUserSkipPrioritySteps() - .isStopOnStackNewObjects())) { + .getUserData() + .getUserSkipPrioritySteps() + .isStopOnStackNewObjects())) { // new objects on stack -- disable "pass until stack resolved" passedUntilStackResolved = false; } else { @@ -1235,8 +1235,8 @@ public class HumanPlayer extends PlayerImpl { if (passedAllTurns || passedUntilEndStepBeforeMyTurn || (!getControllingPlayersUserData(game) - .getUserSkipPrioritySteps() - .isStopOnDeclareAttackers() + .getUserSkipPrioritySteps() + .isStopOnDeclareAttackers() && (passedTurn || passedTurnSkipStack || passedUntilEndOfTurn @@ -1419,7 +1419,7 @@ public class HumanPlayer extends PlayerImpl { /** * Selects a defender for an attacker and adds the attacker to combat * - * @param defenders - list of possible defender + * @param defenders - list of possible defender * @param attackerId - UUID of attacker * @param game * @return @@ -1817,17 +1817,20 @@ public class HumanPlayer extends PlayerImpl { Map modeMap = new LinkedHashMap<>(); AvailableModes: for (Mode mode : modes.getAvailableModes(source, game)) { - int timesSelected = 0; + int timesSelected = modes.getSelectedStats(mode.getId()); for (UUID selectedModeId : modes.getSelectedModes()) { Mode selectedMode = modes.get(selectedModeId); if (mode.getId().equals(selectedMode.getId())) { + // mode selected if (modes.isEachModeMoreThanOnce()) { - timesSelected++; + // can select again } else { - continue AvailableModes; + // hide mode from dialog + continue AvailableModes; // TODO: test 2x cheat here } } } + if (mode.getTargets().canChoose(source.getSourceId(), source.getControllerId(), game)) { // and needed targets have to be available String modeText = mode.getEffects().getText(mode); if (obj != null) { @@ -1852,11 +1855,12 @@ public class HumanPlayer extends PlayerImpl { if (response.getUUID() != null) { for (Mode mode : modes.getAvailableModes(source, game)) { if (mode.getId().equals(response.getUUID())) { + // TODO: add checks on 2x selects (cheaters can rewrite client side code and select same mode multiple times) + // reason: wrong setup eachModeMoreThanOnce and eachModeOnlyOnce in many cards return mode; } } - } - else if (modes.getSelectedModes().size() >= modes.getMinModes()) { + } else if (modes.getSelectedModes().size() >= modes.getMinModes()) { /* let the player cancel mode selection if they do not need to select any further modes */ done = true; } diff --git a/Mage/src/main/java/mage/abilities/Modes.java b/Mage/src/main/java/mage/abilities/Modes.java index 77b8bb3afe..2417b494e3 100644 --- a/Mage/src/main/java/mage/abilities/Modes.java +++ b/Mage/src/main/java/mage/abilities/Modes.java @@ -19,13 +19,15 @@ import java.util.*; public class Modes extends LinkedHashMap { private Mode currentMode; // the current mode of the selected modes - private final List selectedModes = new ArrayList<>(); + private final List selectedModes = new ArrayList<>(); // all selected modes (this + duplicate) + private final Map duplicateModes = new LinkedHashMap<>(); // for 2x selects: copy mode and put it to duplicate list + private final Map duplicateToOriginalModeRefs = new LinkedHashMap<>(); // for 2x selects: stores ref from duplicate to original mode + private int minModes; private int maxModes; private TargetController modeChooser; private boolean eachModeMoreThanOnce; // each mode can be selected multiple times during one choice private boolean eachModeOnlyOnce; // state if each mode can be chosen only once as long as the source object exists - private final Map duplicateModes = new LinkedHashMap<>(); private OptionalAdditionalModeSourceCosts optionalAdditionalModeSourceCosts = null; // only set if costs have to be paid private Filter maxModesFilter = null; // calculates the max number of available modes private boolean isRandom = false; @@ -49,6 +51,8 @@ public class Modes extends LinkedHashMap { for (Map.Entry entry : modes.duplicateModes.entrySet()) { duplicateModes.put(entry.getKey(), entry.getValue().copy()); } + duplicateToOriginalModeRefs.putAll(modes.duplicateToOriginalModeRefs); + this.minModes = modes.minModes; this.maxModes = modes.maxModes; this.selectedModes.addAll(modes.getSelectedModes()); @@ -116,6 +120,32 @@ public class Modes extends LinkedHashMap { return selectedModes; } + public int getSelectedStats(UUID modeId) { + int count = 0; + if (this.selectedModes.contains(modeId)) { + + // single select + count++; + + // multiple select (all 2x select generate new duplicate mode) + UUID originalId; + if (this.duplicateModes.containsKey(modeId)) { + // modeId is duplicate + originalId = this.duplicateToOriginalModeRefs.get(modeId); + } else { + // modeId is original + originalId = modeId; + } + for (UUID id : this.duplicateToOriginalModeRefs.values()) { + if (id.equals(originalId)) { + count++; + } + } + } + + return count; + } + public void setMinModes(int minModes) { this.minModes = minModes; } @@ -168,6 +198,7 @@ public class Modes extends LinkedHashMap { if (this.size() > 1) { this.selectedModes.clear(); this.duplicateModes.clear(); + this.duplicateToOriginalModeRefs.clear(); if (this.isRandom) { List modes = getAvailableModes(source, game); this.addSelectedMode(modes.get(RandomUtil.nextInt(modes.size())).getId()); @@ -286,9 +317,11 @@ public class Modes extends LinkedHashMap { private void addSelectedMode(UUID modeId) { if (selectedModes.contains(modeId) && eachModeMoreThanOnce) { Mode duplicateMode = get(modeId).copy(); + UUID originalId = modeId; duplicateMode.setRandomId(); modeId = duplicateMode.getId(); duplicateModes.put(modeId, duplicateMode); + duplicateToOriginalModeRefs.put(duplicateMode.getId(), originalId); } this.selectedModes.add(modeId); @@ -329,7 +362,7 @@ public class Modes extends LinkedHashMap { nonAvailableModes = getAlreadySelectedModes(source, game); } for (Mode mode : this.values()) { - if (isEachModeOnlyOnce() && nonAvailableModes != null && nonAvailableModes.contains(mode.getId())) { + if (isEachModeOnlyOnce() && nonAvailableModes.contains(mode.getId())) { continue; } availableModes.add(mode);