allow human players to make mass select, sometimes

Signed-off-by: Jamin W. Collins <jamin.collins@gmail.com>
This commit is contained in:
Jamin W. Collins
2018-03-10 14:43:42 -07:00
parent 876083b085
commit a564f49381
10 changed files with 199 additions and 58 deletions

View File

@@ -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.

View File

@@ -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) {
@@ -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.

View File

@@ -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();

View File

@@ -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());

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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),

View File

@@ -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();

View File

@@ -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();