From da2e66b624c72c6ebf6171467b3930f447663875 Mon Sep 17 00:00:00 2001 From: Robbatog Date: Thu, 3 Nov 2022 15:20:51 +0100 Subject: [PATCH] Enforce quest- and world's rules before starting a duel. E.g. no Vintage cards allowed in player's deck while in Random Standard world. --- .../src/main/java/forge/game/GameFormat.java | 68 +++++++++++++++---- .../java/forge/gamemodes/quest/QuestUtil.java | 15 +++- 2 files changed, 66 insertions(+), 17 deletions(-) diff --git a/forge-game/src/main/java/forge/game/GameFormat.java b/forge-game/src/main/java/forge/game/GameFormat.java index d27bba2b5fe..29b1474c8c2 100644 --- a/forge-game/src/main/java/forge/game/GameFormat.java +++ b/forge-game/src/main/java/forge/game/GameFormat.java @@ -245,27 +245,65 @@ public class GameFormat implements Comparable { } private boolean isPoolLegal(final CardPool allCards) { - for (Entry poolEntry : allCards) { - if (!filterRules.apply(poolEntry.getKey())) { - return false; //all cards in deck must pass card predicate to pass deck predicate - } - } - - if(!restrictedCardNames_ro.isEmpty() || restrictedLegendary ) { - for (Entry poolEntry : allCards) { - if( poolEntry.getValue().intValue() > 1 && (restrictedCardNames_ro.contains(poolEntry.getKey().getName()) - || (poolEntry.getKey().getRules().getType().isLegendary() - && !poolEntry.getKey().getRules().getType().isPlaneswalker() && restrictedLegendary))) - return false; - } - } - return true; + return getPoolLegalityProblem(allCards) == null; } public boolean isDeckLegal(final Deck deck) { return isPoolLegal(deck.getAllCardsInASinglePool()); } + private String getPoolLegalityProblem(final CardPool allCards) { + // Check filter rules + { + final List erroneousCI = new ArrayList<>(); + for (Entry poolEntry : allCards) { + if (!filterRules.apply(poolEntry.getKey())) { + erroneousCI.add(poolEntry.getKey()); + } + } + if (erroneousCI.size() > 0) { + final StringBuilder sb = new StringBuilder("contains the following illegal cards:\n"); + for (final PaperCard cp : erroneousCI) { + sb.append("\n").append(cp.getName()); + } + return sb.toString(); + } + } + // Check number of restricted and legendary-restricted cards + if(!restrictedCardNames_ro.isEmpty() || restrictedLegendary ) { + final List erroneousRestricted = new ArrayList<>(); + for (Entry poolEntry : allCards) { + boolean isRestricted = restrictedCardNames_ro.contains(poolEntry.getKey().getName()); + boolean isLegendaryNonPlaneswalker = poolEntry.getKey().getRules().getType().isLegendary() + && !poolEntry.getKey().getRules().getType().isPlaneswalker() && restrictedLegendary; + if( poolEntry.getValue() > 1 && (isRestricted || isLegendaryNonPlaneswalker)) { + erroneousRestricted.add(poolEntry.getKey()); + } + } + if (erroneousRestricted.size() > 0) { + final StringBuilder sb = new StringBuilder("contains more than one copy of the following restricted cards:\n"); + for (final PaperCard cp : erroneousRestricted) { + sb.append("\n").append(cp.getName()); + } + return sb.toString(); + } + } + return null; + } + + /** + * Check the conformance of a deck in this GameFormat. + * Will check each card's set legality, and ensure no banned and max one of each restricted card exists. + * @param deck The deck to analyse + * @return An error string describing the errors and associated cards, or null if no errors + */ + public String getDeckConformanceProblem(final Deck deck) { + if (deck == null) { + return "is not selected"; + } + return getPoolLegalityProblem(deck.getAllCardsInASinglePool()); + } + @Override public String toString() { return this.name; diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtil.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtil.java index 38e831daaa3..680c36cf9cc 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtil.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestUtil.java @@ -664,7 +664,7 @@ public class QuestUtil { } if (FModel.getPreferences().getPrefBoolean(FPref.ENFORCE_DECK_LEGALITY)) { - final String errorMessage = getDeckConformanceProblems(deck); + final String errorMessage = getDeckConformanceProblemsBeforeGame(deck); if (null != errorMessage) { SOptionPane.showErrorDialog(localizer.getMessage("lblInvalidDeckDesc").replace("%n",errorMessage), "Invalid Deck"); return false; @@ -674,9 +674,15 @@ public class QuestUtil { return true; } - public static String getDeckConformanceProblems(Deck deck){ + public static String getDeckConformanceProblemsBeforeGame(Deck deck){ + // Challenges with fixed decks override conformance settings + if(event instanceof QuestEventChallenge && ((QuestEventChallenge) event).getHumanDeck() != null) + return null; + + //Check quest mode's generic deck construction rules: minimum cards in deck, sideboard etc String errorMessage = GameType.Quest.getDeckFormat().getDeckConformanceProblem(deck); + //Check the quest mode's generic deck construction rules: minimum cards in deck, sideboard etc if(errorMessage != null) return errorMessage; //return immediately if the deck does not conform to quest requirements //Check for all applicable deck construction rules per this quests's saved DeckConstructionRules enum @@ -685,6 +691,11 @@ public class QuestUtil { errorMessage = GameType.Commander.getDeckFormat().getDeckConformanceProblem(deck); break; } + if(errorMessage != null) return errorMessage; + + //Check for this quest- and World's deck construction rules: allowed sets, banned/restricted cards etc + if(FModel.getQuest().getFormat() != null) + errorMessage = FModel.getQuest().getFormat().getDeckConformanceProblem(deck); return errorMessage; }