Refactor ExpansionSet and its subclasses (#8382)

* Don't purge cards-by-rarity cache between tournaments

* Move DOM and WAR specific checks out of base ExpansionSet class

* Move Battlebond specific methods out of base ExpansionSet class

* Refactor ExpansionSet and subclasses

* Zendikar boosters also only have full-art basic lands

* Fix Eldritch Moon (#8171)
This commit is contained in:
Alex W. Jackson 2021-10-13 11:47:23 -04:00 committed by GitHub
parent 819f4c4c6c
commit 7926193e34
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
41 changed files with 589 additions and 1067 deletions

View file

@ -7,7 +7,6 @@ import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -21,8 +20,6 @@ public final class AetherRevolt extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private AetherRevolt() {
super("Aether Revolt", "AER", ExpansionSet.buildDate(2017, 1, 20), SetType.EXPANSION);
this.blockName = "Kaladesh";
@ -34,8 +31,8 @@ public final class AetherRevolt extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.ratioBoosterSpecialCommon = 144;
this.maxCardNumberInBooster = 184;
this.ratioBoosterSpecialLand = 144;
cards.add(new SetCardInfo("Aegis Automaton", 141, Rarity.COMMON, mage.cards.a.AegisAutomaton.class));
cards.add(new SetCardInfo("Aerial Modification", 1, Rarity.UNCOMMON, mage.cards.a.AerialModification.class));
@ -237,15 +234,12 @@ public final class AetherRevolt extends ExpansionSet {
}
@Override
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes("MPS");
criteria.minCardNumber(31);
criteria.maxCardNumber(54);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findCardsByRarity(rarity);
if (rarity == Rarity.SPECIAL) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("MPS")));
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() < 31);
}
return new ArrayList<>(savedSpecialLand);
return cardInfos;
}
}

View file

@ -1,4 +1,3 @@
package mage.sets;
import mage.cards.ExpansionSet;
@ -8,7 +7,6 @@ import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -23,8 +21,6 @@ public final class Amonkhet extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private Amonkhet() {
super("Amonkhet", "AKH", ExpansionSet.buildDate(2017, 4, 28), SetType.EXPANSION);
this.blockName = "Amonkhet";
@ -35,8 +31,8 @@ public final class Amonkhet extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.ratioBoosterSpecialCommon = 129;
this.maxCardNumberInBooster = 269;
this.ratioBoosterSpecialLand = 144;
cards.add(new SetCardInfo("Ahn-Crop Champion", 194, Rarity.UNCOMMON, mage.cards.a.AhnCropChampion.class));
cards.add(new SetCardInfo("Ahn-Crop Crasher", 117, Rarity.UNCOMMON, mage.cards.a.AhnCropCrasher.class));
@ -328,15 +324,12 @@ public final class Amonkhet extends ExpansionSet {
}
@Override
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes("MP2");
criteria.minCardNumber(1);
criteria.maxCardNumber(30);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findCardsByRarity(rarity);
if (rarity == Rarity.SPECIAL) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("MP2")));
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() > 30);
}
return new ArrayList<>(savedSpecialLand);
return cardInfos;
}
}

View file

@ -1,14 +1,9 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -22,8 +17,6 @@ public class AmonkhetRemastered extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private AmonkhetRemastered() {
super("Amonkhet Remastered", "AKR", ExpansionSet.buildDate(2020, 8, 13), SetType.MAGIC_ARENA);
this.hasBoosters = true;
@ -376,19 +369,4 @@ public class AmonkhetRemastered extends ExpansionSet {
cards.add(new SetCardInfo("Wrath of God", 46, Rarity.RARE, mage.cards.w.WrathOfGod.class));
cards.add(new SetCardInfo("Zealot of the God-Pharaoh", 181, Rarity.COMMON, mage.cards.z.ZealotOfTheGodPharaoh.class));
}
@Override
// the common taplands replacing the basic land
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code);
criteria.rarities(Rarity.COMMON);
criteria.types(CardType.LAND);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
}
return new ArrayList<>(savedSpecialLand);
}
}

View file

@ -7,7 +7,6 @@ import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -21,8 +20,6 @@ public final class BattleForZendikar extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private BattleForZendikar() {
super("Battle for Zendikar", "BFZ", ExpansionSet.buildDate(2015, 10, 2), SetType.EXPANSION);
this.blockName = "Battle for Zendikar";
@ -33,8 +30,7 @@ public final class BattleForZendikar extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.numBoosterSpecial = 0;
this.ratioBoosterSpecialLand = 144;
this.ratioBoosterSpecialCommon = 144;
cards.add(new SetCardInfo("Adverse Conditions", 54, Rarity.UNCOMMON, mage.cards.a.AdverseConditions.class));
cards.add(new SetCardInfo("Akoum Firebird", 138, Rarity.MYTHIC, mage.cards.a.AkoumFirebird.class));
@ -338,15 +334,15 @@ public final class BattleForZendikar extends ExpansionSet {
}
@Override
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes("EXP");
criteria.minCardNumber(1);
criteria.maxCardNumber(45);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findCardsByRarity(rarity);
if (rarity == Rarity.LAND) {
// only the full-art basic lands are found in boosters
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumber().contains("a"));
} else if (rarity == Rarity.SPECIAL) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("EXP")));
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() > 25);
}
return new ArrayList<>(savedSpecialLand);
return cardInfos;
}
}

View file

@ -1,9 +1,17 @@
package mage.sets;
import mage.abilities.Ability;
import mage.abilities.keyword.PartnerWithAbility;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
* @author TheElk801
*/
@ -20,7 +28,6 @@ public final class Battlebond extends ExpansionSet {
this.blockName = "Battlebond";
this.hasBasicLands = true;
this.hasBoosters = true;
this.hasPartnerMechanic = true;
this.numBoosterLands = 1;
this.numBoosterCommon = 10;
this.numBoosterUncommon = 3;
@ -287,4 +294,85 @@ public final class Battlebond extends ExpansionSet {
cards.add(new SetCardInfo("Zndrsplt, Eye of Wisdom", 5, Rarity.RARE, mage.cards.z.ZndrspltEyeOfWisdom.class));
}
@Override
public List<Card> tryBooster() {
List<Card> booster = new ArrayList<>();
boolean partnerAllowed = true;
List<CardInfo> uncommons = getCardsByRarity(Rarity.UNCOMMON);
for (int i = 0; i < numBoosterUncommon; i++) {
while (true) {
addToBooster(booster, uncommons);
int check = addMissingPartner(booster, partnerAllowed, numBoosterUncommon - 1, i);
if (check == 1) {
break;
}
if (check == 2) {
partnerAllowed = false;
//Be sure to account for the added card
if (i != numBoosterUncommon - 1) {
i += 1;
}
break;
}
}
}
List<CardInfo> commons = getCardsByRarity(Rarity.COMMON);
for (int i = 0; i < numBoosterCommon; i++) {
addToBooster(booster, commons);
}
List<CardInfo> rares = getCardsByRarity(Rarity.RARE);
List<CardInfo> mythics = getCardsByRarity(Rarity.MYTHIC);
for (int i = 0; i < numBoosterRare; i++) {
List<CardInfo> cards = (checkMythic() ? mythics : rares);
while (true) {
addToBooster(booster, cards);
int check = addMissingPartner(booster, partnerAllowed, -1, 1);
if (check == 1) {
break;
}
if (check == 2) {
partnerAllowed = false;
break;
}
}
}
List<CardInfo> basicLands = getCardsByRarity(Rarity.LAND);
for (int i = 0; i < numBoosterLands; i++) {
addToBooster(booster, basicLands);
}
return booster;
}
private int addMissingPartner(List<Card> booster, boolean partnerAllowed, int max, int i) {
Card sourceCard = booster.get(booster.size() - 1);
for (Ability ability : sourceCard.getAbilities()) {
//Check if fetched card has the PartnerWithAbility
if (ability instanceof PartnerWithAbility) {
String partnerName = ((PartnerWithAbility) ability).getPartnerName();
//Check if the pack already contains a partner pair
if (partnerAllowed) {
//Added card always replaces an uncommon card
Card card = CardRepository.instance.findCardWPreferredSet(partnerName, sourceCard.getExpansionSetCode(), false).getCard();
if (i < max) {
booster.add(card);
} else {
booster.set(0, card);
}
//2 return value indicates found partner
return 2;
} else {
//If partner already exists, remove card and loop again
booster.remove(booster.size() - 1);
return 0;
}
}
}
return 1;
}
}

View file

@ -1,4 +1,3 @@
package mage.sets;
import mage.cards.ExpansionSet;
@ -8,7 +7,6 @@ import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -192,23 +190,12 @@ public final class Coldsnap extends ExpansionSet {
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
if (rarity != Rarity.COMMON) {
return super.getCardsByRarity(rarity);
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findCardsByRarity(rarity);
if (rarity == Rarity.COMMON) {
// Snow basic lands are included in boosters as regular commons
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).rarities(Rarity.LAND)));
}
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos != null) {
return new ArrayList(savedCardsInfos);
}
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code).rarities(rarity);
savedCardsInfos = CardRepository.instance.findCards(criteria);
// Snow basic lands are included in boosters as regular commons, not in a land slot
criteria = new CardCriteria();
criteria.setCodes(this.code).rarities(Rarity.LAND);
savedCardsInfos.addAll(CardRepository.instance.findCards(criteria));
savedCards.put(rarity, savedCardsInfos);
// Return a copy of the saved cards information, as not to modify the original.
return new ArrayList(savedCardsInfos);
return cardInfos;
}
}

View file

@ -1,16 +1,9 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
* @author TheElk801
*/
@ -22,13 +15,10 @@ public final class CoreSet2019 extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private CoreSet2019() {
super("Core Set 2019", "M19", ExpansionSet.buildDate(2018, 7, 13), SetType.CORE);
this.hasBoosters = true;
this.hasBasicLands = true;
this.numBoosterSpecial = 0;
this.numBoosterLands = 1;
this.numBoosterCommon = 10;
this.numBoosterUncommon = 3;
@ -358,40 +348,4 @@ public final class CoreSet2019 extends ExpansionSet {
cards.add(new SetCardInfo("Windreader Sphinx", 84, Rarity.RARE, mage.cards.w.WindreaderSphinx.class));
cards.add(new SetCardInfo("Woodland Stream", 260, Rarity.COMMON, mage.cards.w.WoodlandStream.class));
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
// Common cards retrievement of Core Set 2019 boosters - prevent the retrievement of the common lands (e.g. Meandering River)
if (rarity == Rarity.COMMON) {
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos == null) {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.COMMON);
criteria.setCodes(this.code).notTypes(CardType.LAND);
savedCardsInfos = CardRepository.instance.findCards(criteria);
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
}
savedCards.put(rarity, savedCardsInfos);
}
// Return a copy of the saved cards information, as not to let modify the original.
return new ArrayList<>(savedCardsInfos);
} else {
return super.getCardsByRarity(rarity);
}
}
@Override
// the common taplands replacing the basic land
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code);
criteria.rarities(Rarity.COMMON);
criteria.types(CardType.LAND);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
}
return new ArrayList<>(savedSpecialLand);
}
}

View file

@ -1,16 +1,9 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
* @author TheElk801
*/
@ -22,13 +15,10 @@ public final class CoreSet2020 extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private CoreSet2020() {
super("Core Set 2020", "M20", ExpansionSet.buildDate(2019, 7, 12), SetType.CORE);
this.hasBoosters = true;
this.hasBasicLands = true;
this.numBoosterSpecial = 0;
this.numBoosterLands = 1;
this.numBoosterCommon = 10;
this.numBoosterUncommon = 3;
@ -387,40 +377,4 @@ public final class CoreSet2020 extends ExpansionSet {
cards.add(new SetCardInfo("Yoked Ox", 41, Rarity.COMMON, mage.cards.y.YokedOx.class));
cards.add(new SetCardInfo("Zephyr Charge", 82, Rarity.COMMON, mage.cards.z.ZephyrCharge.class));
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
// Common cards retrievement of Core Set 2020 boosters - prevent the retrievement of the common lands
if (rarity == Rarity.COMMON) {
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos == null) {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.COMMON);
criteria.setCodes(this.code).notTypes(CardType.LAND);
savedCardsInfos = CardRepository.instance.findCards(criteria);
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
}
savedCards.put(rarity, savedCardsInfos);
}
// Return a copy of the saved cards information, as not to let modify the original.
return new ArrayList<>(savedCardsInfos);
} else {
return super.getCardsByRarity(rarity);
}
}
@Override
// the common taplands replacing the basic land
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code);
criteria.rarities(Rarity.COMMON);
criteria.types(CardType.LAND);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
}
return new ArrayList<>(savedSpecialLand);
}
}

View file

@ -1,14 +1,10 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -22,13 +18,10 @@ public final class CoreSet2021 extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private CoreSet2021() {
super("Core Set 2021", "M21", ExpansionSet.buildDate(2020, 7, 3), SetType.CORE);
this.hasBoosters = true;
this.hasBasicLands = true;
this.numBoosterSpecial = 0;
this.numBoosterLands = 1;
this.numBoosterCommon = 10;
this.numBoosterUncommon = 3;
@ -437,43 +430,13 @@ public final class CoreSet2021 extends ExpansionSet {
cards.add(new SetCardInfo("Witch's Cauldron", 129, Rarity.UNCOMMON, mage.cards.w.WitchsCauldron.class));
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
if (rarity != Rarity.COMMON) {
return super.getCardsByRarity(rarity);
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findSpecialCardsByRarity(rarity);
if (rarity == Rarity.LAND) {
// Radiant Fountain is a normal common
cardInfos.removeIf(cardInfo -> "Radiant Fountain".equals(cardInfo.getName()));
}
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos != null) {
return new ArrayList(savedCardsInfos);
}
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code).notTypes(CardType.LAND);
criteria.rarities(rarity).doubleFaced(false);
savedCardsInfos = CardRepository.instance.findCards(criteria);
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
}
criteria = new CardCriteria();
criteria.setCodes(this.code).nameExact("Radiant Fountain");
savedCardsInfos.addAll(CardRepository.instance.findCards(criteria));
savedCards.put(rarity, savedCardsInfos);
// Return a copy of the saved cards information, as not to modify the original.
return new ArrayList(savedCardsInfos);
}
@Override
// the common taplands replacing the basic land
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code);
criteria.rarities(Rarity.COMMON);
criteria.types(CardType.LAND);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
savedSpecialLand.removeIf(cardInfo -> "Radiant Fountain".equals(cardInfo.getName()));
}
return new ArrayList<>(savedSpecialLand);
return cardInfos;
}
}

View file

@ -1,13 +1,17 @@
package mage.sets;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.collation.BoosterCollator;
import mage.collation.BoosterStructure;
import mage.collation.CardRun;
import mage.collation.RarityConfiguration;
import mage.constants.Rarity;
import mage.constants.SetType;
import mage.util.RandomUtil;
import java.util.ArrayList;
import java.util.List;
@ -209,6 +213,25 @@ public final class DarkAscension extends ExpansionSet {
cards.add(new SetCardInfo("Zombie Apocalypse", 80, Rarity.RARE, mage.cards.z.ZombieApocalypse.class));
}
@Override
protected void addDoubleFace(List<Card> booster) {
Rarity rarity;
for (int i = 0; i < numBoosterDoubleFaced; i++) {
int rarityKey = RandomUtil.nextInt(121);
if (rarityKey < 72) {
rarity = Rarity.COMMON;
} else if (rarityKey < 108) {
rarity = Rarity.UNCOMMON;
} else if (rarityKey < 117) {
rarity = Rarity.RARE;
} else {
rarity = Rarity.MYTHIC;
}
List<CardInfo> doubleFacedCards = getSpecialCardsByRarity(rarity);
addToBooster(booster, doubleFacedCards);
}
}
@Override
public BoosterCollator createCollator() {
return new DarkAscensionCollator();

View file

@ -1,5 +1,6 @@
package mage.sets;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.collation.BoosterCollator;
import mage.collation.BoosterStructure;
@ -32,7 +33,6 @@ public final class Dominaria extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.needsLegendCreature = true;
this.maxCardNumberInBooster = 269;
cards.add(new SetCardInfo("Academy Drake", 40, Rarity.COMMON, mage.cards.a.AcademyDrake.class));
@ -317,6 +317,12 @@ public final class Dominaria extends ExpansionSet {
cards.add(new SetCardInfo("Zhalfirin Void", 249, Rarity.UNCOMMON, mage.cards.z.ZhalfirinVoid.class));
}
@Override
protected boolean boosterIsValid(List<Card> booster) {
return super.boosterIsValid(booster)
&& booster.stream().anyMatch(card -> card.isLegendary() && card.isCreature());
}
@Override
public BoosterCollator createCollator() {
return new DominariaCollator();

View file

@ -1,5 +1,6 @@
package mage.sets;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
@ -7,8 +8,8 @@ import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import mage.util.RandomUtil;
import java.util.ArrayList;
import java.util.List;
/**
@ -22,8 +23,6 @@ public final class DragonsMaze extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialRares = new ArrayList<>();
private DragonsMaze() {
super("Dragon's Maze", "DGM", ExpansionSet.buildDate(2013, 5, 3), SetType.EXPANSION);
this.blockName = "Return to Ravnica";
@ -194,61 +193,36 @@ public final class DragonsMaze extends ExpansionSet {
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
if (rarity == Rarity.COMMON) {
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos == null) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code).notTypes(CardType.LAND);
criteria.rarities(rarity).doubleFaced(false);
savedCardsInfos = CardRepository.instance.findCards(criteria);
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
}
savedCards.put(rarity, savedCardsInfos);
}
// Return a copy of the saved cards information, as not to modify the original.
return new ArrayList<>(savedCardsInfos);
protected void addSpecialCards(List<Card> booster, int number) {
// number is here always 1
// the land print sheets are believed to contain 23 copies of each Guildgate,
// one copy of each RTR and GTC shockland, and two copies of Maze's End
Rarity rarity;
int rarityKey = RandomUtil.nextInt(242);
if (rarityKey < 230) {
rarity = Rarity.COMMON;
} else if (rarityKey < 240) {
rarity = Rarity.RARE;
} else {
return super.getCardsByRarity(rarity);
rarity = Rarity.MYTHIC;
}
addToBooster(booster, getSpecialCardsByRarity(rarity));
}
@Override
public List<CardInfo> getSpecialCommon() {
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.COMMON).setCodes(this.code).types(CardType.LAND);
return CardRepository.instance.findCards(criteria);
}
@Override
public List<CardInfo> getSpecialRare() {
if (savedSpecialRares.isEmpty()) {
fillSpecialRares("GTC", "Breeding Pool");
fillSpecialRares("GTC", "Godless Shrine");
fillSpecialRares("GTC", "Sacred Foundry");
fillSpecialRares("GTC", "Stomping Ground");
fillSpecialRares("GTC", "Watery Grave");
fillSpecialRares("RTR", "Blood Crypt");
fillSpecialRares("RTR", "Hallowed Fountain");
fillSpecialRares("RTR", "Overgrown Tomb");
fillSpecialRares("RTR", "Steam Vents");
fillSpecialRares("RTR", "Temple Garden");
criteria.rarities(rarity).types(CardType.LAND);
if (rarity == Rarity.RARE) {
// shocklands
criteria.setCodes("RTR", "GTC");
} else {
// Guildgates and Maze's End
criteria.setCodes(this.code);
}
return new ArrayList<>(savedSpecialRares);
List<CardInfo> cardInfos = CardRepository.instance.findCards(criteria);
cardInfos.removeIf(cardInfo -> (cardInfo.getName().equals("Grove of the Guardian")
|| cardInfo.getName().equals("Thespian's Stage")));
return cardInfos;
}
private void fillSpecialRares(String setCode, String cardName) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(setCode).name(cardName);
savedSpecialRares.addAll(CardRepository.instance.findCards(criteria));
}
@Override
public List<CardInfo> getSpecialMythic() {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.MYTHIC).setCodes(this.code).types(CardType.LAND);
return CardRepository.instance.findCards(criteria);
}
}

View file

@ -1,8 +1,15 @@
package mage.sets;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import mage.util.RandomUtil;
import java.util.List;
/**
* @author fireshoes
@ -26,6 +33,7 @@ public final class EldritchMoon extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.ratioBoosterSpecialCommon = 8;
this.numBoosterDoubleFaced = 1;
cards.add(new SetCardInfo("Abandon Reason", 115, Rarity.UNCOMMON, mage.cards.a.AbandonReason.class));
@ -252,4 +260,57 @@ public final class EldritchMoon extends ExpansionSet {
cards.add(new SetCardInfo("Woodland Patrol", 180, Rarity.COMMON, mage.cards.w.WoodlandPatrol.class));
cards.add(new SetCardInfo("Wretched Gryff", 12, Rarity.COMMON, mage.cards.w.WretchedGryff.class));
}
// add common or uncommon double faced card to booster
// 60/120 packs contain one of 4 common DFCs and 60/120 packs contain one of 10 uncommon DFCs
@Override
protected void addDoubleFace(List<Card> booster) {
Rarity rarity;
for (int i = 0; i < numBoosterDoubleFaced; i++) {
if (RandomUtil.nextInt(120) < 60) {
rarity = Rarity.COMMON;
} else {
rarity = Rarity.UNCOMMON;
}
addToBooster(booster, getSpecialCardsByRarity(rarity));
}
}
// Then about an eighth of the packs will have a second double-faced card, which will be a rare or mythic rare
// 10/12 of such packs contain one of 5 rare DFCs and 2/12 packs contain one of 2 mythic DFCs
@Override
protected void addSpecialCards(List<Card> booster, int number) {
// number is here always 1
Rarity rarity;
if (RandomUtil.nextInt(12) < 10) {
rarity = Rarity.RARE;
} else {
rarity = Rarity.MYTHIC;
}
addToBooster(booster, getSpecialCardsByRarity(rarity));
}
// xmage doesn't recognize meldable cards as DFCs, so have to add them manually for now
private static final String[] commonMeldCards = {"Graf Rats", "Midnight Scavengers"};
private static final String[] rareMeldCards = {"Bruna, the Fading Light", "Hanweir Battlements", "Hanweir Garrison"};
private static final String[] mythicMeldCards = {"Gisela, the Broken Blade"};
@Override
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findSpecialCardsByRarity(rarity);
String[] meldCardNames = {};
if (rarity == Rarity.COMMON) {
meldCardNames = commonMeldCards;
} else if (rarity == Rarity.RARE) {
meldCardNames = rareMeldCards;
} else if (rarity == Rarity.MYTHIC) {
meldCardNames = mythicMeldCards;
}
for (String name : meldCardNames) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria()
.setCodes(this.code)
.nameExact(name)));
}
return cardInfos;
}
}

View file

@ -1,9 +1,6 @@
package mage.sets;
import java.util.ArrayList;
import java.util.List;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
@ -11,6 +8,9 @@ import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import mage.util.RandomUtil;
import java.util.List;
/**
* @author fireshoes
@ -19,9 +19,6 @@ public final class FateReforged extends ExpansionSet {
private static final FateReforged instance = new FateReforged();
private List<CardInfo> savedSpecialRares = new ArrayList<>();
private List<CardInfo> savedSpecialCommon = new ArrayList<>();
public static FateReforged getInstance() {
return instance;
}
@ -37,7 +34,6 @@ public final class FateReforged extends ExpansionSet {
this.numBoosterCommon = 10;
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.numBoosterDoubleFaced = -1;
this.ratioBoosterMythic = 8;
cards.add(new SetCardInfo("Abzan Advantage", 2, Rarity.COMMON, mage.cards.a.AbzanAdvantage.class));
@ -228,60 +224,28 @@ public final class FateReforged extends ExpansionSet {
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
// Common cards retrievement of Fate Reforged boosters - prevent the retrievement of the common lands (e.g. Blossoming Sands)
if (rarity == Rarity.COMMON) {
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos == null) {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.COMMON);
criteria.setCodes(this.code).notTypes(CardType.LAND);
savedCardsInfos = CardRepository.instance.findCards(criteria);
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
}
savedCards.put(rarity, savedCardsInfos);
}
// Return a copy of the saved cards information, as not to let modify the original.
return new ArrayList<>(savedCardsInfos);
protected void addSpecialCards(List<Card> booster, int number) {
// number is here always 1
Rarity rarity;
if (RandomUtil.nextInt(24) < 23) {
rarity = Rarity.COMMON;
} else {
return super.getCardsByRarity(rarity);
rarity = Rarity.RARE;
}
addToBooster(booster, getSpecialCardsByRarity(rarity));
}
@Override
public List<CardInfo> getSpecialCommon() {
if (savedSpecialCommon.isEmpty()) {
// the 10 common lands from Fate Reforged can show up in the basic lands slot
// http://magic.wizards.com/en/articles/archive/feature/fetching-look-fate-reforged-2014-12-24
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.COMMON).setCodes(this.code).types(CardType.LAND);
savedSpecialCommon = CardRepository.instance.findCards(criteria);
criteria.rarities(Rarity.LAND).setCodes(this.code);
savedSpecialCommon.addAll(CardRepository.instance.findCards(criteria));
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
CardCriteria criteria = new CardCriteria();
criteria.rarities(rarity).types(CardType.LAND);
if (rarity == Rarity.RARE) {
// fetchlands
criteria.setCodes("KTK");
} else {
// gainlands
criteria.setCodes(this.code);
}
return new ArrayList<>(savedSpecialCommon);
}
@Override
public List<CardInfo> getSpecialRare() {
if (savedSpecialRares.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes("KTK").name("Bloodstained Mire");
savedSpecialRares.addAll(CardRepository.instance.findCards(criteria));
criteria = new CardCriteria();
criteria.setCodes("KTK").name("Flooded Strand");
savedSpecialRares.addAll(CardRepository.instance.findCards(criteria));
criteria = new CardCriteria();
criteria.setCodes("KTK").name("Polluted Delta");
savedSpecialRares.addAll(CardRepository.instance.findCards(criteria));
criteria = new CardCriteria();
criteria.setCodes("KTK").name("Windswept Heath");
savedSpecialRares.addAll(CardRepository.instance.findCards(criteria));
criteria = new CardCriteria();
criteria.setCodes("KTK").name("Wooded Foothills");
savedSpecialRares.addAll(CardRepository.instance.findCards(criteria));
}
return new ArrayList<>(savedSpecialRares);
return CardRepository.instance.findCards(criteria);
}
}

View file

@ -1,16 +1,12 @@
package mage.sets;
import java.util.ArrayList;
import java.util.List;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.List;
public final class GuildsOfRavnica extends ExpansionSet {
private static final GuildsOfRavnica instance = new GuildsOfRavnica();
@ -23,12 +19,12 @@ public final class GuildsOfRavnica extends ExpansionSet {
super("Guilds of Ravnica", "GRN", ExpansionSet.buildDate(2018, 10, 5), SetType.EXPANSION);
this.blockName = "Guilds of Ravnica";
this.hasBoosters = true;
this.numBoosterSpecial = 1;
this.numBoosterLands = 0;
this.numBoosterLands = 1;
this.numBoosterCommon = 10;
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.ratioBoosterSpecialLand = 1; // replace all basic lands
this.maxCardNumberInBooster = 259;
cards.add(new SetCardInfo("Affectionate Indrik", 121, Rarity.UNCOMMON, mage.cards.a.AffectionateIndrik.class));
@ -307,40 +303,12 @@ public final class GuildsOfRavnica extends ExpansionSet {
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
if (rarity == Rarity.COMMON) {
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos == null) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code).notTypes(CardType.LAND);
criteria.rarities(rarity).doubleFaced(false);
savedCardsInfos = CardRepository.instance.findCards(criteria);
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
}
criteria = new CardCriteria();
// Gateway Plaza is a normal common: https://twitter.com/EliShffrn/status/1043156989218414593s
criteria.setCodes(this.code).nameExact("Gateway Plaza");
savedCardsInfos.addAll(CardRepository.instance.findCards(criteria));
savedCards.put(rarity, savedCardsInfos);
}
// Return a copy of the saved cards information, as not to modify the original.
return new ArrayList<>(savedCardsInfos);
} else {
return super.getCardsByRarity(rarity);
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findSpecialCardsByRarity(rarity);
if (rarity == Rarity.LAND) {
// Gateway Plaza is a normal common
cardInfos.removeIf(cardInfo -> "Gateway Plaza".equals(cardInfo.getName()));
}
}
@Override
public List<CardInfo> getSpecialCommon() {
List<CardInfo> specialCards = getCardsByRarity(Rarity.SPECIAL);
if (specialCards.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.COMMON).setCodes(this.code).name("Guildgate");
List<CardInfo> specialCardsSave = CardRepository.instance.findCards(criteria);
savedCards.put(Rarity.SPECIAL, specialCardsSave);
specialCards.addAll(specialCardsSave);
}
return specialCards;
return cardInfos;
}
}

View file

@ -1,4 +1,3 @@
package mage.sets;
import mage.cards.ExpansionSet;
@ -8,7 +7,6 @@ import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -22,8 +20,6 @@ public final class HourOfDevastation extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private HourOfDevastation() {
super("Hour of Devastation", "HOU", ExpansionSet.buildDate(2017, 7, 14), SetType.EXPANSION);
this.blockName = "Amonkhet";
@ -35,7 +31,7 @@ public final class HourOfDevastation extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.ratioBoosterSpecialLand = 144;
this.ratioBoosterSpecialCommon = 129;
this.maxCardNumberInBooster = 199;
cards.add(new SetCardInfo("Abandoned Sarcophagus", 158, Rarity.RARE, mage.cards.a.AbandonedSarcophagus.class));
@ -250,15 +246,12 @@ public final class HourOfDevastation extends ExpansionSet {
}
@Override
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes("MP2");
criteria.minCardNumber(31);
criteria.maxCardNumber(54);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findCardsByRarity(rarity);
if (rarity == Rarity.SPECIAL) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("MP2")));
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() < 31);
}
return new ArrayList<>(savedSpecialLand);
return cardInfos;
}
}

View file

@ -1,14 +1,10 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@ -56,8 +52,6 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private IkoriaLairOfBehemoths() {
super("Ikoria: Lair of Behemoths", "IKO", ExpansionSet.buildDate(2020, 4, 24), SetType.EXPANSION);
this.blockName = "Ikoria: Lair of Behemoths";
@ -463,41 +457,12 @@ public final class IkoriaLairOfBehemoths extends ExpansionSet {
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
if (rarity != Rarity.COMMON) {
return super.getCardsByRarity(rarity);
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findSpecialCardsByRarity(rarity);
if (rarity == Rarity.LAND) {
// Evolving Wilds is a normal common
cardInfos.removeIf(cardInfo -> "Evolving Wilds".equals(cardInfo.getName()));
}
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos != null) {
return new ArrayList(savedCardsInfos);
}
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code).notTypes(CardType.LAND);
criteria.rarities(rarity).doubleFaced(false);
savedCardsInfos = CardRepository.instance.findCards(criteria);
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
}
criteria = new CardCriteria();
criteria.setCodes(this.code).nameExact("Evolving Wilds");
savedCardsInfos.addAll(CardRepository.instance.findCards(criteria));
savedCards.put(rarity, savedCardsInfos);
// Return a copy of the saved cards information, as not to modify the original.
return new ArrayList(savedCardsInfos);
}
@Override
// the common taplands replacing the basic land
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code);
criteria.rarities(Rarity.COMMON);
criteria.types(CardType.LAND);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
savedSpecialLand.removeIf(cardInfo -> "Evolving Wilds".equals(cardInfo.getName()));
}
return new ArrayList(savedSpecialLand);
return cardInfos;
}
}

View file

@ -7,7 +7,6 @@ import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -21,8 +20,6 @@ public final class Kaladesh extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private Kaladesh() {
super("Kaladesh", "KLD", ExpansionSet.buildDate(2016, 9, 30), SetType.EXPANSION);
this.blockName = "Kaladesh";
@ -33,9 +30,8 @@ public final class Kaladesh extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.numBoosterSpecial = 0;
this.ratioBoosterSpecialCommon = 144;
this.maxCardNumberInBooster = 264;
this.ratioBoosterSpecialLand = 144;
cards.add(new SetCardInfo("Accomplished Automaton", 191, Rarity.COMMON, mage.cards.a.AccomplishedAutomaton.class));
cards.add(new SetCardInfo("Acrobatic Maneuver", 1, Rarity.COMMON, mage.cards.a.AcrobaticManeuver.class));
@ -318,15 +314,12 @@ public final class Kaladesh extends ExpansionSet {
}
@Override
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes("MPS");
criteria.minCardNumber(1);
criteria.maxCardNumber(30);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findCardsByRarity(rarity);
if (rarity == Rarity.SPECIAL) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("MPS")));
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() > 30);
}
return new ArrayList<>(savedSpecialLand);
return cardInfos;
}
}

View file

@ -1,11 +1,9 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardInfo;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**

View file

@ -1,14 +1,11 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.collation.BoosterCollator;
import mage.collation.BoosterStructure;
import mage.collation.CardRun;
import mage.collation.RarityConfiguration;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import mage.constants.SuperType;
@ -27,8 +24,6 @@ public final class Kaldheim extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private Kaldheim() {
super("Kaldheim", "KHM", ExpansionSet.buildDate(2021, 2, 5), SetType.EXPANSION);
this.blockName = "Kaldheim";
@ -451,46 +446,13 @@ public final class Kaldheim extends ExpansionSet {
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
if (savedCards.containsKey(rarity)) {
return new ArrayList<>(savedCards.get(rarity));
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findSpecialCardsByRarity(rarity);
if (rarity == Rarity.LAND) {
// Shimmerdrift Vale is a normal common
cardInfos.removeIf(cardInfo -> "Shimmerdrift Vale".equals(cardInfo.getName()));
}
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code);
criteria.rarities(rarity);
List<CardInfo> savedCardsInfos = CardRepository.instance.findCards(criteria);
switch (rarity) {
case LAND:
savedCardsInfos.removeIf(cardInfo -> !cardInfo.getSupertypes().contains(SuperType.SNOW));
savedCardsInfos.removeIf(cardInfo -> !cardInfo.getSupertypes().contains(SuperType.BASIC));
break;
case COMMON:
savedCardsInfos.removeIf(cardInfo ->
cardInfo.getCard().isSnow()
&& cardInfo.getCard().isLand()
&& !cardInfo.getCard().getName().equals("Shimmerdrift Vale")
);
break;
}
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
savedCards.put(rarity, savedCardsInfos);
return new ArrayList<>(savedCardsInfos);
}
@Override
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code);
criteria.types(CardType.LAND);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
savedSpecialLand.removeIf(cardInfo -> cardInfo.getSupertypes().contains(SuperType.BASIC));
savedSpecialLand.removeIf(cardInfo -> !cardInfo.getSupertypes().contains(SuperType.SNOW));
savedSpecialLand.removeIf(cardInfo -> cardInfo.getName().equals("Shimmerdrift Vale"));
savedSpecialLand.removeIf(cardInfo -> cardInfo.getName().equals("Faceless Haven"));
savedSpecialLand.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() > maxCardNumberInBooster);
}
return new ArrayList<>(savedSpecialLand);
return cardInfos;
}
@Override

View file

@ -1,14 +1,10 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -22,8 +18,6 @@ public final class MastersEditionIV extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private MastersEditionIV() {
super("Masters Edition IV", "ME4", ExpansionSet.buildDate(2011, 1, 10), SetType.MAGIC_ONLINE);
this.hasBasicLands = false;
@ -305,16 +299,12 @@ public final class MastersEditionIV extends ExpansionSet {
}
@Override
public List<CardInfo> getSpecialLand() {
// ME4 replace all basic lands with special (1 per booster)
// https://mtg.gamepedia.com/Masters_Edition_IV
if (savedSpecialLand.isEmpty()) {
savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Mine")));
savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Power Plant")));
savedSpecialLand.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code).name("Urza's Tower")));
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findSpecialCardsByRarity(rarity);
if (rarity == Rarity.LAND) {
// Oasis is a normal common
cardInfos.removeIf(cardInfo -> "Oasis".equals(cardInfo.getName()));
}
return new ArrayList<>(savedSpecialLand);
return cardInfos;
}
}
}

View file

@ -2,7 +2,9 @@ package mage.sets;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.collation.BoosterCollator;
import mage.collation.BoosterStructure;
import mage.collation.CardRun;
@ -34,6 +36,7 @@ public final class ModernHorizons2 extends ExpansionSet {
this.numBoosterCommon = 10;
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.numBoosterSpecial = 1;
this.ratioBoosterMythic = 7;
this.maxCardNumberInBooster = 303;
@ -532,32 +535,30 @@ public final class ModernHorizons2 extends ExpansionSet {
}
@Override
public List<Card> tryBooster() {
List<Card> booster = super.tryBooster();
addReprints(booster);
return booster;
}
private void addReprints(List<Card> booster) {
final Rarity rarity;
int i = RandomUtil.nextInt(120);
if (i < 4) {
protected void addSpecialCards(List<Card> booster, int number) {
// number is here always 1
Rarity rarity;
int rarityKey = RandomUtil.nextInt(120);
if (rarityKey < 4) {
rarity = Rarity.MYTHIC;
} else if (i < 40) {
} else if (rarityKey < 40) {
rarity = Rarity.RARE;
} else {
rarity = Rarity.UNCOMMON;
}
List<CardInfo> cards = super.getCardsByRarity(rarity);
cards.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() < 262);
addToBooster(booster, cards);
List<CardInfo> reprintCards = getSpecialCardsByRarity(rarity);
addToBooster(booster, reprintCards);
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
List<CardInfo> cards = super.getCardsByRarity(rarity);
cards.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() >= 262);
return cards;
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = CardRepository
.instance
.findCards(new CardCriteria().setCodes(this.code).rarities(rarity));
cardInfos.removeIf(cardInfo -> (
cardInfo.getCardNumberAsInt() < 262
|| cardInfo.getCardNumberAsInt() > maxCardNumberInBooster));
return cardInfos;
}
@Override

View file

@ -7,7 +7,6 @@ import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
/**
@ -21,8 +20,6 @@ public final class OathOfTheGatewatch extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialLand = new ArrayList<>();
private OathOfTheGatewatch() {
super("Oath of the Gatewatch", "OGW", ExpansionSet.buildDate(2016, 1, 22), SetType.EXPANSION);
this.blockName = "Battle for Zendikar";
@ -34,7 +31,7 @@ public final class OathOfTheGatewatch extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.ratioBoosterSpecialLand = 48;
this.ratioBoosterSpecialCommon = 144;
cards.add(new SetCardInfo("Abstruse Interference", 40, Rarity.COMMON, mage.cards.a.AbstruseInterference.class));
cards.add(new SetCardInfo("Affa Protector", 14, Rarity.COMMON, mage.cards.a.AffaProtector.class));
@ -226,25 +223,15 @@ public final class OathOfTheGatewatch extends ExpansionSet {
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
List<CardInfo> cards = super.getCardsByRarity(rarity);
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findCardsByRarity(rarity);
if (rarity == Rarity.COMMON) {
// only the full-art versions of Wastes are found in boosters
cards.removeIf(cardInfo -> cardInfo.getCardNumber().contains("a"));
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumber().contains("a"));
} else if (rarity == Rarity.SPECIAL) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("EXP")));
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() < 26);
}
return cards;
}
@Override
public List<CardInfo> getSpecialLand() {
if (savedSpecialLand.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes("EXP");
criteria.minCardNumber(26);
criteria.maxCardNumber(45);
savedSpecialLand.addAll(CardRepository.instance.findCards(criteria));
}
return new ArrayList<>(savedSpecialLand);
return cardInfos;
}
}

View file

@ -1,14 +1,10 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import java.util.ArrayList;
import java.util.List;
public final class RavnicaAllegiance extends ExpansionSet {
@ -23,12 +19,12 @@ public final class RavnicaAllegiance extends ExpansionSet {
super("Ravnica Allegiance", "RNA", ExpansionSet.buildDate(2019, 1, 25), SetType.EXPANSION);
this.blockName = "Guilds of Ravnica";
this.hasBoosters = true;
this.numBoosterSpecial = 1;
this.numBoosterLands = 0;
this.numBoosterLands = 1;
this.numBoosterCommon = 10;
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.ratioBoosterSpecialLand = 1; // replace all basic lands
this.maxCardNumberInBooster = 259;
cards.add(new SetCardInfo("Absorb", 151, Rarity.RARE, mage.cards.a.Absorb.class));
@ -307,40 +303,12 @@ public final class RavnicaAllegiance extends ExpansionSet {
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
if (rarity == Rarity.COMMON) {
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos == null) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code).notTypes(CardType.LAND);
criteria.rarities(rarity).doubleFaced(false);
savedCardsInfos = CardRepository.instance.findCards(criteria);
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster);
}
criteria = new CardCriteria();
// Gateway Plaza is a normal common: https://twitter.com/EliShffrn/status/1043156989218414593s
criteria.setCodes(this.code).nameExact("Gateway Plaza");
savedCardsInfos.addAll(CardRepository.instance.findCards(criteria));
savedCards.put(rarity, savedCardsInfos);
}
// Return a copy of the saved cards information, as not to modify the original.
return new ArrayList<>(savedCardsInfos);
} else {
return super.getCardsByRarity(rarity);
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findSpecialCardsByRarity(rarity);
if (rarity == Rarity.LAND) {
// Gateway Plaza is a normal common
cardInfos.removeIf(cardInfo -> "Gateway Plaza".equals(cardInfo.getName()));
}
}
@Override
public List<CardInfo> getSpecialCommon() {
List<CardInfo> specialCards = getCardsByRarity(Rarity.SPECIAL);
if (specialCards.isEmpty()) {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.COMMON).setCodes(this.code).name("Guildgate");
List<CardInfo> specialCardsSave = CardRepository.instance.findCards(criteria);
savedCards.put(Rarity.SPECIAL, specialCardsSave);
specialCards.addAll(specialCardsSave);
}
return specialCards;
return cardInfos;
}
}

View file

@ -2,15 +2,10 @@ package mage.sets;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import mage.util.RandomUtil;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
/**
@ -24,8 +19,6 @@ public final class ShadowsOverInnistrad extends ExpansionSet {
return instance;
}
private final EnumMap<Rarity, List<CardInfo>> savedDoubleFacedCards;
private ShadowsOverInnistrad() {
super("Shadows over Innistrad", "SOI", ExpansionSet.buildDate(2016, 4, 8), SetType.EXPANSION);
this.blockName = "Shadows over Innistrad";
@ -36,10 +29,9 @@ public final class ShadowsOverInnistrad extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.ratioBoosterSpecialCommon = 8;
this.numBoosterDoubleFaced = 1;
savedDoubleFacedCards = new EnumMap<>(Rarity.class);
cards.add(new SetCardInfo("Aberrant Researcher", 49, Rarity.UNCOMMON, mage.cards.a.AberrantResearcher.class));
cards.add(new SetCardInfo("Accursed Witch", 97, Rarity.UNCOMMON, mage.cards.a.AccursedWitch.class));
cards.add(new SetCardInfo("Aim High", 193, Rarity.COMMON, mage.cards.a.AimHigh.class));
@ -377,53 +369,32 @@ public final class ShadowsOverInnistrad extends ExpansionSet {
cards.add(new SetCardInfo("Woodland Stream", 282, Rarity.UNCOMMON, mage.cards.w.WoodlandStream.class));
}
/* add double faced card for SOI booster
* add only common or uncommon
80/120 packs contain one of 20 uncommon DFCs and 40/120 packs contain one of 4 common DFCs
*/
// add common or uncommon double faced card to booster
// 40/120 packs contain one of 4 common DFCs and 80/120 packs contain one of 20 uncommon DFCs
@Override
public void addDoubleFace(List<Card> booster) {
protected void addDoubleFace(List<Card> booster) {
Rarity rarity;
for (int i = 0; i < numBoosterDoubleFaced; i++) {
List<CardInfo> doubleFacedCards;
if (RandomUtil.nextInt(15) < 10) {
doubleFacedCards = getDoubleFacedCardsByRarity(Rarity.UNCOMMON);
if (RandomUtil.nextInt(120) < 40) {
rarity = Rarity.COMMON;
} else {
doubleFacedCards = getDoubleFacedCardsByRarity(Rarity.COMMON);
rarity = Rarity.UNCOMMON;
}
addToBooster(booster, doubleFacedCards);
addToBooster(booster, getSpecialCardsByRarity(rarity));
}
}
private List<CardInfo> getDoubleFacedCardsByRarity(Rarity rarity) {
List<CardInfo> savedCardsInfos = savedDoubleFacedCards.get(rarity);
if (savedCardsInfos == null) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(getCode());
criteria.rarities(rarity);
criteria.doubleFaced(true);
savedCardsInfos = CardRepository.instance.findCards(criteria);
savedDoubleFacedCards.put(rarity, savedCardsInfos);
}
// Return a copy of the saved cards information, as not to let modify the original.
return new ArrayList<>(savedCardsInfos);
}
// Then about an eighth of the packs will have a second double-faced card, which will be a rare or mythic rare
// 12/15 of such packs contain one of 6 rare DFCs and 3/15 packs contain one of 3 mythic DFCs
@Override
public int getNumberOfSpecialCommons() {
// Then about an eighth of the packs will have a second double-faced card, which will be a rare or mythic rare.
return RandomUtil.nextInt(8) == 0 ? 1 : 0;
}
@Override
public void addSpecialCommon(List<Card> booster, int number) {
protected void addSpecialCards(List<Card> booster, int number) {
// number is here always 1
List<CardInfo> doubleFacedCards;
if (RandomUtil.nextInt(8) > 0) {
doubleFacedCards = getDoubleFacedCardsByRarity(Rarity.RARE);
Rarity rarity;
if (RandomUtil.nextInt(15) < 12) {
rarity = Rarity.RARE;
} else {
doubleFacedCards = getDoubleFacedCardsByRarity(Rarity.MYTHIC);
rarity = Rarity.MYTHIC;
}
addToBooster(booster, doubleFacedCards);
addToBooster(booster, getSpecialCardsByRarity(rarity));
}
}

View file

@ -36,6 +36,7 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet {
this.numBoosterCommon = 9;
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.numBoosterSpecial = 2;
this.ratioBoosterMythic = 7.4;
this.maxCardNumberInBooster = 275;
@ -424,50 +425,43 @@ public final class StrixhavenSchoolOfMages extends ExpansionSet {
}
@Override
public List<Card> tryBooster() {
List<Card> booster = super.tryBooster();
addArchive(booster);
addLesson(booster);
return booster;
}
private void addArchive(List<Card> booster) {
protected void addSpecialCards(List<Card> booster, int number) {
// number is here always 2
// Boosters have one card from STA, odds are 2/3 for uncommon, 4/15 for rare, 1/15 for mythic
final Rarity rarity;
int i = RandomUtil.nextInt(15);
if (i == 14) {
Rarity rarity;
int rarityKey = RandomUtil.nextInt(15);
if (rarityKey == 14) {
rarity = Rarity.MYTHIC;
} else if (i >= 10) {
} else if (rarityKey >= 10) {
rarity = Rarity.RARE;
} else {
rarity = Rarity.UNCOMMON;
}
addToBooster(booster, StrixhavenMysticalArchive.getInstance().getCardsByRarity(rarity));
}
private void addLesson(List<Card> booster) {
// Boosters have one Lesson card
final Rarity rarity;
int i = RandomUtil.nextInt(148);
if (i == 0) {
rarityKey = RandomUtil.nextInt(148);
if (rarityKey == 0) {
rarity = Rarity.MYTHIC;
} else if (i < 11) {
} else if (rarityKey < 11) {
rarity = Rarity.RARE;
} else {
rarity = Rarity.COMMON;
}
List<CardInfo> cards = super.getCardsByRarity(rarity);
cards.removeIf(cardInfo -> !cardInfo.getCard().hasSubtype(SubType.LESSON, null));
addToBooster(booster, cards);
addToBooster(booster, getSpecialCardsByRarity(rarity));
}
@Override
public List<CardInfo> getCardsByRarity(Rarity rarity) {
List<CardInfo> cards = super.getCardsByRarity(rarity);
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findSpecialCardsByRarity(rarity);
if (rarity != Rarity.UNCOMMON) {
cards.removeIf(cardInfo -> cardInfo.getCard().hasSubtype(SubType.LESSON, null));
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria()
.setCodes(this.code)
.rarities(rarity)
.subtypes("Lesson")));
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() > maxCardNumberInBooster);
}
return cards;
return cardInfos;
}
@Override

View file

@ -1,8 +1,8 @@
package mage.sets;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
@ -25,7 +25,9 @@ public final class TimeSpiral extends ExpansionSet {
this.numBoosterCommon = 10;
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.numBoosterSpecial = 1;
this.ratioBoosterMythic = 0;
cards.add(new SetCardInfo("Academy Ruins", 269, Rarity.RARE, mage.cards.a.AcademyRuins.class));
cards.add(new SetCardInfo("Aether Web", 189, Rarity.COMMON, mage.cards.a.AetherWeb.class));
cards.add(new SetCardInfo("Aetherflame Wall", 142, Rarity.COMMON, mage.cards.a.AetherflameWall.class));
@ -330,11 +332,11 @@ public final class TimeSpiral extends ExpansionSet {
}
@Override
public List<Card> createBooster() {
List<Card> booster = super.createBooster();
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.SPECIAL).setCodes("TSB");
addToBooster(booster, CardRepository.instance.findCards(criteria));
return booster;
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findCardsByRarity(rarity);
if (rarity == Rarity.SPECIAL) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes("TSB")));
}
return cardInfos;
}
}

View file

@ -1,9 +1,6 @@
package mage.sets;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.collation.BoosterCollator;
import mage.collation.BoosterStructure;
import mage.collation.CardRun;
@ -25,8 +22,6 @@ public class TimeSpiralRemastered extends ExpansionSet {
return instance;
}
private final List<CardInfo> savedSpecialBonus = new ArrayList<>();
private TimeSpiralRemastered() {
super("Time Spiral Remastered", "TSR", ExpansionSet.buildDate(2021, 3, 19), SetType.SUPPLEMENTAL);
this.hasBoosters = true;
@ -451,17 +446,6 @@ public class TimeSpiralRemastered extends ExpansionSet {
cards.add(new SetCardInfo("Zulaport Cutthroat", 337, Rarity.SPECIAL, mage.cards.z.ZulaportCutthroat.class));
}
@Override
public List<CardInfo> getSpecialBonus() {
if (savedSpecialBonus.isEmpty()) {
savedSpecialBonus.addAll(CardRepository.instance.findCards(new CardCriteria().setCodes(this.code)));
savedSpecialBonus.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() > 410);
savedSpecialBonus.removeIf(cardInfo -> cardInfo.getCardNumberAsInt() <= 289);
}
return new ArrayList<>(savedSpecialBonus);
}
@Override
public BoosterCollator createCollator() {
return new TimeSpiralRemasteredCollator();

View file

@ -24,31 +24,31 @@ public final class TimeSpiralTimeshifted extends ExpansionSet {
cards.add(new SetCardInfo("Akroma, Angel of Wrath", 1, Rarity.SPECIAL, mage.cards.a.AkromaAngelOfWrath.class));
cards.add(new SetCardInfo("Arena", 117, Rarity.SPECIAL, mage.cards.a.Arena.class));
cards.add(new SetCardInfo("Assault // Battery", 106, Rarity.SPECIAL, mage.cards.a.AssaultBattery.class));
cards.add(new SetCardInfo("Auratog", 2, Rarity.COMMON, mage.cards.a.Auratog.class));
cards.add(new SetCardInfo("Avalanche Riders", 55, Rarity.COMMON, mage.cards.a.AvalancheRiders.class));
cards.add(new SetCardInfo("Auratog", 2, Rarity.SPECIAL, mage.cards.a.Auratog.class));
cards.add(new SetCardInfo("Avalanche Riders", 55, Rarity.SPECIAL, mage.cards.a.AvalancheRiders.class));
cards.add(new SetCardInfo("Avatar of Woe", 37, Rarity.SPECIAL, mage.cards.a.AvatarOfWoe.class));
cards.add(new SetCardInfo("Avoid Fate", 73, Rarity.SPECIAL, mage.cards.a.AvoidFate.class));
cards.add(new SetCardInfo("Bad Moon", 38, Rarity.RARE, mage.cards.b.BadMoon.class));
cards.add(new SetCardInfo("Bad Moon", 38, Rarity.SPECIAL, mage.cards.b.BadMoon.class));
cards.add(new SetCardInfo("Browbeat", 56, Rarity.SPECIAL, mage.cards.b.Browbeat.class));
cards.add(new SetCardInfo("Call of the Herd", 74, Rarity.COMMON, mage.cards.c.CallOfTheHerd.class));
cards.add(new SetCardInfo("Call of the Herd", 74, Rarity.SPECIAL, mage.cards.c.CallOfTheHerd.class));
cards.add(new SetCardInfo("Celestial Dawn", 3, Rarity.SPECIAL, mage.cards.c.CelestialDawn.class));
cards.add(new SetCardInfo("Claws of Gix", 107, Rarity.COMMON, mage.cards.c.ClawsOfGix.class));
cards.add(new SetCardInfo("Coalition Victory", 91, Rarity.RARE, mage.cards.c.CoalitionVictory.class));
cards.add(new SetCardInfo("Claws of Gix", 107, Rarity.SPECIAL, mage.cards.c.ClawsOfGix.class));
cards.add(new SetCardInfo("Coalition Victory", 91, Rarity.SPECIAL, mage.cards.c.CoalitionVictory.class));
cards.add(new SetCardInfo("Cockatrice", 75, Rarity.SPECIAL, mage.cards.c.Cockatrice.class));
cards.add(new SetCardInfo("Consecrate Land", 4, Rarity.SPECIAL, mage.cards.c.ConsecrateLand.class));
cards.add(new SetCardInfo("Conspiracy", 39, Rarity.SPECIAL, mage.cards.c.Conspiracy.class));
cards.add(new SetCardInfo("Craw Giant", 76, Rarity.SPECIAL, mage.cards.c.CrawGiant.class));
cards.add(new SetCardInfo("Dandan", 19, Rarity.SPECIAL, mage.cards.d.Dandan.class));
cards.add(new SetCardInfo("Darkness", 40, Rarity.COMMON, mage.cards.d.Darkness.class));
cards.add(new SetCardInfo("Dauthi Slayer", 41, Rarity.COMMON, mage.cards.d.DauthiSlayer.class));
cards.add(new SetCardInfo("Darkness", 40, Rarity.SPECIAL, mage.cards.d.Darkness.class));
cards.add(new SetCardInfo("Dauthi Slayer", 41, Rarity.SPECIAL, mage.cards.d.DauthiSlayer.class));
cards.add(new SetCardInfo("Defiant Vanguard", 5, Rarity.SPECIAL, mage.cards.d.DefiantVanguard.class));
cards.add(new SetCardInfo("Desert", 118, Rarity.SPECIAL, mage.cards.d.Desert.class));
cards.add(new SetCardInfo("Desolation Giant", 57, Rarity.SPECIAL, mage.cards.d.DesolationGiant.class));
cards.add(new SetCardInfo("Disenchant", 6, Rarity.COMMON, mage.cards.d.Disenchant.class));
cards.add(new SetCardInfo("Disenchant", 6, Rarity.SPECIAL, mage.cards.d.Disenchant.class));
cards.add(new SetCardInfo("Disintegrate", 58, Rarity.SPECIAL, mage.cards.d.Disintegrate.class));
cards.add(new SetCardInfo("Dodecapod", 108, Rarity.SPECIAL, mage.cards.d.Dodecapod.class));
cards.add(new SetCardInfo("Dragonstorm", 60, Rarity.SPECIAL, mage.cards.d.Dragonstorm.class));
cards.add(new SetCardInfo("Dragon Whelp", 59, Rarity.COMMON, mage.cards.d.DragonWhelp.class));
cards.add(new SetCardInfo("Dragon Whelp", 59, Rarity.SPECIAL, mage.cards.d.DragonWhelp.class));
cards.add(new SetCardInfo("Enduring Renewal", 7, Rarity.SPECIAL, mage.cards.e.EnduringRenewal.class));
cards.add(new SetCardInfo("Eron the Relentless", 61, Rarity.SPECIAL, mage.cards.e.EronTheRelentless.class));
cards.add(new SetCardInfo("Essence Sliver", 8, Rarity.SPECIAL, mage.cards.e.EssenceSliver.class));
@ -58,43 +58,43 @@ public final class TimeSpiralTimeshifted extends ExpansionSet {
cards.add(new SetCardInfo("Fiery Justice", 92, Rarity.SPECIAL, mage.cards.f.FieryJustice.class));
cards.add(new SetCardInfo("Fiery Temper", 62, Rarity.SPECIAL, mage.cards.f.FieryTemper.class));
cards.add(new SetCardInfo("Fire Whip", 63, Rarity.SPECIAL, mage.cards.f.FireWhip.class));
cards.add(new SetCardInfo("Flying Men", 20, Rarity.COMMON, mage.cards.f.FlyingMen.class));
cards.add(new SetCardInfo("Funeral Charm", 44, Rarity.COMMON, mage.cards.f.FuneralCharm.class));
cards.add(new SetCardInfo("Flying Men", 20, Rarity.SPECIAL, mage.cards.f.FlyingMen.class));
cards.add(new SetCardInfo("Funeral Charm", 44, Rarity.SPECIAL, mage.cards.f.FuneralCharm.class));
cards.add(new SetCardInfo("Gaea's Blessing", 77, Rarity.SPECIAL, mage.cards.g.GaeasBlessing.class));
cards.add(new SetCardInfo("Gaea's Liege", 78, Rarity.SPECIAL, mage.cards.g.GaeasLiege.class));
cards.add(new SetCardInfo("Gemstone Mine", 119, Rarity.RARE, mage.cards.g.GemstoneMine.class));
cards.add(new SetCardInfo("Gemstone Mine", 119, Rarity.SPECIAL, mage.cards.g.GemstoneMine.class));
cards.add(new SetCardInfo("Ghost Ship", 21, Rarity.SPECIAL, mage.cards.g.GhostShip.class));
cards.add(new SetCardInfo("Giant Oyster", 22, Rarity.SPECIAL, mage.cards.g.GiantOyster.class));
cards.add(new SetCardInfo("Goblin Snowman", 64, Rarity.UNCOMMON, mage.cards.g.GoblinSnowman.class));
cards.add(new SetCardInfo("Goblin Snowman", 64, Rarity.SPECIAL, mage.cards.g.GoblinSnowman.class));
cards.add(new SetCardInfo("Grinning Totem", 110, Rarity.SPECIAL, mage.cards.g.GrinningTotem.class));
cards.add(new SetCardInfo("Hail Storm", 79, Rarity.SPECIAL, mage.cards.h.HailStorm.class));
cards.add(new SetCardInfo("Honorable Passage", 9, Rarity.SPECIAL, mage.cards.h.HonorablePassage.class));
cards.add(new SetCardInfo("Hunting Moa", 80, Rarity.COMMON, mage.cards.h.HuntingMoa.class));
cards.add(new SetCardInfo("Hunting Moa", 80, Rarity.SPECIAL, mage.cards.h.HuntingMoa.class));
cards.add(new SetCardInfo("Icatian Javelineers", 10, Rarity.SPECIAL, mage.cards.i.IcatianJavelineers.class));
cards.add(new SetCardInfo("Jasmine Boreal", 93, Rarity.COMMON, mage.cards.j.JasmineBoreal.class));
cards.add(new SetCardInfo("Jasmine Boreal", 93, Rarity.SPECIAL, mage.cards.j.JasmineBoreal.class));
cards.add(new SetCardInfo("Jolrael, Empress of Beasts", 81, Rarity.SPECIAL, mage.cards.j.JolraelEmpressOfBeasts.class));
cards.add(new SetCardInfo("Kobold Taskmaster", 65, Rarity.SPECIAL, mage.cards.k.KoboldTaskmaster.class));
cards.add(new SetCardInfo("Krosan Cloudscraper", 82, Rarity.SPECIAL, mage.cards.k.KrosanCloudscraper.class));
cards.add(new SetCardInfo("Leviathan", 23, Rarity.RARE, mage.cards.l.Leviathan.class));
cards.add(new SetCardInfo("Lightning Angel", 94, Rarity.COMMON, mage.cards.l.LightningAngel.class));
cards.add(new SetCardInfo("Lord of Atlantis", 24, Rarity.RARE, mage.cards.l.LordOfAtlantis.class));
cards.add(new SetCardInfo("Leviathan", 23, Rarity.SPECIAL, mage.cards.l.Leviathan.class));
cards.add(new SetCardInfo("Lightning Angel", 94, Rarity.SPECIAL, mage.cards.l.LightningAngel.class));
cards.add(new SetCardInfo("Lord of Atlantis", 24, Rarity.SPECIAL, mage.cards.l.LordOfAtlantis.class));
cards.add(new SetCardInfo("Merfolk Assassin", 25, Rarity.SPECIAL, mage.cards.m.MerfolkAssassin.class));
cards.add(new SetCardInfo("Merieke Ri Berit", 95, Rarity.SPECIAL, mage.cards.m.MeriekeRiBerit.class));
cards.add(new SetCardInfo("Mindless Automaton", 111, Rarity.SPECIAL, mage.cards.m.MindlessAutomaton.class));
cards.add(new SetCardInfo("Mirari", 112, Rarity.SPECIAL, mage.cards.m.Mirari.class));
cards.add(new SetCardInfo("Mistform Ultimus", 26, Rarity.SPECIAL, mage.cards.m.MistformUltimus.class));
cards.add(new SetCardInfo("Moorish Cavalry", 11, Rarity.COMMON, mage.cards.m.MoorishCavalry.class));
cards.add(new SetCardInfo("Moorish Cavalry", 11, Rarity.SPECIAL, mage.cards.m.MoorishCavalry.class));
cards.add(new SetCardInfo("Mystic Enforcer", 96, Rarity.SPECIAL, mage.cards.m.MysticEnforcer.class));
cards.add(new SetCardInfo("Mystic Snake", 97, Rarity.COMMON, mage.cards.m.MysticSnake.class));
cards.add(new SetCardInfo("Mystic Snake", 97, Rarity.SPECIAL, mage.cards.m.MysticSnake.class));
cards.add(new SetCardInfo("Nicol Bolas", 98, Rarity.SPECIAL, mage.cards.n.NicolBolas.class));
cards.add(new SetCardInfo("Orcish Librarian", 66, Rarity.SPECIAL, mage.cards.o.OrcishLibrarian.class));
cards.add(new SetCardInfo("Orgg", 67, Rarity.SPECIAL, mage.cards.o.Orgg.class));
cards.add(new SetCardInfo("Ovinomancer", 27, Rarity.SPECIAL, mage.cards.o.Ovinomancer.class));
cards.add(new SetCardInfo("Pandemonium", 68, Rarity.SPECIAL, mage.cards.p.Pandemonium.class));
cards.add(new SetCardInfo("Pendelhaven", 120, Rarity.COMMON, mage.cards.p.Pendelhaven.class));
cards.add(new SetCardInfo("Pendelhaven", 120, Rarity.SPECIAL, mage.cards.p.Pendelhaven.class));
cards.add(new SetCardInfo("Pirate Ship", 28, Rarity.SPECIAL, mage.cards.p.PirateShip.class));
cards.add(new SetCardInfo("Prodigal Sorcerer", 29, Rarity.COMMON, mage.cards.p.ProdigalSorcerer.class));
cards.add(new SetCardInfo("Psionic Blast", 30, Rarity.COMMON, mage.cards.p.PsionicBlast.class));
cards.add(new SetCardInfo("Prodigal Sorcerer", 29, Rarity.SPECIAL, mage.cards.p.ProdigalSorcerer.class));
cards.add(new SetCardInfo("Psionic Blast", 30, Rarity.SPECIAL, mage.cards.p.PsionicBlast.class));
cards.add(new SetCardInfo("Resurrection", 12, Rarity.SPECIAL, mage.cards.r.Resurrection.class));
cards.add(new SetCardInfo("Sacred Mesa", 13, Rarity.SPECIAL, mage.cards.s.SacredMesa.class));
cards.add(new SetCardInfo("Safe Haven", 121, Rarity.SPECIAL, mage.cards.s.SafeHaven.class));
@ -102,29 +102,29 @@ public final class TimeSpiralTimeshifted extends ExpansionSet {
cards.add(new SetCardInfo("Sengir Autocrat", 45, Rarity.SPECIAL, mage.cards.s.SengirAutocrat.class));
cards.add(new SetCardInfo("Serrated Arrows", 114, Rarity.SPECIAL, mage.cards.s.SerratedArrows.class));
cards.add(new SetCardInfo("Shadow Guildmage", 46, Rarity.SPECIAL, mage.cards.s.ShadowGuildmage.class));
cards.add(new SetCardInfo("Shadowmage Infiltrator", 99, Rarity.COMMON, mage.cards.s.ShadowmageInfiltrator.class));
cards.add(new SetCardInfo("Shadowmage Infiltrator", 99, Rarity.SPECIAL, mage.cards.s.ShadowmageInfiltrator.class));
cards.add(new SetCardInfo("Sindbad", 31, Rarity.SPECIAL, mage.cards.s.Sindbad.class));
cards.add(new SetCardInfo("Sol'kanar the Swamp King", 100, Rarity.COMMON, mage.cards.s.SolkanarTheSwampKing.class));
cards.add(new SetCardInfo("Soltari Priest", 14, Rarity.COMMON, mage.cards.s.SoltariPriest.class));
cards.add(new SetCardInfo("Sol'kanar the Swamp King", 100, Rarity.SPECIAL, mage.cards.s.SolkanarTheSwampKing.class));
cards.add(new SetCardInfo("Soltari Priest", 14, Rarity.SPECIAL, mage.cards.s.SoltariPriest.class));
cards.add(new SetCardInfo("Soul Collector", 47, Rarity.SPECIAL, mage.cards.s.SoulCollector.class));
cards.add(new SetCardInfo("Spike Feeder", 84, Rarity.COMMON, mage.cards.s.SpikeFeeder.class));
cards.add(new SetCardInfo("Spike Feeder", 84, Rarity.SPECIAL, mage.cards.s.SpikeFeeder.class));
cards.add(new SetCardInfo("Spined Sliver", 101, Rarity.SPECIAL, mage.cards.s.SpinedSliver.class));
cards.add(new SetCardInfo("Spitting Slug", 85, Rarity.SPECIAL, mage.cards.s.SpittingSlug.class));
cards.add(new SetCardInfo("Squire", 15, Rarity.COMMON, mage.cards.s.Squire.class));
cards.add(new SetCardInfo("Squire", 15, Rarity.SPECIAL, mage.cards.s.Squire.class));
cards.add(new SetCardInfo("Stormbind", 102, Rarity.SPECIAL, mage.cards.s.Stormbind.class));
cards.add(new SetCardInfo("Stormscape Familiar", 32, Rarity.COMMON, mage.cards.s.StormscapeFamiliar.class));
cards.add(new SetCardInfo("Stupor", 48, Rarity.COMMON, mage.cards.s.Stupor.class));
cards.add(new SetCardInfo("Suq'Ata Lancer", 69, Rarity.COMMON, mage.cards.s.SuqAtaLancer.class));
cards.add(new SetCardInfo("Stormscape Familiar", 32, Rarity.SPECIAL, mage.cards.s.StormscapeFamiliar.class));
cards.add(new SetCardInfo("Stupor", 48, Rarity.SPECIAL, mage.cards.s.Stupor.class));
cards.add(new SetCardInfo("Suq'Ata Lancer", 69, Rarity.SPECIAL, mage.cards.s.SuqAtaLancer.class));
cards.add(new SetCardInfo("Swamp Mosquito", 49, Rarity.SPECIAL, mage.cards.s.SwampMosquito.class));
cards.add(new SetCardInfo("Teferi's Moat", 103, Rarity.SPECIAL, mage.cards.t.TeferisMoat.class));
cards.add(new SetCardInfo("Thallid", 86, Rarity.COMMON, mage.cards.t.Thallid.class));
cards.add(new SetCardInfo("Thallid", 86, Rarity.SPECIAL, mage.cards.t.Thallid.class));
cards.add(new SetCardInfo("The Rack", 113, Rarity.SPECIAL, mage.cards.t.TheRack.class));
cards.add(new SetCardInfo("Thornscape Battlemage", 87, Rarity.SPECIAL, mage.cards.t.ThornscapeBattlemage.class));
cards.add(new SetCardInfo("Tormod's Crypt", 115, Rarity.COMMON, mage.cards.t.TormodsCrypt.class));
cards.add(new SetCardInfo("Tribal Flames", 70, Rarity.COMMON, mage.cards.t.TribalFlames.class));
cards.add(new SetCardInfo("Twisted Abomination", 50, Rarity.COMMON, mage.cards.t.TwistedAbomination.class));
cards.add(new SetCardInfo("Tormod's Crypt", 115, Rarity.SPECIAL, mage.cards.t.TormodsCrypt.class));
cards.add(new SetCardInfo("Tribal Flames", 70, Rarity.SPECIAL, mage.cards.t.TribalFlames.class));
cards.add(new SetCardInfo("Twisted Abomination", 50, Rarity.SPECIAL, mage.cards.t.TwistedAbomination.class));
cards.add(new SetCardInfo("Uncle Istvan", 51, Rarity.SPECIAL, mage.cards.u.UncleIstvan.class));
cards.add(new SetCardInfo("Undead Warchief", 52, Rarity.UNCOMMON, mage.cards.u.UndeadWarchief.class));
cards.add(new SetCardInfo("Undead Warchief", 52, Rarity.SPECIAL, mage.cards.u.UndeadWarchief.class));
cards.add(new SetCardInfo("Undertaker", 53, Rarity.SPECIAL, mage.cards.u.Undertaker.class));
cards.add(new SetCardInfo("Unstable Mutation", 33, Rarity.SPECIAL, mage.cards.u.UnstableMutation.class));
cards.add(new SetCardInfo("Uthden Troll", 71, Rarity.SPECIAL, mage.cards.u.UthdenTroll.class));
@ -133,14 +133,14 @@ public final class TimeSpiralTimeshifted extends ExpansionSet {
cards.add(new SetCardInfo("Vhati il-Dal", 104, Rarity.SPECIAL, mage.cards.v.VhatiIlDal.class));
cards.add(new SetCardInfo("Void", 105, Rarity.SPECIAL, mage.cards.v.Void.class));
cards.add(new SetCardInfo("Voidmage Prodigy", 34, Rarity.SPECIAL, mage.cards.v.VoidmageProdigy.class));
cards.add(new SetCardInfo("Wall of Roots", 89, Rarity.COMMON, mage.cards.w.WallOfRoots.class));
cards.add(new SetCardInfo("Wall of Roots", 89, Rarity.SPECIAL, mage.cards.w.WallOfRoots.class));
cards.add(new SetCardInfo("War Barge", 116, Rarity.SPECIAL, mage.cards.w.WarBarge.class));
cards.add(new SetCardInfo("Whirling Dervish", 90, Rarity.COMMON, mage.cards.w.WhirlingDervish.class));
cards.add(new SetCardInfo("Whirling Dervish", 90, Rarity.SPECIAL, mage.cards.w.WhirlingDervish.class));
cards.add(new SetCardInfo("Whispers of the Muse", 35, Rarity.SPECIAL, mage.cards.w.WhispersOfTheMuse.class));
cards.add(new SetCardInfo("Wildfire Emissary", 72, Rarity.SPECIAL, mage.cards.w.WildfireEmissary.class));
cards.add(new SetCardInfo("Willbender", 36, Rarity.SPECIAL, mage.cards.w.Willbender.class));
cards.add(new SetCardInfo("Witch Hunter", 17, Rarity.SPECIAL, mage.cards.w.WitchHunter.class));
cards.add(new SetCardInfo("Withered Wretch", 54, Rarity.RARE, mage.cards.w.WitheredWretch.class));
cards.add(new SetCardInfo("Zhalfirin Commander", 18, Rarity.COMMON, mage.cards.z.ZhalfirinCommander.class));
cards.add(new SetCardInfo("Withered Wretch", 54, Rarity.SPECIAL, mage.cards.w.WitheredWretch.class));
cards.add(new SetCardInfo("Zhalfirin Commander", 18, Rarity.SPECIAL, mage.cards.z.ZhalfirinCommander.class));
}
}

View file

@ -1,11 +1,11 @@
package mage.sets;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.constants.Rarity;
import mage.constants.SetType;
import mage.util.RandomUtil;
import java.util.List;
@ -30,6 +30,7 @@ public final class VintageMasters extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
cards.add(new SetCardInfo("Academy Elite", 55, Rarity.RARE, mage.cards.a.AcademyElite.class));
cards.add(new SetCardInfo("Addle", 103, Rarity.COMMON, mage.cards.a.Addle.class));
cards.add(new SetCardInfo("Aether Mutation", 241, Rarity.UNCOMMON, mage.cards.a.AetherMutation.class));
cards.add(new SetCardInfo("Afterlife", 10, Rarity.COMMON, mage.cards.a.Afterlife.class));
@ -92,6 +93,7 @@ public final class VintageMasters extends ExpansionSet {
cards.add(new SetCardInfo("Control Magic", 63, Rarity.RARE, mage.cards.c.ControlMagic.class));
cards.add(new SetCardInfo("Council's Judgment", 20, Rarity.RARE, mage.cards.c.CouncilsJudgment.class));
cards.add(new SetCardInfo("Counterspell", 64, Rarity.COMMON, mage.cards.c.Counterspell.class));
cards.add(new SetCardInfo("Crater Hellion", 157, Rarity.RARE, mage.cards.c.CraterHellion.class));
cards.add(new SetCardInfo("Crescendo of War", 21, Rarity.RARE, mage.cards.c.CrescendoOfWar.class));
cards.add(new SetCardInfo("Crovax the Cursed", 110, Rarity.RARE, mage.cards.c.CrovaxTheCursed.class));
cards.add(new SetCardInfo("Cruel Bargain", 111, Rarity.RARE, mage.cards.c.CruelBargain.class));
@ -154,6 +156,7 @@ public final class VintageMasters extends ExpansionSet {
cards.add(new SetCardInfo("Goblin Goon", 166, Rarity.UNCOMMON, mage.cards.g.GoblinGoon.class));
cards.add(new SetCardInfo("Goblin Lackey", 167, Rarity.RARE, mage.cards.g.GoblinLackey.class));
cards.add(new SetCardInfo("Goblin Matron", 168, Rarity.COMMON, mage.cards.g.GoblinMatron.class));
cards.add(new SetCardInfo("Goblin Patrol", 169, Rarity.COMMON, mage.cards.g.GoblinPatrol.class));
cards.add(new SetCardInfo("Goblin Piledriver", 170, Rarity.RARE, mage.cards.g.GoblinPiledriver.class));
cards.add(new SetCardInfo("Goblin Ringleader", 171, Rarity.UNCOMMON, mage.cards.g.GoblinRingleader.class));
cards.add(new SetCardInfo("Goblin Settler", 172, Rarity.UNCOMMON, mage.cards.g.GoblinSettler.class));
@ -190,6 +193,7 @@ public final class VintageMasters extends ExpansionSet {
cards.add(new SetCardInfo("Lake of the Dead", 302, Rarity.RARE, mage.cards.l.LakeOfTheDead.class));
cards.add(new SetCardInfo("Laquatus's Champion", 125, Rarity.RARE, mage.cards.l.LaquatussChampion.class));
cards.add(new SetCardInfo("Library of Alexandria", 303, Rarity.MYTHIC, mage.cards.l.LibraryOfAlexandria.class));
cards.add(new SetCardInfo("Lightning Dragon", 177, Rarity.RARE, mage.cards.l.LightningDragon.class));
cards.add(new SetCardInfo("Lightning Rift", 178, Rarity.UNCOMMON, mage.cards.l.LightningRift.class));
cards.add(new SetCardInfo("Lion's Eye Diamond", 271, Rarity.MYTHIC, mage.cards.l.LionsEyeDiamond.class));
cards.add(new SetCardInfo("Living Death", 126, Rarity.RARE, mage.cards.l.LivingDeath.class));
@ -230,6 +234,7 @@ public final class VintageMasters extends ExpansionSet {
cards.add(new SetCardInfo("Obsessive Search", 83, Rarity.COMMON, mage.cards.o.ObsessiveSearch.class));
cards.add(new SetCardInfo("Ophidian", 84, Rarity.COMMON, mage.cards.o.Ophidian.class));
cards.add(new SetCardInfo("Orcish Lumberjack", 179, Rarity.COMMON, mage.cards.o.OrcishLumberjack.class));
cards.add(new SetCardInfo("Owl Familiar", 85, Rarity.COMMON, mage.cards.o.OwlFamiliar.class));
cards.add(new SetCardInfo("Palinchron", 86, Rarity.RARE, mage.cards.p.Palinchron.class));
cards.add(new SetCardInfo("Parallax Wave", 37, Rarity.RARE, mage.cards.p.ParallaxWave.class));
cards.add(new SetCardInfo("Paralyze", 132, Rarity.COMMON, mage.cards.p.Paralyze.class));
@ -353,38 +358,25 @@ public final class VintageMasters extends ExpansionSet {
}
@Override
public List<CardInfo> getSpecialCommon() {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.COMMON).setCodes(this.code);
return CardRepository.instance.findCards(criteria);
protected void addSpecialCards(List<Card> booster, int number) {
// number is here always 1
Rarity rarity;
// You will open a Power Nine card about once in 53 packs
if (RandomUtil.nextInt(53) == 0) {
rarity = Rarity.BONUS;
} else {
// assuming same distribution as foils in paper Masters sets, 10:3:1 C:U:R
int rarityKey = RandomUtil.nextInt(112);
if (rarityKey < 80) {
rarity = Rarity.COMMON;
} else if (rarityKey < 104) {
rarity = Rarity.UNCOMMON;
} else if (rarityKey < 111) {
rarity = Rarity.RARE;
} else {
rarity = Rarity.MYTHIC;
}
}
addToBooster(booster, getCardsByRarity(rarity));
}
@Override
public List<CardInfo> getSpecialUncommon() {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.UNCOMMON).setCodes(this.code);
return CardRepository.instance.findCards(criteria);
}
@Override
public List<CardInfo> getSpecialRare() {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.RARE).setCodes(this.code);
return CardRepository.instance.findCards(criteria);
}
@Override
public List<CardInfo> getSpecialMythic() {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.MYTHIC).setCodes(this.code);
return CardRepository.instance.findCards(criteria);
}
@Override
public List<CardInfo> getSpecialBonus() {
CardCriteria criteria = new CardCriteria();
criteria.rarities(Rarity.BONUS).setCodes(this.code);
return CardRepository.instance.findCards(criteria);
}
}

View file

@ -1,5 +1,7 @@
package mage.sets;
import mage.MageObject;
import mage.cards.Card;
import mage.cards.ExpansionSet;
import mage.collation.BoosterCollator;
import mage.collation.BoosterStructure;
@ -28,7 +30,6 @@ public final class WarOfTheSpark extends ExpansionSet {
this.numBoosterUncommon = 3;
this.numBoosterRare = 1;
this.ratioBoosterMythic = 8;
this.needsPlaneswalker = true;
this.maxCardNumberInBooster = 264;
cards.add(new SetCardInfo("Ahn-Crop Invader", 113, Rarity.COMMON, mage.cards.a.AhnCropInvader.class));
@ -344,6 +345,12 @@ public final class WarOfTheSpark extends ExpansionSet {
cards.add(new SetCardInfo("Widespread Brutality", 226, Rarity.RARE, mage.cards.w.WidespreadBrutality.class));
}
@Override
protected boolean boosterIsValid(List<Card> booster) {
return super.boosterIsValid(booster)
&& booster.stream().filter(MageObject::isPlaneswalker).count() == 1;
}
@Override
public BoosterCollator createCollator() {
return new WarOfTheSparkCollator();

View file

@ -3,6 +3,7 @@ package mage.sets;
import mage.ObjectColor;
import mage.cards.CardGraphicInfo;
import mage.cards.ExpansionSet;
import mage.cards.repository.CardInfo;
import mage.collation.BoosterCollator;
import mage.collation.BoosterStructure;
import mage.collation.CardRun;
@ -305,6 +306,16 @@ public final class Zendikar extends ExpansionSet {
cards.add(new SetCardInfo("Zendikar Farguide", 194, Rarity.COMMON, mage.cards.z.ZendikarFarguide.class));
}
@Override
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = super.findCardsByRarity(rarity);
if (rarity == Rarity.LAND) {
// only the full-art basic lands are found in boosters
cardInfos.removeIf(cardInfo -> cardInfo.getCardNumber().contains("a"));
}
return cardInfos;
}
@Override
public BoosterCollator createCollator() {
return new ZendikarCollator();

View file

@ -177,29 +177,6 @@ public class BoosterGenerationTest extends MageTestBase {
assertFalse(str(booster), contains(booster, basics, null));
}
@Test
public void testMastersEditionIV_UrzaSpecialLandsList() {
List<String> needUrzaList = new ArrayList<>(Arrays.asList(
"Urza's Mine",
"Urza's Power Plant",
"Urza's Tower"
));
List<CardInfo> setOrzaList = MastersEditionIV.getInstance().getSpecialLand();
Assert.assertEquals("Urza special lands must have 4 variation for each of 3 card", 3 * 4, setOrzaList.size());
List<String> foundUrzaList = new ArrayList<>();
for (CardInfo cardInfo : setOrzaList) {
Assert.assertTrue("card " + cardInfo.getName() + " must be in urza's list", needUrzaList.contains(cardInfo.getName()));
foundUrzaList.add(cardInfo.getName());
}
for (String needName : needUrzaList) {
Assert.assertTrue("can't find need card " + needName + " in special land list", foundUrzaList.contains(needName));
}
}
@Test
public void testMastersEditionIV_UrzaSpecialLandInBoosters() {
// ME4 replace all basic lands with special (1 per booster)

View file

@ -1,13 +1,11 @@
package mage.cards;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Ability;
import mage.abilities.keyword.PartnerWithAbility;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.collation.BoosterCollator;
import mage.constants.CardType;
import mage.constants.Rarity;
import mage.constants.SetType;
import mage.util.CardUtil;
@ -118,23 +116,22 @@ public abstract class ExpansionSet implements Serializable {
protected int numBoosterLands;
protected int ratioBoosterSpecialLand = 0; // if > 0 basic lands are replaced with special land with probability ratioBoosterSpecialLandNumerator / ratioBoosterSpecialLand
protected int ratioBoosterSpecialLandNumerator = 1;
protected int ratioBoosterSpecialCommon = 0; // if > 0 one common is replaced with special card with probability 1 / ratioBoosterSpecialCommon
protected int numBoosterCommon;
protected int numBoosterUncommon;
protected int numBoosterRare;
protected int numBoosterDoubleFaced; // -1 = include normally 0 = exclude 1-n = include explicit
protected double ratioBoosterMythic;
protected boolean hasPartnerMechanic = false;
protected boolean needsLegendCreature = false;
protected boolean needsPlaneswalker = false;
protected boolean validateBoosterColors = true;
protected double rejectMissingColorProbability = 0.8;
protected double rejectSameColorUncommonsProbability = 0.8;
protected int maxCardNumberInBooster; // used to omit cards with collector numbers beyond the regular cards in a set for boosters
protected final EnumMap<Rarity, List<CardInfo>> savedCards;
protected final EnumMap<Rarity, List<CardInfo>> savedCards = new EnumMap<>(Rarity.class);
protected final EnumMap<Rarity, List<CardInfo>> savedSpecialCards = new EnumMap<>(Rarity.class);
protected final Map<String, CardInfo> inBoosterMap = new HashMap<>();
public ExpansionSet(String name, String code, Date releaseDate, SetType setType) {
@ -143,7 +140,6 @@ public abstract class ExpansionSet implements Serializable {
this.releaseDate = releaseDate;
this.setType = setType;
this.maxCardNumberInBooster = Integer.MAX_VALUE;
savedCards = new EnumMap<>(Rarity.class);
}
public String getName() {
@ -214,35 +210,6 @@ public abstract class ExpansionSet implements Serializable {
return theBooster;
}
protected int addMissingPartner(List<Card> booster, boolean partnerAllowed, int max, int i) {
Card sourceCard = booster.get(booster.size() - 1);
for (Ability ability : sourceCard.getAbilities()) {
//Check if fetched card has the PartnerWithAbility
if (ability instanceof PartnerWithAbility) {
String partnerName = ((PartnerWithAbility) ability).getPartnerName();
//Check if the pack already contains a partner pair
if (partnerAllowed) {
//Added card always replaces an uncommon card
Card card = CardRepository.instance.findCardWPreferredSet(partnerName, sourceCard.getExpansionSetCode(), false).getCard();
if (i < max) {
booster.add(card);
} else {
booster.set(0, card);
}
//2 return value indicates found partner
return 2;
} else {
//If partner already exists, remove card and loop again
booster.remove(booster.size() - 1);
return 0;
}
}
}
return 1;
}
protected void addToBooster(List<Card> booster, List<CardInfo> cards) {
if (!cards.isEmpty()) {
CardInfo cardInfo = cards.remove(RandomUtil.nextInt(cards.size()));
@ -266,16 +233,7 @@ public abstract class ExpansionSet implements Serializable {
}
for (int i = 0; i < 100; i++) {//don't want to somehow loop forever
List<Card> booster;
if (hasPartnerMechanic) {
// battlebond's partners cards
booster = createPartnerBooster();
} else {
// all other sets
booster = tryBooster();
}
List<Card> booster = tryBooster();
if (boosterIsValid(booster)) {
return booster;
}
@ -324,13 +282,6 @@ public abstract class ExpansionSet implements Serializable {
}
}
if (needsLegendCreature) {
return booster.stream().anyMatch(card -> card.isLegendary() && card.isCreature());
}
if (needsPlaneswalker) {
return booster.stream().filter(MageObject::isPlaneswalker).count() == 1;
}
// TODO: add partner check
// TODO: add booster size check?
return true;
@ -388,88 +339,10 @@ public abstract class ExpansionSet implements Serializable {
return true;
}
private boolean checkMythic() {
protected boolean checkMythic() {
return ratioBoosterMythic > 0 && ratioBoosterMythic * RandomUtil.nextDouble() <= 1;
}
public List<Card> createPartnerBooster() {
List<Card> booster = new ArrayList<>();
boolean partnerAllowed = true;
List<CardInfo> uncommons = getCardsByRarity(Rarity.UNCOMMON);
for (int i = 0; i < numBoosterUncommon; i++) {
while (true) {
addToBooster(booster, uncommons);
int check = addMissingPartner(booster, partnerAllowed, numBoosterUncommon - 1, i);
if (check == 1) {
break;
}
if (check == 2) {
partnerAllowed = false;
//Be sure to account for the added card
if (i != numBoosterUncommon - 1) {
i += 1;
}
break;
}
}
}
int numSpecialCommons = getNumberOfSpecialCommons();
int numCommonsToGenerate = numBoosterCommon - numSpecialCommons;
List<CardInfo> commons = getCardsByRarity(Rarity.COMMON);
for (int i = 0; i < numCommonsToGenerate; i++) {
addToBooster(booster, commons);
}
List<CardInfo> rares = getCardsByRarity(Rarity.RARE);
List<CardInfo> mythics = getCardsByRarity(Rarity.MYTHIC);
for (int i = 0; i < numBoosterRare; i++) {
if (checkMythic()) {
while (true) {
addToBooster(booster, mythics);
int check = addMissingPartner(booster, partnerAllowed, -1, 1);
if (check == 1) {
break;
}
if (check == 2) {
partnerAllowed = false;
break;
}
}
} else {
while (true) {
addToBooster(booster, rares);
int check = addMissingPartner(booster, partnerAllowed, -1, 1);
if (check == 1) {
break;
}
if (check == 2) {
partnerAllowed = false;
break;
}
}
}
}
if (numBoosterLands > 0) {
List<CardInfo> specialLands = getSpecialLand();
List<CardInfo> basicLands = getCardsByRarity(Rarity.LAND);
for (int i = 0; i < numBoosterLands; i++) {
if (ratioBoosterSpecialLand > 0 && RandomUtil.nextInt(ratioBoosterSpecialLand) < ratioBoosterSpecialLandNumerator && specialLands != null) {
addToBooster(booster, specialLands);
} else {
addToBooster(booster, basicLands);
}
}
}
return booster;
}
public List<Card> tryBooster() {
List<Card> booster = new ArrayList<>();
if (!hasBoosters) {
@ -477,28 +350,29 @@ public abstract class ExpansionSet implements Serializable {
}
if (numBoosterLands > 0) {
List<CardInfo> specialLands = getSpecialLand();
List<CardInfo> specialLands = getSpecialCardsByRarity(Rarity.LAND);
List<CardInfo> basicLands = getCardsByRarity(Rarity.LAND);
for (int i = 0; i < numBoosterLands; i++) {
if (ratioBoosterSpecialLand > 0 && RandomUtil.nextInt(ratioBoosterSpecialLand) < ratioBoosterSpecialLandNumerator && specialLands != null) {
if (ratioBoosterSpecialLand > 0 && RandomUtil.nextInt(ratioBoosterSpecialLand) < ratioBoosterSpecialLandNumerator) {
addToBooster(booster, specialLands);
} else {
addToBooster(booster, basicLands);
}
}
}
int numSpecialCommons = getNumberOfSpecialCommons();
int numCommonsToGenerate = numBoosterCommon - numSpecialCommons;
int numCommonsToGenerate = numBoosterCommon;
int numSpecialToGenerate = numBoosterSpecial;
if (ratioBoosterSpecialCommon > 0 && RandomUtil.nextInt(ratioBoosterSpecialCommon) < 1) {
--numCommonsToGenerate;
++numSpecialToGenerate;
}
List<CardInfo> commons = getCardsByRarity(Rarity.COMMON);
for (int i = 0; i < numCommonsToGenerate; i++) {
addToBooster(booster, commons);
}
if (numSpecialCommons > 0) { // e.g. used to conditionally replace common cards in the booster
addSpecialCommon(booster, numSpecialCommons);
}
List<CardInfo> uncommons = getCardsByRarity(Rarity.UNCOMMON);
for (int i = 0; i < numBoosterUncommon; i++) {
addToBooster(booster, uncommons);
@ -518,8 +392,8 @@ public abstract class ExpansionSet implements Serializable {
addDoubleFace(booster);
}
if (numBoosterSpecial > 0) {
addSpecial(booster);
if (numSpecialToGenerate > 0) {
addSpecialCards(booster, numSpecialToGenerate);
}
return booster;
@ -528,21 +402,27 @@ public abstract class ExpansionSet implements Serializable {
/* add double faced card for Innistrad booster
* rarity near as the normal distribution
*/
public void addDoubleFace(List<Card> booster) {
protected void addDoubleFace(List<Card> booster) {
Rarity rarity;
for (int i = 0; i < numBoosterDoubleFaced; i++) {
CardCriteria criteria = new CardCriteria();
criteria.setCodes(this.code).doubleFaced(true);
if (RandomUtil.nextInt(15) < 10) {
criteria.rarities(Rarity.COMMON);
} else if (RandomUtil.nextInt(5) < 4) {
criteria.rarities(Rarity.UNCOMMON);
} else if (RandomUtil.nextInt(8) < 7) {
criteria.rarities(Rarity.RARE);
int rarityKey = RandomUtil.nextInt(121);
if (rarityKey < 66) {
rarity = Rarity.COMMON;
} else if (rarityKey < 108) {
rarity = Rarity.UNCOMMON;
} else if (rarityKey < 120) {
rarity = Rarity.RARE;
} else {
criteria.rarities(Rarity.MYTHIC);
rarity = Rarity.MYTHIC;
}
List<CardInfo> doubleFacedCards = CardRepository.instance.findCards(criteria);
addToBooster(booster, doubleFacedCards);
addToBooster(booster, getSpecialCardsByRarity(rarity));
}
}
protected void addSpecialCards(List<Card> booster, int number) {
List<CardInfo> specialCards = getCardsByRarity(Rarity.SPECIAL);
for (int i = 0; i < number; i++) {
addToBooster(booster, specialCards);
}
}
@ -551,75 +431,6 @@ public abstract class ExpansionSet implements Serializable {
return new GregorianCalendar(year, month - 1, day).getTime();
}
/**
* Can be overwritten if sometimes special cards will be generated instead
* of common slots
*
* @return
*/
public int getNumberOfSpecialCommons() {
return 0;
}
/**
* Can be overwritten to add a replacement for common card in boosters
*
* @param booster
* @param number
*/
public void addSpecialCommon(List<Card> booster, int number) {
}
private void addSpecial(List<Card> booster) {
int specialCards = 0;
List<CardInfo> specialBonus = getSpecialBonus();
specialCards += specialBonus.size();
List<CardInfo> specialMythic = getSpecialMythic();
specialCards += specialMythic.size();
List<CardInfo> specialRare = getSpecialRare();
specialCards += specialRare.size();
List<CardInfo> specialUncommon = getSpecialUncommon();
specialCards += specialUncommon.size();
List<CardInfo> specialCommon = getSpecialCommon();
specialCards += specialCommon.size();
if (specialCards > 0) {
for (int i = 0; i < numBoosterSpecial; i++) {
if (!specialCommon.isEmpty()
&& RandomUtil.nextInt(15) < 10) {
addToBooster(booster, specialCommon);
continue;
}
if (!specialUncommon.isEmpty()
&& RandomUtil.nextInt(4) < 3) {
addToBooster(booster, specialUncommon);
continue;
}
if (!specialRare.isEmpty()
&& RandomUtil.nextInt(8) < 7) {
addToBooster(booster, specialRare);
continue;
}
if (!specialMythic.isEmpty()) {
if (specialBonus.isEmpty() || RandomUtil.nextInt(3) < 2) {
addToBooster(booster, specialMythic);
continue;
}
}
if (!specialBonus.isEmpty()) {
addToBooster(booster, specialBonus);
continue;
}
i--;
}
}
}
public boolean hasBoosters() {
return hasBoosters;
}
@ -628,58 +439,89 @@ public abstract class ExpansionSet implements Serializable {
return hasBasicLands;
}
public List<CardInfo> getCardsByRarity(Rarity rarity) {
List<CardInfo> savedCardsInfos = savedCards.get(rarity);
if (savedCardsInfos == null) {
CardCriteria criteria = new CardCriteria();
if (rarity == Rarity.LAND && !hasBasicLands && parentSet != null) {
// get basic lands from parent set if this set doesn't have them
criteria.setCodes(parentSet.code);
} else {
criteria.setCodes(this.code);
}
criteria.rarities(rarity);
if (numBoosterDoubleFaced > -1) {
criteria.doubleFaced(false);
}
savedCardsInfos = CardRepository.instance.findCards(criteria);
// Workaround after card number is numeric
if (maxCardNumberInBooster != Integer.MAX_VALUE) {
savedCardsInfos.removeIf(next -> next.getCardNumberAsInt() > maxCardNumberInBooster && rarity != Rarity.LAND);
}
savedCards.put(rarity, savedCardsInfos);
public final synchronized List<CardInfo> getCardsByRarity(Rarity rarity) {
List<CardInfo> savedCardInfos = savedCards.get(rarity);
if (savedCardInfos == null) {
savedCardInfos = findCardsByRarity(rarity);
savedCards.put(rarity, savedCardInfos);
}
// Return a copy of the saved cards information, as not to let modify the original.
return new ArrayList<>(savedCardsInfos);
return new ArrayList<>(savedCardInfos);
}
public List<CardInfo> getSpecialCommon() {
return new ArrayList<>();
public final synchronized List<CardInfo> getSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> savedCardInfos = savedSpecialCards.get(rarity);
if (savedCardInfos == null) {
savedCardInfos = findSpecialCardsByRarity(rarity);
savedSpecialCards.put(rarity, savedCardInfos);
}
// Return a copy of the saved cards information, as not to let modify the original.
return new ArrayList<>(savedCardInfos);
}
public List<CardInfo> getSpecialUncommon() {
return new ArrayList<>();
protected List<CardInfo> findCardsByRarity(Rarity rarity) {
// get basic lands from parent set if this set doesn't have them
if (rarity == Rarity.LAND && !hasBasicLands && parentSet != null) {
return parentSet.getCardsByRarity(rarity);
}
List<CardInfo> cardInfos = CardRepository.instance.findCards(new CardCriteria()
.setCodes(this.code)
.rarities(rarity));
cardInfos.removeIf(next -> (
next.getCardNumberAsInt() > maxCardNumberInBooster
|| next.getCardNumber().contains("*")
|| next.getCardNumber().contains("+")));
// special slot cards must not also appear in regular slots of their rarity
// special land slot cards must not appear in regular common slots either
List<CardInfo> specialCards = getSpecialCardsByRarity(rarity);
if (rarity == Rarity.COMMON && ratioBoosterSpecialLand > 0) {
specialCards.addAll(getSpecialCardsByRarity(Rarity.LAND));
}
cardInfos.removeAll(specialCards);
return cardInfos;
}
public List<CardInfo> getSpecialRare() {
return new ArrayList<>();
}
/**
* "Special cards" are cards that have common/uncommon/rare/mythic rarities
* but can only appear in a specific slot in boosters. Examples are DFCs in
* Innistrad sets and common nonbasic lands in many post-2018 sets.
*
* Note that Rarity.SPECIAL and Rarity.BONUS cards are not normally treated
* as "special cards" because by default boosters don't even have slots for
* those rarities.
*
* Also note that getCardsByRarity calls getSpecialCardsByRarity to exclude
* special cards from non-special booster slots, so sets that override this
* method must not call getCardsByRarity in it or infinite recursion will occur.
*/
protected List<CardInfo> findSpecialCardsByRarity(Rarity rarity) {
List<CardInfo> cardInfos = new ArrayList<>();
public List<CardInfo> getSpecialMythic() {
return new ArrayList<>();
}
// if set has special land slot, assume all common lands are special cards
if (rarity == Rarity.LAND && ratioBoosterSpecialLand > 0) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria()
.setCodes(this.code)
.rarities(Rarity.COMMON)
.types(CardType.LAND)));
}
public List<CardInfo> getSpecialBonus() {
return new ArrayList<>();
}
// if set has special slot(s) for DFCs, they are special cards
if (numBoosterDoubleFaced > 0) {
cardInfos.addAll(CardRepository.instance.findCards(new CardCriteria()
.setCodes(this.code)
.rarities(rarity)
.doubleFaced(true)));
}
public List<CardInfo> getSpecialLand() {
return new ArrayList<>();
}
cardInfos.removeIf(next -> (
next.getCardNumberAsInt() > maxCardNumberInBooster
|| next.getCardNumber().contains("*")
|| next.getCardNumber().contains("+")));
public void removeSavedCards() {
savedCards.clear();
return cardInfos;
}
public int getMaxCardNumberInBooster() {

View file

@ -479,6 +479,16 @@ public class CardInfo {
return modalDoubleFacesSecondSideName;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || !(o instanceof CardInfo)) return false;
CardInfo other = (CardInfo) o;
return (this.name.equals(other.name)
&& this.setCode.equals(other.setCode)
&& this.cardNumber.equals(other.cardNumber));
}
@Override
public String toString() {
return String.format("%s (%s, %s)", getName(), getSetCode(), getCardNumber());

View file

@ -34,7 +34,6 @@ public class BoosterDraft extends DraftImpl {
}
boosterNum++;
}
resetBufferedCards();
this.fireEndDraftEvent();
}

View file

@ -44,8 +44,6 @@ public interface Draft extends MageItem, Serializable {
void addPlayerQueryEventListener(Listener<PlayerQueryEvent> listener);
void firePickCardEvent(UUID playerId);
void resetBufferedCards();
boolean isAbort();
void setAbort(boolean abort);

View file

@ -305,16 +305,4 @@ public abstract class DraftImpl implements Draft {
started = true;
}
@Override
public void resetBufferedCards() {
Set<ExpansionSet> setsDone = new HashSet<>();
for (ExpansionSet set : sets) {
if (!setsDone.contains(set)) {
set.removeSavedCards();
setsDone.add(set);
}
}
}
}

View file

@ -38,7 +38,6 @@ public class RichManBoosterDraft extends DraftImpl {
}
boosterNum++;
}
resetBufferedCards();
this.fireEndDraftEvent();
}

View file

@ -36,7 +36,6 @@ public class RichManCubeBoosterDraft extends DraftImpl {
}
boosterNum++;
}
resetBufferedCards();
this.fireEndDraftEvent();
}

View file

@ -419,20 +419,9 @@ public abstract class TournamentImpl implements Tournament {
}
}
}
resetBufferedCards();
nextStep();
}
public void resetBufferedCards() {
Set<ExpansionSet> setsDone = new HashSet<>();
for (ExpansionSet set : sets) {
if (!setsDone.contains(set)) {
set.removeSavedCards();
setsDone.add(set);
}
}
}
public void playMatch(TournamentPairing pair) {
options.getMatchOptions().getPlayerTypes().clear();
options.getMatchOptions().getPlayerTypes().add(pair.getPlayer1().getPlayerType());