mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-15 10:18:01 +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);
|
||||
}
|
||||
|
||||
@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
|
||||
public SpellAbility chooseSingleSpellForEffect(java.util.List<SpellAbility> spells, SpellAbility sa, String title) {
|
||||
ApiType api = sa.getApi();
|
||||
@@ -988,6 +996,14 @@ public class PlayerControllerAi extends PlayerController {
|
||||
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
|
||||
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.
|
||||
|
||||
@@ -864,6 +864,14 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
fetchList.sort();
|
||||
|
||||
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++) {
|
||||
if (sa.hasParam("DifferentNames")) {
|
||||
for (Card c : chosenCards) {
|
||||
@@ -871,7 +879,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
}
|
||||
}
|
||||
if (sa.hasParam("DifferentCMC")) {
|
||||
for (Card c: chosenCards) {
|
||||
for (Card c : chosenCards) {
|
||||
fetchList = CardLists.filter(fetchList, Predicates.not(CardPredicates.sharesCMCWith(c)));
|
||||
}
|
||||
}
|
||||
@@ -901,11 +909,9 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
decider.getController().reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix());
|
||||
}
|
||||
c = Aggregates.random(fetchList);
|
||||
}
|
||||
else if (defined && !sa.hasParam("ChooseFromDefined")) {
|
||||
} else if (defined && !sa.hasParam("ChooseFromDefined")) {
|
||||
c = Iterables.getFirst(fetchList, null);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
String title = selectPrompt;
|
||||
if (changeNum > 1) { //indicate progress if multiple cards being chosen
|
||||
title += " (" + (i + 1) + " / " + changeNum + ")";
|
||||
@@ -934,6 +940,7 @@ public class ChangeZoneEffect extends SpellAbilityEffect {
|
||||
totcmc -= c.getCMC();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sa.hasParam("ShuffleChangedPile")) {
|
||||
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>
|
||||
* 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 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 confirmBidAction(SpellAbility sa, PlayerActionConfirmMode bidlife, String string, int bid, Player winner);
|
||||
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
|
||||
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 awaitNextInput();
|
||||
|
||||
@@ -920,6 +920,14 @@ public final class CMatchUI
|
||||
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
|
||||
public void setPlayerAvatar(final LobbyPlayer player, final IHasIcon ihi) {
|
||||
avatarImages.put(player.getName(), ihi.getIconImageKey());
|
||||
|
||||
@@ -171,6 +171,12 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
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
|
||||
public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) {
|
||||
return true;
|
||||
@@ -610,6 +616,12 @@ public class PlayerControllerForTests extends PlayerController {
|
||||
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
|
||||
public void resetAtEndOfTurn() {
|
||||
// Not used by the controller for tests
|
||||
|
||||
@@ -502,6 +502,11 @@ public class MatchController extends AbstractGuiGame {
|
||||
}.invokeAndWait();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GameEntityView> chooseEntitiesForEffect(String title, List<? extends GameEntityView> optionList, DelayedReveal delayedReveal) {
|
||||
return SGuiChoose.order(title, "Selected", (List<GameEntityView>) optionList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCard(final CardView card) {
|
||||
// doesn't need to do anything
|
||||
|
||||
@@ -143,7 +143,9 @@ public interface IGuiGame {
|
||||
<T> List<T> insertInList(String title, T newItem, List<T> oldItems);
|
||||
|
||||
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);
|
||||
boolean openZones(Collection<ZoneType> zones, Map<PlayerView, Object> players);
|
||||
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),
|
||||
sideboard (Mode.SERVER, List.class, CardPool.class, CardPool.class),
|
||||
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),
|
||||
// TODO case "setPlayerAvatar":
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<GameEntityView> chooseEntitiesForEffect(final String title, final List<? extends GameEntityView> optionList, final DelayedReveal delayedReveal) {
|
||||
return sendAndWait(ProtocolMethod.chooseEntitiesForEffect, title, optionList, delayedReveal);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setCard(final CardView card) {
|
||||
updateGameView();
|
||||
|
||||
@@ -453,6 +453,70 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
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
|
||||
public int chooseNumber(final SpellAbility sa, final String title, final int min, final int max) {
|
||||
if (min >= max) {
|
||||
@@ -1633,6 +1697,12 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont
|
||||
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
|
||||
public boolean isGuiPlayer() {
|
||||
return lobbyPlayer == GamePlayerUtil.getGuiPlayer();
|
||||
|
||||
Reference in New Issue
Block a user