mirror of
https://github.com/magefree/mage.git
synced 2025-12-26 21:42:07 -08:00
[READY FOR REVIEW] Implement a "multi-amount" dialog (#7528)
* Implemented chooseTargetAmount and new GUI dialog (distribute damage, distribute mana) * Added tests and AI support; * Test framework: added aliases support in TargetAmount dialogs; Co-authored-by: Oleg Agafonov <jaydi85@gmail.com>
This commit is contained in:
parent
042aa61ad4
commit
600cac6fc7
35 changed files with 1209 additions and 232 deletions
|
|
@ -9,6 +9,7 @@ import mage.abilities.dynamicvalue.DynamicValue;
|
|||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.abilities.mana.builder.ConditionalManaBuilder;
|
||||
import mage.choices.ChoiceColor;
|
||||
import mage.constants.MultiAmountType;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
|
@ -87,29 +88,26 @@ public class AddConditionalManaOfAnyColorEffect extends ManaEffect {
|
|||
if (game != null) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
ConditionalMana mana = null;
|
||||
int value = amount.calculate(game, source, this);
|
||||
ChoiceColor choice = new ChoiceColor(true);
|
||||
for (int i = 0; i < value; i++) {
|
||||
if (choice.getChoice() == null) {
|
||||
if (value > 0) {
|
||||
if (oneChoice || value == 1) {
|
||||
ChoiceColor choice = new ChoiceColor(true);
|
||||
controller.choose(outcome, choice, game);
|
||||
}
|
||||
if (choice.getChoice() == null) {
|
||||
return null;
|
||||
}
|
||||
if (oneChoice) {
|
||||
mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(value), source, game).build());
|
||||
break;
|
||||
} else {
|
||||
if (mana == null) {
|
||||
mana = new ConditionalMana(manaBuilder.setMana(choice.getMana(1), source, game).build());
|
||||
} else {
|
||||
mana.add(choice.getMana(1));
|
||||
if (choice.getChoice() == null) {
|
||||
return null;
|
||||
}
|
||||
choice.clearChoice();
|
||||
return new ConditionalMana(manaBuilder.setMana(choice.getMana(value), source, game).build());
|
||||
}
|
||||
List<String> manaStrings = new ArrayList<>(5);
|
||||
manaStrings.add("W");
|
||||
manaStrings.add("U");
|
||||
manaStrings.add("B");
|
||||
manaStrings.add("R");
|
||||
manaStrings.add("G");
|
||||
List<Integer> choices = controller.getMultiAmount(this.outcome, manaStrings, 0, value, MultiAmountType.MANA, game);
|
||||
Mana mana = new Mana(choices.get(0), choices.get(1), choices.get(2), choices.get(3), choices.get(4), 0, 0, 0);
|
||||
return new ConditionalMana(manaBuilder.setMana(mana, source, game).build());
|
||||
}
|
||||
return mana;
|
||||
}
|
||||
}
|
||||
return new Mana();
|
||||
|
|
|
|||
|
|
@ -1,11 +1,5 @@
|
|||
package mage.abilities.effects.mana;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import mage.Mana;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
|
|
@ -13,10 +7,14 @@ import mage.abilities.dynamicvalue.common.StaticValue;
|
|||
import mage.abilities.mana.ManaOptions;
|
||||
import mage.constants.ColoredManaSymbol;
|
||||
import mage.constants.ManaType;
|
||||
import mage.constants.MultiAmountType;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* @author LevelX2
|
||||
*/
|
||||
|
|
@ -27,7 +25,13 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
|
|||
private final DynamicValue netAmount;
|
||||
|
||||
public AddManaInAnyCombinationEffect(int amount) {
|
||||
this(StaticValue.get(amount), StaticValue.get(amount), ColoredManaSymbol.B, ColoredManaSymbol.U, ColoredManaSymbol.R, ColoredManaSymbol.W, ColoredManaSymbol.G);
|
||||
this(StaticValue.get(amount), StaticValue.get(amount),
|
||||
ColoredManaSymbol.W,
|
||||
ColoredManaSymbol.U,
|
||||
ColoredManaSymbol.B,
|
||||
ColoredManaSymbol.R,
|
||||
ColoredManaSymbol.G
|
||||
);
|
||||
}
|
||||
|
||||
public AddManaInAnyCombinationEffect(int amount, ColoredManaSymbol... coloredManaSymbols) {
|
||||
|
|
@ -106,26 +110,20 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
|
|||
public Mana produceMana(Game game, Ability source) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player != null) {
|
||||
int size = manaSymbols.size();
|
||||
Mana mana = new Mana();
|
||||
int amountOfManaLeft = amount.calculate(game, source, this);
|
||||
int maxAmount = amountOfManaLeft;
|
||||
|
||||
while (amountOfManaLeft > 0 && player.canRespond()) {
|
||||
for (ColoredManaSymbol coloredManaSymbol : manaSymbols) {
|
||||
int number = player.getAmount(0, amountOfManaLeft, "Distribute mana by color (" + mana.count()
|
||||
+ " of " + maxAmount + " done). How many <b>" + coloredManaSymbol.getColorHtmlName() + "</b> mana to add (enter 0 to pass to next color)?", game);
|
||||
if (number > 0) {
|
||||
for (int i = 0; i < number; i++) {
|
||||
mana.add(new Mana(coloredManaSymbol));
|
||||
}
|
||||
amountOfManaLeft -= number;
|
||||
}
|
||||
if (amountOfManaLeft == 0) {
|
||||
break;
|
||||
}
|
||||
List<String> manaStrings = new ArrayList<>(size);
|
||||
for (ColoredManaSymbol coloredManaSymbol : manaSymbols) {
|
||||
manaStrings.add(coloredManaSymbol.toString());
|
||||
}
|
||||
List<Integer> manaList = player.getMultiAmount(this.outcome, manaStrings, 0, amount.calculate(game, source, this), MultiAmountType.MANA, game);
|
||||
for (int i = 0; i < size; i++) {
|
||||
ColoredManaSymbol coloredManaSymbol = manaSymbols.get(i);
|
||||
int amount = manaList.get(i);
|
||||
for (int j = 0; j < amount; j++) {
|
||||
mana.add(new Mana(coloredManaSymbol));
|
||||
}
|
||||
}
|
||||
|
||||
return mana;
|
||||
}
|
||||
return null;
|
||||
|
|
@ -134,7 +132,7 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
|
|||
@Override
|
||||
public Set<ManaType> getProducableManaTypes(Game game, Ability source) {
|
||||
Set<ManaType> manaTypes = new HashSet<>();
|
||||
for(ColoredManaSymbol coloredManaSymbol: manaSymbols) {
|
||||
for (ColoredManaSymbol coloredManaSymbol : manaSymbols) {
|
||||
if (coloredManaSymbol.equals(ColoredManaSymbol.B)) {
|
||||
manaTypes.add(ManaType.BLACK);
|
||||
}
|
||||
|
|
@ -156,7 +154,8 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
|
|||
|
||||
private String setText() {
|
||||
StringBuilder sb = new StringBuilder("Add ");
|
||||
sb.append(CardUtil.numberToText(amount.toString()));
|
||||
String amountString = CardUtil.numberToText(amount.toString());
|
||||
sb.append(amountString);
|
||||
sb.append(" mana in any combination of ");
|
||||
if (manaSymbols.size() == 5) {
|
||||
sb.append("colors");
|
||||
|
|
@ -170,6 +169,10 @@ public class AddManaInAnyCombinationEffect extends ManaEffect {
|
|||
sb.append('{').append(coloredManaSymbol.toString()).append('}');
|
||||
}
|
||||
}
|
||||
if (amountString.equals("X")) {
|
||||
sb.append(", where X is ");
|
||||
sb.append(amount.getMessage());
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import mage.abilities.Ability;
|
|||
import mage.abilities.Mode;
|
||||
import mage.abilities.dynamicvalue.DynamicValue;
|
||||
import mage.choices.ChoiceColor;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.MultiAmountType;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
|
|
@ -143,18 +143,23 @@ public class DynamicManaEffect extends ManaEffect {
|
|||
computedMana.setColorless(count);
|
||||
} else if (baseMana.getAny() > 0) {
|
||||
Player controller = game.getPlayer(source.getControllerId());
|
||||
if (controller != null) {
|
||||
ChoiceColor choiceColor = new ChoiceColor(true);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (!choiceColor.isChosen()) {
|
||||
if (!controller.choose(Outcome.Benefit, choiceColor, game)) {
|
||||
return computedMana;
|
||||
}
|
||||
}
|
||||
choiceColor.increaseMana(computedMana);
|
||||
if (!oneChoice) {
|
||||
choiceColor.clearChoice();
|
||||
if (controller != null && count > 0) {
|
||||
if (oneChoice || count == 1) {
|
||||
ChoiceColor choice = new ChoiceColor(true);
|
||||
controller.choose(outcome, choice, game);
|
||||
if (choice.getChoice() == null) {
|
||||
return computedMana;
|
||||
}
|
||||
computedMana.add(choice.getMana(count));
|
||||
} else {
|
||||
List<String> manaStrings = new ArrayList<>(5);
|
||||
manaStrings.add("W");
|
||||
manaStrings.add("U");
|
||||
manaStrings.add("B");
|
||||
manaStrings.add("R");
|
||||
manaStrings.add("G");
|
||||
List<Integer> 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));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -56,6 +56,9 @@ public class Choices extends ArrayList<Choice> {
|
|||
public boolean choose(Game game, Ability source) {
|
||||
if (this.size() > 0) {
|
||||
Player player = game.getPlayer(source.getControllerId());
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
while (!isChosen()) {
|
||||
Choice choice = this.getUnchosen().get(0);
|
||||
if (!player.choose(outcome, choice, game)) {
|
||||
|
|
|
|||
108
Mage/src/main/java/mage/constants/MultiAmountType.java
Normal file
108
Mage/src/main/java/mage/constants/MultiAmountType.java
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
package mage.constants;
|
||||
|
||||
import com.google.common.collect.Iterables;
|
||||
import mage.util.CardUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
public enum MultiAmountType {
|
||||
|
||||
MANA("Add mana", "Distribute mana among colors"),
|
||||
DAMAGE("Assign damage", "Assign damage among targets");
|
||||
|
||||
private final String title;
|
||||
private final String header;
|
||||
|
||||
MultiAmountType(String title, String header) {
|
||||
this.title = title;
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public String getHeader() {
|
||||
return header;
|
||||
}
|
||||
|
||||
public static List<Integer> prepareDefaltValues(int count, int min, int max) {
|
||||
// default values must be assigned from first to last by minimum values
|
||||
List<Integer> res = new ArrayList<>();
|
||||
if (count == 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// fill list
|
||||
IntStream.range(0, count).forEach(i -> res.add(0));
|
||||
|
||||
// fill values
|
||||
if (min > 0) {
|
||||
res.set(0, min);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static List<Integer> prepareMaxValues(int count, int min, int max) {
|
||||
// fill max values as much as possible
|
||||
List<Integer> res = new ArrayList<>();
|
||||
if (count == 0) {
|
||||
return res;
|
||||
}
|
||||
|
||||
// fill list
|
||||
int startingValue = max / count;
|
||||
IntStream.range(0, count).forEach(i -> res.add(startingValue));
|
||||
|
||||
// fill values
|
||||
// from first to last until complete
|
||||
List<Integer> resIndexes = new ArrayList<>(res.size());
|
||||
IntStream.range(0, res.size()).forEach(resIndexes::add);
|
||||
// infinite iterator (no needs with starting values use, but can be used later for different logic)
|
||||
Iterator<Integer> resIterator = Iterables.cycle(resIndexes).iterator();
|
||||
int valueInc = 1;
|
||||
int valueTotal = startingValue * count;
|
||||
while (valueTotal < max) {
|
||||
int currentIndex = resIterator.next();
|
||||
int newValue = CardUtil.overflowInc(res.get(currentIndex), valueInc);
|
||||
res.set(currentIndex, newValue);
|
||||
valueTotal += valueInc;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
public static boolean isGoodValues(List<Integer> values, int count, int min, int max) {
|
||||
if (values.size() != count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
int currentSum = values.stream().mapToInt(i -> i).sum();
|
||||
return currentSum >= min && currentSum <= max;
|
||||
}
|
||||
|
||||
public static List<Integer> parseAnswer(String answerToParse, int count, int min, int max, boolean returnDefaultOnError) {
|
||||
List<Integer> res = new ArrayList<>();
|
||||
|
||||
// parse
|
||||
String normalValue = answerToParse.trim();
|
||||
if (!normalValue.isEmpty()) {
|
||||
Arrays.stream(normalValue.split(" ")).forEach(valueStr -> {
|
||||
res.add(CardUtil.parseIntWithDefault(valueStr, 0));
|
||||
});
|
||||
}
|
||||
|
||||
// data check
|
||||
if (returnDefaultOnError && !isGoodValues(res, count, min, max)) {
|
||||
// on broken data - return default
|
||||
return prepareDefaltValues(count, min, max);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
@ -277,6 +277,8 @@ public interface Game extends MageItem, Serializable {
|
|||
|
||||
void fireGetAmountEvent(UUID playerId, String message, int min, int max);
|
||||
|
||||
void fireGetMultiAmountEvent(UUID playerId, List<String> messages, int min, int max, Map<String, Serializable> options);
|
||||
|
||||
void fireChoosePileEvent(UUID playerId, String message, List<? extends Card> pile1, List<? extends Card> pile2);
|
||||
|
||||
void fireInformEvent(String message);
|
||||
|
|
|
|||
|
|
@ -2515,6 +2515,14 @@ public abstract class GameImpl implements Game, Serializable {
|
|||
playerQueryEventSource.amount(playerId, message, min, max);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireGetMultiAmountEvent(UUID playerId, List<String> messages, int min, int max, Map<String, Serializable> options) {
|
||||
if (simulation) {
|
||||
return;
|
||||
}
|
||||
playerQueryEventSource.multiAmount(playerId, messages, min, max, options);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fireChooseChoiceEvent(UUID playerId, Choice choice) {
|
||||
if (simulation) {
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
|||
PLAY_MANA,
|
||||
PLAY_X_MANA,
|
||||
AMOUNT,
|
||||
MULTI_AMOUNT,
|
||||
PICK_CARD,
|
||||
CONSTRUCT,
|
||||
CHOOSE_PILE,
|
||||
|
|
@ -58,8 +59,11 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
|||
private List<? extends Card> pile1;
|
||||
private List<? extends Card> pile2;
|
||||
private Choice choice;
|
||||
private List<String> messages;
|
||||
|
||||
private PlayerQueryEvent(UUID playerId, String message, List<? extends Ability> abilities, Set<String> choices, Set<UUID> targets, Cards cards, QueryType queryType, int min, int max, boolean required, Map<String, Serializable> options) {
|
||||
private PlayerQueryEvent(UUID playerId, String message, List<? extends Ability> abilities, Set<String> choices,
|
||||
Set<UUID> targets, Cards cards, QueryType queryType, int min, int max, boolean required,
|
||||
Map<String, Serializable> options, List<String> messages) {
|
||||
super(playerId);
|
||||
this.queryType = queryType;
|
||||
this.message = message;
|
||||
|
|
@ -77,6 +81,7 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
|||
this.options = options;
|
||||
}
|
||||
this.options.put("queryType", queryType);
|
||||
this.messages = messages;
|
||||
}
|
||||
|
||||
private PlayerQueryEvent(UUID playerId, String message, List<Card> booster, QueryType queryType, int time) {
|
||||
|
|
@ -143,7 +148,7 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
|||
}
|
||||
options.put("originalId", source.getOriginalId());
|
||||
}
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.ASK, 0, 0, false, options);
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.ASK, 0, 0, false, options, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent chooseAbilityEvent(UUID playerId, String message, String objectName, List<? extends ActivatedAbility> choices) {
|
||||
|
|
@ -152,7 +157,7 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
|||
nameAsSet = new HashSet<>();
|
||||
nameAsSet.add(objectName);
|
||||
}
|
||||
return new PlayerQueryEvent(playerId, message, choices, nameAsSet, null, null, QueryType.CHOOSE_ABILITY, 0, 0, false, null);
|
||||
return new PlayerQueryEvent(playerId, message, choices, nameAsSet, null, null, QueryType.CHOOSE_ABILITY, 0, 0, false, null, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent choosePileEvent(UUID playerId, String message, List<? extends Card> pile1, List<? extends Card> pile2) {
|
||||
|
|
@ -168,19 +173,19 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
|||
}
|
||||
|
||||
public static PlayerQueryEvent targetEvent(UUID playerId, String message, Set<UUID> targets, boolean required) {
|
||||
return new PlayerQueryEvent(playerId, message, null, null, targets, null, QueryType.PICK_TARGET, 0, 0, required, null);
|
||||
return new PlayerQueryEvent(playerId, message, null, null, targets, null, QueryType.PICK_TARGET, 0, 0, required, null, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent targetEvent(UUID playerId, String message, Set<UUID> targets, boolean required, Map<String, Serializable> options) {
|
||||
return new PlayerQueryEvent(playerId, message, null, null, targets, null, QueryType.PICK_TARGET, 0, 0, required, options);
|
||||
return new PlayerQueryEvent(playerId, message, null, null, targets, null, QueryType.PICK_TARGET, 0, 0, required, options, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent targetEvent(UUID playerId, String message, Cards cards, boolean required, Map<String, Serializable> options) {
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, cards, QueryType.PICK_TARGET, 0, 0, required, options);
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, cards, QueryType.PICK_TARGET, 0, 0, required, options, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent targetEvent(UUID playerId, String message, List<TriggeredAbility> abilities) {
|
||||
return new PlayerQueryEvent(playerId, message, abilities, null, null, null, QueryType.PICK_ABILITY, 0, 0, true, null);
|
||||
return new PlayerQueryEvent(playerId, message, abilities, null, null, null, QueryType.PICK_ABILITY, 0, 0, true, null, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent targetEvent(UUID playerId, String message, List<Permanent> perms, boolean required) {
|
||||
|
|
@ -188,23 +193,27 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
|||
}
|
||||
|
||||
public static PlayerQueryEvent selectEvent(UUID playerId, String message) {
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.SELECT, 0, 0, false, null);
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.SELECT, 0, 0, false, null, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent selectEvent(UUID playerId, String message, Map<String, Serializable> options) {
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.SELECT, 0, 0, false, options);
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.SELECT, 0, 0, false, options, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent playManaEvent(UUID playerId, String message, Map<String, Serializable> options) {
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_MANA, 0, 0, false, options);
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_MANA, 0, 0, false, options, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent playXManaEvent(UUID playerId, String message) {
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_X_MANA, 0, 0, false, null);
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.PLAY_X_MANA, 0, 0, false, null, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent amountEvent(UUID playerId, String message, int min, int max) {
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.AMOUNT, min, max, false, null);
|
||||
return new PlayerQueryEvent(playerId, message, null, null, null, null, QueryType.AMOUNT, min, max, false, null, null);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent multiAmountEvent(UUID playerId, List<String> messages, int min, int max, Map<String, Serializable> options) {
|
||||
return new PlayerQueryEvent(playerId, null, null, null, null, null, QueryType.MULTI_AMOUNT, min, max, false, options, messages);
|
||||
}
|
||||
|
||||
public static PlayerQueryEvent pickCard(UUID playerId, String message, List<Card> booster, int time) {
|
||||
|
|
@ -287,4 +296,7 @@ public class PlayerQueryEvent extends EventObject implements ExternalEvent, Seri
|
|||
return choice;
|
||||
}
|
||||
|
||||
public List<String> getMessages() {
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,6 +84,10 @@ public class PlayerQueryEventSource implements EventSource<PlayerQueryEvent>, Se
|
|||
dispatcher.fireEvent(PlayerQueryEvent.amountEvent(playerId, message, min, max));
|
||||
}
|
||||
|
||||
public void multiAmount(UUID playerId, List<String> messages, int min, int max, Map<String, Serializable> options) {
|
||||
dispatcher.fireEvent(PlayerQueryEvent.multiAmountEvent(playerId, messages, min, max, options));
|
||||
}
|
||||
|
||||
public void chooseChoice(UUID playerId, Choice choice) {
|
||||
dispatcher.fireEvent(PlayerQueryEvent.chooseChoiceEvent(playerId, choice));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -708,6 +708,19 @@ public interface Player extends MageItem, Copyable<Player> {
|
|||
|
||||
int getAmount(int min, int max, String message, Game game);
|
||||
|
||||
/**
|
||||
* Player distributes amount among multiple options
|
||||
*
|
||||
* @param outcome AI hint
|
||||
* @param messages List of options to distribute amount among
|
||||
* @param min Minimum value per option
|
||||
* @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.
|
||||
*/
|
||||
List<Integer> getMultiAmount(Outcome outcome, List<String> messages, int min, int max, MultiAmountType type, Game game);
|
||||
|
||||
void sideboard(Match match, Deck deck);
|
||||
|
||||
void construct(Tournament tournament, Deck deck);
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import mage.cards.Card;
|
|||
import mage.cards.Cards;
|
||||
import mage.cards.decks.Deck;
|
||||
import mage.choices.Choice;
|
||||
import mage.constants.MultiAmountType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.RangeOfInfluence;
|
||||
import mage.filter.FilterMana;
|
||||
|
|
@ -205,6 +206,11 @@ public class StubPlayer extends PlayerImpl implements Player {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> getMultiAmount(Outcome outcome, List<String> messages, int min, int max, MultiAmountType type, Game game) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sideboard(Match match, Deck deck) {
|
||||
|
||||
|
|
|
|||
|
|
@ -147,4 +147,8 @@ public interface Target extends Serializable {
|
|||
String getChooseHint();
|
||||
|
||||
void setEventReporting(boolean shouldReport);
|
||||
|
||||
int getSize();
|
||||
|
||||
boolean contains(UUID targetId);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import mage.abilities.dynamicvalue.DynamicValue;
|
|||
import mage.abilities.dynamicvalue.common.StaticValue;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
|
@ -59,7 +60,7 @@ public abstract class TargetAmount extends TargetImpl {
|
|||
public void clearChosen() {
|
||||
super.clearChosen();
|
||||
amountWasSet = false;
|
||||
// remainingAmount = amount;
|
||||
// remainingAmount = amount; // auto-calced on target remove
|
||||
}
|
||||
|
||||
public void setAmountDefinition(DynamicValue amount) {
|
||||
|
|
@ -71,6 +72,9 @@ public abstract class TargetAmount extends TargetImpl {
|
|||
amountWasSet = true;
|
||||
}
|
||||
|
||||
public int getAmountTotal(Game game, Ability source) {
|
||||
return amount.calculate(game, source, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTarget(UUID id, int amount, Ability source, Game game, boolean skipEvent) {
|
||||
|
|
@ -92,17 +96,25 @@ public abstract class TargetAmount extends TargetImpl {
|
|||
|
||||
@Override
|
||||
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!amountWasSet) {
|
||||
setAmount(source, game);
|
||||
}
|
||||
chosen = isChosen();
|
||||
while (remainingAmount > 0) {
|
||||
if (!player.canRespond()) {
|
||||
return chosen;
|
||||
}
|
||||
if (!getTargetController(game, playerId).chooseTargetAmount(outcome, this, source, game)) {
|
||||
return chosen;
|
||||
}
|
||||
chosen = isChosen();
|
||||
}
|
||||
return chosen = true;
|
||||
return chosen;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -163,4 +175,12 @@ public abstract class TargetAmount extends TargetImpl {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTargetAmount(UUID targetId, int amount, Ability source, Game game) {
|
||||
if (!amountWasSet) {
|
||||
setAmount(source, game);
|
||||
}
|
||||
remainingAmount -= (amount - this.getTargetAmount(targetId));
|
||||
this.setTargetAmount(targetId, amount, game);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -278,51 +278,60 @@ public abstract class TargetImpl implements Target {
|
|||
|
||||
@Override
|
||||
public boolean choose(Outcome outcome, UUID playerId, UUID sourceId, Game game) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player == null) {
|
||||
Player targetController = getTargetController(game, playerId);
|
||||
if (targetController == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (!isChosen() && !doneChosing()) {
|
||||
if (!player.canRespond()) {
|
||||
return chosen = targets.size() >= getNumberOfTargets();
|
||||
chosen = targets.size() >= getNumberOfTargets();
|
||||
do {
|
||||
if (!targetController.canRespond()) {
|
||||
return chosen;
|
||||
}
|
||||
chosen = targets.size() >= getNumberOfTargets();
|
||||
if (!player.choose(outcome, this, sourceId, game)) {
|
||||
if (!targetController.choose(outcome, this, sourceId, game)) {
|
||||
return chosen;
|
||||
}
|
||||
chosen = targets.size() >= getNumberOfTargets();
|
||||
}
|
||||
return chosen = true;
|
||||
} while (!isChosen() && !doneChosing());
|
||||
return chosen;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean chooseTarget(Outcome outcome, UUID playerId, Ability source, Game game) {
|
||||
Player player = game.getPlayer(playerId);
|
||||
if (player == null) {
|
||||
Player targetController = getTargetController(game, playerId);
|
||||
if (targetController == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<UUID> possibleTargets = new ArrayList<>(possibleTargets(source.getSourceId(), playerId, game));
|
||||
while (!isChosen() && !doneChosing()) {
|
||||
if (!player.canRespond()) {
|
||||
return chosen = targets.size() >= getNumberOfTargets();
|
||||
|
||||
chosen = targets.size() >= getNumberOfTargets();
|
||||
do {
|
||||
if (!targetController.canRespond()) {
|
||||
return chosen;
|
||||
}
|
||||
chosen = targets.size() >= getNumberOfTargets();
|
||||
if (isRandom()) {
|
||||
if (!possibleTargets.isEmpty()) {
|
||||
int index = RandomUtil.nextInt(possibleTargets.size());
|
||||
this.addTarget(possibleTargets.get(index), source, game);
|
||||
possibleTargets.remove(index);
|
||||
} else {
|
||||
if (possibleTargets.isEmpty()) {
|
||||
return chosen;
|
||||
}
|
||||
} else if (!getTargetController(game, playerId).chooseTarget(outcome, this, source, game)) {
|
||||
// find valid target
|
||||
while (!possibleTargets.isEmpty()) {
|
||||
int index = RandomUtil.nextInt(possibleTargets.size());
|
||||
if (this.canTarget(playerId, possibleTargets.get(index), source, game)) {
|
||||
this.addTarget(possibleTargets.get(index), source, game);
|
||||
possibleTargets.remove(index);
|
||||
break;
|
||||
} else {
|
||||
possibleTargets.remove(index);
|
||||
}
|
||||
}
|
||||
} else if (!targetController.chooseTarget(outcome, this, source, game)) {
|
||||
return chosen;
|
||||
}
|
||||
chosen = targets.size() >= getNumberOfTargets();
|
||||
}
|
||||
return chosen = true;
|
||||
} while (!isChosen() && !doneChosing());
|
||||
|
||||
return chosen;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -574,4 +583,14 @@ public abstract class TargetImpl implements Target {
|
|||
public void setEventReporting(boolean shouldReport) {
|
||||
this.shouldReportEvents = shouldReport;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return targets.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(UUID targetId) {
|
||||
return targets.containsKey(targetId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import mage.abilities.Ability;
|
|||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.game.events.GameEvent;
|
||||
import mage.players.Player;
|
||||
import mage.target.targetpointer.*;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
|
@ -65,6 +66,7 @@ public class Targets extends ArrayList<Target> {
|
|||
if (!canChoose(source.getSourceId(), playerId, game)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//int state = game.bookmarkState();
|
||||
while (!isChosen()) {
|
||||
Target target = this.getUnchosen().get(0);
|
||||
|
|
|
|||
|
|
@ -72,21 +72,19 @@ public class TargetCardInLibrary extends TargetCard {
|
|||
}
|
||||
cards.sort(Comparator.comparing(MageObject::getName));
|
||||
Cards cardsId = new CardsImpl();
|
||||
cards.forEach((card) -> {
|
||||
cardsId.add(card);
|
||||
});
|
||||
cards.forEach(cardsId::add);
|
||||
|
||||
while (!isChosen() && !doneChosing()) {
|
||||
chosen = targets.size() >= getMinNumberOfTargets();
|
||||
do {
|
||||
if (!player.canRespond()) {
|
||||
return chosen = targets.size() >= minNumberOfTargets;
|
||||
return chosen;
|
||||
}
|
||||
chosen = targets.size() >= minNumberOfTargets;
|
||||
if (!player.chooseTarget(outcome, cardsId, this, null, game)) {
|
||||
return chosen;
|
||||
}
|
||||
chosen = targets.size() >= minNumberOfTargets;
|
||||
}
|
||||
return chosen = true;
|
||||
chosen = targets.size() >= getMinNumberOfTargets();
|
||||
} while (!isChosen() && !doneChosing());
|
||||
return chosen;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1271,6 +1271,16 @@ public final class CardUtil {
|
|||
return res;
|
||||
}
|
||||
|
||||
public static int parseIntWithDefault(String value, int defaultValue) {
|
||||
int res;
|
||||
try {
|
||||
res = Integer.parseInt(value);
|
||||
} catch(NumberFormatException ex) {
|
||||
res = defaultValue;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Find mapping from original to copied card (e.g. map original left side with copied left side)
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue