From 4554dee721fbb50f382a9a03604d6577624a54c0 Mon Sep 17 00:00:00 2001 From: Chris H Date: Wed, 31 Jul 2024 19:11:11 -0400 Subject: [PATCH] Generate individual booster slots with individual replacement percentages Convert MH3 to booster slot percentages --- .../src/main/java/forge/StaticData.java | 20 +-- .../src/main/java/forge/card/CardEdition.java | 115 ++++++++------ .../src/main/java/forge/item/BoosterBox.java | 3 +- .../src/main/java/forge/item/BoosterPack.java | 11 +- .../src/main/java/forge/item/BoosterSlot.java | 72 +++++++++ .../main/java/forge/item/BoxedProduct.java | 2 +- .../src/main/java/forge/item/FatPack.java | 10 +- .../main/java/forge/item/SealedProduct.java | 141 +----------------- .../main/java/forge/item/SealedTemplate.java | 132 ++++++++++++++++ .../forge/item/SealedTemplateWithSlots.java | 21 +++ .../main/java/forge/item/TournamentPack.java | 7 +- .../item/generation/BoosterGenerator.java | 81 +++++++--- .../item/generation/UnOpenedProduct.java | 39 ++--- .../game/ability/effects/MakeCardEffect.java | 14 +- .../src/test/java/forge/BoosterDraftTest.java | 4 +- .../util/AdventureEventController.java | 4 +- .../src/forge/adventure/util/CardUtil.java | 4 +- forge-gui/res/editions/Modern Horizons 3.txt | 43 +++++- .../forge/gamemodes/limited/BoosterDraft.java | 6 +- .../gamemodes/limited/CustomLimited.java | 25 ++-- .../limited/SealedCardPoolGenerator.java | 6 +- .../forge/gamemodes/quest/QuestUtilCards.java | 43 ++---- .../gamemodes/quest/QuestUtilUnlockSets.java | 12 +- .../quest/QuestWinLoseController.java | 38 ++--- .../src/main/java/forge/model/MetaSet.java | 15 +- 25 files changed, 520 insertions(+), 348 deletions(-) create mode 100644 forge-core/src/main/java/forge/item/BoosterSlot.java create mode 100644 forge-core/src/main/java/forge/item/SealedTemplate.java create mode 100644 forge-core/src/main/java/forge/item/SealedTemplateWithSlots.java diff --git a/forge-core/src/main/java/forge/StaticData.java b/forge-core/src/main/java/forge/StaticData.java index d2d681d2eb5..adfa2544cc5 100644 --- a/forge-core/src/main/java/forge/StaticData.java +++ b/forge-core/src/main/java/forge/StaticData.java @@ -52,9 +52,9 @@ public class StaticData { private boolean sourceImageForClone; // Loaded lazily: - private IStorage boosters; - private IStorage specialBoosters; - private IStorage tournaments; + private IStorage boosters; + private IStorage specialBoosters; + private IStorage tournaments; private IStorage fatPacks; private IStorage boosterBoxes; private IStorage printSheets; @@ -362,23 +362,23 @@ public class StaticData { return this.commonCards.contains(cr.cardName) || this.variantCards.contains(cr.cardName); } - /** @return {@link forge.util.storage.IStorage}<{@link forge.item.SealedProduct.Template}> */ - public final IStorage getTournamentPacks() { + /** @return {@link forge.util.storage.IStorage}<{@link forge.item.SealedTemplate}> */ + public final IStorage getTournamentPacks() { if (tournaments == null) - tournaments = new StorageBase<>("Starter sets", new SealedProduct.Template.Reader(new File(blockDataFolder, "starters.txt"))); + tournaments = new StorageBase<>("Starter sets", new SealedTemplate.Reader(new File(blockDataFolder, "starters.txt"))); return tournaments; } - /** @return {@link forge.util.storage.IStorage}<{@link forge.item.SealedProduct.Template}> */ - public final IStorage getBoosters() { + /** @return {@link forge.util.storage.IStorage}<{@link forge.item.SealedTemplate}> */ + public final IStorage getBoosters() { if (boosters == null) boosters = new StorageBase<>("Boosters", editions.getBoosterGenerator()); return boosters; } - public final IStorage getSpecialBoosters() { + public final IStorage getSpecialBoosters() { if (specialBoosters == null) - specialBoosters = new StorageBase<>("Special boosters", new SealedProduct.Template.Reader(new File(blockDataFolder, "boosters-special.txt"))); + specialBoosters = new StorageBase<>("Special boosters", new SealedTemplate.Reader(new File(blockDataFolder, "boosters-special.txt"))); return specialBoosters; } diff --git a/forge-core/src/main/java/forge/card/CardEdition.java b/forge-core/src/main/java/forge/card/CardEdition.java index e3b96a48b1c..ec9457f4302 100644 --- a/forge-core/src/main/java/forge/card/CardEdition.java +++ b/forge-core/src/main/java/forge/card/CardEdition.java @@ -22,8 +22,10 @@ import com.google.common.collect.*; import forge.StaticData; import forge.card.CardDb.CardArtPreference; import forge.deck.CardPool; +import forge.item.BoosterSlot; import forge.item.PaperCard; -import forge.item.SealedProduct; +import forge.item.SealedTemplate; +import forge.item.SealedTemplateWithSlots; import forge.util.*; import forge.util.storage.StorageBase; import forge.util.storage.StorageReaderBase; @@ -276,6 +278,7 @@ public final class CardEdition implements Comparable { private String fatPackExtraSlots = ""; // Booster/draft info + private List boosterSlots = null; private boolean smallSetOverride = false; private boolean foilAlwaysInCommonSlot = false; private FoilType foilType = FoilType.NOT_SUPPORTED; @@ -298,8 +301,8 @@ public final class CardEdition implements Comparable { private final Map> customPrintSheetsToParse; private int boosterArts = 1; - private SealedProduct.Template boosterTpl = null; - private final Map boosterTemplates = new HashMap<>(); + private SealedTemplate boosterTpl = null; + private final Map boosterTemplates = new HashMap<>(); private CardEdition(ListMultimap cardMap, Map tokens, Map> customPrintSheetsToParse) { this.cardMap = cardMap; @@ -471,10 +474,10 @@ public final class CardEdition implements Comparable { return boosterArts; } - public SealedProduct.Template getBoosterTemplate() { + public SealedTemplate getBoosterTemplate() { return getBoosterTemplate("Draft"); } - public SealedProduct.Template getBoosterTemplate(String boosterType) { + public SealedTemplate getBoosterTemplate(String boosterType) { return boosterTemplates.get(boosterType); } public String getRandomBoosterKind() { @@ -575,15 +578,23 @@ public final class CardEdition implements Comparable { ); ListMultimap cardMap = ArrayListMultimap.create(); + List boosterSlots = Lists.newArrayList(); Map tokenNormalized = new HashMap<>(); Map> customPrintSheetsToParse = new HashMap<>(); List editionSectionsWithCollectorNumbers = EditionSectionWithCollectorNumbers.getNames(); + FileSection metadata = FileSection.parse(contents.get("metadata"), FileSection.EQUALS_KV_SEPARATOR); + List boosterSlotsToParse = Lists.newArrayList(); + if (metadata.contains("BoosterSlots")) { + boosterSlotsToParse = Lists.newArrayList(metadata.get("BoosterSlots").split(",")); + } + for (String sectionName : contents.keySet()) { // skip reserved section names like 'metadata' and 'tokens' that are handled separately if (reservedSectionNames.contains(sectionName)) { continue; } + // parse sections of the format " " if (editionSectionsWithCollectorNumbers.contains(sectionName)) { for(String line : contents.get(sectionName)) { @@ -602,10 +613,12 @@ public final class CardEdition implements Comparable { cardMap.put(sectionName, cis); } - } - // save custom print sheets of the format " ||" - // to parse later when printsheets are loaded lazily (and the cardpool is already initialized) - else { + } else if (boosterSlotsToParse.contains(sectionName)) { + // parse booster slots of the format "Base=N\n|Replace= " + boosterSlots.add(BoosterSlot.parseSlot(sectionName, contents.get(sectionName))); + } else { + // save custom print sheets of the format " ||" + // to parse later when printsheets are loaded lazily (and the cardpool is already initialized) customPrintSheetsToParse.put(sectionName, contents.get(sectionName)); } } @@ -625,31 +638,37 @@ public final class CardEdition implements Comparable { } CardEdition res = new CardEdition(cardMap, tokenNormalized, customPrintSheetsToParse); - + res.boosterSlots = boosterSlots; // parse metadata section - FileSection section = FileSection.parse(contents.get("metadata"), FileSection.EQUALS_KV_SEPARATOR); - res.name = section.get("name"); - res.date = parseDate(section.get("date")); - res.code = section.get("code"); - res.code2 = section.get("code2"); + res.name = metadata.get("name"); + res.date = parseDate(metadata.get("date")); + res.code = metadata.get("code"); + res.code2 = metadata.get("code2"); if (res.code2 == null) { res.code2 = res.code; } - res.scryfallCode = section.get("ScryfallCode"); + res.scryfallCode = metadata.get("ScryfallCode"); if (res.scryfallCode == null) { res.scryfallCode = res.code; } - res.cardsLanguage = section.get("CardLang"); + res.cardsLanguage = metadata.get("CardLang"); if (res.cardsLanguage == null) { res.cardsLanguage = "en"; } - res.boosterArts = section.getInt("BoosterCovers", 1); - String boosterDesc = section.get("Booster"); + res.boosterArts = metadata.getInt("BoosterCovers", 1); - if (section.contains("Booster")) { + String boosterDesc = metadata.get("Booster"); + + if (metadata.contains("Booster")) { // Historical naming convention in Forge for "DraftBooster" - res.boosterTpl = new SealedProduct.Template(res.code, SealedProduct.Template.Reader.parseSlots(boosterDesc)); + // Do i have access to editions slots? + if (res.boosterSlots != null) { + res.boosterTpl = new SealedTemplateWithSlots(res.code, SealedTemplate.Reader.parseSlots(boosterDesc), res.boosterSlots); + } else { + res.boosterTpl = new SealedTemplate(res.code, SealedTemplate.Reader.parseSlots(boosterDesc)); + } + res.boosterTemplates.put("Draft", res.boosterTpl); } @@ -657,18 +676,18 @@ public final class CardEdition implements Comparable { // Theme boosters aren't here because they are closer to preconstructed decks, and should be treated as such for (String type : boostertype) { String name = type + "Booster"; - if (section.contains(name)) { - res.boosterTemplates.put(type, new SealedProduct.Template(res.code, SealedProduct.Template.Reader.parseSlots(section.get(name)))); + if (metadata.contains(name)) { + res.boosterTemplates.put(type, new SealedTemplate(res.code, SealedTemplate.Reader.parseSlots(metadata.get(name)))); } } - res.alias = section.get("alias"); - res.borderColor = BorderColor.valueOf(section.get("border", "Black").toUpperCase(Locale.ENGLISH)); + res.alias = metadata.get("alias"); + res.borderColor = BorderColor.valueOf(metadata.get("border", "Black").toUpperCase(Locale.ENGLISH)); Type enumType = Type.UNKNOWN; if (this.isCustomEditions){ enumType = Type.CUSTOM_SET; // Forcing ThirdParty Edition Type to avoid inconsistencies } else { - String type = section.get("type"); + String type = metadata.get("type"); if (null != type && !type.isEmpty()) { try { enumType = Type.valueOf(type.toUpperCase(Locale.ENGLISH)); @@ -680,12 +699,12 @@ public final class CardEdition implements Comparable { } res.type = enumType; - res.prerelease = section.get("Prerelease", null); - res.boosterBoxCount = Integer.parseInt(section.get("BoosterBox", enumType.getBoosterBoxDefault())); - res.fatPackCount = Integer.parseInt(section.get("FatPack", enumType.getFatPackDefault())); - res.fatPackExtraSlots = section.get("FatPackExtraSlots", ""); + res.prerelease = metadata.get("Prerelease", null); + res.boosterBoxCount = Integer.parseInt(metadata.get("BoosterBox", enumType.getBoosterBoxDefault())); + res.fatPackCount = Integer.parseInt(metadata.get("FatPack", enumType.getFatPackDefault())); + res.fatPackExtraSlots = metadata.get("FatPackExtraSlots", ""); - switch (section.get("foil", "newstyle").toLowerCase()) { + switch (metadata.get("foil", "newstyle").toLowerCase()) { case "notsupported": res.foilType = FoilType.NOT_SUPPORTED; break; @@ -701,25 +720,25 @@ public final class CardEdition implements Comparable { res.foilType = FoilType.NOT_SUPPORTED; break; } - String[] replaceCommon = section.get("ChanceReplaceCommonWith", "0F Common").split(" ", 2); + String[] replaceCommon = metadata.get("ChanceReplaceCommonWith", "0F Common").split(" ", 2); res.chanceReplaceCommonWith = Double.parseDouble(replaceCommon[0]); res.slotReplaceCommonWith = replaceCommon[1]; - res.foilChanceInBooster = section.getDouble("FoilChanceInBooster", 21.43F) / 100.0F; + res.foilChanceInBooster = metadata.getDouble("FoilChanceInBooster", 21.43F) / 100.0F; - res.foilAlwaysInCommonSlot = section.getBoolean("FoilAlwaysInCommonSlot", true); - res.additionalSheetForFoils = section.get("AdditionalSheetForFoils", ""); + res.foilAlwaysInCommonSlot = metadata.getBoolean("FoilAlwaysInCommonSlot", true); + res.additionalSheetForFoils = metadata.get("AdditionalSheetForFoils", ""); - res.additionalUnlockSet = section.get("AdditionalSetUnlockedInQuest", ""); // e.g. Time Spiral Timeshifted (TSB) for Time Spiral + res.additionalUnlockSet = metadata.get("AdditionalSetUnlockedInQuest", ""); // e.g. Time Spiral Timeshifted (TSB) for Time Spiral - res.smallSetOverride = section.getBoolean("TreatAsSmallSet", false); // for "small" sets with over 200 cards (e.g. Eldritch Moon) - res.doublePickDuringDraft = section.get("DoublePick", ""); // "FirstPick" or "Always" + res.smallSetOverride = metadata.getBoolean("TreatAsSmallSet", false); // for "small" sets with over 200 cards (e.g. Eldritch Moon) + res.doublePickDuringDraft = metadata.get("DoublePick", ""); // "FirstPick" or "Always" - res.boosterMustContain = section.get("BoosterMustContain", ""); // e.g. Dominaria guaranteed legendary creature - res.boosterReplaceSlotFromPrintSheet = section.get("BoosterReplaceSlotFromPrintSheet", ""); // e.g. Zendikar Rising guaranteed double-faced card - res.sheetReplaceCardFromSheet = section.get("SheetReplaceCardFromSheet", ""); - res.sheetReplaceCardFromSheet2 = section.get("SheetReplaceCardFromSheet2", ""); - res.chaosDraftThemes = section.get("ChaosDraftThemes", "").split(";"); // semicolon separated list of theme names + res.boosterMustContain = metadata.get("BoosterMustContain", ""); // e.g. Dominaria guaranteed legendary creature + res.boosterReplaceSlotFromPrintSheet = metadata.get("BoosterReplaceSlotFromPrintSheet", ""); // e.g. Zendikar Rising guaranteed double-faced card + res.sheetReplaceCardFromSheet = metadata.get("SheetReplaceCardFromSheet", ""); + res.sheetReplaceCardFromSheet2 = metadata.get("SheetReplaceCardFromSheet2", ""); + res.chaosDraftThemes = metadata.get("ChaosDraftThemes", "").split(";"); // semicolon separated list of theme names return res; } @@ -806,11 +825,11 @@ public final class CardEdition implements Comparable { public final Comparator CARD_EDITION_COMPARATOR = Comparator.comparing(c -> Collection.this.get(c.getEdition())); - public IItemReader getBoosterGenerator() { - return new StorageReaderBase(null) { + public IItemReader getBoosterGenerator() { + return new StorageReaderBase(null) { @Override - public Map readAll() { - Map map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + public Map readAll() { + Map map = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); for (CardEdition ce : Collection.this) { List boosterTypes = Lists.newArrayList(ce.getAvailableBoosterTypes()); for (String type : boosterTypes) { @@ -823,7 +842,7 @@ public final class CardEdition implements Comparable { } @Override - public String getItemKey(SealedProduct.Template item) { + public String getItemKey(SealedTemplate item) { return item.getEdition(); } diff --git a/forge-core/src/main/java/forge/item/BoosterBox.java b/forge-core/src/main/java/forge/item/BoosterBox.java index 54cd2f480c0..3bf78cceb28 100644 --- a/forge-core/src/main/java/forge/item/BoosterBox.java +++ b/forge-core/src/main/java/forge/item/BoosterBox.java @@ -18,6 +18,7 @@ package forge.item; +import com.google.common.base.Function; import java.util.ArrayList; import org.apache.commons.lang3.tuple.Pair; @@ -63,7 +64,7 @@ public class BoosterBox extends BoxedProduct { return super.getTotalCards() * fpData.getCntBoosters() + fpData.getNumberOfCardsExpected(); } - public static class Template extends SealedProduct.Template { + public static class Template extends SealedTemplate { private final int cntBoosters; public int getCntBoosters() { return cntBoosters; } diff --git a/forge-core/src/main/java/forge/item/BoosterPack.java b/forge-core/src/main/java/forge/item/BoosterPack.java index 33d5245cdbb..0c71603a5ee 100644 --- a/forge-core/src/main/java/forge/item/BoosterPack.java +++ b/forge-core/src/main/java/forge/item/BoosterPack.java @@ -18,7 +18,7 @@ package forge.item; -import org.apache.commons.lang3.tuple.Pair; +import com.google.common.base.Function; import com.google.common.collect.ImmutableList; @@ -27,6 +27,7 @@ import forge.StaticData; import forge.card.CardEdition; import forge.item.generation.BoosterSlots; import forge.util.MyRandom; +import org.apache.commons.lang3.tuple.Pair; public class BoosterPack extends SealedProduct { private final int artIndex; @@ -34,14 +35,14 @@ public class BoosterPack extends SealedProduct { public static BoosterPack fromSet(CardEdition edition) { String boosterKind = edition.getRandomBoosterKind(); - Template d = edition.getBoosterTemplate(boosterKind); + SealedTemplate d = edition.getBoosterTemplate(boosterKind); StringBuilder sb = new StringBuilder(edition.getName()); sb.append(" ").append(boosterKind); return new BoosterPack(sb.toString(), d); } public static BoosterPack fromColor(final String color) { - return new BoosterPack(color, new Template("?", ImmutableList.of( + return new BoosterPack(color, new SealedTemplate("?", ImmutableList.of( Pair.of(BoosterSlots.COMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 11), Pair.of(BoosterSlots.UNCOMMON + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 3), Pair.of(BoosterSlots.RARE_MYTHIC + ":color(\"" + color + "\"):!" + BoosterSlots.LAND, 1), @@ -49,7 +50,7 @@ public class BoosterPack extends SealedProduct { )); } - public BoosterPack(final String name0, final Template boosterData) { + public BoosterPack(final String name0, final SealedTemplate boosterData) { super(name0, boosterData); if (specialSets.contains(boosterData.getEdition()) || boosterData.getEdition().equals("?")) { @@ -86,7 +87,7 @@ public class BoosterPack extends SealedProduct { return new BoosterPack(name, contents); } - public Template getBoosterData() { + public SealedTemplate getBoosterData() { return contents; } diff --git a/forge-core/src/main/java/forge/item/BoosterSlot.java b/forge-core/src/main/java/forge/item/BoosterSlot.java new file mode 100644 index 00000000000..3b5e1ce65b6 --- /dev/null +++ b/forge-core/src/main/java/forge/item/BoosterSlot.java @@ -0,0 +1,72 @@ +package forge.item; + +import com.google.common.collect.Lists; + +import java.util.List; +import java.util.TreeMap; + +public class BoosterSlot { + private final String slotName; + private String baseRarity; + private float startRange = 0.0f; + private final TreeMap slotPercentages = new TreeMap<>(); + + public BoosterSlot(final String slotName, final List contents) { + this.slotName = slotName; + this.baseRarity = null; + parseContents(contents); + } + + public final String getSlotName() { + return slotName; + } + + public static BoosterSlot parseSlot(final String slotName, final List contents) { + return new BoosterSlot(slotName, contents); + } + + private void parseContents(List contents) { + for (String content : contents) { + if (content.startsWith("#")) { + continue; + } + String[] parts = content.split("=", 2); + String key = parts[0]; + String value = parts[1]; + + if (key.equalsIgnoreCase("Base")) { + baseRarity = value; + } else if (key.equalsIgnoreCase("Replace")) { + // Are there other things? + String[] replaceParts = value.split(" ", 2); + float pct = Float.parseFloat(replaceParts[0]); + startRange += pct; + slotPercentages.put(startRange, replaceParts[1]); + } + } + } + + public List getSlotSheet(int amount) { + // For the first item in the slot, run float percentages + List sheets = Lists.newArrayList(); + sheets.add(replaceSlot()); + for(int i = 1; i < amount; i++) { + sheets.add(baseRarity); + } + return sheets; + } + + public String replaceSlot() { + double rand = Math.random() * 100; + for (Float key : slotPercentages.keySet()) { + if (rand < key) { + System.out.println("Replaced a base slot! " + slotName + " -> " + slotPercentages.get(key)); + + return slotPercentages.get(key); + } + } + + // If we didn't find a key, return the base rarity from that edition + return baseRarity; + } +} diff --git a/forge-core/src/main/java/forge/item/BoxedProduct.java b/forge-core/src/main/java/forge/item/BoxedProduct.java index 6bfdc48b438..c3e0373e3e9 100644 --- a/forge-core/src/main/java/forge/item/BoxedProduct.java +++ b/forge-core/src/main/java/forge/item/BoxedProduct.java @@ -10,7 +10,7 @@ public abstract class BoxedProduct extends SealedProduct { private int numberOfPacks; - public BoxedProduct(String name0, Template boosterData, int numberOfPacks) { + public BoxedProduct(String name0, SealedTemplate boosterData, int numberOfPacks) { super(name0, boosterData); this.numberOfPacks = numberOfPacks; } diff --git a/forge-core/src/main/java/forge/item/FatPack.java b/forge-core/src/main/java/forge/item/FatPack.java index cbe8dd31fce..cb590bca2e8 100644 --- a/forge-core/src/main/java/forge/item/FatPack.java +++ b/forge-core/src/main/java/forge/item/FatPack.java @@ -18,14 +18,14 @@ package forge.item; -import java.util.List; - -import org.apache.commons.lang3.tuple.Pair; - +import com.google.common.base.Function; import forge.ImageKeys; import forge.StaticData; import forge.card.CardEdition; import forge.item.generation.BoosterGenerator; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.List; public class FatPack extends BoxedProduct { public static FatPack fromSet(final CardEdition edition) { @@ -72,7 +72,7 @@ public class FatPack extends BoxedProduct { return super.getTotalCards() * fpData.getCntBoosters() + fpData.getNumberOfCardsExpected(); } - public static class Template extends SealedProduct.Template { + public static class Template extends SealedTemplate { private final int cntBoosters; public int getCntBoosters() { return cntBoosters; } diff --git a/forge-core/src/main/java/forge/item/SealedProduct.java b/forge-core/src/main/java/forge/item/SealedProduct.java index 0070f9ca1e1..fac7607f05a 100644 --- a/forge-core/src/main/java/forge/item/SealedProduct.java +++ b/forge-core/src/main/java/forge/item/SealedProduct.java @@ -18,32 +18,22 @@ package forge.item; -import java.io.File; -import java.util.ArrayList; -import java.util.List; - -import org.apache.commons.lang3.tuple.ImmutablePair; -import org.apache.commons.lang3.tuple.Pair; - -import com.google.common.base.Function; import com.google.common.base.Predicate; import com.google.common.base.Predicates; import com.google.common.collect.Iterables; -import com.google.common.collect.Lists; - import forge.StaticData; import forge.card.CardRulesPredicates; import forge.item.generation.BoosterGenerator; -import forge.item.generation.BoosterSlots; import forge.util.Aggregates; -import forge.util.TextUtil; -import forge.util.storage.StorageReaderFile; + +import java.util.ArrayList; +import java.util.List; public abstract class SealedProduct implements InventoryItemFromSet { public static final List specialSets = new ArrayList<>(); - protected final Template contents; + protected final SealedTemplate contents; protected final String name; private final int hash; protected List cards = null; @@ -57,7 +47,7 @@ public abstract class SealedProduct implements InventoryItemFromSet { specialSets.add("Colorless"); } - public SealedProduct(String name0, Template boosterData) { + public SealedProduct(String name0, SealedTemplate boosterData) { if (null == name0) { throw new IllegalArgumentException("name0 must not be null"); } if (null == boosterData) { throw new IllegalArgumentException("boosterData for " + name0 + " must not be null"); @@ -115,6 +105,7 @@ public abstract class SealedProduct implements InventoryItemFromSet { protected List generate() { return BoosterGenerator.getBoosterPack(contents); + } protected PaperCard getRandomBasicLand(final String setCode) { @@ -127,124 +118,4 @@ public abstract class SealedProduct implements InventoryItemFromSet { Predicates.compose(CardRulesPredicates.Presets.IS_BASIC_LAND, PaperCard::getRules)); return Aggregates.random(Iterables.filter(StaticData.instance().getCommonCards().getAllCards(), cardsRule), count); } - - - public static class Template { - - @SuppressWarnings("unchecked") - public final static Template genericDraftBooster = new Template(null, Lists.newArrayList( - Pair.of(BoosterSlots.COMMON, 10), Pair.of(BoosterSlots.UNCOMMON, 3), - Pair.of(BoosterSlots.RARE_MYTHIC, 1), Pair.of(BoosterSlots.BASIC_LAND, 1) - )); - - protected final List> slots; - protected final String name; - - public final List> getSlots() { - return slots; - } - - public final String getName() { - return name; - } - - public boolean hasSlot(String s) - { - for (Pair slot : getSlots()) { - String slotName = slot.getLeft(); - // Anything after a space or ! or : is not part of the slot's main type - if (slotName.split("[ :!]")[0].equals(s)) { - return true; - } - } - return false; - } - - public final String getEdition() { - return name; - } - public Template(Iterable> itrSlots) - { - this(null, itrSlots); - } - - public Template(String name0, Iterable> itrSlots) - { - slots = Lists.newArrayList(itrSlots); - name = name0; - } - - public Template(String code, String boosterDesc) { - this(code, Reader.parseSlots(boosterDesc)); - } - - public int getNumberOfCardsExpected() { - int sum = 0; - for(Pair p : slots) { - sum += p.getRight(); - } - return sum; - } - - @Override - public String toString() { - StringBuilder s = new StringBuilder(); - - s.append("consisting of "); - for(Pair p : slots) { - s.append(p.getRight()).append(" ").append(p.getLeft()).append(", "); - } - - // trim the last comma and space - s.replace(s.length() - 2, s.length(), ""); - - // put an 'and' before the previous comma - int lastCommaIdx = s.lastIndexOf(","); - if (0 < lastCommaIdx) { - s.replace(lastCommaIdx+1, lastCommaIdx+1, " and"); - } - - return s.toString(); - } - - @Override - public boolean equals(final Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - Template template = (Template) o; - - return slots.equals(template.slots) && name.equals(template.name); - } - - @Override - public int hashCode() { - int result = slots.hashCode(); - result = 31 * result + name.hashCode(); - return result; - } - - public final static class Reader extends StorageReaderFile