mirror of
https://github.com/Card-Forge/forge.git
synced 2025-11-19 12:18:00 +00:00
- Adding deck conversion tools that I'm using to convert MTGDecks.net deck lists and XMage cube definitions. Might be useful as a basis for converters from other deck formats too.
This commit is contained in:
4
.gitattributes
vendored
4
.gitattributes
vendored
@@ -20746,6 +20746,10 @@ forge-gui/src/main/java/forge/util/package-info.java -text
|
|||||||
forge-gui/src/main/resources/log4jConfig.config -text
|
forge-gui/src/main/resources/log4jConfig.config -text
|
||||||
forge-gui/src/main/resources/proxy-template.ftl -text
|
forge-gui/src/main/resources/proxy-template.ftl -text
|
||||||
forge-gui/src/site/apt/index.apt -text
|
forge-gui/src/site/apt/index.apt -text
|
||||||
|
forge-gui/tools/DeckConversionTools/mtgdecksnet_convert.py -text
|
||||||
|
forge-gui/tools/DeckConversionTools/mtgdecksnet_decksort.py -text
|
||||||
|
forge-gui/tools/DeckConversionTools/mtgdecksnet_decksort_EDH.py -text
|
||||||
|
forge-gui/tools/DeckConversionTools/xmage_cube_convert.py -text
|
||||||
forge-gui/tools/EditionTracking.py -text
|
forge-gui/tools/EditionTracking.py -text
|
||||||
forge-gui/tools/PerSetTracking.py svneol=native#text/x-python
|
forge-gui/tools/PerSetTracking.py svneol=native#text/x-python
|
||||||
forge-gui/tools/RankingScraper.py -text
|
forge-gui/tools/RankingScraper.py -text
|
||||||
|
|||||||
250
forge-gui/tools/DeckConversionTools/mtgdecksnet_convert.py
Executable file
250
forge-gui/tools/DeckConversionTools/mtgdecksnet_convert.py
Executable file
@@ -0,0 +1,250 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
# Modify key directories here
|
||||||
|
# Recommended parameters: -i -w (will add ! to all unsupported decks) OR -i -f (will only convert supported decks)
|
||||||
|
CARDSFOLDER = "../../res/cardsfolder"
|
||||||
|
DECKFOLDER = "."
|
||||||
|
OUT_DECKFOLDER = "./ForgeDecks"
|
||||||
|
|
||||||
|
import argparse, os, re
|
||||||
|
|
||||||
|
print("Agetian's MtgDecks.net DEC to MTG Forge Deck Converter v3.3a\n")
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Convert MtgDecks.net DEC to Forge DCK.")
|
||||||
|
|
||||||
|
parser.add_argument("-f", action="store_true", help="only convert decks that have 100% card support in Forge")
|
||||||
|
parser.add_argument("-i", action="store_true", help="add MtgDecksNet deck ID to the output deck name for uniqueness")
|
||||||
|
parser.add_argument("-t", help="only convert decks that belong to the specified format")
|
||||||
|
parser.add_argument("-w", action="store_true", help="add [!] to the name of decks that have unsupported cards")
|
||||||
|
parser.add_argument("-U", action="store_true", help="preserve UTF-8 characters in file names")
|
||||||
|
parser.add_argument("-D", action="store_true", help="sort converted decks into folders according to format")
|
||||||
|
parser.add_argument("-P", action="store_true", help="convert period (.) to underscore (_) in deck names")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# simple structural self-test (can this tool work?)
|
||||||
|
if not (os.access(os.path.join(CARDSFOLDER,"a","abu_jafar.txt"),os.F_OK) or os.access(os.path.join("decks"),os.F_OK)):
|
||||||
|
print("Fatal error:\n This utility requires the 'cardsfolder' folder with unpacked card files at " + CARDSFOLDER + " and the 'decks' folder with .dck files at " + DECKFOLDER + " in order to operate. Exiting.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# basic variables
|
||||||
|
cardlist = {}
|
||||||
|
total_cards = 0
|
||||||
|
ai_playable_cards = 0
|
||||||
|
total_decks = 0
|
||||||
|
playable_decks = 0
|
||||||
|
nonplayable_in_deck = 0
|
||||||
|
|
||||||
|
# main algorithm
|
||||||
|
print("Loading cards...")
|
||||||
|
for root, dirs, files in os.walk(CARDSFOLDER):
|
||||||
|
for name in files:
|
||||||
|
if name.find(".txt") != -1:
|
||||||
|
total_cards += 1
|
||||||
|
fullpath = os.path.join(root, name)
|
||||||
|
cardtext = open(fullpath).read()
|
||||||
|
cardtext_lower = cardtext.lower()
|
||||||
|
cardname_literal = cardtext.replace('\r','').split('\n')[0].split(':')
|
||||||
|
cardname = ":".join(cardname_literal[1:]).strip()
|
||||||
|
if (cardtext_lower.find("alternatemode:split") != -1) or (cardtext_lower.find("alternatemode: split") != -1):
|
||||||
|
# split card, special handling needed
|
||||||
|
cardsplittext = cardtext.replace('\r','').split('\n')
|
||||||
|
cardnames = []
|
||||||
|
for line in cardsplittext:
|
||||||
|
if line.lower().find("name:") != -1:
|
||||||
|
cardnames.extend([line.split('\n')[0].split(':')[1]])
|
||||||
|
cardname = " // ".join(cardnames)
|
||||||
|
if cardtext.lower().find("remaideck") != -1:
|
||||||
|
cardlist[cardname] = 0
|
||||||
|
else:
|
||||||
|
cardlist[cardname] = 1
|
||||||
|
ai_playable_cards += 1
|
||||||
|
|
||||||
|
perc_playable = (float(ai_playable_cards) / total_cards) * 100
|
||||||
|
perc_unplayable = ((float(total_cards) - ai_playable_cards) / total_cards) * 100
|
||||||
|
|
||||||
|
print("Loaded %d cards, among them %d playable by the AI (%d%%), %d unplayable by the AI (%d%%).\n" % (total_cards, ai_playable_cards, perc_playable, total_cards - ai_playable_cards, perc_unplayable))
|
||||||
|
|
||||||
|
re_Metadata = '^//(.*), a (.*) deck by (.*)$'
|
||||||
|
re_Metadata2 = '^//(.*) a ([A-Za-z]+) MTG deck played by (.*) in (.*) - MTGDECKS.NET.*$'
|
||||||
|
re_DeckID = '^([0-9]+)\.dec$'
|
||||||
|
re_Maindeck = '^([0-9]+) (.*)$'
|
||||||
|
re_Sideboard = '^SB:[ \t]+([0-9]+) (.*)$'
|
||||||
|
re_Timeinfo = '<!--.*>$'
|
||||||
|
|
||||||
|
unsupportedList = []
|
||||||
|
badChars = ['/', '\\', '*']
|
||||||
|
|
||||||
|
print("Converting decks...")
|
||||||
|
for root, dirs, files in os.walk(DECKFOLDER):
|
||||||
|
for name in files:
|
||||||
|
if name.find(".dec") != -1:
|
||||||
|
print("Converting deck: " + name + "...")
|
||||||
|
deck_id = -1
|
||||||
|
s_DeckID = re.search(re_DeckID, name)
|
||||||
|
if s_DeckID:
|
||||||
|
deck_id = s_DeckID.groups()[0]
|
||||||
|
fullpath = os.path.join(root, name)
|
||||||
|
deckdata = open(fullpath).readlines()
|
||||||
|
name = ""
|
||||||
|
creator = ""
|
||||||
|
format = ""
|
||||||
|
maindeck = []
|
||||||
|
maindeck_cards = 0
|
||||||
|
sideboard = []
|
||||||
|
supported = True
|
||||||
|
deckHasUnsupportedCards = False
|
||||||
|
|
||||||
|
for line in deckdata:
|
||||||
|
#line = line.replace("\xE1", "a")
|
||||||
|
#line = line.replace("\xFB", "u")
|
||||||
|
#line = line.replace("\xE9", "e")
|
||||||
|
line = line.replace("\xC3\x86", "AE")
|
||||||
|
line = line.replace("\xC3\xA9", "e")
|
||||||
|
line = line.replace("\xC3\xBB", "u")
|
||||||
|
line = line.replace("\xC3\xA1", "a")
|
||||||
|
line = line.replace("\xC3\xAD", "i")
|
||||||
|
#line = line.replace("Unravel the Aether", "Unravel the AEther")
|
||||||
|
#line = line.replace("Aether", "AEther")
|
||||||
|
line = line.replace("Chandra, Roaring Flame", "Chandra, Fire of Kaladesh")
|
||||||
|
line = line.replace("Nissa, Sage Animist", "Nissa, Vastwood Seer")
|
||||||
|
line = line.replace("Neck Breaker", "Breakneck Rider")
|
||||||
|
line = line.replace("\xC3\xB6", "o")
|
||||||
|
line = line.replace("\x97", "-")
|
||||||
|
line = line.replace("\x91", "'")
|
||||||
|
line = line.replace("\xFB", "u")
|
||||||
|
line = line.replace("\xFC", "u")
|
||||||
|
line = line.replace("\xC4", "A")
|
||||||
|
timepos = line.find("<!")
|
||||||
|
if timepos > -1:
|
||||||
|
line = line[0:timepos]
|
||||||
|
isCardSupported = True
|
||||||
|
mode = 0
|
||||||
|
s_Metadata = re.search(re_Metadata, line)
|
||||||
|
if not s_Metadata:
|
||||||
|
s_Metadata = re.search(re_Metadata2, line)
|
||||||
|
mode = 1
|
||||||
|
if s_Metadata:
|
||||||
|
name = s_Metadata.groups()[0].strip()
|
||||||
|
format = s_Metadata.groups()[1].strip()
|
||||||
|
creator = s_Metadata.groups()[2].strip()
|
||||||
|
event = ""
|
||||||
|
if mode == 1:
|
||||||
|
event = s_Metadata.groups()[3].strip()
|
||||||
|
for badChar in badChars:
|
||||||
|
event = event.replace(badChar, "-")
|
||||||
|
if args.t and args.t != format:
|
||||||
|
print("Skipping an off-format deck " + name + " (format = " + format + ")")
|
||||||
|
supported = False
|
||||||
|
continue
|
||||||
|
s_Maindeck = re.search(re_Maindeck, line)
|
||||||
|
if s_Maindeck:
|
||||||
|
cardAmount = s_Maindeck.groups()[0].strip()
|
||||||
|
cardName = s_Maindeck.groups()[1].strip()
|
||||||
|
if not cardlist.has_key(cardName) and not cardlist.has_key(cardName.replace("Aether", "AEther")) and not cardlist.has_key(cardName.replace("AEther", "Aether")):
|
||||||
|
print("Unsupported card (MAIN): " + cardName)
|
||||||
|
if args.f:
|
||||||
|
supported = False
|
||||||
|
else:
|
||||||
|
isCardSupported = False
|
||||||
|
deckHasUnsupportedCards = True
|
||||||
|
if not cardName in unsupportedList:
|
||||||
|
unsupportedList.extend([cardName])
|
||||||
|
if cardlist.has_key(cardName):
|
||||||
|
mdline = cardAmount + " " + cardName
|
||||||
|
elif cardlist.has_key(cardName.replace("Aether", "AEther")):
|
||||||
|
mdline = cardAmount + " " + cardName.replace("Aether", "AEther")
|
||||||
|
elif cardlist.has_key(cardName.replace("AEther", "Aether")):
|
||||||
|
mdline = cardAmount + " " + cardName.replace("AEther", "Aether")
|
||||||
|
else:
|
||||||
|
mdline = cardAmount + " " + cardName # for the purposes of unsupported cards
|
||||||
|
if isCardSupported:
|
||||||
|
maindeck.extend([mdline])
|
||||||
|
else:
|
||||||
|
maindeck.extend(["#"+mdline])
|
||||||
|
maindeck_cards += int(cardAmount)
|
||||||
|
continue
|
||||||
|
s_Sideboard = re.search(re_Sideboard, line)
|
||||||
|
if s_Sideboard:
|
||||||
|
cardAmount = s_Sideboard.groups()[0].strip()
|
||||||
|
cardName = s_Sideboard.groups()[1].strip()
|
||||||
|
if not cardlist.has_key(cardName) and not cardlist.has_key(cardName.replace("Aether", "AEther")) and not cardlist.has_key(cardName.replace("AEther", "Aether")):
|
||||||
|
print("Unsupported card (SIDE): " + cardName)
|
||||||
|
if args.f:
|
||||||
|
supported = False
|
||||||
|
else:
|
||||||
|
isCardSupported = False
|
||||||
|
deckHasUnsupportedCards = True
|
||||||
|
if not cardName in unsupportedList:
|
||||||
|
unsupportedList.extend([cardName])
|
||||||
|
if cardlist.has_key(cardName):
|
||||||
|
sdline = cardAmount + " " + cardName
|
||||||
|
elif cardlist.has_key(cardName.replace("Aether", "AEther")):
|
||||||
|
sdline = cardAmount + " " + cardName.replace("Aether", "AEther")
|
||||||
|
elif cardlist.has_key(cardName.replace("AEther", "Aether")):
|
||||||
|
sdline = cardAmount + " " + cardName.replace("AEther", "Aether")
|
||||||
|
else:
|
||||||
|
sdline = cardAmount + " " + cardName # for the purposes of unsupported cards
|
||||||
|
if isCardSupported:
|
||||||
|
sideboard.extend([sdline])
|
||||||
|
else:
|
||||||
|
sideboard.extend(["#"+sdline])
|
||||||
|
continue
|
||||||
|
|
||||||
|
# convert here
|
||||||
|
if supported and len(maindeck) > 0:
|
||||||
|
if creator != "":
|
||||||
|
deckname = creator + " - " + name + " ("
|
||||||
|
else:
|
||||||
|
deckname = name + " ("
|
||||||
|
deckname += format
|
||||||
|
if args.i and deck_id > -1:
|
||||||
|
if format != "":
|
||||||
|
deckname += ", #" + deck_id
|
||||||
|
else:
|
||||||
|
deckname += "#" + deck_id
|
||||||
|
deckname += ")"
|
||||||
|
#deckname = (c for c in deckname if ord(c) < 128)
|
||||||
|
if not args.U:
|
||||||
|
deckname = re.sub(r'[^\x00-\x7F]', '@', deckname)
|
||||||
|
deckname = re.sub(r'[/\\]', '-', deckname)
|
||||||
|
deckname = re.sub(r'[?*]', '_', deckname)
|
||||||
|
if args.w and deckHasUnsupportedCards:
|
||||||
|
deckname += " [!]"
|
||||||
|
if args.D:
|
||||||
|
if deckHasUnsupportedCards:
|
||||||
|
outname = "Unsupported" + "/" + format + "/"
|
||||||
|
pathToUnsupported = "./" + OUT_DECKFOLDER + "/Unsupported/" + format
|
||||||
|
if not os.path.isdir(pathToUnsupported):
|
||||||
|
os.makedirs(pathToUnsupported)
|
||||||
|
else:
|
||||||
|
outname = format + "/"
|
||||||
|
if not os.path.isdir("./" + OUT_DECKFOLDER + "/" + format):
|
||||||
|
os.makedirs("./" + OUT_DECKFOLDER + "/" + format)
|
||||||
|
else:
|
||||||
|
outname = ""
|
||||||
|
outname += deckname + ".dck"
|
||||||
|
print ("Writing converted deck: " + outname)
|
||||||
|
dck = open(OUT_DECKFOLDER + "/" + outname, "w")
|
||||||
|
dck.write("#EVENT:"+event+"\n")
|
||||||
|
dck.write("[metadata]\n")
|
||||||
|
dck.write("Name="+deckname+"\n")
|
||||||
|
dck.write("[general]\n")
|
||||||
|
dck.write("Constructed\n")
|
||||||
|
dck.write("[Main]\n")
|
||||||
|
for m in maindeck:
|
||||||
|
dck.write(m+"\n")
|
||||||
|
if not (format == "Commander" and len(sideboard) == 1):
|
||||||
|
dck.write("[Sideboard]\n")
|
||||||
|
else:
|
||||||
|
dck.write("[Commander]\n")
|
||||||
|
for s in sideboard:
|
||||||
|
dck.write(s+"\n")
|
||||||
|
|
||||||
|
# write out unsupported cards
|
||||||
|
log = open("dec2forge.log", "w")
|
||||||
|
log.write("Unsupported cards:\n")
|
||||||
|
for uns in unsupportedList:
|
||||||
|
log.write(uns+"\n")
|
||||||
|
|
||||||
159
forge-gui/tools/DeckConversionTools/mtgdecksnet_decksort.py
Executable file
159
forge-gui/tools/DeckConversionTools/mtgdecksnet_decksort.py
Executable file
@@ -0,0 +1,159 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
CARDSFOLDER = "../../res/cardsfolder"
|
||||||
|
EDITIONS = "../../res/editions"
|
||||||
|
DECKFOLDER = "."
|
||||||
|
|
||||||
|
import argparse, os, re, shutil
|
||||||
|
|
||||||
|
print("Agetian's MTG Forge Deck Sorter v1.4\n")
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Sort decks into folders (by edition).")
|
||||||
|
parser.add_argument("-d", action="store_true", help="physically delete original (unsorted) decks")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# simple structural self-test (can this tool work?)
|
||||||
|
if not (os.access(os.path.join(CARDSFOLDER,"a","abu_jafar.txt"),os.F_OK) or os.access(os.path.join("decks"),os.F_OK) or os.access(os.path.join(EDITIONS,"Alara Reborn.txt"),os.F_OK)):
|
||||||
|
print("Fatal error:\n This utility requires the 'cardsfolder' folder with unpacked card files at " + CARDSFOLDER + ", 'editions' folder at " + EDITIONS + " and the 'decks' folder with .dck files at " + DECKFOLDER + " in order to operate. Exiting.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# basic variables
|
||||||
|
editions = {}
|
||||||
|
edition_names = {}
|
||||||
|
cards_by_edition = {}
|
||||||
|
total_editions = 0
|
||||||
|
|
||||||
|
ignore_cards = ['Swamp', 'Plains', 'Mountain', 'Island', 'Forest']
|
||||||
|
|
||||||
|
# regexes
|
||||||
|
re_Code = '^Code=(.*)$'
|
||||||
|
re_Date = '^Date=([0-9]+-[0-9]+-[0-9]+)$'
|
||||||
|
re_Date2 = '^Date=([0-9]+-[0-9]+)$'
|
||||||
|
re_Type = '^Type=(.*)$'
|
||||||
|
re_Name = '^Name=(.*)$'
|
||||||
|
re_Card = '^[0-9]* *[A-Z] (.*)$'
|
||||||
|
|
||||||
|
# main algorithm
|
||||||
|
print("Loading editions...")
|
||||||
|
for root, dirs, files in os.walk(EDITIONS):
|
||||||
|
for name in files:
|
||||||
|
if name.find(".txt") != -1:
|
||||||
|
total_editions += 1
|
||||||
|
fullpath = os.path.join(root, name)
|
||||||
|
edition = open(fullpath).readlines()
|
||||||
|
foundCards = False
|
||||||
|
code = ""
|
||||||
|
date = ""
|
||||||
|
etype = ""
|
||||||
|
name = ""
|
||||||
|
for line in edition:
|
||||||
|
line = line.replace("\r\n","")
|
||||||
|
if not foundCards:
|
||||||
|
if line == "[cards]":
|
||||||
|
foundCards = True
|
||||||
|
else:
|
||||||
|
s_Code = re.search(re_Code, line)
|
||||||
|
if s_Code:
|
||||||
|
code = s_Code.groups()[0]
|
||||||
|
#print("Code found: " + code)
|
||||||
|
s_Date = re.search(re_Date, line)
|
||||||
|
if s_Date:
|
||||||
|
date = s_Date.groups()[0]
|
||||||
|
#print("Date found: " + date)
|
||||||
|
s_Date2 = re.search(re_Date2, line)
|
||||||
|
if s_Date2:
|
||||||
|
date = s_Date2.groups()[0] + "-01"
|
||||||
|
#print("Date found: " + date)
|
||||||
|
s_Type = re.search(re_Type, line)
|
||||||
|
if s_Type:
|
||||||
|
etype = s_Type.groups()[0]
|
||||||
|
#print("Type found: " + etype)
|
||||||
|
s_Name = re.search(re_Name, line)
|
||||||
|
if s_Name:
|
||||||
|
name = s_Name.groups()[0]
|
||||||
|
#print("Name found: " + name)
|
||||||
|
else:
|
||||||
|
if etype != "Expansion" and etype != "Core" and etype != "Starter":
|
||||||
|
#print("NOT LOADING: " + code)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if editions.keys().count(code) == 0:
|
||||||
|
editions[code] = date
|
||||||
|
edition_names[code] = name
|
||||||
|
#print(editions)
|
||||||
|
s_Card = re.search(re_Card, line)
|
||||||
|
if s_Card:
|
||||||
|
card = s_Card.groups()[0].strip()
|
||||||
|
#print("Card found: " + card)
|
||||||
|
if cards_by_edition.keys().count(card) == 0:
|
||||||
|
cards_by_edition[card] = []
|
||||||
|
cards_by_edition[card].append(code)
|
||||||
|
|
||||||
|
|
||||||
|
print("Loaded " + str(len(editions)) + " editions.")
|
||||||
|
|
||||||
|
def get_latest_set_for_card(card):
|
||||||
|
cdate = "0000-00-00"
|
||||||
|
edition = "XXX"
|
||||||
|
if ignore_cards.count(card) != 0:
|
||||||
|
return "LEA"
|
||||||
|
if cards_by_edition.keys().count(card) == 0:
|
||||||
|
#print("Warning: couldn't determine an edition for card: " + card)
|
||||||
|
return "LEA"
|
||||||
|
for code in cards_by_edition[card]:
|
||||||
|
if editions[code] > cdate:
|
||||||
|
cdate = editions[code]
|
||||||
|
edition = code
|
||||||
|
return edition
|
||||||
|
|
||||||
|
def get_earliest_set_for_card(card):
|
||||||
|
cdate = "9999-99-99"
|
||||||
|
edition = "XXX"
|
||||||
|
if cards_by_edition.keys().count(card) == 0:
|
||||||
|
#print("Warning: couldn't determine an edition for card: " + card)
|
||||||
|
return "LEA"
|
||||||
|
for code in cards_by_edition[card]:
|
||||||
|
if editions[code] < cdate:
|
||||||
|
cdate = editions[code]
|
||||||
|
edition = code
|
||||||
|
return edition
|
||||||
|
|
||||||
|
def get_latest_set_for_deck(deck):
|
||||||
|
edition = "LEA"
|
||||||
|
for line in deck.split('\n'):
|
||||||
|
#print("Line: " + line)
|
||||||
|
regexobj = re.search('^([0-9]+) +([^|]+)', line)
|
||||||
|
if regexobj:
|
||||||
|
cardname = regexobj.groups()[1].replace('\n','').replace('\r','').strip()
|
||||||
|
earliest_for_card = get_earliest_set_for_card(cardname)
|
||||||
|
#print("Card: " + cardname + ", latest for it: " + latest_for_card)
|
||||||
|
if editions[earliest_for_card] > editions[edition]:
|
||||||
|
edition = earliest_for_card
|
||||||
|
return edition
|
||||||
|
|
||||||
|
def get_event_for_deck(deck):
|
||||||
|
evline = deck.split('\n')[0].split("#EVENT:")
|
||||||
|
if len(evline) == 2:
|
||||||
|
return evline[1]
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
print("Scanning decks...")
|
||||||
|
for root, dirs, files in os.walk(DECKFOLDER):
|
||||||
|
for name in files:
|
||||||
|
if name.find(".dck") != -1:
|
||||||
|
total_decks += 1
|
||||||
|
fullpath = os.path.join(root, name)
|
||||||
|
deckdata = open(fullpath).read()
|
||||||
|
set_for_deck = edition_names[get_latest_set_for_deck(deckdata)]
|
||||||
|
event_for_deck = get_event_for_deck(deckdata)
|
||||||
|
if event_for_deck[len(event_for_deck)-1] == ".":
|
||||||
|
event_for_deck = event_for_deck[0:len(event_for_deck)-1]
|
||||||
|
print("Deck: " + name + ", Set: " + set_for_deck + ", Event: " + event_for_deck)
|
||||||
|
if not os.access(os.path.join(root, set_for_deck, event_for_deck), os.F_OK):
|
||||||
|
os.makedirs(os.path.join(root, set_for_deck, event_for_deck))
|
||||||
|
shutil.copy(fullpath, os.path.join(root, set_for_deck, event_for_deck, name))
|
||||||
|
if args.d:
|
||||||
|
os.remove(fullpath)
|
||||||
|
|
||||||
162
forge-gui/tools/DeckConversionTools/mtgdecksnet_decksort_EDH.py
Executable file
162
forge-gui/tools/DeckConversionTools/mtgdecksnet_decksort_EDH.py
Executable file
@@ -0,0 +1,162 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
|
||||||
|
CARDSFOLDER = "../../res/cardsfolder"
|
||||||
|
EDITIONS = "../../res/editions"
|
||||||
|
DECKFOLDER = "."
|
||||||
|
|
||||||
|
import argparse, os, re, shutil
|
||||||
|
|
||||||
|
print("Agetian's MTG Forge Deck Sorter v1.4\n")
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser(description="Sort decks into folders (by edition).")
|
||||||
|
parser.add_argument("-d", action="store_true", help="physically delete original (unsorted) decks")
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
# simple structural self-test (can this tool work?)
|
||||||
|
if not (os.access(os.path.join(CARDSFOLDER,"a","abu_jafar.txt"),os.F_OK) or os.access(os.path.join("decks"),os.F_OK) or os.access(os.path.join(EDITIONS,"Alara Reborn.txt"),os.F_OK)):
|
||||||
|
print("Fatal error:\n This utility requires the 'cardsfolder' folder with unpacked card files at " + CARDSFOLDER + ", 'editions' folder at " + EDITIONS + " and the 'decks' folder with .dck files at " + DECKFOLDER + " in order to operate. Exiting.")
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# basic variables
|
||||||
|
editions = {}
|
||||||
|
edition_names = {}
|
||||||
|
cards_by_edition = {}
|
||||||
|
total_editions = 0
|
||||||
|
|
||||||
|
ignore_cards = ['Swamp', 'Plains', 'Mountain', 'Island', 'Forest']
|
||||||
|
|
||||||
|
# regexes
|
||||||
|
re_Code = '^Code=(.*)$'
|
||||||
|
re_Date = '^Date=([0-9]+-[0-9]+-[0-9]+)$'
|
||||||
|
re_Date2 = '^Date=([0-9]+-[0-9]+)$'
|
||||||
|
re_Type = '^Type=(.*)$'
|
||||||
|
re_Name = '^Name=(.*)$'
|
||||||
|
re_Card = '^[0-9]* *[A-Z] (.*)$'
|
||||||
|
|
||||||
|
# main algorithm
|
||||||
|
print("Loading editions...")
|
||||||
|
for root, dirs, files in os.walk(EDITIONS):
|
||||||
|
for name in files:
|
||||||
|
if name.find(".txt") != -1:
|
||||||
|
total_editions += 1
|
||||||
|
fullpath = os.path.join(root, name)
|
||||||
|
edition = open(fullpath).readlines()
|
||||||
|
foundCards = False
|
||||||
|
code = ""
|
||||||
|
date = ""
|
||||||
|
etype = ""
|
||||||
|
name = ""
|
||||||
|
for line in edition:
|
||||||
|
line = line.replace("\r\n","")
|
||||||
|
if not foundCards:
|
||||||
|
if line == "[cards]":
|
||||||
|
foundCards = True
|
||||||
|
else:
|
||||||
|
s_Code = re.search(re_Code, line)
|
||||||
|
if s_Code:
|
||||||
|
code = s_Code.groups()[0]
|
||||||
|
#print("Code found: " + code)
|
||||||
|
s_Date = re.search(re_Date, line)
|
||||||
|
if s_Date:
|
||||||
|
date = s_Date.groups()[0]
|
||||||
|
#print("Date found: " + date)
|
||||||
|
s_Date2 = re.search(re_Date2, line)
|
||||||
|
if s_Date2:
|
||||||
|
date = s_Date2.groups()[0] + "-01"
|
||||||
|
#print("Date found: " + date)
|
||||||
|
s_Type = re.search(re_Type, line)
|
||||||
|
if s_Type:
|
||||||
|
etype = s_Type.groups()[0]
|
||||||
|
#print("Type found: " + etype)
|
||||||
|
s_Name = re.search(re_Name, line)
|
||||||
|
if s_Name:
|
||||||
|
name = s_Name.groups()[0]
|
||||||
|
#print("Name found: " + name)
|
||||||
|
else:
|
||||||
|
if etype != "Expansion" and etype != "Core" and etype != "Starter" and etype != "Other":
|
||||||
|
#print("NOT LOADING: " + code)
|
||||||
|
continue
|
||||||
|
if code == "EXP" or code == "MPS":
|
||||||
|
#print("NOT LOADING: " + code)
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
if editions.keys().count(code) == 0:
|
||||||
|
editions[code] = date
|
||||||
|
edition_names[code] = name
|
||||||
|
#print(editions)
|
||||||
|
s_Card = re.search(re_Card, line)
|
||||||
|
if s_Card:
|
||||||
|
card = s_Card.groups()[0].strip()
|
||||||
|
#print("Card found: " + card)
|
||||||
|
if cards_by_edition.keys().count(card) == 0:
|
||||||
|
cards_by_edition[card] = []
|
||||||
|
cards_by_edition[card].append(code)
|
||||||
|
|
||||||
|
|
||||||
|
print("Loaded " + str(len(editions)) + " editions.")
|
||||||
|
|
||||||
|
def get_latest_set_for_card(card):
|
||||||
|
cdate = "0000-00-00"
|
||||||
|
edition = "XXX"
|
||||||
|
if ignore_cards.count(card) != 0:
|
||||||
|
return "LEA"
|
||||||
|
if cards_by_edition.keys().count(card) == 0:
|
||||||
|
#print("Warning: couldn't determine an edition for card: " + card)
|
||||||
|
return "LEA"
|
||||||
|
for code in cards_by_edition[card]:
|
||||||
|
if editions[code] > cdate:
|
||||||
|
cdate = editions[code]
|
||||||
|
edition = code
|
||||||
|
return edition
|
||||||
|
|
||||||
|
def get_earliest_set_for_card(card):
|
||||||
|
cdate = "9999-99-99"
|
||||||
|
edition = "XXX"
|
||||||
|
if cards_by_edition.keys().count(card) == 0:
|
||||||
|
#print("Warning: couldn't determine an edition for card: " + card)
|
||||||
|
return "LEA"
|
||||||
|
for code in cards_by_edition[card]:
|
||||||
|
if editions[code] < cdate:
|
||||||
|
cdate = editions[code]
|
||||||
|
edition = code
|
||||||
|
return edition
|
||||||
|
|
||||||
|
def get_latest_set_for_deck(deck):
|
||||||
|
edition = "LEA"
|
||||||
|
for line in deck.split('\n'):
|
||||||
|
#print("Line: " + line)
|
||||||
|
regexobj = re.search('^([0-9]+) +([^|]+)', line)
|
||||||
|
if regexobj:
|
||||||
|
cardname = regexobj.groups()[1].replace('\n','').replace('\r','').strip()
|
||||||
|
earliest_for_card = get_earliest_set_for_card(cardname)
|
||||||
|
#print("Card: " + cardname + ", latest for it: " + latest_for_card)
|
||||||
|
if editions[earliest_for_card] > editions[edition]:
|
||||||
|
edition = earliest_for_card
|
||||||
|
return edition
|
||||||
|
|
||||||
|
def get_event_for_deck(deck):
|
||||||
|
evline = deck.split('\n')[0].split("#EVENT:")
|
||||||
|
if len(evline) == 2:
|
||||||
|
return evline[1]
|
||||||
|
else:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
print("Scanning decks...")
|
||||||
|
for root, dirs, files in os.walk(DECKFOLDER):
|
||||||
|
for name in files:
|
||||||
|
if name.find(".dck") != -1:
|
||||||
|
total_decks += 1
|
||||||
|
fullpath = os.path.join(root, name)
|
||||||
|
deckdata = open(fullpath).read()
|
||||||
|
set_for_deck = edition_names[get_latest_set_for_deck(deckdata)]
|
||||||
|
event_for_deck = get_event_for_deck(deckdata)
|
||||||
|
if event_for_deck[len(event_for_deck)-1] == ".":
|
||||||
|
event_for_deck = event_for_deck[0:len(event_for_deck)-1]
|
||||||
|
print("Deck: " + name + ", Set: " + set_for_deck + ", Event: " + event_for_deck)
|
||||||
|
if not os.access(os.path.join(root, set_for_deck, event_for_deck), os.F_OK):
|
||||||
|
os.makedirs(os.path.join(root, set_for_deck, event_for_deck))
|
||||||
|
shutil.copy(fullpath, os.path.join(root, set_for_deck, event_for_deck, name))
|
||||||
|
if args.d:
|
||||||
|
os.remove(fullpath)
|
||||||
|
|
||||||
72
forge-gui/tools/DeckConversionTools/xmage_cube_convert.py
Executable file
72
forge-gui/tools/DeckConversionTools/xmage_cube_convert.py
Executable file
@@ -0,0 +1,72 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
# basic variables
|
||||||
|
CARDSFOLDER = "../../res/cardsfolder"
|
||||||
|
cardlist = []
|
||||||
|
total_cards = 0
|
||||||
|
|
||||||
|
# main algorithm
|
||||||
|
print("Loading cards...")
|
||||||
|
for root, dirs, files in os.walk(CARDSFOLDER):
|
||||||
|
for name in files:
|
||||||
|
if name.find(".txt") != -1:
|
||||||
|
total_cards += 1
|
||||||
|
fullpath = os.path.join(root, name)
|
||||||
|
cardtext = open(fullpath).read()
|
||||||
|
cardtext_lower = cardtext.lower()
|
||||||
|
cardname_literal = cardtext.replace('\r','').split('\n')[0].split(':')
|
||||||
|
cardname = ":".join(cardname_literal[1:]).strip()
|
||||||
|
if (cardtext_lower.find("alternatemode:split") != -1) or (cardtext_lower.find("alternatemode: split") != -1):
|
||||||
|
# split card, special handling needed
|
||||||
|
cardsplittext = cardtext.replace('\r','').split('\n')
|
||||||
|
cardnames = []
|
||||||
|
for line in cardsplittext:
|
||||||
|
if line.lower().find("name:") != -1:
|
||||||
|
cardnames.extend([line.split('\n')[0].split(':')[1]])
|
||||||
|
cardname = " // ".join(cardnames)
|
||||||
|
|
||||||
|
cardlist.extend([cardname])
|
||||||
|
|
||||||
|
print("Loaded %d cards.\n" % total_cards)
|
||||||
|
|
||||||
|
filewalker = os.walk(".")
|
||||||
|
for elem in filewalker:
|
||||||
|
for filename in elem[2]:
|
||||||
|
if filename.endswith(".java"):
|
||||||
|
fully_supported = True
|
||||||
|
f = open(filename, "r")
|
||||||
|
cubename = filename.replace(".java", "")
|
||||||
|
cards = []
|
||||||
|
for line in f.readlines():
|
||||||
|
if line.find("super") != -1:
|
||||||
|
cubename = line[line.find('"')+1:]
|
||||||
|
cubename = cubename[0:cubename.find('"')]
|
||||||
|
# print(cubename)
|
||||||
|
if line.find("cubeCards.add") != -1:
|
||||||
|
line = line.strip()
|
||||||
|
cardname = line[line.find('"')+1:]
|
||||||
|
cardname = cardname[0:cardname.find('"')].replace("'Sleeping Dragon'", '"Sleeping Dragon"').replace("Mardu Woe Reaper","Mardu Woe-Reaper").strip()
|
||||||
|
if cardname not in cardlist:
|
||||||
|
print("Unsupported card in '" + cubename +"': " + cardname)
|
||||||
|
fully_supported = False
|
||||||
|
else:
|
||||||
|
cards.extend([cardname])
|
||||||
|
# print(cardname)
|
||||||
|
if fully_supported:
|
||||||
|
print("'" + cubename + "' is FULLY SUPPORTED")
|
||||||
|
print("")
|
||||||
|
out = open("cube/" + cubename + ".dck", "w")
|
||||||
|
out.write("[metadata]\n")
|
||||||
|
out.write("Name="+cubename+"\n")
|
||||||
|
out.write("[main]\n")
|
||||||
|
for card in cards:
|
||||||
|
out.write("1 " + card + "\n")
|
||||||
|
out.close()
|
||||||
|
out = open("draft/" + cubename + ".draft", "w")
|
||||||
|
out.write("Name:"+cubename+"\n")
|
||||||
|
out.write("DeckFile:"+cubename+"\n")
|
||||||
|
out.write("Singleton:True\n")
|
||||||
|
out.write("\n")
|
||||||
|
out.write("Booster: 15 Any\n")
|
||||||
|
out.write("NumPacks: 3\n")
|
||||||
|
out.close()
|
||||||
Reference in New Issue
Block a user