#!/usr/bin/env python3 #note: image templates: https://boardgamegeek.com/thread/813176/card-templates #TODO: dynamic text sizes #TODO: remote upload? import json from PIL import Image, ImageDraw, ImageFont import textwrap import math import os import sys if getattr(sys, 'frozen', False): # we are running in a bundle bundle_dir = sys._MEIPASS else: # we are running in a normal Python environment bundle_dir = os.path.dirname(os.path.abspath(__file__)) IMG_HERO_CHAR_FRONT = Image.open(bundle_dir + "/images/HeroCharFront.png") IMG_HERO_CHAR_BACK = Image.open(bundle_dir + "/images/HeroCharBack.png") IMG_HERO_DECK = Image.open(bundle_dir + "/images/HeroCard.png") IMG_TARGET_HP = Image.open(bundle_dir + "/images/targetHP.png") FONT_KEYWORD = ImageFont.truetype(bundle_dir + "/fonts/RedStateBlueStateBB_reg.otf", size=40) FONT_TITLE = ImageFont.truetype(bundle_dir + "/fonts/CrashLandingBB.otf", size=60) FONT_DESCRIPTION = ImageFont.truetype(bundle_dir + "/fonts/RedStateBlueStateBB_reg.otf", size=30) FONT_TARGET_HP = ImageFont.truetype(bundle_dir + "/fonts/CrashLandingBB.otf", size=120) FONT_CHAR_HP = ImageFont.truetype(bundle_dir + "/fonts/ap.ttf", size=120) def drawTextIf(draw, position, font, card, key, wrap=False): if key in card: text = str(card[key]) if wrap: # badly wrap text to fit on card text = '\n'.join(textwrap.fill(line.strip(), wrap, break_long_words=False, replace_whitespace=False) for line in text.splitlines()) draw.text(position, text, font=font, fill="#000000") def addCardToBase(baseImg, baseX, cardNum, cardImg): baseImg.paste(cardImg, ((cardNum % baseX) * cardImg.width, int(cardNum / baseX) * cardImg.height)) def makeFaces(deckJson, outfile): # TODO: probably a more optimal way to fit cards baseX = math.ceil(math.sqrt(len(deckJson['deck']) + len(deckJson['character']) * 2)) baseImage = Image.new('RGB', (IMG_HERO_DECK.width * baseX, IMG_HERO_DECK.height * baseX)) cardNum = 0 # Make a card for each hero character card for card in deckJson['character']: cardFrontImg = IMG_HERO_CHAR_FRONT.copy() draw = ImageDraw.Draw(cardFrontImg) drawTextIf(draw, (150, 70), FONT_CHAR_HP, card, 'name') drawTextIf(draw, (100, 900), FONT_DESCRIPTION, card, 'power', 40) drawTextIf(draw, (53, 260), FONT_CHAR_HP, card, 'hp') addCardToBase(baseImage, baseX, cardNum, cardFrontImg) cardNum += 1 cardBackImg = IMG_HERO_CHAR_BACK.copy() draw = ImageDraw.Draw(cardBackImg) drawTextIf(draw, (150, 70), FONT_CHAR_HP, card, 'name') drawTextIf(draw, (80, 800), FONT_DESCRIPTION, card, 'incapacitated', 43) addCardToBase(baseImage, baseX, cardNum, cardBackImg) cardNum += 1 # Make a card for each card in the deck for card in deckJson['deck']: cardImg = IMG_HERO_DECK.copy() if 'hp' in card: # Draw HP marker, if it exists # self mask for transparency cardImg.paste(IMG_TARGET_HP, (510, 36), IMG_TARGET_HP) draw = ImageDraw.Draw(cardImg) drawTextIf(draw, (52, 50), FONT_TITLE, card, 'name') drawTextIf(draw, (85, 610), FONT_KEYWORD, card, 'type') drawTextIf(draw, (52, 670), FONT_DESCRIPTION, card, 'description', 46) drawTextIf(draw, (120, 910), FONT_DESCRIPTION, card, 'flavor') drawTextIf(draw, (600, 40), FONT_TARGET_HP, card, 'hp') addCardToBase(baseImage, baseX, cardNum, cardImg) cardNum += 1 baseImage.save(outfile + ".png", "PNG") return (baseX, baseX) def makeJson(deckJson, imgWidth, imgHeight, outfile): with open(bundle_dir + "/templates/deck.json") as f: outJson = json.load(f) with open(bundle_dir + "/templates/card.json") as f: cardTemplate = json.load(f) # number of cards in x and y direction outJson['ObjectStates'][0]['CustomDeck']['1'].update( {"NumWidth": imgWidth, "NumHeight": imgHeight, #"FaceURL": "http://adam-desktop.dyn.wpi.edu:8000/" + outfile + ".png", "FaceURL": "file://" + os.getcwd() + "/" + outfile + ".png", "BackURL": "http://cloud-3.steamusercontent.com/ugc/156906385556221451/CE2C3AFE1759790CB0B532FFD636D05A99EC91F4/"}) # decks start at (10 * deck id) cardNum = 100 for card in deckJson['character']: outJson['ObjectStates'][0]['DeckIDs'].append(cardNum) # add front card with back as second state cardOutBack = cardTemplate.copy() cardOutBack.update({"Nickname": card['name'], "Description": "Incapacitated", "CardID": cardNum + 1}) cardOut = cardTemplate.copy() cardOut.update({"Nickname": card['name'], "Description": "Active", "CardID": cardNum, "States": {"2": cardOutBack}}) outJson['ObjectStates'][0]['ContainedObjects'].append(cardOut) cardNum += 2 for card in deckJson['deck']: for i in range(0, card['count']): outJson['ObjectStates'][0]['DeckIDs'].append(cardNum) # add a card object thing for each card, and give it a name cardOut = cardTemplate.copy() cardOut.update({"Nickname": card['name'], "Description": card.get('type', ""), "CardID": cardNum}) outJson['ObjectStates'][0]['ContainedObjects'].append(cardOut) cardNum += 1 with open(outfile + ".json", "w") as f: json.dump(outJson, f) if __name__ == '__main__': if len(sys.argv) < 3: print("not enough arguments!") inputJson = input("Input file: ") outfile = input("Output file (no suffix): ") else: inputJson = sys.argv[1] outfile = sys.argv[2] with open(inputJson) as f: deckJson = json.load(f) imgWidth, imgHeight = makeFaces(deckJson, outfile) makeJson(deckJson, imgWidth, imgHeight, outfile) else: with open("hero_3.json") as f: deckJson = json.load(f) outfile = "out" imgWidth, imgHeight = makeFaces(deckJson, outfile) makeJson(deckJson, imgWidth, imgHeight, outfile)