Skip to content
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,7 @@ $RECYCLE.BIN/
BaBOC/
TSSSF/
WSotT/

# Key-containing config
# =========================
imgur_auth.py
17 changes: 15 additions & 2 deletions PIL_Helper.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
from PIL import Image, ImageFont, ImageDraw, ImageOps
import os, glob
from StringIO import StringIO
import os, glob, requests
from math import ceil

class BadNetStatusException(Exception):
'''
An exception for LoadImageFromURL to throw if it has
problems fetching the remote URL.
'''

def BuildFont(fontname, fontsize):
return ImageFont.truetype(fontname, fontsize)

Expand Down Expand Up @@ -101,7 +108,7 @@ def AddText(image, text, font, fill=(0,0,0), anchor=(0,0),
# If current line is blank, just change y and skip to next
if not line == "":
if padline == True:
line = " {0} ".format(line)
line = u" {0} ".format(line)
line_width, line_height = font.getsize(line)
if halign == "left":
x_pos = start_x
Expand Down Expand Up @@ -201,6 +208,12 @@ def BuildPage(card_list, grid_width, grid_height, filename,
def BlankImage(w, h, color=(255,255,255), image_type="RGBA"):
return Image.new(image_type, (w, h), color=color)

def LoadImageFromURL(url):
r = requests.get(url)
if r.status_code is not 200:
raise BadNetStatusException(r.status_code)
return Image.open(StringIO(r.content))

def LoadImage(filepath, fallback="blank.png"):
try:
return Image.open(filepath)
Expand Down
143 changes: 87 additions & 56 deletions TSSSF_CardGen.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import os, glob, shutil, traceback, random
import PIL_Helper

TYPE, PICTURE, SYMBOLS, TITLE, KEYWORDS, BODY, FLAVOR, EXPANSION, CLIENT = range(9)
TYPE, PICTURE, SYMBOLS, TITLE, KEYWORDS, BODY, FLAVOR, EXPANSION, COPYRIGHT = range(9)
DIRECTORY = "TSSSF"
ARTIST = "Pixel Prism"

Expand All @@ -26,6 +26,7 @@
ExpansionIconsPath = ResourcePath + "/expansion icons/"
CardBacksPath = ResourcePath + "/card backs/"
FontsPath = ResourcePath + "/fonts/"
PlaceholderPath = ResourcePath + "/placeholder art/"

VassalTemplatesPath = DIRECTORY + "/vassal templates/"
VassalWorkspacePath = DIRECTORY + "/vassal workspace/"
Expand All @@ -34,6 +35,7 @@

VassalCard = [0]
ART_WIDTH = 600
ART_HEIGHT = 443
base_w = 889
base_h = 1215
base_w_center = base_w / 2
Expand All @@ -44,6 +46,7 @@
textmaxwidth = 689

croprect = (50, 63, 788 + 50, 1088 + 63)
ART_CROPRECT=(0,0,ART_WIDTH, ART_HEIGHT)

TextHeightThresholds = [363, 378, 600]
TitleWidthThresholds = [50] # This is in #characters, fix later plox
Expand Down Expand Up @@ -83,33 +86,34 @@
}

ArtMissing = [
PIL_Helper.LoadImage(CardPath + "artmissing01.png"),
PIL_Helper.LoadImage(CardPath + "artmissing02.png"),
PIL_Helper.LoadImage(CardPath + "artmissing03.png"),
PIL_Helper.LoadImage(CardPath + "artmissing04.png"),
PIL_Helper.LoadImage(CardPath + "artmissing05.png"),
PIL_Helper.LoadImage(CardPath + "artmissing06.png"),
PIL_Helper.LoadImage(CardPath + "artmissing07.png"),
PIL_Helper.LoadImage(PlaceholderPath + "artmissing01.png"),
PIL_Helper.LoadImage(PlaceholderPath + "artmissing02.png"),
PIL_Helper.LoadImage(PlaceholderPath + "artmissing03.png"),
PIL_Helper.LoadImage(PlaceholderPath + "artmissing04.png"),
PIL_Helper.LoadImage(PlaceholderPath + "artmissing05.png"),
PIL_Helper.LoadImage(PlaceholderPath + "artmissing06.png"),
PIL_Helper.LoadImage(PlaceholderPath + "artmissing07.png"),
]

Frames = {
"START": PIL_Helper.LoadImage(BleedTemplatesPath + "BLEED-Blank-Start-bleed.png"),
"Warning": PIL_Helper.LoadImage(CardPath + "BLEED_Card - Warning.png"),
"Pony": PIL_Helper.LoadImage(BleedTemplatesPath + "BLEED-Blank-Pony-bleed.png"),
"Ship": PIL_Helper.LoadImage(BleedTemplatesPath + "BLEED-Blank-Ship-bleed.png"),
"Rules1": PIL_Helper.LoadImage(CardPath + "BLEED_Rules1.png"),
"Rules3": PIL_Helper.LoadImage(CardPath + "BLEED_Rules3.png"),
"Rules5": PIL_Helper.LoadImage(CardPath + "BLEED_Rules5.png"),
"Goal": PIL_Helper.LoadImage(BleedTemplatesPath + "BLEED-Blank-Goal-bleed.png"),
"Derpy": PIL_Helper.LoadImage(CardPath + "BLEED_Card - Derpy Hooves.png"),
"TestSubject": PIL_Helper.LoadImage(CardPath + "BLEED_Card - OverlayTest Subject Cheerilee.png")
"start": PIL_Helper.LoadImage(BleedTemplatesPath + "BLEED-Blank-Start-bleed.png"),
"warning": PIL_Helper.LoadImage(CardPath + "BLEED_Card - Warning.png"),
"pony": PIL_Helper.LoadImage(BleedTemplatesPath + "BLEED-Blank-Pony-bleed.png"),
"ship": PIL_Helper.LoadImage(BleedTemplatesPath + "BLEED-Blank-Ship-bleed.png"),
"rules1": PIL_Helper.LoadImage(CardPath + "BLEED_Rules1.png"),
"rules3": PIL_Helper.LoadImage(CardPath + "BLEED_Rules3.png"),
"rules5": PIL_Helper.LoadImage(CardPath + "BLEED_Rules5.png"),
"goal": PIL_Helper.LoadImage(BleedTemplatesPath + "BLEED-Blank-Goal-bleed.png"),
"derpy": PIL_Helper.LoadImage(CardPath + "BLEED_Card - Derpy Hooves.png"),
"testsubject": PIL_Helper.LoadImage(CardPath + "BLEED_Card - OverlayTest Subject Cheerilee.png")
}

Symbols = {
"male": PIL_Helper.LoadImage(SymbolsPath + "Symbol-male.png"),
"female": PIL_Helper.LoadImage(SymbolsPath + "Symbol-Female.png"),
"malefemale": PIL_Helper.LoadImage(SymbolsPath + "Symbol-MaleFemale.png"),
"earth pony": PIL_Helper.LoadImage(SymbolsPath + "Symbol-Earth-Pony.png"),
"earthpony": PIL_Helper.LoadImage(SymbolsPath + "Symbol-Earth-Pony.png"),
"unicorn": PIL_Helper.LoadImage(SymbolsPath + "Symbol-Unicorn.png"),
"uniearth": PIL_Helper.LoadImage(SymbolsPath + "symbol-uniearth.png"),
"pegasus": PIL_Helper.LoadImage(SymbolsPath + "Symbol-Pegasus.png"),
Expand All @@ -129,7 +133,7 @@
"3-4": PIL_Helper.LoadImage(SymbolsPath + "symbol-34.png"),
"2-3": PIL_Helper.LoadImage(SymbolsPath + "symbol-23.png")
}
TIMELINE_SYMBOL_LIST = ["Dystopian"]
TIMELINE_SYMBOL_LIST = ["dystopian"]

Expansions = {
"Everfree14": PIL_Helper.LoadImage(ExpansionIconsPath + "symbol-Everfree14.png"),
Expand All @@ -155,7 +159,10 @@
"Ponycon 2015": PIL_Helper.LoadImage(ExpansionIconsPath + "symbol-ponynyc.png"),
"Patreon": PIL_Helper.LoadImage(ExpansionIconsPath + "symbol-Patreon.png"),
"Gameshow": PIL_Helper.LoadImage(ExpansionIconsPath + "symbol-gameshow.png"),
"BABScon": PIL_Helper.LoadImage(ExpansionIconsPath + "symbol-BABScon.png")
"BABScon": PIL_Helper.LoadImage(ExpansionIconsPath + "symbol-BABScon.png"),
"web-outline": PIL_Helper.LoadImage(ExpansionIconsPath + "symbol-web-circledark.png"),
"web-white": PIL_Helper.LoadImage(ExpansionIconsPath + "symbol-www.png"),
"web-grey": PIL_Helper.LoadImage(ExpansionIconsPath + "symbol-web-circlegrey.png")
}

ColorDict = {
Expand Down Expand Up @@ -196,22 +203,22 @@
}

backs = {
"START": PIL_Helper.LoadImage(CardBacksPath + "Back-Start.png"),
"Pony": PIL_Helper.LoadImage(CardBacksPath + "Back-Main.png"),
"Goal": PIL_Helper.LoadImage(CardBacksPath + "Back-Goals.png"),
"Ship": PIL_Helper.LoadImage(CardBacksPath + "Back-Ships.png"),
"Card": PIL_Helper.LoadImage(CardBacksPath + "Back-Main.png"),
"Shipwrecker": PIL_Helper.LoadImage(CardBacksPath + "Back-Main.png"),
"BLANK": PIL_Helper.LoadImage(CardBacksPath + "Blank - Intentionally Left Blank.png"),
"Rules1": PIL_Helper.LoadImage(CardPath + "Rules2.png"),
"Rules3": PIL_Helper.LoadImage(CardPath + "Rules4.png"),
"Rules5": PIL_Helper.LoadImage(CardPath + "Rules6.png"),
"TestSubject": PIL_Helper.LoadImage(CardBacksPath + "Back-Main.png"),
"Warning": PIL_Helper.LoadImage(CardPath + "Card - Contact.png")
"start": PIL_Helper.LoadImage(CardBacksPath + "Back-Start.png"),
"pony": PIL_Helper.LoadImage(CardBacksPath + "Back-Main.png"),
"goal": PIL_Helper.LoadImage(CardBacksPath + "Back-Goals.png"),
"ship": PIL_Helper.LoadImage(CardBacksPath + "Back-Ships.png"),
"card": PIL_Helper.LoadImage(CardBacksPath + "Back-Main.png"),
"shipwrecker": PIL_Helper.LoadImage(CardBacksPath + "Back-Main.png"),
"blank": PIL_Helper.LoadImage(CardBacksPath + "Blank - Intentionally Left Blank.png"),
"rules1": PIL_Helper.LoadImage(CardPath + "Rules2.png"),
"rules3": PIL_Helper.LoadImage(CardPath + "Rules4.png"),
"rules5": PIL_Helper.LoadImage(CardPath + "Rules6.png"),
"testsubject": PIL_Helper.LoadImage(CardBacksPath + "Back-Main.png"),
"warning": PIL_Helper.LoadImage(CardPath + "Card - Contact.png")
}

special_card_types = ["Rules1", "Rules3", "Rules5", "Warning", "Derpy", "Card"]
special_cards_with_copyright = ["Derpy"]
special_card_types = ["rules1", "rules3", "rules5", "warning", "derpy", "card"]
special_cards_with_copyright = ["derpy"]


def FixFileName(tagin):
Expand Down Expand Up @@ -262,6 +269,13 @@ def SaveCard(filepath, image_to_save):
filepath = "{}_{:>03}{}".format(basepath, i, extension)
image_to_save.save(filepath, dpi=(300, 300))

def BuildSingleCard(linein):
tags = linein.strip('\n').strip('\r').replace(r'\n', '\n').split('`')
im_bleed = PickCardFunc(tags[TYPE], tags)
im_crop = im_bleed.crop(croprect)
im_vassal = PIL_Helper.ResizeImage(im_crop, VASSAL_SCALE)

return (im_bleed, im_crop, im_vassal)

def BuildCard(data):
picture = None
Expand Down Expand Up @@ -304,26 +318,27 @@ def BuildCard(data):

def BuildBack(data):
if type(data).__name__ == 'dict':
card_type = data['type']
card_type = data['type'].lower()
else:
card = data.strip('\n').strip('\r').replace(r'\n', '\n').split('`')
card_type = card[TYPE]
card_type = card[TYPE].lower()

return backs[card_type]


def PickCardFunc(card_type, data):
if card_type == "START":
card_type = card_type.lower()
if card_type == "start":
return MakeStartCard(data)
elif card_type == "Pony":
elif card_type == "pony":
return MakePonyCard(data)
elif card_type == "Ship":
elif card_type == "ship":
return MakeShipCard(data)
elif card_type == "Goal":
elif card_type == "goal":
return MakeGoalCard(data)
elif card_type == "BLANK":
elif card_type == "blank":
return MakeBlankCard()
elif card_type == "TestSubject":
elif card_type == "testsubject":
return MakePonyCard(data)
elif card_type in special_card_types:
return MakeSpecialCard(data)
Expand All @@ -332,25 +347,38 @@ def PickCardFunc(card_type, data):


def GetFrame(card_type):
return Frames[card_type].copy()
return Frames[card_type.lower()].copy()


def AddCardArt(image, filename, anchor):
if filename == "NOART":
return
if os.path.exists(os.path.join(CardPath, filename)):
if filename.startswith("http"):
try:
art = PIL_Helper.LoadImageFromURL(filename)
except PIL_Helper.BadNetStatusException as e:
art = random.choice(ArtMissing)
elif os.path.exists(os.path.join(CardPath, filename)) and filename != "":
art = PIL_Helper.LoadImage(os.path.join(CardPath, filename))
else:
art = random.choice(ArtMissing)
# Find desired height of image based on width of 600 px
w, h = art.size
h = int((float(ART_WIDTH) / w) * h)
# Resize image to fit in frame
art = PIL_Helper.ResizeImage(art, (ART_WIDTH, h))
if float(w) / float(h) < float(ART_WIDTH) / float(ART_HEIGHT):
h = int((float(ART_WIDTH) / w) * h)
# Resize image to fit in frame
art = PIL_Helper.ResizeImage(art, (ART_WIDTH, h))
else:
w = int((float(ART_HEIGHT) / h) * w)
# Resize image to fit in frame
art = PIL_Helper.ResizeImage(art, (w, ART_HEIGHT))

art = art.crop(ART_CROPRECT)
image.paste(art, anchor)


def AddSymbols(image, symbols, card_type=""):
symbols = [x.lower() for x in symbols]
# Remove any timeline symbols from the symbols list
pruned_symbols = set(symbols) - set(TIMELINE_SYMBOL_LIST)
if card_type == "Goal":
Expand All @@ -365,7 +393,7 @@ def AddSymbols(image, symbols, card_type=""):
positions = [Anchors["Symbol1"], Anchors["Symbol2"]]

for index, s in enumerate(symbols):
sym = Symbols.get(s.lower(), None)
sym = Symbols.get(s, None)
if sym:
if s in TIMELINE_SYMBOL_LIST:
image.paste(sym, Anchors["TimelineSymbol"], sym)
Expand Down Expand Up @@ -491,15 +519,18 @@ def CopyrightText(card, image, color, artist):
if type(card).__name__ == 'dict':
client = card.get('client')
else:
if len(card) - 1 >= CLIENT:
client = str(card[CLIENT])
if len(card) - 1 >= COPYRIGHT:
client = unicode(card[COPYRIGHT])

if client is not None:
card_set += " " + client
text = "{}; TSSSF by Horrible People Games. Art by {}.".format(
card_set,
artist
)
if "TSSSF by Horrible People Games" in client:
text = client
else:
text = "{}; TSSSF by Horrible People Games. Art by {}.".format(
card_set,
artist
)
PIL_Helper.AddText(
image=image,
text=text,
Expand Down Expand Up @@ -673,7 +704,7 @@ def MakeSpecialCard(card):
def MakeSpecialCardJSON(data):
print repr(data['picture'])
image = GetFrame(data['picture'])
if data['picture'] in special_cards_with_copyright:
if data['picture'].lower() in special_cards_with_copyright:
CopyrightText(data, image, ColorDict["Copyright"], data.get('artist', ARTIST))
if Expansion_Icon is not None:
AddExpansionJSON(image, Expansion_Icon)
Expand All @@ -683,7 +714,7 @@ def MakeSpecialCardJSON(data):
def MakeSpecialCardPON(data):
print repr(data[PICTURE])
image = GetFrame(data[PICTURE])
if data[PICTURE] in special_cards_with_copyright:
if data[PICTURE].lower() in special_cards_with_copyright:
CopyrightText(data, image, ColorDict["Copyright"], ARTIST)
if len(data) > EXPANSION:
AddExpansion(image, data[EXPANSION])
Expand Down
4 changes: 4 additions & 0 deletions imgur_auth.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#If you wish to use imgur-save functionality, put your API key here
CLIENT_ID = ''
CLIENT_SECRET = ''

Loading