1
0
Fork 0
mirror of https://github.com/ldericher/fftcgtool synced 2025-01-15 15:02:59 +00:00

major cleanup

This commit is contained in:
Jörn-Michael Miehe 2021-08-17 17:37:28 +02:00
parent 730775a660
commit 8360eb69b2
9 changed files with 130 additions and 108 deletions

View file

@ -4,18 +4,14 @@ import yaml
from PIL import Image from PIL import Image
from .cards import Cards from .cards import Cards
from .grid import Grid
from .imageloader import ImageLoader from .imageloader import ImageLoader
from .utils import GRID, RESOLUTION, BOOK_YML_NAME
class Book: class Book:
def __init__(self, cards: Cards, grid: tuple[int, int], resolution: tuple[int, int], language: str, def __init__(self, cards: Cards, language: str, num_threads: int):
num_threads: int):
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# transform grid into Grid
grid = Grid(grid)
# sort cards by element, then alphabetically # sort cards by element, then alphabetically
cards.sort(key=lambda x: x.name) cards.sort(key=lambda x: x.name)
cards.sort(key=lambda x: "Multi" if len(x.elements) > 1 else x.elements[0]) cards.sort(key=lambda x: "Multi" if len(x.elements) > 1 else x.elements[0])
@ -28,22 +24,22 @@ class Book:
) )
# multi-threaded download # multi-threaded download
images = ImageLoader.load(urls, resolution, language, num_threads) images = ImageLoader.load(urls, language, num_threads)
# card back Image # card back Image
back_image = images.pop(-1) back_image = images.pop(-1)
self.__pages = [] self.__pages = []
for page_images, page_cards in zip(grid.chunks(images), grid.chunks(cards)): for page_images, page_cards in zip(GRID.chunks(images), GRID.chunks(cards)):
# create book page Image # create book page Image
page_image = Image.new("RGB", grid * resolution) page_image = Image.new("RGB", GRID * RESOLUTION)
logger.info(f"New image: {page_image.size[0]}x{page_image.size[1]}") logger.info(f"New image: {page_image.size[0]}x{page_image.size[1]}")
# paste card faces onto page # paste card faces onto page
for i, image in enumerate(page_images): for i, image in enumerate(page_images):
grid.paste(page_image, i, image) GRID.paste(page_image, i, image)
# paste card back in last position # paste card back in last position
grid.paste(page_image, grid.capacity, back_image) GRID.paste(page_image, GRID.capacity, back_image)
# save page # save page
self.__pages.append({ self.__pages.append({
@ -51,15 +47,12 @@ class Book:
"cards": page_cards, "cards": page_cards,
}) })
def __getitem__(self, index: int) -> Image.Image: def save(self, file_name: str) -> None:
return self.__pages[index]["image"]
def save(self, file_name: str, book_yml_name: str) -> None:
book: dict[str, dict[str, any]] book: dict[str, dict[str, any]]
# load book.yml file # load book.yml file
try: try:
with open(book_yml_name, "r") as file: with open(BOOK_YML_NAME, "r") as file:
book = yaml.load(file, Loader=yaml.Loader) book = yaml.load(file, Loader=yaml.Loader)
except FileNotFoundError: except FileNotFoundError:
book = {} book = {}
@ -73,5 +66,5 @@ class Book:
book[fn] = {"cards": page["cards"]} book[fn] = {"cards": page["cards"]}
# update book.yml file # update book.yml file
with open(book_yml_name, "w") as file: with open(BOOK_YML_NAME, "w") as file:
yaml.dump(book, file, Dumper=yaml.Dumper) yaml.dump(book, file, Dumper=yaml.Dumper)

View file

@ -1,33 +1,12 @@
from __future__ import annotations
import json import json
import re import re
import yaml import yaml
from .code import Code from .code import Code
from .utils import encircle_symbol
def encircle_symbol(symbol: str, negative: bool):
symbol = symbol[0].upper()
base_symbols: tuple[str, str] = "", ""
if symbol.isalpha():
if negative:
base_symbols = "A", "🅐"
else:
base_symbols = "A", ""
elif symbol == "0":
if negative:
base_symbols = "0", "🄌"
else:
base_symbols = "0", ""
elif symbol.isnumeric():
if negative:
base_symbols = "1", ""
else:
base_symbols = "1", ""
symbol_num = ord(symbol) - ord(base_symbols[0])
return chr(ord(base_symbols[1]) + symbol_num)
class Card(yaml.YAMLObject): class Card(yaml.YAMLObject):
@ -53,7 +32,7 @@ class Card(yaml.YAMLObject):
self.__text = text self.__text = text
@classmethod @classmethod
def from_data(cls, data: dict[str, any], language: str): def from_data(cls, data: dict[str, any], language: str) -> Card:
if not data: if not data:
return cls( return cls(
code=Code(""), code=Code(""),

View file

@ -1,45 +1,60 @@
from __future__ import annotations
import yaml import yaml
_DictOfDicts = dict[str, dict[str, any]] from fftcg.code import Code
from fftcg.utils import BOOK_YML_NAME
class CardDB(_DictOfDicts): class CardDB:
def __init__(self, book_yml_name: str): __instance: CardDB = None
book: _DictOfDicts
@classmethod
def get(cls) -> CardDB:
if not CardDB.__instance:
CardDB.__instance = CardDB()
return CardDB.__instance
def __init__(self):
self.__content: dict[Code, dict[str, any]] = {}
def __getitem__(self, code: Code) -> dict[str, any]:
return self.__content[str(code)]
def load(self):
# load book.yml file # load book.yml file
book: dict
try: try:
with open(book_yml_name, "r") as file: with open(BOOK_YML_NAME, "r") as file:
book = yaml.load(file, Loader=yaml.Loader) book = yaml.load(file, Loader=yaml.Loader)
except FileNotFoundError: except FileNotFoundError:
book = {} book = {}
# "invert" book into card database: # "invert" book into card database:
# every card is indexable by its code # every card is indexable by its code
carddb: _DictOfDicts = {} self.__content.clear()
for fn, content in book.items(): for file_name, content in book.items():
carddb |= { self.__content |= {
str(card.code): { str(card.code): {
"card": card, "card": card,
"file": fn, "file": file_name,
"index": i, "index": i,
} for i, card in enumerate(content["cards"]) } for i, card in enumerate(content["cards"])
} }
super().__init__(carddb)
# write carddb.yml file # write carddb.yml file
with open("carddb.yml", "w") as file: with open("carddb.yml", "w") as file:
yaml.dump(self, file, Dumper=yaml.Dumper) yaml.dump(self.__content, file, Dumper=yaml.Dumper)
def make_deck(self, filters): # def make_deck(self, filters):
# filter codes by card criteria # # filter codes by card criteria
codes = [ # codes = [
content["card"].code # content["card"].code
for content in self.values() # for content in self.__content.values()
if all([f(content["card"]) for f in filters]) # if all([f(content["card"]) for f in filters])
] # ]
#
from .ttsdeck import TTSDeck # from .ttsdeck import TTSDeck
return TTSDeck(codes, self) # return TTSDeck(codes)

View file

@ -1,12 +1,13 @@
from __future__ import annotations
from PIL import Image from PIL import Image
_Point = tuple[int, int] _Point = tuple[int, int]
class Grid(_Point): class Grid(tuple[int, int]):
def __mul__(self, other: _Point) -> _Point: def __mul__(self, other: Grid) -> Grid:
other = Grid(other) return Grid((self.x * other.x, self.y * other.y))
return self.x * other.x, self.y * other.y
@property @property
def x(self): def x(self):

View file

@ -6,6 +6,8 @@ import threading
import requests import requests
from PIL import Image from PIL import Image
from fftcg.utils import RESOLUTION
class ImageLoader(threading.Thread): class ImageLoader(threading.Thread):
def __init__(self, url_queue: queue.Queue, resolution: tuple[int, int], language: str): def __init__(self, url_queue: queue.Queue, resolution: tuple[int, int], language: str):
@ -44,14 +46,14 @@ class ImageLoader(threading.Thread):
self.__queue.task_done() self.__queue.task_done()
@classmethod @classmethod
def load(cls, urls: list[str], resolution: tuple[int, int], language: str, num_threads: int) -> list[Image.Image]: def load(cls, urls: list[str], language: str, num_threads: int) -> list[Image.Image]:
url_queue = queue.Queue() url_queue = queue.Queue()
for url in urls: for url in urls:
url_queue.put(url) url_queue.put(url)
loaders = [] loaders = []
for _ in range(num_threads): for _ in range(num_threads):
loader = cls(url_queue, resolution, language) loader = cls(url_queue, RESOLUTION, language)
loaders.append(loader) loaders.append(loader)
loader.start() loader.start()

View file

@ -3,6 +3,7 @@ import logging
import roman import roman
from .cards import Cards from .cards import Cards
from .ttsdeck import TTSDeck
class Opus(Cards): class Opus(Cards):
@ -60,3 +61,32 @@ class Opus(Cards):
@property @property
def filename(self) -> str: def filename(self) -> str:
return self.__filename return self.__filename
@property
def elemental_decks(self) -> list[TTSDeck]:
if self.name in ["Promo", "Boss Deck Chaos"]:
return [TTSDeck([
card.code
for card in self
])]
else:
def element_filter(element: str):
return lambda card: card.elements == [element]
# simple cases: create lambdas for base elemental decks
base_elements = ["Fire", "Ice", "Wind", "Earth", "Lightning", "Water"]
filters = [element_filter(elem) for elem in base_elements]
filters += [
# light/darkness elemental deck
lambda card: card.elements == ["Light"] or card.elements == ["Darkness"],
# multi element deck
lambda card: len(card.elements) > 1,
]
return [TTSDeck([
card.code
for card in self
if f(card)
]) for f in filters]

View file

@ -5,8 +5,9 @@ from .code import Code
class TTSDeck(list[dict[str, any]]): class TTSDeck(list[dict[str, any]]):
def __init__(self, codes: list[Code], carddb: CardDB): def __init__(self, codes: list[Code]):
super().__init__([carddb[str(code)] for code in codes]) carddb = CardDB.get()
super().__init__([carddb[code] for code in codes])
def __str__(self) -> str: def __str__(self) -> str:
face_urls = list(set([entry["file"] for entry in self])) face_urls = list(set([entry["file"] for entry in self]))

31
fftcg/utils.py Normal file
View file

@ -0,0 +1,31 @@
from .grid import Grid
# constants
GRID = Grid((10, 7)) # default in TTsim: 10 columns, 7 rows
RESOLUTION = Grid((429, 600)) # default in TTsim: 480x670 pixels per card
BOOK_YML_NAME = "book.yml"
# functions
def encircle_symbol(symbol: str, negative: bool):
symbol = symbol[0].upper()
base_symbols: tuple[str, str] = "", ""
if symbol.isalpha():
if negative:
base_symbols = "A", "🅐"
else:
base_symbols = "A", ""
elif symbol == "0":
if negative:
base_symbols = "0", "🄌"
else:
base_symbols = "0", ""
elif symbol.isnumeric():
if negative:
base_symbols = "1", ""
else:
base_symbols = "1", ""
symbol_num = ord(symbol) - ord(base_symbols[0])
return chr(ord(base_symbols[1]) + symbol_num)

42
main.py
View file

@ -5,11 +5,6 @@ import os
import fftcg import fftcg
# constants
GRID = 10, 7 # default in TTsim: 10 columns, 7 rows
RESOLUTION = 429, 600 # default in TTsim: 480x670 pixels per card
BOOK_YML_NAME = "book.yml"
def main() -> None: def main() -> None:
# set up CLI # set up CLI
@ -45,40 +40,15 @@ def main() -> None:
# main program # main program
opus = fftcg.Opus(args.opus_id) opus = fftcg.Opus(args.opus_id)
book = fftcg.Book(opus, GRID, RESOLUTION, "eg", args.num_threads) book = fftcg.Book(opus, "eg", args.num_threads)
book.save(opus.filename, BOOK_YML_NAME) book.save(opus.filename)
# create elemental decks for opus # create elemental decks for opus
carddb = fftcg.CardDB(BOOK_YML_NAME) carddb = fftcg.CardDB.get()
carddb.load()
def opus_filter(card: fftcg.Card): for deck in opus.elemental_decks:
return card.code.opus == opus.number print(deck)
filters: list
if opus.number == "PR":
filters = [[opus_filter]]
else:
def element_filter(element: str):
return lambda card: card.elements == [element]
# simple cases: create lambdas for base elemental decks
base_elements = ["Fire", "Ice", "Wind", "Earth", "Lightning", "Water"]
element_filters = [element_filter(elem) for elem in base_elements]
element_filters += [
# light/darkness elemental deck
lambda card: card.elements == ["Light"] or card.elements == ["Darkness"],
# multi element deck
lambda card: len(card.elements) > 1,
]
# add in the opus_filter for all elemental decks
filters = list(zip([opus_filter] * len(element_filters), element_filters))
# make the decks
for f in filters:
print(carddb.make_deck(f))
# bye # bye
logging.info("Done. Put the generated JSON files in your 'Saved Objects' Folder.") logging.info("Done. Put the generated JSON files in your 'Saved Objects' Folder.")