GUI: improved choose creature type dialog - important creature types added to the start of the list and marked as my/opponent (closes #8478)

This commit is contained in:
Oleg Agafonov 2024-07-19 03:17:53 +04:00
parent 7b2e9b390c
commit a974af3c61
41 changed files with 239 additions and 138 deletions

View file

@ -36,14 +36,14 @@ public class ChooseCreatureTypeEffect extends OneShotEffect {
mageObject = game.getObject(source);
}
if (controller != null && mageObject != null) {
Choice typeChoice = new ChoiceCreatureType(mageObject);
Choice typeChoice = new ChoiceCreatureType(game, source);
if (controller.choose(outcome, typeChoice, game)) {
if (!game.isSimulation()) {
game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + typeChoice.getChoice());
game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + typeChoice.getChoiceKey());
}
game.getState().setValue(source.getSourceId() + "_type", SubType.byDescription(typeChoice.getChoice()));
game.getState().setValue(source.getSourceId() + "_type", SubType.byDescription(typeChoice.getChoiceKey()));
if (mageObject instanceof Permanent) {
((Permanent) mageObject).addInfo("chosen type", CardUtil.addToolTipMarkTags("Chosen type: " + typeChoice.getChoice()), game);
((Permanent) mageObject).addInfo("chosen type", CardUtil.addToolTipMarkTags("Chosen type: " + typeChoice.getChoiceKey()), game);
}
return true;
}

View file

@ -50,22 +50,22 @@ public class BecomesChosenCreatureTypeTargetEffect extends OneShotEffect {
Card card = game.getCard(source.getSourceId());
String chosenType = "";
if (player != null && card != null) {
Choice typeChoice = new ChoiceCreatureType();
Choice typeChoice = new ChoiceCreatureType(game, source);
String msg = "Choose a creature type";
if (nonWall) {
msg += " other than Wall";
}
typeChoice.setMessage(msg);
if (nonWall) {
typeChoice.getChoices().remove(SubType.WALL.getDescription());
typeChoice.getKeyChoices().remove(SubType.WALL.getDescription());
}
while (!player.choose(Outcome.BoostCreature, typeChoice, game)) {
if (!player.canRespond()) {
return false;
}
}
game.informPlayers(card.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoice());
chosenType = typeChoice.getChoice();
game.informPlayers(card.getName() + ": " + player.getLogName() + " has chosen " + typeChoice.getChoiceKey());
chosenType = typeChoice.getChoiceKey();
if (chosenType != null && !chosenType.isEmpty()) {
// ADD TYPE TO TARGET
ContinuousEffect effect = new BecomesCreatureTypeTargetEffect(duration, SubType.byDescription(chosenType));

View file

@ -1,11 +1,14 @@
package mage.choices;
import mage.game.Game;
import mage.players.Player;
import mage.util.Copyable;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
/**
* @author BetaSteward_at_googlemail.com, JayDi85
@ -107,4 +110,11 @@ public interface Choice extends Serializable, Copyable<Choice> {
void setRandomChoice();
boolean setChoiceByAnswers(List<String> answers, boolean removeSelectAnswerFromList);
/**
* Run additional code before player start to choose (example: add info and hints for choosing player)
*/
void onChooseStart(Game game, UUID choosingPlayerId);
void onChooseEnd(Game game, UUID choosingPlayerId, String choiceResult);
}

View file

@ -1,35 +1,39 @@
package mage.choices;
import mage.MageObject;
import mage.abilities.Ability;
import mage.constants.SubType;
import mage.game.Game;
import mage.players.Player;
import java.util.LinkedHashSet;
import java.util.*;
import java.util.stream.Collectors;
/**
* Game's choose dialog to ask about creature type. Return getChoice
*/
public class ChoiceCreatureType extends ChoiceImpl {
private static final String DEFAULT_MESSAGE = "Choose a creature type";
public ChoiceCreatureType() {
this(true, DEFAULT_MESSAGE, null);
public ChoiceCreatureType(Game game, Ability source) {
this(game, source, true, DEFAULT_MESSAGE);
}
public ChoiceCreatureType(MageObject source) {
this(true, DEFAULT_MESSAGE, source);
}
public ChoiceCreatureType(String chooseMessage, MageObject source) {
this(true, chooseMessage, source);
}
public ChoiceCreatureType(boolean required, String chooseMessage, MageObject source) {
public ChoiceCreatureType(Game game, Ability source, boolean required, String chooseMessage) {
super(required);
this.setChoices(SubType.getCreatureTypes().stream().map(SubType::toString).collect(Collectors.toCollection(LinkedHashSet::new)));
this.setMessage(chooseMessage);
if (source != null) {
this.setSubMessage(source.getIdName());
MageObject sourceObject = source == null ? null : game.getObject(source);
if (sourceObject != null) {
this.setSubMessage(sourceObject.getLogName());
}
this.setSearchEnabled(true);
this.setSearchEnabled(true);
// collect basic info
// additional info will be added onChooseStart
SubType.getCreatureTypes().stream().map(SubType::toString).forEach(value -> {
this.withItem(value, value, null, null, null);
});
}
protected ChoiceCreatureType(final ChoiceCreatureType choice) {
@ -40,4 +44,71 @@ public class ChoiceCreatureType extends ChoiceImpl {
public ChoiceCreatureType copy() {
return new ChoiceCreatureType(this);
}
@Override
public void onChooseStart(Game game, UUID choosingPlayerId) {
// add additional info about important creature types (collect it from all public zone and own hand)
Set<String> myTypes = new HashSet<>();
Set<String> opponentTypes = new HashSet<>();
game.getState().getPlayersInRange(choosingPlayerId, game).forEach(playerId -> {
Player player = game.getPlayer(playerId);
if (player == null) {
return;
}
Set<String> list = playerId.equals(choosingPlayerId) ? myTypes : opponentTypes;
// own hand
if (playerId.equals(choosingPlayerId)) {
player.getHand().getCards(game).forEach(card -> {
list.addAll(card.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList()));
});
}
// battlefield
game.getBattlefield().getAllActivePermanents(playerId).forEach(permanent -> {
list.addAll(permanent.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList()));
});
// graveyard
player.getGraveyard().getCards(game).forEach(card -> {
list.addAll(card.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList()));
});
// exile
game.getExile().getAllCards(game, playerId).forEach(card -> {
list.addAll(card.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList()));
});
});
// stack
game.getStack().forEach(stackObject -> {
if (stackObject.isControlledBy(choosingPlayerId)) {
myTypes.addAll(stackObject.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList()));
} else {
opponentTypes.addAll(stackObject.getSubtype(game).stream().map(SubType::toString).collect(Collectors.toList()));
}
});
// sort order: me -> opponent -> not used
this.keyChoices.forEach((key, value) -> {
String additionalInfo = "";
Integer orderPriority = 0;
if (myTypes.contains(key)) {
additionalInfo += "me";
orderPriority -= 100;
}
if (opponentTypes.contains(key)) {
if (!additionalInfo.isEmpty()) {
additionalInfo += ", ";
}
additionalInfo += "opponent";
orderPriority -= 10;
}
this.keyChoices.put(key, key + (additionalInfo.isEmpty() ? "" : " (" + additionalInfo + ")"));
this.sortData.put(key, orderPriority);
});
}
}

View file

@ -1,5 +1,6 @@
package mage.choices;
import mage.game.Game;
import mage.util.CardUtil;
import mage.util.RandomUtil;
import org.apache.log4j.Logger;
@ -400,4 +401,14 @@ public class ChoiceImpl implements Choice {
logger.error("Empty choice dialog in " + this.getClass().getCanonicalName(), new Throwable());
}
}
@Override
public void onChooseStart(Game game, UUID choosingPlayerId) {
// nothing to do
}
@Override
public void onChooseEnd(Game game, UUID choosingPlayerId, String choiceResult) {
// nothing to do
}
}

View file

@ -15,7 +15,6 @@ import mage.players.Player;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.stream.Collectors;
/**
* @author TheElk801
@ -101,39 +100,43 @@ class VolosJournalTokenEffect extends OneShotEffect {
Player player = game.getPlayer(source.getControllerId());
if (player == null) {
return true;
return false;
}
Permanent permanent = game.getPermanent(source.getSourceId());
if (permanent == null) {
return false;
}
Set<String> types = VolosJournalToken.getNotedTypes(game, permanent);
Set<String> notedTypes = VolosJournalToken.getNotedTypes(game, permanent);
ChoiceCreatureType choice = new ChoiceCreatureType();
ChoiceCreatureType choice = new ChoiceCreatureType(game, source);
// By default ChoiceCreatureType pre-populates all creatures into choices
// Limit the available choices to those on the creature being cast
if (!spell.isAllCreatureTypes(game)) {
choice.setChoices(
spell.getSubtype(game)
.stream()
.filter(subType -> subType.getSubTypeSet() == SubTypeSet.CreatureType)
.map(SubType::getDescription)
.collect(Collectors.toSet())
);
choice.getKeyChoices().clear();
spell.getSubtype(game)
.stream()
.filter(subType -> subType.getSubTypeSet() == SubTypeSet.CreatureType)
.map(SubType::getDescription)
.forEach(subType -> {
choice.withItem(subType, subType, null, null, null);
});
}
// Remove from the possible choices the subtypes which have already been chosen.
choice.getChoices().removeIf(types::contains);
choice.getKeyChoices().keySet().removeIf(notedTypes::contains);
switch (choice.getChoices().size()) {
switch (choice.getKeyChoices().size()) {
case 0:
return false;
case 1:
types.add(choice.getChoices().stream().findFirst().get());
notedTypes.add(choice.getKeyChoices().keySet().stream().findFirst().get());
return true;
}
player.choose(outcome, choice, game);
types.add(choice.getChoice());
notedTypes.add(choice.getChoiceKey());
return true;
}
}