commit f4273338d84d7a8b01694cdc98866f3933423407 Author: Adam Goldsmith Date: Sat Apr 22 18:13:01 2017 -0400 Initial commit working: hero decks and character cards, without art diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e77ef3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/*.png +/*.json diff --git a/SotMDeckBuilder.py b/SotMDeckBuilder.py new file mode 100644 index 0000000..cdb43c9 --- /dev/null +++ b/SotMDeckBuilder.py @@ -0,0 +1,139 @@ +#!/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 + +IMG_HERO_CHAR_FRONT = Image.open("images/HeroCharFront.png") +IMG_HERO_CHAR_BACK = Image.open("images/HeroCharBack.png") +IMG_HERO_DECK = Image.open("images/HeroCard.png") +IMG_TARGET_HP = Image.open("images/targetHP.png") + +FONT_KEYWORD = ImageFont.truetype("fonts/RedStateBlueStateBB_reg.otf", size=40) +FONT_TITLE = ImageFont.truetype("fonts/CrashLandingBB.otf", size=60) +FONT_DESCRIPTION = ImageFont.truetype("fonts/RedStateBlueStateBB_reg.otf", size=30) +FONT_TARGET_HP = ImageFont.truetype("fonts/CrashLandingBB.otf", size=120) +FONT_CHAR_HP = ImageFont.truetype("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("templates/deck.json") as f: + outJson = json.load(f) + with open("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) + +with open("hero_3.json") as f: + deckJson = json.load(f) + +outfile = "out" + +imgWidth, imgHeight = makeFaces(deckJson, outfile) +makeJson(deckJson, imgWidth, imgHeight, outfile) diff --git a/fonts/CrashLandingBB.otf b/fonts/CrashLandingBB.otf new file mode 100644 index 0000000..222ce63 Binary files /dev/null and b/fonts/CrashLandingBB.otf differ diff --git a/fonts/RedStateBlueStateBB_reg.otf b/fonts/RedStateBlueStateBB_reg.otf new file mode 100644 index 0000000..674d8c3 Binary files /dev/null and b/fonts/RedStateBlueStateBB_reg.otf differ diff --git a/fonts/ap.ttf b/fonts/ap.ttf new file mode 100644 index 0000000..78a74dc Binary files /dev/null and b/fonts/ap.ttf differ diff --git a/images/HeroCard.png b/images/HeroCard.png new file mode 100644 index 0000000..0824b1d Binary files /dev/null and b/images/HeroCard.png differ diff --git a/images/HeroCharBack.png b/images/HeroCharBack.png new file mode 100644 index 0000000..06ec22b Binary files /dev/null and b/images/HeroCharBack.png differ diff --git a/images/HeroCharFront.png b/images/HeroCharFront.png new file mode 100644 index 0000000..ed1f55e Binary files /dev/null and b/images/HeroCharFront.png differ diff --git a/images/HeroFront.png b/images/HeroFront.png new file mode 100644 index 0000000..6109d8e Binary files /dev/null and b/images/HeroFront.png differ diff --git a/images/source/Hero Card.psd b/images/source/Hero Card.psd new file mode 100644 index 0000000..0e3eb59 Binary files /dev/null and b/images/source/Hero Card.psd differ diff --git a/images/source/Hero Character (Back).psd b/images/source/Hero Character (Back).psd new file mode 100644 index 0000000..7807837 Binary files /dev/null and b/images/source/Hero Character (Back).psd differ diff --git a/images/source/Hero Character (Front).psd b/images/source/Hero Character (Front).psd new file mode 100644 index 0000000..e5b6531 Binary files /dev/null and b/images/source/Hero Character (Front).psd differ diff --git a/images/targetHP.png b/images/targetHP.png new file mode 100644 index 0000000..ba38de3 Binary files /dev/null and b/images/targetHP.png differ