diff --git a/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.java b/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.java index a17c509e7e2..0988125a6d3 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/PickMultiNumberDialog.java @@ -89,7 +89,7 @@ public class PickMultiNumberDialog extends MageDialog { labelList.add(label); JSpinner spinner = new JSpinner(); - spinner.setModel(new SpinnerNumberModel(0, messages.get(i).min, messages.get(i).max, 1)); + spinner.setModel(new SpinnerNumberModel(messages.get(i).min, messages.get(i).min, messages.get(i).max, 1)); spinnerC.weightx = 0.5; spinnerC.gridx = 1; spinnerC.gridy = i; 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 bed27151fc2..22041ea0c16 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 @@ -888,6 +888,14 @@ public class HumanPlayer extends PlayerImpl { } int amountTotal = target.getAmountTotal(game, source); + if (amountTotal == 0) { + return false; // nothing to distribute + } + MultiAmountType multiAmountType = source.getRule().contains("damage") ? MultiAmountType.DAMAGE : MultiAmountType.P1P1; + + // 601.2d. If the spell requires the player to divide or distribute an effect (such as damage or counters) + // among one or more targets, the player announces the division. + // Each of these targets must receive at least one of whatever is being divided. // Two steps logic: // 1. Select targets @@ -896,7 +904,7 @@ public class HumanPlayer extends PlayerImpl { // 1. Select targets while (canRespond()) { Set possibleTargetIds = target.possibleTargets(abilityControllerId, source, game); - boolean required = target.isRequired(source != null ? source.getSourceId() : null, game); + boolean required = target.isRequired(source.getSourceId(), game); if (possibleTargetIds.isEmpty() || target.getSize() >= target.getNumberOfTargets()) { required = false; @@ -928,9 +936,9 @@ public class HumanPlayer extends PlayerImpl { 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); + String multiType = multiAmountType == MultiAmountType.DAMAGE ? " to divide %d damage" : " to distribute %d counters"; + String message = target.getMessage() + String.format(multiType, amountTotal); + game.fireSelectTargetEvent(playerId, new MessageToClient(message, getRelatedObjectName(source, game)), possibleTargetIds, required, options); } waitForResponse(game); @@ -941,7 +949,9 @@ public class HumanPlayer extends PlayerImpl { if (target.contains(responseId)) { // unselect target.remove(responseId); - } else if (possibleTargetIds.contains(responseId) && target.canTarget(abilityControllerId, responseId, source, game)) { + } else if (possibleTargetIds.contains(responseId) + && target.canTarget(abilityControllerId, responseId, source, game) + && target.getSize() < amountTotal) { // select target.addTarget(responseId, source, game); } @@ -958,6 +968,26 @@ public class HumanPlayer extends PlayerImpl { // 2. Distribute amount between selected targets + // if only one target, it gets full amount, no possible choice + if (targets.size() == 1) { + target.setTargetAmount(targets.get(0), amountTotal, source, game); + return true; + } + + // if number of targets equal to amount, each get 1, no possible choice + if (targets.size() == amountTotal) { + for (UUID targetId : targets) { + target.setTargetAmount(targetId, 1, source, game); + } + return true; + } + + // should not be able to have more targets than amount, but in such case it's illegal + if (targets.size() > amountTotal) { + target.clearChosen(); + return false; + } + // prepare targets list with p/t or life stats (cause that's dialog used for damage distribute) List targetNames = new ArrayList<>(); for (UUID targetId : targets) { @@ -978,7 +1008,6 @@ public class HumanPlayer extends PlayerImpl { } } - MultiAmountType multiAmountType = source.toString().contains("counters") ? MultiAmountType.P1P1 : MultiAmountType.DAMAGE; // ask and assign new amount List targetValues = getMultiAmount(outcome, targetNames, 1, amountTotal, multiAmountType, game); for (int i = 0; i < targetValues.size(); i++) { diff --git a/Mage.Sets/src/mage/cards/k/KlauthUnrivaledAncient.java b/Mage.Sets/src/mage/cards/k/KlauthUnrivaledAncient.java index 58255b3ac81..f2caa447793 100644 --- a/Mage.Sets/src/mage/cards/k/KlauthUnrivaledAncient.java +++ b/Mage.Sets/src/mage/cards/k/KlauthUnrivaledAncient.java @@ -94,7 +94,7 @@ class KlauthUnrivaledAncientEffect extends OneShotEffect { .mapToInt(MageInt::getValue) .sum(); List manaList = player.getMultiAmount( - outcome, manaSymbols, attackerPower, attackerPower, MultiAmountType.MANA, game + outcome, manaSymbols, 0, attackerPower, MultiAmountType.MANA, game ); player.getManaPool().addMana( new KlauthUnrivaledAncientConditionalMana(manaList), game, source, true diff --git a/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java index b5db7799c1f..1377f7f949d 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/AddManaInAnyCombinationEffect.java @@ -132,7 +132,7 @@ public class AddManaInAnyCombinationEffect extends ManaEffect { // Ask player for color distribution int manaAmount = amount.calculate(game, source, this); - List manaList = player.getMultiAmount(this.outcome, manaStrings, manaAmount, manaAmount, MultiAmountType.MANA, game); + List manaList = player.getMultiAmount(this.outcome, manaStrings, 0, manaAmount, MultiAmountType.MANA, game); // Convert choices to mana for (int i = 0; i < size; i++) { diff --git a/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java b/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java index 0c237d970b9..25e4e56003e 100644 --- a/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java +++ b/Mage/src/main/java/mage/abilities/effects/mana/DynamicManaEffect.java @@ -159,7 +159,7 @@ public class DynamicManaEffect extends ManaEffect { manaStrings.add("B"); manaStrings.add("R"); manaStrings.add("G"); - List choices = controller.getMultiAmount(this.outcome, manaStrings, count, count, MultiAmountType.MANA, game); + List choices = controller.getMultiAmount(this.outcome, manaStrings, 0, count, MultiAmountType.MANA, game); computedMana.add(new Mana(choices.get(0), choices.get(1), choices.get(2), choices.get(3), choices.get(4), 0, 0, 0)); } } diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 0cf2a27fa2d..2965b1da565 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -758,8 +758,9 @@ public interface Player extends MageItem, Copyable { * @return List of integers with size equal to messages.size(). The sum of the integers is equal to max. */ default List getMultiAmount(Outcome outcome, List messages, int min, int max, MultiAmountType type, Game game) { - List constraints = messages.stream().map(s -> new MultiAmountMessage(s, 0, max)).collect(Collectors.toList()); - + List constraints = messages.stream() + .map(s -> new MultiAmountMessage(s, min, max)) + .collect(Collectors.toList()); return getMultiAmountWithIndividualConstraints(outcome, constraints, min, max, type, game); } @@ -768,8 +769,8 @@ public interface Player extends MageItem, Copyable { * * @param outcome AI hint * @param messages List of options to distribute amount among. Each option has a constraint on the min, max chosen for it - * @param totalMin Total minimum amount to be distributed - * @param totalMax Total amount to be distributed + * @param min Total minimum amount to be distributed + * @param max Total amount to be distributed * @param type MultiAmountType enum to set dialog options such as title and header * @param game Game * @return List of integers with size equal to messages.size(). The sum of the integers is equal to max.