forked from External/mage
other: reworked target selection: (#13638)
- WIP: AI and multi targets, human and X=0 use cases, human and impossible targets use cases;
- improved stability and shared logic (related to #13606, #11134, #11666, continue from a53eb66b58, close #13617, close #13613);
- improved test logs and debug info to show more target info on errors;
- improved test framework to support multiple addTarget calls;
- improved test framework to find bad commands order for targets (related to #11666);
- fixed game freezes on auto-choice usages with disconnected or under control players (related to #11285);
- gui, game: fixed that player doesn't mark avatar as selected/green in "up to" targeting;
- gui, game: fixed small font in some popup messages on big screens (related to #969);
- gui, game: added min targets info for target selection dialog;
- for devs: added new cheat option to call and test any game dialog (define own dialogs, targets, etc in HumanDialogsTester);
- for devs: now tests require complete an any or up to target selection by addTarget + TestPlayer.TARGET_SKIP or setChoice + TestPlayer.CHOICE_SKIP (if not all max/possible targets used);
- for devs: added detail targets info for activate/trigger/cast, can be useful to debug unit tests, auto-choose or AI (see DebugUtil.GAME_SHOW_CHOOSE_TARGET_LOGS)
This commit is contained in:
parent
80d62727e1
commit
133e4fe425
84 changed files with 2737 additions and 743 deletions
|
|
@ -0,0 +1,201 @@
|
|||
package mage.utils.testers;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.choices.Choice;
|
||||
import mage.choices.ChoiceHintType;
|
||||
import mage.choices.ChoiceImpl;
|
||||
import mage.constants.Outcome;
|
||||
import mage.game.Game;
|
||||
import mage.players.Player;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Part of testable game dialogs
|
||||
* <p>
|
||||
* Helper class to create additional options in cheat menu - allow to test any game dialogs with any settings
|
||||
* Allow to call game dialogs from you or from your opponent, e.g. from AI player
|
||||
* <p>
|
||||
* All existing human's choose dialogs (search by waitForResponse):
|
||||
* <p>
|
||||
* Support of single dialogs (can be called inside effects):
|
||||
* [x] choose(target)
|
||||
* [x] choose(cards)
|
||||
* [x] choose(choice)
|
||||
* [x] chooseTarget(target)
|
||||
* [x] chooseTarget(cards)
|
||||
* [x] chooseTargetAmount
|
||||
* [x] chooseUse
|
||||
* [x] choosePile
|
||||
* [ ] announceXMana // TODO: implement
|
||||
* [ ] announceXCost // TODO: implement
|
||||
* [ ] getAmount // TODO: implement
|
||||
* [ ] getMultiAmountWithIndividualConstraints // TODO: implement
|
||||
* <p>
|
||||
* Support of priority dialogs (can be called by game engine, some can be implemented in theory):
|
||||
* --- priority
|
||||
* --- playManaHandling
|
||||
* --- activateSpecialAction
|
||||
* --- activateAbility
|
||||
* --- chooseAbilityForCast
|
||||
* --- chooseLandOrSpellAbility
|
||||
* --- chooseMode
|
||||
* --- chooseMulligan
|
||||
* --- chooseReplacementEffect
|
||||
* --- chooseTriggeredAbility
|
||||
* --- selectAttackers
|
||||
* --- selectBlockers
|
||||
* --- selectCombatGroup (part of selectBlockers)
|
||||
* <p>
|
||||
* Support of outdated dialogs (not used anymore)
|
||||
* --- announceRepetitions (part of removed macro feature)
|
||||
*
|
||||
* @author JayDi85
|
||||
*/
|
||||
public class TestableDialogsRunner {
|
||||
|
||||
private final List<TestableDialog> dialogs = new ArrayList<>();
|
||||
|
||||
static final int LAST_SELECTED_GROUP_ID = 997;
|
||||
static final int LAST_SELECTED_DIALOG_ID = 998;
|
||||
|
||||
// for better UX - save last selected options, so can return to it later
|
||||
// it's ok to have it global, cause test mode for local and single user environment
|
||||
static String lastSelectedGroup = null;
|
||||
static TestableDialog lastSelectedDialog = null;
|
||||
|
||||
|
||||
public TestableDialogsRunner() {
|
||||
ChooseTargetTestableDialog.register(this);
|
||||
ChooseCardsTestableDialog.register(this);
|
||||
ChooseUseTestableDialog.register(this);
|
||||
ChooseChoiceTestableDialog.register(this);
|
||||
ChoosePileTestableDialog.register(this);
|
||||
ChooseAmountTestableDialog.register(this);
|
||||
}
|
||||
|
||||
void registerDialog(TestableDialog dialog) {
|
||||
this.dialogs.add(dialog);
|
||||
}
|
||||
|
||||
public void selectAndShowTestableDialog(Player player, Ability source, Game game, Player opponent) {
|
||||
// select group or fast links
|
||||
List<String> groups = this.dialogs.stream()
|
||||
.map(TestableDialog::getGroup)
|
||||
.distinct()
|
||||
.sorted()
|
||||
.collect(Collectors.toList());
|
||||
Choice choice = prepareSelectGroupChoice(groups);
|
||||
player.choose(Outcome.Benefit, choice, game);
|
||||
String needGroup = null;
|
||||
TestableDialog needDialog = null;
|
||||
if (choice.getChoiceKey() != null) {
|
||||
int needIndex = Integer.parseInt(choice.getChoiceKey());
|
||||
if (needIndex == LAST_SELECTED_GROUP_ID && lastSelectedGroup != null) {
|
||||
// fast link to group
|
||||
needGroup = lastSelectedGroup;
|
||||
} else if (needIndex == LAST_SELECTED_DIALOG_ID && lastSelectedDialog != null) {
|
||||
// fast link to dialog
|
||||
needGroup = lastSelectedDialog.getGroup();
|
||||
needDialog = lastSelectedDialog;
|
||||
} else if (needIndex < groups.size()) {
|
||||
// group
|
||||
needGroup = groups.get(needIndex);
|
||||
}
|
||||
}
|
||||
if (needGroup == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// select dialog
|
||||
if (needDialog == null) {
|
||||
choice = prepareSelectDialogChoice(needGroup);
|
||||
player.choose(Outcome.Benefit, choice, game);
|
||||
if (choice.getChoiceKey() != null) {
|
||||
int needIndex = Integer.parseInt(choice.getChoiceKey());
|
||||
if (needIndex < this.dialogs.size()) {
|
||||
needDialog = this.dialogs.get(needIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (needDialog == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// all fine, can show it and finish
|
||||
lastSelectedGroup = needGroup;
|
||||
lastSelectedDialog = needDialog;
|
||||
List<String> resInfo = needDialog.showDialog(player, source, game, opponent);
|
||||
needDialog.showResult(player, game, String.join("<br>", resInfo));
|
||||
}
|
||||
|
||||
private Choice prepareSelectGroupChoice(List<String> groups) {
|
||||
// try to choose group or fast links
|
||||
|
||||
Choice choice = new ChoiceImpl(false);
|
||||
choice.setMessage("Choose dialogs group to run");
|
||||
|
||||
// main groups
|
||||
int recNumber = 0;
|
||||
for (int i = 0; i < groups.size(); i++) {
|
||||
recNumber++;
|
||||
String group = groups.get(i);
|
||||
choice.withItem(
|
||||
String.valueOf(i),
|
||||
String.format("%02d. %s", recNumber, group),
|
||||
recNumber,
|
||||
ChoiceHintType.TEXT,
|
||||
String.join("<br>", group)
|
||||
);
|
||||
}
|
||||
|
||||
// fast link to last group
|
||||
String lastGroupInfo = String.format(" -> last group: %s", lastSelectedGroup == null ? "not used" : lastSelectedGroup);
|
||||
choice.withItem(
|
||||
String.valueOf(LAST_SELECTED_GROUP_ID),
|
||||
lastGroupInfo,
|
||||
-2,
|
||||
ChoiceHintType.TEXT,
|
||||
lastGroupInfo
|
||||
);
|
||||
|
||||
// fast link to last dialog
|
||||
String lastDialogName = (lastSelectedDialog == null ? "not used" : String.format("%s - %s",
|
||||
lastSelectedDialog.getName(), lastSelectedDialog.getDescription()));
|
||||
String lastDialogInfo = String.format(" -> last dialog: %s", lastDialogName);
|
||||
choice.withItem(
|
||||
String.valueOf(LAST_SELECTED_DIALOG_ID),
|
||||
lastDialogInfo,
|
||||
-1,
|
||||
ChoiceHintType.TEXT,
|
||||
lastDialogInfo
|
||||
);
|
||||
|
||||
return choice;
|
||||
}
|
||||
|
||||
private Choice prepareSelectDialogChoice(String needGroup) {
|
||||
Choice choice = new ChoiceImpl(false);
|
||||
choice.setMessage("Choose game dialog to run from " + needGroup);
|
||||
int recNumber = 0;
|
||||
for (int i = 0; i < this.dialogs.size(); i++) {
|
||||
TestableDialog dialog = this.dialogs.get(i);
|
||||
if (!dialog.getGroup().equals(needGroup)) {
|
||||
continue;
|
||||
}
|
||||
recNumber++;
|
||||
String info = String.format("%s - %s - %s", dialog.getGroup(), dialog.getName(), dialog.getDescription());
|
||||
choice.withItem(
|
||||
String.valueOf(i),
|
||||
String.format("%02d. %s", recNumber, info),
|
||||
recNumber,
|
||||
ChoiceHintType.TEXT,
|
||||
String.join("<br>", info)
|
||||
);
|
||||
}
|
||||
return choice;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue