diff --git a/.gitattributes b/.gitattributes index b3a3a68fcb3..2e203e7b378 100644 --- a/.gitattributes +++ b/.gitattributes @@ -13,6 +13,7 @@ /pom.xml svneol=native#text/xml res/AllTokens.txt svneol=native#text/plain res/PerSetTracking.py svneol=native#text/x-python +res/assignSetInfo.py -text res/blockdata/blocks.txt svneol=native#text/plain res/blockdata/boosters.txt -text res/blockdata/fantasyblocks.txt -text @@ -12425,6 +12426,7 @@ res/licenses/multiline-label-license.txt svneol=native#text/plain res/licenses/xpp3-license.txt svneol=native#text/plain res/licenses/xstream-license.txt svneol=native#text/plain res/mtg-data.txt svneol=native#text/plain +res/mtgdata-sets-to-forge.txt -text res/oracleScript.py -text svneol=unset#text/x-python res/pics_link/card-pictures_a.txt svneol=native#text/plain res/pics_link/card-pictures_b.txt svneol=native#text/plain diff --git a/CHANGES.txt b/CHANGES.txt index 985c4d0c695..5a7156417af 100644 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -6,7 +6,13 @@ Forge Beta: 03-##-2013 ver 1.3.10 Release Notes: +A new quest world by Serrasmurf based on Ravinca has been added. +An effort is being made to implement the split cards (e.g. Fire/Ice) in Forge. + +The "Scale Image Larger" option in the preferences should be fixed. It should no longer continue to scale images larger when it is disabled. + +The fat packs should now be available for purchase in the quest mode card shop. New Cards: @@ -15,6 +21,13 @@ Dimensional Breach Lim-Dul's Vault Eureka Hypergenesis +Death by Dragons +Carpet of Flowers +Goblin Welder +Delaying Shield +Fatal Lore +Library of Lat-Nam +Misfortune New Phenomenons: diff --git a/res/assignSetInfo.py b/res/assignSetInfo.py new file mode 100644 index 00000000000..f398074d412 --- /dev/null +++ b/res/assignSetInfo.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python + +import os,sys,fnmatch,re + +pathToMtgData = "mtg-data.txt" +pathToSetsMatchTable = "mtgdata-sets-to-forge.txt" + +class cis: # CardInSet + def __init__(self): + self.rarity = "C" + self.arts = 0 + + def __str__(self): + return self.rarityFull() if self.arts <= 1 else "{} x{}".format(self.rarityFull(), self.arts) + + def __repr__(self): + return self.__str__() + + def rarityFull(self): + if (self.rarity == "B"): + return "Land" + if (self.rarity == "R"): + return "Rare" + if (self.rarity == "U"): + return "Uncommon" + if (self.rarity == "S"): + return "Special" + if (self.rarity == "M"): + return "Mythic" + return "Common" + + +if __name__ == '__main__': + if not os.path.exists(pathToMtgData) : + print("This script requires the text version of Arch's mtg-data to be present.You can download it from slightlymagic.net's forum and either place the text version next to this script or edit this script and provide the path to the file at the top.") + print("Press Enter to exit") + raw_input("") + sys.exit() + + setCodes = [] + setCodeToName = {} + setCodeToForge = {} + mtgDataCards = {} + + + hasFetchedSets = False + hasFetchedCardName = False + tmpName = "" + line = "" + prevline = "" + + #Parse mtg-data + print("Parsing mtg-data...") + with open(pathToMtgData) as mtgdata : + for line in mtgdata : + # Parse the sets at the top of the mtgdata file + if not hasFetchedSets : + if line != "\n" : + splitLine = line.split(' ') + code = splitLine[0] + setCodeToName[code] = splitLine[-1].replace('\n', '') + #print splitLine, code, setCodeToName[code] + setCodes.append(code) + else : + hasFetchedSets = True + + # Once all sets are parsed, time to parse the cards + elif hasFetchedSets : + if not hasFetchedCardName : + tmpName = line.rstrip() + hasFetchedCardName = True + oracle = "" + + else: + oracle += line + + if line == "\n" : + #mtgOracleCards[tmpName] = oracle.replace(prevline, '') + + sets = prevline.split(", ") + editions = {} + for i in range(len(sets)): + ee = sets[i].split(' ') + setName = ee[0] + if not setName in editions: + editions[setName] = cis() + editions[setName].rarity = ee[1].strip() + prints = int(ee[2][2:3]) if len(ee) > 2 else 1 + editions[setName].arts += prints + #print sets + mtgDataCards[tmpName] = editions + hasFetchedCardName = False + + prevline = line + + + print("Matching mtg-data and Forge sets") + with open(pathToSetsMatchTable) as setsMatch : + for line in setsMatch: + if line[0:3] == "---": + code = line[3:].split(" ")[0] + setCodeToForge[code] = None + + elif line[0:3] == "===": + code = line[3:].split(" ")[0] + setCodeToForge[code] = code; + else: + code1 = line.split(" ")[0] + code2 = line.split(" ")[1] + setCodeToForge[code1] = code2 + + + folder = "cardsfolder" + for root, dirnames, filenames in os.walk(folder): + for fileName in fnmatch.filter(filenames, '*.txt'): + if fileName.startswith('.'): + continue + + cardfile = open(os.path.join(root, fileName), 'r') + + firstLine = cardfile.readline().strip() + cardName = firstLine[5:] + + print (cardName, fileName) + validLines = [] + validLines.append(firstLine) + + for line in cardfile.readlines(): + if line[:8] != "SetInfo:" and line[:8] != "SVar:Rar": + validLines.append(line.strip()) + cardfile.close() + + for e in mtgDataCards[cardName]: + if not setCodeToForge[e] is None: + validLines.append( "SetInfo:{} {}".format(setCodeToForge[e], mtgDataCards[cardName][e]) ) + + toWrite = "\n".join(validLines) + + cardfile = open(os.path.join(root, fileName), 'w') + cardfile.write(toWrite) + cardfile.close(); + \ No newline at end of file diff --git a/res/blockdata/fatpacks.txt b/res/blockdata/fatpacks.txt index d82894cd75b..e8d69f35247 100644 --- a/res/blockdata/fatpacks.txt +++ b/res/blockdata/fatpacks.txt @@ -32,18 +32,18 @@ Set:EVE|LandSet:SHM|Boosters:8|BasicLands:40 Set:ALA|LandSet:ALA|Boosters:8|BasicLands:40 Set:CFX|LandSet:ALA|Boosters:8|BasicLands:40 Set:ARB|LandSet:ALA|Boosters:8|BasicLands:40 -Set:M10|LandSet:M10|Boosters:8|BasicLands:80 -Set:ZEN|LandSet:ZEN|Boosters:8|BasicLands:80 -Set:WWK|LandSet:ZEN|Boosters:8|BasicLands:80 -Set:ROE|LandSet:ROE|Boosters:8|BasicLands:80 -Set:M11|LandSet:M11|Boosters:8|BasicLands:80 -Set:SOM|LandSet:SOM|Boosters:9|BasicLands:80 +Set:M10|LandSet:M10|Boosters:8|BasicLands:40 +Set:ZEN|LandSet:ZEN|Boosters:8|BasicLands:40 +Set:WWK|LandSet:ZEN|Boosters:8|BasicLands:40 +Set:ROE|LandSet:ROE|Boosters:8|BasicLands:40 +Set:M11|LandSet:M11|Boosters:8|BasicLands:40 +Set:SOM|LandSet:SOM|Boosters:8|BasicLands:40 Set:MBS|LandSet:MBS|Boosters:9|BasicLands:80 Set:NPH|LandSet:NPH|Boosters:9|BasicLands:80 Set:M12|LandSet:M12|Boosters:9|BasicLands:80 -Set:ISD|LandSet:ISD|Boosters:9|BasicLands:60 -Set:DKA|LandSet:ISD|Boosters:9|BasicLands:60 -Set:AVR|LandSet:AVR|Boosters:9|BasicLands:60 +Set:ISD|LandSet:ISD|Boosters:9|BasicLands:70 +Set:DKA|LandSet:ISD|Boosters:9|BasicLands:70 +Set:AVR|LandSet:AVR|Boosters:9|BasicLands:80 Set:M13|LandSet:M13|Boosters:9|BasicLands:80 -Set:RTR|LandSet:RTR|Boosters:9|BasicLands:40 -Set:GTC|LandSet:RTR|Boosters:9|BasicLands:40 \ No newline at end of file +Set:RTR|LandSet:RTR|Boosters:9|BasicLands:80 +Set:GTC|LandSet:RTR|Boosters:9|BasicLands:80 diff --git a/res/cardsfolder/c/carpet_of_flowers.txt b/res/cardsfolder/c/carpet_of_flowers.txt index 162bf57fede..51db2d238bd 100644 --- a/res/cardsfolder/c/carpet_of_flowers.txt +++ b/res/cardsfolder/c/carpet_of_flowers.txt @@ -7,7 +7,8 @@ SVar:ChooseNumber:DB$ ChooseNumber | Min$ 0 | Max$ NumManaMax | References$ NumM SVar:DBMana:DB$ Mana | Amount$ X | Produced$ Any | SubAbility$ CheckPlus SVar:CheckPlus:DB$ StoreSVar | SVar$ CarpetX | Type$ Number | Expression$ 1 | SubAbility$ DBCleanup SVar:DBCleanup:DB$ Cleanup | ClearRemembered$ True -T:Mode$ Phase | Phase$ End of Turn | Execute$ TrigReset | Static$ True +T:Mode$ Phase | Phase$ Cleanup | Execute$ TrigReset | Static$ True +T:Mode$ ChangesZone | Origin$ Battlefield | Destination$ Any | ValidCard$ Card.Self | Execute$ TrigReset | Static$ True SVar:TrigReset:AB$ StoreSVar | Cost$ 0 | SVar$ CarpetX | Type$ Number | Expression$ 0 SVar:X:Count$ChosenNumber SVar:NumManaMax:Count$Valid Island.RememberedPlayerCtrl diff --git a/res/mtgdata-sets-to-forge.txt b/res/mtgdata-sets-to-forge.txt new file mode 100644 index 00000000000..5cc7a6c56ed --- /dev/null +++ b/res/mtgdata-sets-to-forge.txt @@ -0,0 +1,117 @@ +===10E # Tenth Edition +1E LEA # Limited Edition Alpha +2E LEB # Limited Edition Beta +2U 2ED # Unlimited Edition +3E 3ED # Revised Edition +4E 4ED # Fourth Edition +===5DN # Fifth Dawn +5E 5ED # Fifth Edition +6E 6ED # Classic Sixth Edition +7E 7ED # Seventh Edition +===8ED # Eighth Edition +===9ED # Ninth Edition +AL ALL # Alliances +===ALA # Shards of Alara +AN ARN # Arabian Nights +AP APC # Apocalypse +AQ ATQ # Antiquities +===ARB # Alara Reborn +===ARC # Archenemy +---ASTRAL # Astral +===AVR # Avacyn Restored +---BD # Beatdown Box Set +===BOK # Betrayers of Kamigawa +---BR # Battle Royale Box Set +CG UDS # Urza's Destiny +CH CHR # Chronicles +===CHK # Champions of Kamigawa +---CM1 # Commander's Arsenal +CMD COM # Magic: The Gathering-Commander +CON CFX # Conflux +===CSP # Coldsnap +===DD2 # Duel Decks: Jace vs. Chandra +===DDC # Duel Decks: Divine vs. Demonic +===DDD # Duel Decks: Garruk vs. Liliana +===DDE # Duel Decks: Phyrexia vs. the Coalition +===DDF # Duel Decks: Elspeth vs. Tezzeret +===DDG # Duel Decks: Knights vs. Dragons +===DDH # Duel Decks: Ajani vs. Nicol Bolas +===DDI # Duel Decks: Venser vs. Koth +===DDJ # Duel Decks: Izzet vs. Golgari +===DIS # Dissension +DK DRK # The Dark +===DKA # Dark Ascension +===DRB # From the Vault: Dragons +---DREAM # Dreamcast +===DST # Darksteel +===EVE # Eventide +===EVG # Duel Decks: Elves vs. Goblins +EX EXO # Exodus +FE FEM # Fallen Empires +===FUT # Future Sight +===GPT # Guildpact +===GTC # Gatecrash +GU ULG # Urza's Legacy +===H09 # Premium Deck Series: Slivers +HM HML # Homelands +===HOP # Planechase +IA ICE # Ice Age +IN INV # Invasion +===ISD # Innistrad +===JUD # Judgment +LE LEG # Legends +===LGN # Legions +===LRW # Lorwyn +===M10 # Magic 2010 +===M11 # Magic 2011 +===M12 # Magic 2012 +===M13 # Magic 2013 +===MBS # Mirrodin Besieged +---ME2 # Masters Edition II +---ME3 # Masters Edition III +---ME4 # Masters Edition IV +---MED # Masters Edition +MI MIR # Mirage +MM MMQ # Mercadian Masques +===MOR # Morningtide +===MRD # Mirrodin +NE NMS # Nemesis +===NPH # New Phyrexia +OD ODY # Odyssey +===ONS # Onslaught +P2 PO2 # Portal Second Age +P3 S99 # Starter 1999 +P4 S00 # Starter 2000 +===PC2 # Planechase 2012 Edition +===PD2 # Premium Deck Series: Fire and Lightning +===PD3 # Premium Deck Series: Graveborn +PK PTK # Portal Three Kingdoms +===PLC # Planar Chaos +PO POR # Portal +PPR MBP # Promo set for Gatherer +PR PCY # Prophecy +PS PLS # Planeshift +===RAV # Ravnica: City of Guilds +===ROE # Rise of the Eldrazi +===RTR # Return to Ravnica +===SCG # Scourge +===SHM # Shadowmoor +===SOK # Saviors of Kamigawa +===SOM # Scars of Mirrodin +ST STH # Stronghold +TE TMP # Tempest +===TOR # Torment +===TSB # Time Spiral "Timeshifted" +===TSP # Time Spiral +---UG # Unglued +---UNH # Unhinged +UZ USG # Urza's Saga +===V09 # From the Vault: Exiled +===V10 # From the Vault: Relics +===V11 # From the Vault: Legends +===V12 # From the Vault: Realms +===VAN # Vanguard +VI VIS # Visions +WL WTH # Weatherlight +===WWK # Worldwake +===ZEN # Zendikar diff --git a/src/main/java/forge/card/CardRules.java b/src/main/java/forge/card/CardRules.java index e9c83dd69a0..4bf0b0d7929 100644 --- a/src/main/java/forge/card/CardRules.java +++ b/src/main/java/forge/card/CardRules.java @@ -19,6 +19,7 @@ package forge.card; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.TreeMap; @@ -32,7 +33,8 @@ import forge.card.mana.ManaCost; * @version $Id: CardRules.java 9708 2011-08-09 19:34:12Z jendave $ */ public final class CardRules implements ICardCharacteristics { - + private final static EditionCollection editions = new EditionCollection(); // create a copy here, Singletons.model... is not initialized yet. + private final CardSplitType splitType; private final ICardFace mainPart; private final ICardFace otherPart; @@ -48,8 +50,14 @@ public final class CardRules implements ICardCharacteristics { mainPart = faces[0]; otherPart = faces[1]; aiHints = cah; - setsPrinted.putAll(sets); + //System.out.print(faces[0].getName()); + + for (Entry cs : sets.entrySet()) { + if( editions.get(cs.getKey()) != null ) + setsPrinted.put(cs.getKey(), cs.getValue()); + } + if ( setsPrinted.isEmpty() ) { System.err.println(getName() + " was not assigned any set."); setsPrinted.put(CardEdition.UNKNOWN.getCode(), new CardInSet(CardRarity.Common, 1) ); diff --git a/src/main/java/forge/card/CardRulesReader.java b/src/main/java/forge/card/CardRulesReader.java index c68f1fb5b26..02bc6b84a07 100644 --- a/src/main/java/forge/card/CardRulesReader.java +++ b/src/main/java/forge/card/CardRulesReader.java @@ -221,8 +221,7 @@ public class CardRulesReader { } else this.faces[curFace].addSVar(variable, value); } else if ("SetInfo".equals(key)) { - if ( curFace == 0 ) - CardRulesReader.parseSetInfoLine(value, sets); + parseSetInfoLine(value); } break; @@ -247,41 +246,33 @@ public class CardRulesReader { * @param setsData * the current mapping of set names to CardInSet instances */ - private static void parseSetInfoLine(final String value, final Map setsData) { - final int setCodeIx = 0; - final int rarityIx = 1; - final int numPicIx = 3; - + private void parseSetInfoLine(final String value) { // Sample SetInfo line: - // SetInfo:POR|Land|http://magiccards.info/scans/en/po/203.jpg|4 + // SetInfo:POR Land x4 - final String[] pieces = value.split("\\|"); - - if (pieces.length <= rarityIx) { - throw new RuntimeException("SetInfo line <<" + value + ">> has insufficient pieces"); + int i = 0; + String setCode = null; + String txtRarity = "Common"; + String txtCount = "x1"; + + StringTokenizer stt = new StringTokenizer(value, " "); + while(stt.hasMoreTokens()) { + if( i == 0 ) setCode = stt.nextToken(); + if( i == 1 ) txtRarity = stt.nextToken(); + if( i == 2 ) txtCount = stt.nextToken(); + i++; } - final String setCode = pieces[setCodeIx]; - final String txtRarity = pieces[rarityIx]; - // pieces[2] is the magiccards.info URL for illustration #1, which we do - // not need. - int numIllustrations = 1; - if (setsData.containsKey(setCode)) { + int numIllustrations = 1; + if ( i > 2 ) + numIllustrations = Integer.parseInt(txtCount.substring(1)); + + if (sets.containsKey(setCode)) { + System.err.print(faces[0].getName()); throw new RuntimeException("Found multiple SetInfo lines for set code <<" + setCode + ">>"); } - if (pieces.length > numPicIx) { - try { - numIllustrations = Integer.parseInt(pieces[numPicIx]); - } catch (final NumberFormatException nfe) { - throw new RuntimeException("Fourth item of SetInfo is not an integer in <<" + value + ">>"); - } - - if (numIllustrations < 1) { - throw new RuntimeException("Fourth item of SetInfo is not a positive integer, but" + numIllustrations); - } - } CardRarity rarity = null; if ("Land".equals(txtRarity)) { @@ -302,7 +293,7 @@ public class CardRulesReader { final CardInSet cardInSet = new CardInSet(rarity, numIllustrations); - setsData.put(setCode, cardInSet); + sets.put(setCode, cardInSet); } /** diff --git a/src/main/java/forge/gui/deckeditor/controllers/CEditorQuestCardShop.java b/src/main/java/forge/gui/deckeditor/controllers/CEditorQuestCardShop.java index eaf6361342f..1f28ba0835d 100644 --- a/src/main/java/forge/gui/deckeditor/controllers/CEditorQuestCardShop.java +++ b/src/main/java/forge/gui/deckeditor/controllers/CEditorQuestCardShop.java @@ -33,6 +33,7 @@ import com.google.common.base.Function; import forge.Command; import forge.Singletons; +import forge.deck.CardPool; import forge.deck.Deck; import forge.deck.DeckBase; import forge.deck.DeckSection; @@ -214,13 +215,18 @@ public final class CEditorQuestCardShop extends ACEditorBase countDecksForEachCard() { final ItemPool result = new ItemPool(InventoryItem.class); for (final Deck deck : this.questData.getMyDecks()) { - for (final Entry e : deck.getMain()) { + CardPool main = deck.getMain(); + for (final Entry e : main) { result.add(e.getKey()); } - if ( deck.has(DeckSection.Sideboard)) + if (deck.has(DeckSection.Sideboard)) { for (final Entry e : deck.get(DeckSection.Sideboard)) { - result.add(e.getKey()); + // only add card if we haven't already encountered it in main + if (!main.contains(e.getKey())) { + result.add(e.getKey()); + } } + } } return result; } diff --git a/src/main/java/forge/gui/download/GuiDownloader.java b/src/main/java/forge/gui/download/GuiDownloader.java index 591ef3604d2..11c73adb0cd 100644 --- a/src/main/java/forge/gui/download/GuiDownloader.java +++ b/src/main/java/forge/gui/download/GuiDownloader.java @@ -218,8 +218,7 @@ public abstract class GuiDownloader extends DefaultBoundedRangeModel implements * @return a int. */ protected final int getAverageTimePerObject() { - int aTime = 0; - int nz = 10; + int numNonzero = 10; if (this.tptr > 9) { this.tptr = 0; @@ -232,14 +231,12 @@ public abstract class GuiDownloader extends DefaultBoundedRangeModel implements for (int i = 0; i < 10; i++) { tTime += this.times[i]; if (this.times[i] == 0) { - nz--; + numNonzero--; } } - aTime = tTime / nz; - + this.tptr++; - - return aTime; + return tTime / Math.max(1, numNonzero); } /** @@ -253,21 +250,9 @@ public abstract class GuiDownloader extends DefaultBoundedRangeModel implements private void update(final int card) { this.card = card; - /** - * - * TODO: Write javadoc for this type. - * - */ final class Worker implements Runnable { private final int card; - /** - * - * TODO: Write javadoc for Constructor. - * - * @param card - * int - */ Worker(final int card) { this.card = card; } diff --git a/src/main/java/forge/item/ItemPredicate.java b/src/main/java/forge/item/ItemPredicate.java index ac5b3935ddf..071df862937 100644 --- a/src/main/java/forge/item/ItemPredicate.java +++ b/src/main/java/forge/item/ItemPredicate.java @@ -16,7 +16,7 @@ public abstract class ItemPredicate { public static final Predicate IsBoosterPack = Predicates.instanceOf(BoosterPack.class); public static final Predicate IsPrebuiltDeck = Predicates.instanceOf(PreconDeck.class); - public static final Predicate IsFatPack = Predicates.instanceOf(TournamentPack.class); + public static final Predicate IsFatPack = Predicates.instanceOf(FatPack.class); /** * Checks that the inventory item is a Tournament Pack. diff --git a/src/main/java/forge/quest/QuestUtilCards.java b/src/main/java/forge/quest/QuestUtilCards.java index c75e9392a5e..7a3ddb651c0 100644 --- a/src/main/java/forge/quest/QuestUtilCards.java +++ b/src/main/java/forge/quest/QuestUtilCards.java @@ -270,6 +270,7 @@ public final class QuestUtilCards { public void buyPack(final OpenablePack booster, final int value) { if (this.qa.getCredits() >= value) { this.qa.setCredits(this.qa.getCredits() - value); + this.qa.getShopList().remove(booster); this.addAllCards(booster.getCards()); } } @@ -350,11 +351,13 @@ public final class QuestUtilCards { int nToRemoveFromThisDeck = cntInMain + cntInSb - leftInPool; if ( nToRemoveFromThisDeck <= 0 ) continue; // this is not the deck you are looking for - int nToRemoveFromSb = cntInSb - nToRemoveFromThisDeck; - if( nToRemoveFromSb > 0 ) { + int nToRemoveFromSb = Math.min(cntInSb, nToRemoveFromThisDeck); + if (nToRemoveFromSb > 0) { deck.get(DeckSection.Sideboard).remove(card, nToRemoveFromSb); - nToRemoveFromThisDeck -= cntInSb; // actual removed count should be, but I take upper bound here - if ( nToRemoveFromThisDeck <= 0 ) continue; // done here + nToRemoveFromThisDeck -= nToRemoveFromSb; + if (0 >= nToRemoveFromThisDeck) { + continue; // done here + } } deck.getMain().remove(card, nToRemoveFromThisDeck); @@ -508,7 +511,6 @@ public final class QuestUtilCards { * Generate cards in shop. */ public void generateCardsInShop() { - Iterable cardList = null; if (qc.getFormat() == null) { cardList = CardDb.instance().getAllCards(); }