mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 18:28:00 +00:00
Merge branch 'multi-select' into 'master'
allow human players to make mass select, sometimes See merge request core-developers/forge!293
This commit is contained in:
@@ -159,6 +159,14 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer);
|
return SpellApiToAi.Converter.get(api).chooseSingleEntity(player, sa, (FCollection<T>)optionList, isOptional, targetedPlayer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends GameEntity> List<T> chooseEntitiesForEffect(
|
||||||
|
FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title,
|
||||||
|
Player targetedPlayer) {
|
||||||
|
// this isn't used
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title) {
|
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title) {
|
||||||
ApiType api = sa.getApi();
|
ApiType api = sa.getApi();
|
||||||
@@ -988,6 +996,14 @@ public class PlayerControllerAi extends PlayerController {
|
|||||||
return brains.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player, decider);
|
return brains.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player, decider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Card> chooseCardsForZoneChange(
|
||||||
|
ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList,
|
||||||
|
DelayedReveal delayedReveal, String selectPrompt, Player decider) {
|
||||||
|
// this isn't used
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetAtEndOfTurn() {
|
public void resetAtEndOfTurn() {
|
||||||
// TODO - if card memory is ever used to remember something for longer than a turn, make sure it's not reset here.
|
// TODO - if card memory is ever used to remember something for longer than a turn, make sure it's not reset here.
|
||||||
|
|||||||
@@ -864,6 +864,14 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
fetchList.sort();
|
fetchList.sort();
|
||||||
|
|
||||||
CardCollection chosenCards = new CardCollection();
|
CardCollection chosenCards = new CardCollection();
|
||||||
|
// only multi-select if player can select more than one
|
||||||
|
if (changeNum > 1 && allowMultiSelect(decider, sa)) {
|
||||||
|
for (Card card : decider.getController().chooseCardsForZoneChange(destination, origin, sa, fetchList, delayedReveal, selectPrompt, decider)) {
|
||||||
|
chosenCards.add(card);
|
||||||
|
};
|
||||||
|
// maybe prompt the user if they selected fewer than the maximum possible?
|
||||||
|
} else {
|
||||||
|
// one at a time
|
||||||
for (int i = 0; i < changeNum && destination != null; i++) {
|
for (int i = 0; i < changeNum && destination != null; i++) {
|
||||||
if (sa.hasParam("DifferentNames")) {
|
if (sa.hasParam("DifferentNames")) {
|
||||||
for (Card c : chosenCards) {
|
for (Card c : chosenCards) {
|
||||||
@@ -901,11 +909,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
decider.getController().reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
|
decider.getController().reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
|
||||||
}
|
}
|
||||||
c = Aggregates.random(fetchList);
|
c = Aggregates.random(fetchList);
|
||||||
}
|
} else if (defined && !sa.hasParam("ChooseFromDefined")) {
|
||||||
else if (defined && !sa.hasParam("ChooseFromDefined")) {
|
|
||||||
c = Iterables.getFirst(fetchList, null);
|
c = Iterables.getFirst(fetchList, null);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
String title = selectPrompt;
|
String title = selectPrompt;
|
||||||
if (changeNum > 1) { //indicate progress if multiple cards being chosen
|
if (changeNum > 1) { //indicate progress if multiple cards being chosen
|
||||||
title += " (" + (i + 1) + " / " + changeNum + ")";
|
title += " (" + (i + 1) + " / " + changeNum + ")";
|
||||||
@@ -934,6 +940,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
totcmc -= c.getCMC();
|
totcmc -= c.getCMC();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (sa.hasParam("ShuffleChangedPile")) {
|
if (sa.hasParam("ShuffleChangedPile")) {
|
||||||
CardLists.shuffle(chosenCards);
|
CardLists.shuffle(chosenCards);
|
||||||
@@ -1159,6 +1166,17 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static boolean allowMultiSelect(Player decider, SpellAbility sa) {
|
||||||
|
return decider.getController().isGuiPlayer() // limit mass selection to human players for now
|
||||||
|
&& !sa.hasParam("Mandatory") // only handle optional decisions, for now
|
||||||
|
&& !sa.hasParam("ShareLandType")
|
||||||
|
&& !sa.hasParam("DifferentNames")
|
||||||
|
&& !sa.hasParam("DifferentCMC")
|
||||||
|
&& !sa.hasParam("AtRandom")
|
||||||
|
&& (!sa.hasParam("Defined") || sa.hasParam("ChooseFromDefined"))
|
||||||
|
&& sa.getParam("WithTotalCMC") == null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* removeFromStack.
|
* removeFromStack.
|
||||||
|
|||||||
@@ -109,6 +109,8 @@ public abstract class PlayerController {
|
|||||||
public abstract <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player relatedPlayer);
|
public abstract <T extends GameEntity> T chooseSingleEntityForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, boolean isOptional, Player relatedPlayer);
|
||||||
public abstract SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title);
|
public abstract SpellAbility chooseSingleSpellForEffect(List<SpellAbility> spells, SpellAbility sa, String title);
|
||||||
|
|
||||||
|
public abstract <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer);
|
||||||
|
|
||||||
public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message);
|
public abstract boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message);
|
||||||
public abstract boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner);
|
public abstract boolean confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner);
|
||||||
public abstract boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message);
|
public abstract boolean confirmStaticApplication(Card hostCard, GameEntity affected, String logic, String message);
|
||||||
@@ -229,6 +231,8 @@ public abstract class PlayerController {
|
|||||||
// better to have this odd method than those if playerType comparison in ChangeZone
|
// better to have this odd method than those if playerType comparison in ChangeZone
|
||||||
public abstract Card chooseSingleCardForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, boolean isOptional, Player decider);
|
public abstract Card chooseSingleCardForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, boolean isOptional, Player decider);
|
||||||
|
|
||||||
|
public abstract List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, Player decider);
|
||||||
|
|
||||||
public abstract void autoPassCancel();
|
public abstract void autoPassCancel();
|
||||||
|
|
||||||
public abstract void awaitNextInput();
|
public abstract void awaitNextInput();
|
||||||
|
|||||||
@@ -920,6 +920,14 @@ public final class CMatchUI
|
|||||||
return one(title, optionList);
|
return one(title, optionList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GameEntityView> chooseEntitiesForEffect(final String title, final List<? extends GameEntityView> optionList, final DelayedReveal delayedReveal) {
|
||||||
|
if (delayedReveal != null) {
|
||||||
|
reveal(delayedReveal.getMessagePrefix(), delayedReveal.getCards()); //TODO: Merge this into search dialog
|
||||||
|
}
|
||||||
|
return (List) order(title,"Selected", 0, optionList.size(), optionList, null, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setPlayerAvatar(final LobbyPlayer player, final IHasIcon ihi) {
|
public void setPlayerAvatar(final LobbyPlayer player, final IHasIcon ihi) {
|
||||||
avatarImages.put(player.getName(), ihi.getIconImageKey());
|
avatarImages.put(player.getName(), ihi.getIconImageKey());
|
||||||
|
|||||||
@@ -171,6 +171,12 @@ public class PlayerControllerForTests extends PlayerController {
|
|||||||
return chooseItem(spells);
|
return chooseItem(spells);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T extends GameEntity> List<T> chooseEntitiesForEffect(FCollectionView<T> optionList, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer) {
|
||||||
|
// this isn't used
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||||
return true;
|
return true;
|
||||||
@@ -610,6 +616,12 @@ public class PlayerControllerForTests extends PlayerController {
|
|||||||
return ChangeZoneAi.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player, decider);
|
return ChangeZoneAi.chooseCardToHiddenOriginChangeZone(destination, origin, sa, fetchList, player, decider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<Card> chooseCardsForZoneChange(ZoneType destination, List<ZoneType> origin, SpellAbility sa, CardCollection fetchList, DelayedReveal delayedReveal, String selectPrompt, Player decider) {
|
||||||
|
// this isn't used
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void resetAtEndOfTurn() {
|
public void resetAtEndOfTurn() {
|
||||||
// Not used by the controller for tests
|
// Not used by the controller for tests
|
||||||
|
|||||||
@@ -502,6 +502,11 @@ public class MatchController extends AbstractGuiGame {
|
|||||||
}.invokeAndWait();
|
}.invokeAndWait();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GameEntityView> chooseEntitiesForEffect(String title, List<? extends GameEntityView> optionList, DelayedReveal delayedReveal) {
|
||||||
|
return SGuiChoose.order(title, "Selected", (List<GameEntityView>) optionList);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCard(final CardView card) {
|
public void setCard(final CardView card) {
|
||||||
// doesn't need to do anything
|
// doesn't need to do anything
|
||||||
|
|||||||
@@ -143,7 +143,9 @@ public interface IGuiGame {
|
|||||||
<T> List<T> insertInList(String title, T newItem, List<T> oldItems);
|
<T> List<T> insertInList(String title, T newItem, List<T> oldItems);
|
||||||
|
|
||||||
List<PaperCard> sideboard(CardPool sideboard, CardPool main);
|
List<PaperCard> sideboard(CardPool sideboard, CardPool main);
|
||||||
GameEntityView chooseSingleEntityForEffect(String title, List<? extends GameEntityView> optionList, DelayedReveal delayedReveal, boolean isOptional); void setCard(CardView card);
|
GameEntityView chooseSingleEntityForEffect(String title, List<? extends GameEntityView> optionList, DelayedReveal delayedReveal, boolean isOptional);
|
||||||
|
List<GameEntityView> chooseEntitiesForEffect(String title, List<? extends GameEntityView> optionList, DelayedReveal delayedReveal);
|
||||||
|
void setCard(CardView card);
|
||||||
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
|
void setPlayerAvatar(LobbyPlayer player, IHasIcon ihi);
|
||||||
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
|
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
|
||||||
void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor);
|
void restoreOldZones(Map<PlayerView, Object> playersToRestoreZonesFor);
|
||||||
|
|||||||
@@ -62,6 +62,7 @@ public enum ProtocolMethod {
|
|||||||
order (Mode.SERVER, List.class, String.class, String.class, Integer.TYPE, Integer.TYPE, List.class, List.class, CardView.class, Boolean.TYPE),
|
order (Mode.SERVER, List.class, String.class, String.class, Integer.TYPE, Integer.TYPE, List.class, List.class, CardView.class, Boolean.TYPE),
|
||||||
sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class),
|
sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class),
|
||||||
chooseSingleEntityForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, DelayedReveal.class, Boolean.TYPE),
|
chooseSingleEntityForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, DelayedReveal.class, Boolean.TYPE),
|
||||||
|
chooseEntitiesForEffect(Mode.SERVER, GameEntityView.class, String.class, List.class, DelayedReveal.class),
|
||||||
setCard (Mode.SERVER, Void.TYPE, CardView.class),
|
setCard (Mode.SERVER, Void.TYPE, CardView.class),
|
||||||
// TODO case "setPlayerAvatar":
|
// TODO case "setPlayerAvatar":
|
||||||
openZones (Mode.SERVER, Boolean.TYPE, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
|
openZones (Mode.SERVER, Boolean.TYPE, Collection/*ZoneType*/.class, Map/*PlayerView,Object*/.class),
|
||||||
|
|||||||
@@ -231,6 +231,11 @@ public class NetGuiGame extends AbstractGuiGame {
|
|||||||
return sendAndWait(ProtocolMethod.chooseSingleEntityForEffect, title, optionList, delayedReveal, isOptional);
|
return sendAndWait(ProtocolMethod.chooseSingleEntityForEffect, title, optionList, delayedReveal, isOptional);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public List<GameEntityView> chooseEntitiesForEffect(final String title, final List<? extends GameEntityView> optionList, final DelayedReveal delayedReveal) {
|
||||||
|
return sendAndWait(ProtocolMethod.chooseEntitiesForEffect, title, optionList, delayedReveal);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setCard(final CardView card) {
|
public void setCard(final CardView card) {
|
||||||
updateGameView();
|
updateGameView();
|
||||||
|
|||||||
@@ -453,6 +453,70 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
@Override
|
||||||
|
public <T extends GameEntity> List<T> chooseEntitiesForEffect(final FCollectionView<T> optionList,
|
||||||
|
final DelayedReveal delayedReveal, final SpellAbility sa, final String title, final Player targetedPlayer) {
|
||||||
|
// Human is supposed to read the message and understand from it what to
|
||||||
|
// choose
|
||||||
|
if (optionList.isEmpty()) {
|
||||||
|
if (delayedReveal != null) {
|
||||||
|
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(),
|
||||||
|
delayedReveal.getMessagePrefix());
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean canUseSelectCardsInput = true;
|
||||||
|
for (final GameEntity c : optionList) {
|
||||||
|
if (c instanceof Player) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
final Zone cz = ((Card) c).getZone();
|
||||||
|
// can point at cards in own hand and anyone's battlefield
|
||||||
|
final boolean canUiPointAtCards = cz != null
|
||||||
|
&& (cz.is(ZoneType.Hand) && cz.getPlayer() == player || cz.is(ZoneType.Battlefield));
|
||||||
|
if (!canUiPointAtCards) {
|
||||||
|
canUseSelectCardsInput = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (canUseSelectCardsInput) {
|
||||||
|
if (delayedReveal != null) {
|
||||||
|
reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(),
|
||||||
|
delayedReveal.getMessagePrefix());
|
||||||
|
}
|
||||||
|
final InputSelectEntitiesFromList<T> input = new InputSelectEntitiesFromList<T>(this, 0, optionList.size(),
|
||||||
|
optionList, sa);
|
||||||
|
input.setCancelAllowed(true);
|
||||||
|
input.setMessage(MessageUtil.formatMessage(title, player, targetedPlayer));
|
||||||
|
input.showAndWait();
|
||||||
|
return (List<T>) Iterables.getFirst(input.getSelected(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
tempShow(optionList);
|
||||||
|
if (delayedReveal != null) {
|
||||||
|
tempShow(delayedReveal.getCards());
|
||||||
|
}
|
||||||
|
final List<GameEntityView> chosen = getGui().chooseEntitiesForEffect(title,
|
||||||
|
GameEntityView.getEntityCollection(optionList), delayedReveal);
|
||||||
|
endTempShowCards();
|
||||||
|
|
||||||
|
List<T> results = new ArrayList<>();
|
||||||
|
if (chosen instanceof List && chosen.size() > 0) {
|
||||||
|
for (GameEntityView entry: chosen) {
|
||||||
|
if (entry instanceof CardView) {
|
||||||
|
results.add((T)game.getCard((CardView) entry));
|
||||||
|
}
|
||||||
|
if (entry instanceof PlayerView) {
|
||||||
|
results.add((T)game.getPlayer((PlayerView) entry));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int chooseNumber(final SpellAbility sa, final String title, final int min, final int max) {
|
public int chooseNumber(final SpellAbility sa, final String title, final int min, final int max) {
|
||||||
if (min >= max) {
|
if (min >= max) {
|
||||||
@@ -1633,6 +1697,12 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
|||||||
return chooseSingleEntityForEffect(fetchList, delayedReveal, sa, selectPrompt, isOptional, decider);
|
return chooseSingleEntityForEffect(fetchList, delayedReveal, sa, selectPrompt, isOptional, decider);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Card> chooseCardsForZoneChange(final ZoneType destination, final List<ZoneType> origin,
|
||||||
|
final SpellAbility sa, final CardCollection fetchList, final DelayedReveal delayedReveal,
|
||||||
|
final String selectPrompt, final Player decider) {
|
||||||
|
return chooseEntitiesForEffect(fetchList, delayedReveal, sa, selectPrompt, decider);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isGuiPlayer() {
|
public boolean isGuiPlayer() {
|
||||||
return lobbyPlayer == GamePlayerUtil.getGuiPlayer();
|
return lobbyPlayer == GamePlayerUtil.getGuiPlayer();
|
||||||
|
|||||||
Reference in New Issue
Block a user