From cd2044325539c65ffd105d86cacd39821e339720 Mon Sep 17 00:00:00 2001 From: Chris H Date: Thu, 30 May 2019 22:55:31 -0400 Subject: [PATCH] Add a feature to be able to Sideboard for the AI --- .../java/forge/ai/PlayerControllerAi.java | 2 +- .../src/main/java/forge/game/GameRules.java | 9 ++++++ .../src/main/java/forge/game/Match.java | 31 ++++++++++++++++--- .../forge/game/player/PlayerController.java | 2 +- .../src/main/java/forge/gui/GuiChoose.java | 4 +-- .../home/settings/CSubmenuPreferences.java | 3 +- .../home/settings/VSubmenuPreferences.java | 8 +++++ .../java/forge/screens/match/CMatchUI.java | 4 +-- .../util/PlayerControllerForTests.java | 2 +- .../src/forge/deck/FSideboardDialog.java | 4 +-- .../forge/screens/match/MatchController.java | 4 +-- forge-gui/res/languages/en-US.properties | 2 ++ .../main/java/forge/interfaces/IGuiGame.java | 2 +- .../main/java/forge/match/HostedMatch.java | 1 + .../java/forge/net/server/NetGuiGame.java | 4 +-- .../forge/player/PlayerControllerHuman.java | 7 ++--- .../forge/properties/ForgePreferences.java | 1 + 17 files changed, 67 insertions(+), 23 deletions(-) diff --git a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java index 4fabb5db0c8..df39977f5a8 100644 --- a/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java +++ b/forge-ai/src/main/java/forge/ai/PlayerControllerAi.java @@ -91,7 +91,7 @@ public class PlayerControllerAi extends PlayerController { } @Override - public List sideboard(Deck deck, GameType gameType) { + public List sideboard(Deck deck, GameType gameType, String message) { // AI does not know how to sideboard return null; } diff --git a/forge-game/src/main/java/forge/game/GameRules.java b/forge-game/src/main/java/forge/game/GameRules.java index 5e6876e9801..46ceb450cb5 100644 --- a/forge-game/src/main/java/forge/game/GameRules.java +++ b/forge-game/src/main/java/forge/game/GameRules.java @@ -11,6 +11,7 @@ public class GameRules { private int gamesToWinMatch = 2; private boolean playForAnte = false; private boolean matchAnteRarity = false; + private boolean sideboardForAI = false; private final Set appliedVariants = EnumSet.noneOf(GameType.class); // it's a preference, not rule... but I could hardly find a better place for it @@ -65,6 +66,14 @@ public class GameRules { matchAnteRarity = matchRarity; } + public boolean getSideboardForAI() { + return sideboardForAI; + } + + public void setSideboardForAI(final boolean sideboard) { + sideboardForAI = sideboard; + } + public int getGamesToWinMatch() { return gamesToWinMatch; } diff --git a/forge-game/src/main/java/forge/game/Match.java b/forge-game/src/main/java/forge/game/Match.java index 18c4e017e35..c716709122b 100644 --- a/forge-game/src/main/java/forge/game/Match.java +++ b/forge-game/src/main/java/forge/game/Match.java @@ -4,12 +4,14 @@ import com.google.common.collect.*; import forge.LobbyPlayer; import forge.deck.CardPool; import forge.deck.Deck; +import forge.deck.DeckFormat; import forge.deck.DeckSection; import forge.game.card.Card; import forge.game.card.CardCollectionView; import forge.game.event.GameEventAnteCardsSelected; import forge.game.event.GameEventGameFinished; import forge.game.player.Player; +import forge.game.player.PlayerController; import forge.game.player.RegisteredPlayer; import forge.game.trigger.Trigger; import forge.game.zone.PlayerZone; @@ -208,18 +210,39 @@ public class Match { Multimap rAICards = HashMultimap.create(); Multimap removedAnteCards = ArrayListMultimap.create(); - boolean isFirstGame = game.getMatch().getPlayedGames().isEmpty(); - boolean canSideBoard = !isFirstGame && rules.getGameType().isSideboardingAllowed(); - final FCollectionView players = game.getPlayers(); final List playersConditions = game.getMatch().getPlayers(); + + boolean isFirstGame = game.getMatch().getPlayedGames().isEmpty(); + boolean canSideBoard = !isFirstGame && rules.getGameType().isSideboardingAllowed(); + // Only allow this if feature flag is on AND for certain match types + boolean sideboardForAIs = rules.getSideboardForAI() && + rules.getGameType().getDeckFormat().equals(DeckFormat.Constructed); + PlayerController sideboardProxy = null; + if (canSideBoard && sideboardForAIs) { + for (int i = 0; i < playersConditions.size(); i++) { + final Player player = players.get(i); + final RegisteredPlayer psc = playersConditions.get(i); + if (!player.getController().isAI()) { + sideboardProxy = player.getController(); + break; + } + } + } + for (int i = 0; i < playersConditions.size(); i++) { final Player player = players.get(i); final RegisteredPlayer psc = playersConditions.get(i); if (canSideBoard) { + PlayerController person = player.getController(); + if (sideboardProxy != null && person.isAI()) { + person = sideboardProxy; + } + + String forPlayer = " for " + player.getName(); Deck toChange = psc.getDeck(); - List newMain = player.getController().sideboard(toChange, rules.getGameType()); + List newMain = person.sideboard(toChange, rules.getGameType(), forPlayer); if (null != newMain) { CardPool allCards = new CardPool(); allCards.addAll(toChange.get(DeckSection.Main)); 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 527dd1d198d..fc0c09a2625 100644 --- a/forge-game/src/main/java/forge/game/player/PlayerController.java +++ b/forge-game/src/main/java/forge/game/player/PlayerController.java @@ -91,7 +91,7 @@ public abstract class PlayerController { public abstract void playSpellAbilityForFree(SpellAbility copySA, boolean mayChoseNewTargets); public abstract void playSpellAbilityNoStack(SpellAbility effectSA, boolean mayChoseNewTargets); - public abstract List sideboard(final Deck deck, GameType gameType); + public abstract List sideboard(final Deck deck, GameType gameType, String message); public abstract List chooseCardsYouWonToAddToDeck(List losses); public abstract Map assignCombatDamage(Card attacker, CardCollectionView blockers, int damageDealt, GameEntity defender, boolean overrideOrder); diff --git a/forge-gui-desktop/src/main/java/forge/gui/GuiChoose.java b/forge-gui-desktop/src/main/java/forge/gui/GuiChoose.java index 070d21fd1c0..a5db47f9733 100644 --- a/forge-gui-desktop/src/main/java/forge/gui/GuiChoose.java +++ b/forge-gui-desktop/src/main/java/forge/gui/GuiChoose.java @@ -234,10 +234,10 @@ public class GuiChoose { return null; } - public static > List sideboard(final CMatchUI matchUI, final List sideboard, final List deck) { + public static > List sideboard(final CMatchUI matchUI, final List sideboard, final List deck, final String message) { Collections.sort(deck); Collections.sort(sideboard); - return order("Sideboard", "Main Deck", -1, -1, sideboard, deck, null, true, matchUI); + return order("Sideboard" + message, "Main Deck", -1, -1, sideboard, deck, null, true, matchUI); } public static List order(final String title, final String top, final int remainingObjectsMin, final int remainingObjectsMax, diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java index 47585f63f08..62c0be34a84 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/CSubmenuPreferences.java @@ -107,6 +107,7 @@ public enum CSubmenuPreferences implements ICDoc { lstControls.add(Pair.of(view.getCbRandomArtInPools(), FPref.UI_RANDOM_ART_IN_POOLS)); lstControls.add(Pair.of(view.getCbEnforceDeckLegality(), FPref.ENFORCE_DECK_LEGALITY)); lstControls.add(Pair.of(view.getCbPerformanceMode(), FPref.PERFORMANCE_MODE)); + lstControls.add(Pair.of(view.getCbSideboardForAI(), FPref.MATCH_SIDEBOARD_FOR_AI)); lstControls.add(Pair.of(view.getCbFilteredHands(), FPref.FILTERED_HANDS)); lstControls.add(Pair.of(view.getCbCloneImgSource(), FPref.UI_CLONE_MODE_SOURCE)); lstControls.add(Pair.of(view.getCbRemoveSmall(), FPref.DECKGEN_NOSMALL)); @@ -147,7 +148,7 @@ public enum CSubmenuPreferences implements ICDoc { for(final Pair kv : lstControls) { - kv.getKey().addItemListener(new ItemListener() { + kv.getKey().addItemListener(new ItemListener() { @Override public void itemStateChanged(final ItemEvent arg0) { if (updating) { return; } diff --git a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java index b1cebaa82a6..11499dd475e 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java +++ b/forge-gui-desktop/src/main/java/forge/screens/home/settings/VSubmenuPreferences.java @@ -75,6 +75,7 @@ public enum VSubmenuPreferences implements IVSubmenu { private final JCheckBox cbLoadHistoricFormats = new OptionsCheckBox(localizer.getMessage("cbLoadHistoricFormats")); private final JCheckBox cbWorkshopSyntax = new OptionsCheckBox(localizer.getMessage("cbWorkshopSyntax")); private final JCheckBox cbEnforceDeckLegality = new OptionsCheckBox(localizer.getMessage("cbEnforceDeckLegality")); + private final JCheckBox cbSideboardForAI = new OptionsCheckBox(localizer.getMessage("cbSideboardForAI")); private final JCheckBox cbPerformanceMode = new OptionsCheckBox(localizer.getMessage("cbPerformanceMode")); private final JCheckBox cbFilteredHands = new OptionsCheckBox(localizer.getMessage("cbFilteredHands")); private final JCheckBox cbImageFetcher = new OptionsCheckBox(localizer.getMessage("cbImageFetcher")); @@ -195,6 +196,9 @@ public enum VSubmenuPreferences implements IVSubmenu { pnlPrefs.add(cbPerformanceMode, titleConstraints); pnlPrefs.add(new NoteLabel(localizer.getMessage("nlPerformanceMode")), descriptionConstraints); + pnlPrefs.add(cbSideboardForAI, titleConstraints); + pnlPrefs.add(new NoteLabel(localizer.getMessage("nlSideboardForAI")), descriptionConstraints); + pnlPrefs.add(cbFilteredHands, titleConstraints); pnlPrefs.add(new NoteLabel(localizer.getMessage("nlFilteredHands")), descriptionConstraints); @@ -680,6 +684,10 @@ public enum VSubmenuPreferences implements IVSubmenu { return cbPerformanceMode; } + public JCheckBox getCbSideboardForAI() { + return cbSideboardForAI; + } + /** @return {@link javax.swing.JCheckBox} */ public JCheckBox getCbFilteredHands() { return cbFilteredHands; diff --git a/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java b/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java index ee280f85f0b..fb9a3ee19be 100644 --- a/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java +++ b/forge-gui-desktop/src/main/java/forge/screens/match/CMatchUI.java @@ -1022,8 +1022,8 @@ public final class CMatchUI } @Override - public List sideboard(final CardPool sideboard, final CardPool main) { - return GuiChoose.sideboard(this, sideboard.toFlatList(), main.toFlatList()); + public List sideboard(final CardPool sideboard, final CardPool main, final String message) { + return GuiChoose.sideboard(this, sideboard.toFlatList(), main.toFlatList(), message); } @Override 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 da8d1191e82..2e7bfcafb12 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 @@ -112,7 +112,7 @@ public class PlayerControllerForTests extends PlayerController { } @Override - public List sideboard(Deck deck, GameType gameType) { + public List sideboard(Deck deck, GameType gameType, String message) { return null; // refused to side } diff --git a/forge-gui-mobile/src/forge/deck/FSideboardDialog.java b/forge-gui-mobile/src/forge/deck/FSideboardDialog.java index 860ec12f46e..dc2c6ea5abe 100644 --- a/forge-gui-mobile/src/forge/deck/FSideboardDialog.java +++ b/forge-gui-mobile/src/forge/deck/FSideboardDialog.java @@ -23,8 +23,8 @@ public class FSideboardDialog extends FDialog { private final SideboardTabs tabs; private final Callback> callback; - public FSideboardDialog(CardPool sideboard, CardPool main, final Callback> callback0) { - super("Update main deck from sideboard", 1); + public FSideboardDialog(CardPool sideboard, CardPool main, final Callback> callback0, String message) { + super("Update main deck from sideboard" + message, 1); callback = callback0; tabs = add(new SideboardTabs(sideboard, main)); diff --git a/forge-gui-mobile/src/forge/screens/match/MatchController.java b/forge-gui-mobile/src/forge/screens/match/MatchController.java index 0a5a83b664a..83a9c1d5a65 100644 --- a/forge-gui-mobile/src/forge/screens/match/MatchController.java +++ b/forge-gui-mobile/src/forge/screens/match/MatchController.java @@ -481,11 +481,11 @@ public class MatchController extends AbstractGuiGame { } @Override - public List sideboard(final CardPool sideboard, final CardPool main) { + public List sideboard(final CardPool sideboard, final CardPool main, final String message) { return new WaitCallback>() { @Override public void run() { - final FSideboardDialog sideboardDialog = new FSideboardDialog(sideboard, main, this); + final FSideboardDialog sideboardDialog = new FSideboardDialog(sideboard, main, this, message); sideboardDialog.show(); } }.invokeAndWait(); diff --git a/forge-gui/res/languages/en-US.properties b/forge-gui/res/languages/en-US.properties index 4be74394616..164fc2726c2 100644 --- a/forge-gui/res/languages/en-US.properties +++ b/forge-gui/res/languages/en-US.properties @@ -33,6 +33,7 @@ cbLoadCardsLazily = Load Card Scripts Lazily cbLoadHistoricFormats = Load Historic Formats cbWorkshopSyntax = Workshop Syntax Checker cbEnforceDeckLegality = Deck Conformance +cbSideboardForAI = Human Sideboard for AI cbPerformanceMode = Performance Mode cbFilteredHands = Filtered Hands cbImageFetcher = Automatically Download Missing Card Art @@ -86,6 +87,7 @@ nlEnableAICheats = Allow the AI to cheat to gain advantage (for personalities th nlManaBurn = Play with mana burn (from pre-Magic 2010 rules). nlManaLostPrompt = When enabled, you get a warning if passing priority would cause you to lose mana in your mana pool. nlEnforceDeckLegality = Enforces deck legality relevant to each environment (minimum deck sizes, max card count etc). +nlSideboardForAI = Allows users to sideboard with the AIs deck and sideboard in constructed game formats. nlPerformanceMode = Disables additional static abilities checks to speed up the game engine. (Warning: breaks some 'as if had flash' scenarios when casting cards owned by opponents). nlFilteredHands = Generates two starting hands and keeps the one with the closest to average land count for the deck. (Requires restart) nlCloneImgSource = When enabled clones will use their original art instead of the cloned card's art. diff --git a/forge-gui/src/main/java/forge/interfaces/IGuiGame.java b/forge-gui/src/main/java/forge/interfaces/IGuiGame.java index 3c9b08f4679..9b53c385e45 100644 --- a/forge-gui/src/main/java/forge/interfaces/IGuiGame.java +++ b/forge-gui/src/main/java/forge/interfaces/IGuiGame.java @@ -144,7 +144,7 @@ public interface IGuiGame { */ List insertInList(String title, T newItem, List oldItems); - List sideboard(CardPool sideboard, CardPool main); + List sideboard(CardPool sideboard, CardPool main, String message); GameEntityView chooseSingleEntityForEffect(String title, List optionList, DelayedReveal delayedReveal, boolean isOptional); List chooseEntitiesForEffect(String title, List optionList, int min, int max, DelayedReveal delayedReveal); diff --git a/forge-gui/src/main/java/forge/match/HostedMatch.java b/forge-gui/src/main/java/forge/match/HostedMatch.java index d42aaa68072..7535a28d20f 100644 --- a/forge-gui/src/main/java/forge/match/HostedMatch.java +++ b/forge-gui/src/main/java/forge/match/HostedMatch.java @@ -79,6 +79,7 @@ public class HostedMatch { gameRules.setPlayForAnte(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE)); gameRules.setMatchAnteRarity(FModel.getPreferences().getPrefBoolean(FPref.UI_ANTE_MATCH_RARITY)); gameRules.setManaBurn(FModel.getPreferences().getPrefBoolean(FPref.UI_MANABURN)); + gameRules.setSideboardForAI(FModel.getPreferences().getPrefBoolean(FPref.MATCH_SIDEBOARD_FOR_AI)); gameRules.setCanCloneUseTargetsImage(FModel.getPreferences().getPrefBoolean(FPref.UI_CLONE_MODE_SOURCE)); return gameRules; } diff --git a/forge-gui/src/main/java/forge/net/server/NetGuiGame.java b/forge-gui/src/main/java/forge/net/server/NetGuiGame.java index 4d1174c1760..73fe66fc626 100644 --- a/forge-gui/src/main/java/forge/net/server/NetGuiGame.java +++ b/forge-gui/src/main/java/forge/net/server/NetGuiGame.java @@ -234,8 +234,8 @@ public class NetGuiGame extends AbstractGuiGame { } @Override - public List sideboard(final CardPool sideboard, final CardPool main) { - return sendAndWait(ProtocolMethod.sideboard, sideboard, main); + public List sideboard(final CardPool sideboard, final CardPool main, final String message) { + return sendAndWait(ProtocolMethod.sideboard, sideboard, main, message); } @Override diff --git a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java index 8ec57be16e6..c011bccd12e 100644 --- a/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java +++ b/forge-gui/src/main/java/forge/player/PlayerControllerHuman.java @@ -209,11 +209,10 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont } @Override - public List sideboard(final Deck deck, final GameType gameType) { + public List sideboard(final Deck deck, final GameType gameType, String message) { CardPool sideboard = deck.get(DeckSection.Sideboard); if (sideboard == null) { - // Use an empty cardpool instead of null for 75/0 sideboarding - // scenario. + // Use an empty cardpool instead of null for 75/0 sideboarding scenario. sideboard = new CardPool(); } @@ -253,7 +252,7 @@ public class PlayerControllerHuman extends PlayerController implements IGameCont // Sideboard rules have changed for M14, just need to consider min // maindeck and max sideboard sizes // No longer need 1:1 sideboarding in non-limited formats - Object resp = getGui().sideboard(sideboard, main); + Object resp = getGui().sideboard(sideboard, main, message); if (resp instanceof List && !((List) resp).isEmpty() && ((List) resp).get(0) instanceof PaperCard) { diff --git a/forge-gui/src/main/java/forge/properties/ForgePreferences.java b/forge-gui/src/main/java/forge/properties/ForgePreferences.java index 8caea6fe279..747392138d7 100644 --- a/forge-gui/src/main/java/forge/properties/ForgePreferences.java +++ b/forge-gui/src/main/java/forge/properties/ForgePreferences.java @@ -156,6 +156,7 @@ public class ForgePreferences extends PreferencesStore { SUBMENU_SETTINGS ("false"), SUBMENU_UTILITIES ("false"), + MATCH_SIDEBOARD_FOR_AI("true"), // TODO What do when AI knows how to SIdeboard? ENFORCE_DECK_LEGALITY ("true"), PERFORMANCE_MODE ("false"), FILTERED_HANDS ("false"),