From 94064a2a135a57b37e6fe5fe4c4519b7ad008e97 Mon Sep 17 00:00:00 2001 From: "Peter F. Patel-Schneider" Date: Sun, 20 Jan 2019 21:16:34 -0500 Subject: [PATCH 1/4] Fix bug in chooseEntitiesforEffect in Mobile GUI --- forge-gui-mobile/src/forge/screens/match/MatchController.java | 4 +++- forge-gui-mobile/src/forge/toolbox/GuiChoose.java | 2 +- forge-gui/src/main/java/forge/util/gui/SGuiChoose.java | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/forge-gui-mobile/src/forge/screens/match/MatchController.java b/forge-gui-mobile/src/forge/screens/match/MatchController.java index 3eef6be8e79..0a5a83b664a 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchController.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchController.java @@ -517,7 +517,9 @@ public class MatchController extends AbstractGuiGame { @Override public List chooseEntitiesForEffect(String title, List optionList, int min, int max, DelayedReveal delayedReveal) { - return SGuiChoose.order(title, "Selected", min, max, (List) optionList, null); + final int m1 = max >= 0 ? optionList.size() - max : -1; + final int m2 = min >= 0 ? optionList.size() - min : -1; + return SGuiChoose.order(title, "Selected", m1, m2, (List) optionList, null); } @Override diff --git a/forge-gui-mobile/src/forge/toolbox/GuiChoose.java b/forge-gui-mobile/src/forge/toolbox/GuiChoose.java index cee2380a441..0b369d8fe4c 100644 --- a/forge-gui-mobile/src/forge/toolbox/GuiChoose.java +++ b/forge-gui-mobile/src/forge/toolbox/GuiChoose.java @@ -244,8 +244,8 @@ public class GuiChoose { } public static void many(final String title, final String topCaption, int min, int max, final List sourceChoices, CardView referenceCard, final Callback> callback) { - int m2 = min >= 0 ? sourceChoices.size() - min : -1; int m1 = max >= 0 ? sourceChoices.size() - max : -1; + int m2 = min >= 0 ? sourceChoices.size() - min : -1; order(title, topCaption, m1, m2, sourceChoices, null, referenceCard, callback); } diff --git a/forge-gui/src/main/java/forge/util/gui/SGuiChoose.java b/forge-gui/src/main/java/forge/util/gui/SGuiChoose.java index df549cfb93c..0575d80adb6 100644 --- a/forge-gui/src/main/java/forge/util/gui/SGuiChoose.java +++ b/forge-gui/src/main/java/forge/util/gui/SGuiChoose.java @@ -163,8 +163,8 @@ public class SGuiChoose { } public static List many(final String title, final String topCaption, final int min, final int max, final List sourceChoices) { - final int m2 = min >= 0 ? sourceChoices.size() - min : -1; final int m1 = max >= 0 ? sourceChoices.size() - max : -1; + final int m2 = min >= 0 ? sourceChoices.size() - min : -1; return order(title, topCaption, m1, m2, sourceChoices, null); } From aff8d5ce01fa8c2cb1df95a1561adf8c547993dd Mon Sep 17 00:00:00 2001 From: "Peter F. Patel-Schneider" Date: Sat, 19 Jan 2019 15:45:45 -0500 Subject: [PATCH 2/4] Use chooseEntitiesForEffect for dig (except and/or dig) --- .../java/forge/ai/PlayerControllerAi.java | 16 ++- .../forge/game/ability/effects/DigEffect.java | 118 +++++++++++------- .../forge/game/player/PlayerController.java | 3 + .../forge/player/PlayerControllerHuman.java | 17 ++- 4 files changed, 101 insertions(+), 53 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index c7b845e9051..fb9fd6239dd 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -164,8 +164,20 @@ public class PlayerControllerAi extends PlayerController { public List chooseEntitiesForEffect( FCollectionView optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player targetedPlayer) { - // this isn't used - return null; + if (delayedReveal != null) { + reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix()); + } + FCollection remaining = new FCollection(optionList); + List selecteds = new ArrayList(); + T selected; + do { + selected = chooseSingleEntityForEffect(remaining, null, sa, title, selecteds.size()>=min, targetedPlayer); + if ( selected != null ) { + remaining.remove(selected); + selecteds.add(selected); + } + } while ( (selected != null ) && (selecteds.size() < max) ); + return selecteds; } @Override diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index 9d3fd658b19..80b301f6cdc 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -194,7 +194,7 @@ public class DigEffect extends SpellAbilityEffect { } } else { - // If all the cards are valid choices, no need for a separate reveal dialog to the chooser. + // If all the cards are valid choices, no need for a separate reveal dialog to the chooser. pfps?? if (p == chooser && destZone1ChangeNum > 1) { delayedReveal = null; } @@ -238,55 +238,83 @@ public class DigEffect extends SpellAbilityEffect { if (sa.hasParam("RandomOrder")) { CardLists.shuffle(movedCards); } - } - else { + } else { String prompt; - if (sa.hasParam("PrimaryPrompt")) { - prompt = sa.getParam("PrimaryPrompt"); - } else { - prompt = "Choose a card to put into " + destZone1.name(); - if (destZone1.equals(ZoneType.Library)) { - if (libraryPosition == -1) { - prompt = "Choose a card to put on the bottom of {player's} library"; - } - else if (libraryPosition == 0) { - prompt = "Choose a card to put on top of {player's} library"; - } - } - } + if (!andOrValid.equals("")) { // pfps: old way - to be fixed soon - movedCards = new CardCollection(); - for (int i = 0; i < destZone1ChangeNum || (anyNumber && i < numToDig); i++) { - // let user get choice - Card chosen = null; - if (!valid.isEmpty()) { - // If we're choosing multiple cards, only need to show the reveal dialog the first time through. - boolean shouldReveal = (i == 0); - chosen = chooser.getController().chooseSingleEntityForEffect(valid, shouldReveal ? delayedReveal : null, sa, prompt, anyNumber || optional, p); - } - else { - if (i == 0) { - chooser.getController().notifyOfValue(sa, null, "No valid cards"); - } - } + if (sa.hasParam("PrimaryPrompt")) { + prompt = sa.getParam("PrimaryPrompt"); + } else { + prompt = "Choose a card to put into " + destZone1.name(); + if (destZone1.equals(ZoneType.Library)) { + if (libraryPosition == -1) { + prompt = "Choose a card to put on the bottom of {player's} library"; + } + else if (libraryPosition == 0) { + prompt = "Choose a card to put on top of {player's} library"; + } + } + } - if (chosen == null) { - break; - } + movedCards = new CardCollection(); + for (int i = 0; i < destZone1ChangeNum || (anyNumber && i < numToDig); i++) { + // let user get choice + Card chosen = null; + if (!valid.isEmpty()) { + // If we're choosing multiple cards, only need to show the reveal dialog the first time through. + boolean shouldReveal = (i == 0); + chosen = chooser.getController().chooseSingleEntityForEffect(valid, shouldReveal ? delayedReveal : null, sa, prompt, anyNumber || optional, p); + } + else { + if (i == 0) { + chooser.getController().notifyOfValue(sa, null, "No valid cards"); + } + } + if (chosen == null) { break; } + movedCards.add(chosen); + valid.remove(chosen); + if (!andOrValid.equals("")) { + andOrCards.remove(chosen); + if (!chosen.isValid(andOrValid.split(","), host.getController(), host, sa)) { + valid = new CardCollection(andOrCards); + } + else if (!chosen.isValid(changeValid.split(","), host.getController(), host, sa)) { + valid.removeAll((Collection)andOrCards); + } + } + } - movedCards.add(chosen); - valid.remove(chosen); - if (!andOrValid.equals("")) { - andOrCards.remove(chosen); - if (!chosen.isValid(andOrValid.split(","), host.getController(), host, sa)) { - valid = new CardCollection(andOrCards); - } - else if (!chosen.isValid(changeValid.split(","), host.getController(), host, sa)) { - valid.removeAll((Collection)andOrCards); - } - } - } + } else { // pfps: new way + + if (sa.hasParam("PrimaryPrompt")) { + prompt = sa.getParam("PrimaryPrompt"); + } else { + prompt = "Choose card(s) to put into " + destZone1.name(); + if (destZone1.equals(ZoneType.Library)) { + if (libraryPosition == -1) { + prompt = "Choose card(s) to put on the bottom of {player's} library"; + } + else if (libraryPosition == 0) { + prompt = "Choose card(s) to put on top of {player's} library"; + } + } + } + + movedCards = new CardCollection(); + int min = (anyNumber || optional) ? 0 : numToDig; + int max = Math.max(destZone1ChangeNum, anyNumber ? valid.size() : 0); +1 if (!valid.isEmpty()) { + if ( p == chooser ) { // the digger can still see all the dug cards when choosing + chooser.getController().tempShowCards(top); + } + List chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p); + chooser.getController().endTempShowCards(); + movedCards.addAll(chosen); + } else { + chooser.getController().notifyOfValue(sa, null, "No valid cards"); + } + } if (!changeValid.isEmpty() && !sa.hasParam("ExileFaceDown") && !sa.hasParam("NoReveal")) { game.getAction().reveal(movedCards, chooser, true, diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index 33295250a62..f6d58aec623 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -81,6 +81,9 @@ public abstract class PlayerController { public Player getPlayer() { return player; } public LobbyPlayer getLobbyPlayer() { return lobbyPlayer; } + public void tempShowCards(final Iterable cards) { } // show cards in UI until ended + public void endTempShowCards() { } + public final SpellAbility getAbilityToPlay(final Card hostCard, final List abilities) { return getAbilityToPlay(hostCard, abilities, null); } public abstract SpellAbility getAbilityToPlay(Card hostCard, List abilities, ITriggerEvent triggerEvent); diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 1a2c2cb43d1..7818470b439 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -156,7 +156,8 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont c.setMayLookAt(player, true, true); } - private void tempShowCards(final Iterable cards) { + @Override + public void tempShowCards(final Iterable cards) { if (mayLookAtAllCards) { return; } // no needed if this is set @@ -166,7 +167,8 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } } - private void endTempShowCards() { + @Override + public void endTempShowCards() { if (tempShownCards.isEmpty()) { return; } @@ -479,11 +481,14 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } if (useSelectCardsInput(optionList)) { - if (delayedReveal != null) { - reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), - delayedReveal.getMessagePrefix()); - } + // if (delayedReveal != null) { + // reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), + // delayedReveal.getMessagePrefix()); + //} tempShow(optionList); + if (delayedReveal != null) { + tempShow(delayedReveal.getCards()); + } final InputSelectEntitiesFromList input = new InputSelectEntitiesFromList(this, min, max, optionList, sa); input.setCancelAllowed(true); From f0c45cf814d21de919bdd546608664acafacab47 Mon Sep 17 00:00:00 2001 From: "Peter F. Patel-Schneider" Date: Sun, 20 Jan 2019 12:04:02 -0500 Subject: [PATCH 3/4] Add visual chooser for two lists and use it for and/or dig --- .../java/forge/ai/PlayerControllerAi.java | 14 ++ .../forge/game/ability/effects/DigEffect.java | 102 ++++-------- .../forge/game/player/PlayerController.java | 1 + .../java/forge/view/arcane/CardPanel.java | 6 +- .../util/PlayerControllerForTests.java | 6 + .../input/InputSelectEntitiesFromList.java | 6 +- .../match/input/InputSelectFromTwoLists.java | 152 ++++++++++++++++++ .../forge/player/PlayerControllerHuman.java | 97 ++++++----- 8 files changed, 269 insertions(+), 115 deletions(-) create mode 100644 forge-gui/src/main/java/forge/match/input/InputSelectFromTwoLists.java diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index fb9fd6239dd..3ce2d7333da 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -180,6 +180,20 @@ public class PlayerControllerAi extends PlayerController { return selecteds; } + @Override + public List chooseFromTwoListsForEffect(FCollectionView optionList1, FCollectionView optionList2, + boolean optional, DelayedReveal delayedReveal, SpellAbility sa, String title, Player targetedPlayer) { + if (delayedReveal != null) { + reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), delayedReveal.getMessagePrefix()); + } + T selected1 = chooseSingleEntityForEffect(optionList1, null, sa, title, optional, targetedPlayer); + T selected2 = chooseSingleEntityForEffect(optionList2, null, sa, title, optional || selected1!=null, targetedPlayer); + List selecteds = new ArrayList(); + if ( selected1 != null ) { selecteds.add(selected1); } + if ( selected2 != null ) { selecteds.add(selected2); } + return selecteds; + } + @Override public SpellAbility chooseSingleSpellForEffect(java.util.List spells, SpellAbility sa, String title, Map params) { diff --git a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java index 80b301f6cdc..b47d8a6ee18 100644 --- a/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java +++ b/forge-game/src/main/java/forge/game/ability/effects/DigEffect.java @@ -187,7 +187,7 @@ public class DigEffect extends SpellAbilityEffect { if (!andOrValid.equals("")) { andOrCards = CardLists.getValidCards(top, andOrValid.split(","), host.getController(), host, sa); andOrCards.removeAll((Collection)valid); - valid.addAll(andOrCards); + valid.addAll(andOrCards); //pfps need to add andOr cards to valid to have set of all valid cards set up } else { andOrCards = new CardCollection(); @@ -240,80 +240,38 @@ public class DigEffect extends SpellAbilityEffect { } } else { String prompt; + + if (sa.hasParam("PrimaryPrompt")) { + prompt = sa.getParam("PrimaryPrompt"); + } else { + prompt = "Choose card(s) to put into " + destZone1.name(); + if (destZone1.equals(ZoneType.Library)) { + if (libraryPosition == -1) { + prompt = "Choose card(s) to put on the bottom of {player's} library"; + } else if (libraryPosition == 0) { + prompt = "Choose card(s) to put on top of {player's} library"; + } + } + } - if (!andOrValid.equals("")) { // pfps: old way - to be fixed soon - - if (sa.hasParam("PrimaryPrompt")) { - prompt = sa.getParam("PrimaryPrompt"); + movedCards = new CardCollection(); + if (valid.isEmpty()) { + chooser.getController().notifyOfValue(sa, null, "No valid cards"); + } else { + if ( p == chooser ) { // the digger can still see all the dug cards when choosing + chooser.getController().tempShowCards(top); + } + List chosen; + if (!andOrValid.equals("")) { + valid.removeAll(andOrCards); //pfps remove andOr cards to get two two choices set up correctly + chosen = chooser.getController().chooseFromTwoListsForEffect(valid, andOrCards, optional, delayedReveal, sa, prompt, p); } else { - prompt = "Choose a card to put into " + destZone1.name(); - if (destZone1.equals(ZoneType.Library)) { - if (libraryPosition == -1) { - prompt = "Choose a card to put on the bottom of {player's} library"; - } - else if (libraryPosition == 0) { - prompt = "Choose a card to put on top of {player's} library"; - } - } - } - - movedCards = new CardCollection(); - for (int i = 0; i < destZone1ChangeNum || (anyNumber && i < numToDig); i++) { - // let user get choice - Card chosen = null; - if (!valid.isEmpty()) { - // If we're choosing multiple cards, only need to show the reveal dialog the first time through. - boolean shouldReveal = (i == 0); - chosen = chooser.getController().chooseSingleEntityForEffect(valid, shouldReveal ? delayedReveal : null, sa, prompt, anyNumber || optional, p); - } - else { - if (i == 0) { - chooser.getController().notifyOfValue(sa, null, "No valid cards"); - } - } - if (chosen == null) { break; } - movedCards.add(chosen); - valid.remove(chosen); - if (!andOrValid.equals("")) { - andOrCards.remove(chosen); - if (!chosen.isValid(andOrValid.split(","), host.getController(), host, sa)) { - valid = new CardCollection(andOrCards); - } - else if (!chosen.isValid(changeValid.split(","), host.getController(), host, sa)) { - valid.removeAll((Collection)andOrCards); - } - } - } - - } else { // pfps: new way - - if (sa.hasParam("PrimaryPrompt")) { - prompt = sa.getParam("PrimaryPrompt"); - } else { - prompt = "Choose card(s) to put into " + destZone1.name(); - if (destZone1.equals(ZoneType.Library)) { - if (libraryPosition == -1) { - prompt = "Choose card(s) to put on the bottom of {player's} library"; - } - else if (libraryPosition == 0) { - prompt = "Choose card(s) to put on top of {player's} library"; - } - } - } - - movedCards = new CardCollection(); - int min = (anyNumber || optional) ? 0 : numToDig; - int max = Math.max(destZone1ChangeNum, anyNumber ? valid.size() : 0); -1 if (!valid.isEmpty()) { - if ( p == chooser ) { // the digger can still see all the dug cards when choosing - chooser.getController().tempShowCards(top); - } - List chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p); - chooser.getController().endTempShowCards(); - movedCards.addAll(chosen); - } else { - chooser.getController().notifyOfValue(sa, null, "No valid cards"); + int min = (anyNumber || optional) ? 0 : numToDig; + int max = Math.max(destZone1ChangeNum, anyNumber ? valid.size() : 0); + chosen = chooser.getController().chooseEntitiesForEffect(valid, min, max, delayedReveal, sa, prompt, p); } + chooser.getController().endTempShowCards(); + movedCards.addAll(chosen); } if (!changeValid.isEmpty() && !sa.hasParam("ExileFaceDown") && !sa.hasParam("NoReveal")) { diff --git a/forge-game/src/main/java/forge/game/player/PlayerController.java b/forge-game/src/main/java/forge/game/player/PlayerController.java index f6d58aec623..912ea0f92b2 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -115,6 +115,7 @@ public abstract class PlayerController { Map params); public abstract List chooseEntitiesForEffect(FCollectionView optionList, int min, int max, DelayedReveal delayedReveal, SpellAbility sa, String title, Player relatedPlayer); + public abstract List chooseFromTwoListsForEffect(FCollectionView optionList1, FCollectionView optionList2, boolean optional, 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); diff --git a/forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java b/forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java index 895e023a50b..16339ea1602 100644 --- a/forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java +++ b/forge-gui-desktop/src/main/java/forge/view/arcane/CardPanel.java @@ -271,18 +271,18 @@ public class CardPanel extends SkinnedPanel implements CardContainer, IDisposabl // Magenta outline for when card was chosen to pay if (matchUI.isUsedToPay(getCard())) { g2d.setColor(Color.magenta); - final int n2 = Math.max(1, Math.round(2 * cardWidth * CardPanel.SELECTED_BORDER_SIZE)); + final int n2 = Math.max(4, Math.round(2 * cardWidth * CardPanel.SELECTED_BORDER_SIZE)); g2d.fillRoundRect(cardXOffset - n2, (cardYOffset - n2) + offset, cardWidth + (n2 * 2), cardHeight + (n2 * 2), cornerSize + n2, cornerSize + n2); } else if (matchUI.isSelectable(getCard())) { // Cyan outline for selectable cards g2d.setColor(Color.cyan); - final int n2 = Math.max(1, Math.round(2 * cardWidth * CardPanel.SELECTED_BORDER_SIZE)); + final int n2 = Math.max(4, Math.round(2 * cardWidth * CardPanel.SELECTED_BORDER_SIZE)); g2d.fillRoundRect(cardXOffset - n2, (cardYOffset - n2) + offset, cardWidth + (n2 * 2), cardHeight + (n2 * 2), cornerSize + n2, cornerSize + n2); } // Green outline for hover if (isSelected) { g2d.setColor(Color.green); - final int n = Math.max(1, Math.round(cardWidth * CardPanel.SELECTED_BORDER_SIZE)); + final int n = Math.max(4, Math.round(cardWidth * CardPanel.SELECTED_BORDER_SIZE)); g2d.fillRoundRect(cardXOffset - n, (cardYOffset - n) + offset, cardWidth + (n * 2), cardHeight + (n * 2), cornerSize + n , cornerSize + n); } diff --git a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java index 8393f236a1c..c7c3b3d9c5e 100644 --- a/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java +++ b/forge-gui-desktop/src/test/java/forge/gamesimulationtests/util/PlayerControllerForTests.java @@ -179,6 +179,12 @@ public class PlayerControllerForTests extends PlayerController { return null; } + @Override + public List chooseFromTwoListsForEffect(FCollectionView optionList1, FCollectionView optionList2, boolean optional, DelayedReveal delayedReveal, SpellAbility sa, String title, Player targetedPlayer) { + // this isn't used + return null; + } + @Override public boolean confirmAction(SpellAbility sa, PlayerActionConfirmMode mode, String message) { return true; diff --git a/forge-gui/src/main/java/forge/match/input/InputSelectEntitiesFromList.java b/forge-gui/src/main/java/forge/match/input/InputSelectEntitiesFromList.java index 780883a4565..374573b19f3 100644 --- a/forge-gui/src/main/java/forge/match/input/InputSelectEntitiesFromList.java +++ b/forge-gui/src/main/java/forge/match/input/InputSelectEntitiesFromList.java @@ -42,7 +42,7 @@ public class InputSelectEntitiesFromList extends InputSele vCards.add(((Card)c).getView()) ; } } - controller.getGui().setSelectables(vCards); + getController().getGui().setSelectables(vCards); final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates(); for (final GameEntity c : validChoices) { final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null ; @@ -52,8 +52,8 @@ public class InputSelectEntitiesFromList extends InputSele } FThreads.invokeInEdtNowOrLater(new Runnable() { @Override public void run() { - controller.getGui().updateZones(zonesToUpdate); - zonesShown = controller.getGui().tempShowZones(controller.getPlayer().getView(),zonesToUpdate); + getController().getGui().updateZones(zonesToUpdate); + zonesShown = getController().getGui().tempShowZones(controller.getPlayer().getView(),zonesToUpdate); } }); } diff --git a/forge-gui/src/main/java/forge/match/input/InputSelectFromTwoLists.java b/forge-gui/src/main/java/forge/match/input/InputSelectFromTwoLists.java new file mode 100644 index 00000000000..5065b7ac111 --- /dev/null +++ b/forge-gui/src/main/java/forge/match/input/InputSelectFromTwoLists.java @@ -0,0 +1,152 @@ +package forge.match.input; + +import java.util.Collection; +import java.util.List; +import java.util.ArrayList; + +import forge.game.GameEntity; +import forge.game.card.Card; +import forge.game.card.CardView; + +import forge.game.player.Player; +import forge.game.spellability.SpellAbility; +import forge.player.PlayerControllerHuman; +import forge.util.collect.FCollection; +import forge.util.collect.FCollectionView; +import forge.util.ITriggerEvent; +import forge.player.PlayerZoneUpdate; +import forge.player.PlayerZoneUpdates; +import forge.game.zone.Zone; +import forge.FThreads; + +public class InputSelectFromTwoLists extends InputSelectManyBase { + private final FCollectionView valid1, valid2; + private final FCollection validBoth; + private FCollectionView validChoices; + + protected final FCollection selected = new FCollection(); + protected final PlayerZoneUpdates zonesToUpdate = new PlayerZoneUpdates(); + protected Iterable zonesShown; // want to hide these zones when input done + + public InputSelectFromTwoLists(final PlayerControllerHuman controller, final boolean optional, + final FCollectionView list1, final FCollectionView list2, final SpellAbility sa0) { + super(controller, optional?0:1, 2, sa0); + valid1 = list1; + valid2 = list2; + validBoth = new FCollection(valid1); + for ( T v : valid2 ) { validBoth.add(v); } + validChoices = validBoth; + setSelectables(); + + for (final GameEntity c : validChoices) { + final Zone cz = (c instanceof Card) ? ((Card) c).getZone() : null ; + if ( cz != null ) { + zonesToUpdate.add(new PlayerZoneUpdate(cz.getPlayer().getView(),cz.getZoneType())); + } + } + FThreads.invokeInEdtNowOrLater(new Runnable() { + @Override public void run() { + controller.getGui().updateZones(zonesToUpdate); + zonesShown = controller.getGui().tempShowZones(controller.getPlayer().getView(),zonesToUpdate); + } + }); + } + + private void setSelectables() { + ArrayList vCards = new ArrayList(); + getController().getGui().clearSelectables(); + for ( T c : validChoices ) { + if ( c instanceof Card ) { + vCards.add(((Card)c).getView()) ; + } + } + getController().getGui().setSelectables(vCards); + } + + private void setValid() { + boolean selected1 = false, selected2 = false; + for ( T s : selected ) { + if ( valid1.contains(s) ) { selected1 = true; } + if ( valid2.contains(s) ) { selected2 = true; } + } + validChoices = selected1 ? ( selected2 ? FCollection.getEmpty() : valid2 ) : ( selected2 ? valid1 : validBoth ); + setSelectables(); + FThreads.invokeInEdtNowOrLater(new Runnable() { + @Override public void run() { + getController().getGui().updateZones(zonesToUpdate); + } + }); + } + + @Override + protected boolean onCardSelected(final Card c, final List otherCardsToSelect, final ITriggerEvent triggerEvent) { + if (!selectEntity(c)) { + return false; + } + refresh(); + return true; + } + + @Override + public String getActivateAction(final Card card) { + if (validChoices.contains(card)) { + if (selected.contains(card)) { + return "unselect card"; + } + return "select card"; + } + return null; + } + + @Override + protected void onPlayerSelected(final Player p, final ITriggerEvent triggerEvent) { + if (!selectEntity(p)) { + return; + } + refresh(); + } + + @Override + public final Collection getSelected() { + return selected; + } + + @SuppressWarnings("unchecked") + protected boolean selectEntity(final GameEntity c) { + if (!validChoices.contains(c) && !selected.contains(c)) { + return false; + } + + final boolean entityWasSelected = selected.contains(c); + if (entityWasSelected) { + selected.remove(c); + } + else { + selected.add((T)c); + } + setValid(); + onSelectStateChanged(c, !entityWasSelected); + + return true; + } + + // might re-define later + @Override + protected boolean hasEnoughTargets() { return selected.size() >= min; } + @Override + protected boolean hasAllTargets() { return selected.size() >= max; } + + @Override + protected String getMessage() { + return max == Integer.MAX_VALUE + ? String.format(message, selected.size()) + : String.format(message, max - selected.size()); + } + + @Override + protected void onStop() { + getController().getGui().hideZones(getController().getPlayer().getView(),zonesShown); + getController().getGui().clearSelectables(); + super.onStop(); + } +} diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 7818470b439..7777f319c58 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -407,6 +407,15 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return choices; } + // pfps there should be a better way + private GameEntity convertToEntity(final GameEntityView view) { + if (view instanceof CardView) { + return game.getCard((CardView) view); + } else if (view instanceof PlayerView) { + return game.getPlayer((PlayerView) view); + } else return null; + } + @SuppressWarnings("unchecked") @Override public T chooseSingleEntityForEffect(final FCollectionView optionList, @@ -429,12 +438,11 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return Iterables.getFirst(optionList, null); } + tempShow(optionList); + if (delayedReveal != null) { + tempShow(delayedReveal.getCards()); + } if (useSelectCardsInput(optionList)) { - if (delayedReveal != null) { - reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), - delayedReveal.getMessagePrefix()); - } - tempShow(optionList); final InputSelectEntitiesFromList input = new InputSelectEntitiesFromList(this, isOptional ? 0 : 1, 1, optionList, sa); input.setCancelAllowed(isOptional); @@ -444,21 +452,10 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return Iterables.getFirst(input.getSelected(), null); } - tempShow(optionList); - if (delayedReveal != null) { - tempShow(delayedReveal.getCards()); - } final GameEntityView result = getGui().chooseSingleEntityForEffect(title, GameEntityView.getEntityCollection(optionList), delayedReveal, isOptional); endTempShowCards(); - - if (result instanceof CardView) { - return (T) game.getCard((CardView) result); - } - if (result instanceof PlayerView) { - return (T) game.getPlayer((PlayerView) result); - } - return null; + return (T) convertToEntity(result); } @SuppressWarnings("unchecked") @@ -470,8 +467,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont Sentry.getContext().addExtra("Card", sa.getCardView().toString()); Sentry.getContext().addExtra("SpellAbility", sa.toString()); - // Human is supposed to read the message and understand from it what to - // choose + // 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(), @@ -480,15 +476,12 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return null; } + if (delayedReveal != null) { + tempShow(delayedReveal.getCards()); + } + + tempShow(optionList); if (useSelectCardsInput(optionList)) { - // if (delayedReveal != null) { - // reveal(delayedReveal.getCards(), delayedReveal.getZone(), delayedReveal.getOwner(), - // delayedReveal.getMessagePrefix()); - //} - tempShow(optionList); - if (delayedReveal != null) { - tempShow(delayedReveal.getCards()); - } final InputSelectEntitiesFromList input = new InputSelectEntitiesFromList(this, min, max, optionList, sa); input.setCancelAllowed(true); @@ -496,17 +489,12 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont input.showAndWait(); endTempShowCards(); return (List) input.getSelected(); - } + } + final List chosen = getGui().chooseEntitiesForEffect(title, + GameEntityView.getEntityCollection(optionList), min, max, delayedReveal); + endTempShowCards(); - tempShow(optionList); - if (delayedReveal != null) { - tempShow(delayedReveal.getCards()); - } - final List chosen = getGui().chooseEntitiesForEffect(title, - GameEntityView.getEntityCollection(optionList), min, max, delayedReveal); - endTempShowCards(); - - List results = new ArrayList<>(); + List results = new ArrayList<>(); //pfps I'm not sure that the chosens should be modified this way if (chosen instanceof List && chosen.size() > 0) { for (GameEntityView entry: chosen) { if (entry instanceof CardView) { @@ -520,6 +508,41 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont return results; } + @Override + public List chooseFromTwoListsForEffect(final FCollectionView optionList1, final FCollectionView optionList2, + boolean optional, 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 + // useful details for debugging problems with the mass select logic + Sentry.getContext().addExtra("Card", sa.getCardView().toString()); + Sentry.getContext().addExtra("SpellAbility", sa.toString()); + + if (delayedReveal != null) { + tempShow(delayedReveal.getCards()); + } + + tempShow(optionList1); + tempShow(optionList2); + + if (useSelectCardsInput(optionList1) && useSelectCardsInput(optionList2)) { + final InputSelectFromTwoLists input = new InputSelectFromTwoLists(this, optional, optionList1, optionList2, sa); + input.setCancelAllowed(optional); + input.setMessage(MessageUtil.formatMessage(title, player, targetedPlayer)); + input.showAndWait(); + endTempShowCards(); + return (List) input.getSelected(); + } + + final GameEntityView result1 = getGui().chooseSingleEntityForEffect(title, GameEntityView.getEntityCollection(optionList1), null, optional); + final GameEntityView result2 = getGui().chooseSingleEntityForEffect(title, GameEntityView.getEntityCollection(optionList2), null, (result1==null)?optional:true); + endTempShowCards(); + List results = new ArrayList<>(); + GameEntity entity1 = convertToEntity(result1); + if (entity1!=null) { results.add((T) entity1); } + GameEntity entity2 = convertToEntity(result2); + if (entity2!=null) { results.add((T) entity2); } + return results; + } + @Override public int chooseNumber(final SpellAbility sa, final String title, final int min, final int max) { if (min >= max) { From ebcb4e28de161d70cc0b3ad1cd142ffb4fac7a26 Mon Sep 17 00:00:00 2001 From: "Peter F. Patel-Schneider" Date: Sun, 20 Jan 2019 12:31:59 -0500 Subject: [PATCH 4/4] Add desktop GUI and Dig changes to CHANGES.txt --- forge-gui/release-files/CHANGES.txt | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/forge-gui/release-files/CHANGES.txt b/forge-gui/release-files/CHANGES.txt index d0b2b5f36a3..f0a7911cb31 100644 --- a/forge-gui/release-files/CHANGES.txt +++ b/forge-gui/release-files/CHANGES.txt @@ -1,3 +1,10 @@ +- Desktop GUI - +The Desktop GUI can pop up zones (Library, Graveyard, etc.) allow players to select cards from them when the option UI_SELECT_FROM_CARD_DISPLAYS is set. +The Desktop GUI outlines the selectable cards in many situations. This is not done when playing mana costs. + +- Digging - +Multi-card digging (e.g., for Genesis Wave) is done as a single multiple-card selection instead of a sequence of single-card selections. + - Game Night - Support was added for the Game Night box set, including all 10 exclusive cards.