From 2968d7e5e2207e095b8f0afb44048d75a1182db2 Mon Sep 17 00:00:00 2001 From: Robbatog Date: Fri, 4 Nov 2022 15:48:52 +0100 Subject: [PATCH] Introduce Evolving Wilds quest world. It has a random selection of legal core- and expansion sets that rotate periodically based on the number of wins. --- .../src/main/java/forge/game/GameFormat.java | 4 ++ forge-gui/res/quest/world/worlds.txt | 1 + .../forge/gamemodes/quest/QuestWorld.java | 18 +++++++ .../gamemodes/quest/data/GameFormatQuest.java | 48 ++++++++++++++++++- .../forge/gamemodes/quest/data/QuestData.java | 2 +- .../quest/setrotation/ISetRotation.java | 11 +++++ .../setrotation/QueueRandomRotation.java | 48 +++++++++++++++++++ 7 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 forge-gui/src/main/java/forge/gamemodes/quest/setrotation/ISetRotation.java create mode 100644 forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java diff --git a/forge-game/src/main/java/forge/game/GameFormat.java b/forge-game/src/main/java/forge/game/GameFormat.java index 87861ea9c85..e546f721cda 100644 --- a/forge-game/src/main/java/forge/game/GameFormat.java +++ b/forge-game/src/main/java/forge/game/GameFormat.java @@ -558,6 +558,10 @@ public class GameFormat implements Comparable { return this.map.get("Modern"); } + public GameFormat getVintage() { + return this.map.get("Vintage"); + } + public GameFormat getFormat(String format) { return this.map.get(format); } diff --git a/forge-gui/res/quest/world/worlds.txt b/forge-gui/res/quest/world/worlds.txt index 15f8c8a23d1..dbec86fc79c 100644 --- a/forge-gui/res/quest/world/worlds.txt +++ b/forge-gui/res/quest/world/worlds.txt @@ -3,6 +3,7 @@ Name:Random Standard Name:Random Pioneer Name:Random Modern Name:Random Commander +Name:Evolving Wilds Name:Amonkhet|Dir:Amonkhet|Sets:AKH, HOU, AKR Name:Jamuraa|Dir:jamuraa|Sets:5ED, ARN, MIR, VIS, WTH Name:Kamigawa|Dir:2004 Kamigawa|Sets:CHK, BOK, SOK diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/QuestWorld.java b/forge-gui/src/main/java/forge/gamemodes/quest/QuestWorld.java index 18f6e19d93f..10f06900152 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/QuestWorld.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/QuestWorld.java @@ -26,9 +26,12 @@ import java.util.Set; import com.google.common.base.Function; +import forge.card.CardEdition; import forge.deck.Deck; import forge.game.GameFormat; import forge.gamemodes.quest.data.GameFormatQuest; +import forge.gamemodes.quest.setrotation.ISetRotation; +import forge.gamemodes.quest.setrotation.QueueRandomRotation; import forge.item.PaperCard; import forge.model.FModel; import forge.util.storage.StorageReaderFile; @@ -46,6 +49,7 @@ public class QuestWorld implements Comparable{ public static final String MODERNWORLDNAME = "Random Modern"; public static final String RANDOMCOMMANDERWORLDNAME = "Random Commander"; public static final String MAINWORLDNAME = "Main world"; + public static final String EVOLVINGWILDSWORLDNAME = "Evolving Wilds"; private boolean isCustom; @@ -217,6 +221,20 @@ public class QuestWorld implements Comparable{ FModel.getFormats().getFormat("Commander").getBannedCardNames(),false); } + if (useName.equalsIgnoreCase(QuestWorld.EVOLVINGWILDSWORLDNAME)){ + ISetRotation rot = new QueueRandomRotation(6, 5, 1); + List allowedCodes = new ArrayList<>(); + for (CardEdition edition : FModel.getMagicDb().getEditionsTypeMap().get(CardEdition.Type.CORE)) { + allowedCodes.add(edition.getCode()); + } + for (CardEdition edition : FModel.getMagicDb().getEditionsTypeMap().get(CardEdition.Type.EXPANSION)) { + allowedCodes.add(edition.getCode()); + } + useFormat = new GameFormatQuest(QuestWorld.EVOLVINGWILDSWORLDNAME, + allowedCodes, + FModel.getFormats().getVintage().getBannedCardNames(),false, rot); + } + // System.out.println("Creating quest world " + useName + " (index " + useIdx + ", dir: " + useDir); // if (useFormat != null) { System.out.println("SETS: " + sets + "\nBANNED: " + bannedCards); } diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/data/GameFormatQuest.java b/forge-gui/src/main/java/forge/gamemodes/quest/data/GameFormatQuest.java index 51ea2a97090..fc63af93743 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/data/GameFormatQuest.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/data/GameFormatQuest.java @@ -21,9 +21,10 @@ import java.util.ArrayList; import java.util.List; import com.google.common.base.Predicate; - import forge.card.CardEdition; import forge.game.GameFormat; +import forge.gamemodes.quest.setrotation.ISetRotation; +import forge.item.PaperCard; import forge.model.FModel; @@ -38,6 +39,31 @@ public final class GameFormatQuest extends GameFormat { private final boolean allowUnlocks; private int unlocksUsed = 0; + private ISetRotation setRotation; + + @Override + public List getAllowedSetCodes() { + if(setRotation != null) + return setRotation.getCurrentSetCodes(super.getAllowedSetCodes()); + return super.getAllowedSetCodes(); + } + + @Override + public Predicate getFilterRules() { + // Filter must be continuously rebuilt if the format is mutable + if(setRotation != null) + return super.buildFilter(false); + return super.getFilterRules(); + } + + @Override + public Predicate getFilterPrinted() { + // Filter must be continuously rebuilt if the format is mutable + if(setRotation != null) + return super.buildFilter(true); + return super.getFilterRules(); + } + /** * Instantiates a new game format based on two lists. * @@ -48,11 +74,27 @@ public final class GameFormatQuest extends GameFormat { public GameFormatQuest(final String newName, final List setsToAllow, final List cardsToBan) { super(newName, setsToAllow, cardsToBan); allowUnlocks = false; + setRotation = null; } public GameFormatQuest(final String newName, final List setsToAllow, final List cardsToBan, boolean allowSetUnlocks) { super(newName, setsToAllow, cardsToBan); allowUnlocks = allowSetUnlocks; + setRotation = null; + } + + /** + * Instantiates a format that uses an ISetRotation to automatically rotate sets in and out + * @param newName + * @param setsToAllow + * @param cardsToBan + * @param allowSetUnlocks + * @param setRotation an ISetRotation that determines the currently allowed sets + */ + public GameFormatQuest(final String newName, final List setsToAllow, final List cardsToBan, boolean allowSetUnlocks, ISetRotation setRotation) { + super(newName, setsToAllow, cardsToBan); + allowUnlocks = allowSetUnlocks; + this.setRotation = setRotation; } /** @@ -60,12 +102,14 @@ public final class GameFormatQuest extends GameFormat { * * @param toCopy an existing format * @param allowSetUnlocks + * @param setRotation */ - public GameFormatQuest(final GameFormat toCopy, boolean allowSetUnlocks) { + public GameFormatQuest(final GameFormat toCopy, boolean allowSetUnlocks, ISetRotation setRotation) { super(toCopy.getName(), toCopy.getEffectiveDate(), toCopy.getAllowedSetCodes(), toCopy.getBannedCardNames(), toCopy.getRestrictedCards(), toCopy.isRestrictedLegendary(),toCopy.getAdditionalCards(), toCopy.getAllowedRarities(), toCopy.getIndex(), FormatType.CUSTOM, FormatSubType.CUSTOM); allowUnlocks = allowSetUnlocks; + this.setRotation = setRotation; } /** diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/data/QuestData.java b/forge-gui/src/main/java/forge/gamemodes/quest/data/QuestData.java index 390ece7a5a7..ebe426698cc 100644 --- a/forge-gui/src/main/java/forge/gamemodes/quest/data/QuestData.java +++ b/forge-gui/src/main/java/forge/gamemodes/quest/data/QuestData.java @@ -100,7 +100,7 @@ public class QuestData { this.name = name0; if (userFormat != null) { - this.format = new GameFormatQuest(userFormat, allowSetUnlocks); + this.format = new GameFormatQuest(userFormat, allowSetUnlocks, null); } this.mode = mode0; this.achievements = new QuestAchievements(diff); diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/ISetRotation.java b/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/ISetRotation.java new file mode 100644 index 00000000000..f477b7e05e1 --- /dev/null +++ b/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/ISetRotation.java @@ -0,0 +1,11 @@ +package forge.gamemodes.quest.setrotation; + +import java.util.List; + +/** + * Supplies the current rotation of set codes based on the current quest state. + * Used for quest worlds that change their sets automatically over time. + */ +public interface ISetRotation { + public List getCurrentSetCodes(List allowedSetCodes); +} diff --git a/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java b/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java new file mode 100644 index 00000000000..bc52df6736f --- /dev/null +++ b/forge-gui/src/main/java/forge/gamemodes/quest/setrotation/QueueRandomRotation.java @@ -0,0 +1,48 @@ +package forge.gamemodes.quest.setrotation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Random; +import forge.model.FModel; + +import static java.lang.Integer.min; + +/** + * A quest world rotation style where N random concurrent sets are available at any given time. + * S of these sets are rotated out after each set of W number of wins. + */ +public class QueueRandomRotation implements ISetRotation { + + private int concurrentSets; + private int rotateAfterWins; + private int setsPerRotation; + + @Override + public List getCurrentSetCodes(List allSets) { + if(FModel.getQuest() == null) + return allSets; + + // Each unique quest (based on name) gets its own unique set order + int seed = FModel.getQuest().getName().hashCode(); + Random rnd = new Random(seed); + List shuffledSets = new ArrayList<>(allSets); + Collections.shuffle(shuffledSets, rnd); + + List currentCodes = new ArrayList<>(); + int outRotations = FModel.getQuest().getAchievements().getWin() / rotateAfterWins; + int outRotated = outRotations * setsPerRotation; + int setsToAdd = min(concurrentSets, shuffledSets.size()); + for (int i = 0; i < setsToAdd; i++) { + int setToAdd = (i + outRotated) % shuffledSets.size(); + currentCodes.add(shuffledSets.get(setToAdd)); + } + return currentCodes; + } + + public QueueRandomRotation(int concurrentSets, int rotateAfterWins, int setsPerRotation){ + this.concurrentSets = concurrentSets; + this.rotateAfterWins = rotateAfterWins; + this.setsPerRotation = setsPerRotation; + } +}