mirror of
https://github.com/ldericher/fftcgtool
synced 2025-01-15 15:02:59 +00:00
dataclass decorator for code and language, remove clutter from grid
This commit is contained in:
parent
9c6b784c18
commit
77bc884eb6
6 changed files with 64 additions and 89 deletions
|
@ -6,7 +6,7 @@ from PIL import Image
|
||||||
from .cards import Cards
|
from .cards import Cards
|
||||||
from .imageloader import ImageLoader
|
from .imageloader import ImageLoader
|
||||||
from .language import Language
|
from .language import Language
|
||||||
from .utils import GRID, RESOLUTION, CARD_BACK_URL, IMAGES_DIR_NAME
|
from .utils import GRID, RESOLUTION, CARD_BACK_URL, IMAGES_DIR_NAME, chunks, grid_paste
|
||||||
|
|
||||||
|
|
||||||
class Book:
|
class Book:
|
||||||
|
@ -19,7 +19,7 @@ class Book:
|
||||||
|
|
||||||
# all card face URLs
|
# all card face URLs
|
||||||
urls = [
|
urls = [
|
||||||
("https://fftcg.cdn.sewest.net/images/cards/full/{}_{}.jpg", str(card.code), language.image_suffix)
|
("https://fftcg.cdn.sewest.net/images/cards/full/{}_{}.jpg", card.code.long, language.image_suffix)
|
||||||
for card in cards
|
for card in cards
|
||||||
]
|
]
|
||||||
# card back URL
|
# card back URL
|
||||||
|
@ -32,17 +32,19 @@ class Book:
|
||||||
|
|
||||||
self.__pages = []
|
self.__pages = []
|
||||||
|
|
||||||
for page_num, (page_images, page_cards) in enumerate(zip(GRID.chunks(images), GRID.chunks(cards))):
|
for page_num, (page_images, page_cards) in enumerate(zip(
|
||||||
|
chunks(GRID.capacity, images), chunks(GRID.capacity, 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)
|
||||||
|
|
||||||
# set card indices
|
# set card indices
|
||||||
for i, card in enumerate(page_cards):
|
for i, card in enumerate(page_cards):
|
||||||
|
|
|
@ -1,60 +1,39 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import re
|
import re
|
||||||
|
from dataclasses import dataclass, InitVar, field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
class Code:
|
class Code:
|
||||||
__RE_NUM = re.compile(r"([0-9]+)-([0-9]+)([CRHLS]?)")
|
opus: str = field(init=False)
|
||||||
__RE_PROMO = re.compile(r"(PR)-([0-9]+)")
|
serial: int = field(init=False)
|
||||||
__RE_BOSS = re.compile(r"(B)-([0-9]+)")
|
rarity: str = field(init=False, compare=False)
|
||||||
|
code_init: InitVar[str] = field(default="")
|
||||||
|
|
||||||
def __init__(self, code: str):
|
__RE = re.compile(r"([1-9][0-9]*|PR|B)-([0-9]+)([CRHLS]?)", flags=re.UNICODE)
|
||||||
if code[0].isnumeric():
|
|
||||||
# card code starts with a number
|
|
||||||
self.__opus, self.__serial, self.__rarity = \
|
|
||||||
Code.__RE_NUM.match(code).groups()
|
|
||||||
|
|
||||||
elif code.startswith("PR"):
|
def __post_init__(self, code_init: str):
|
||||||
# card code starts with "PR"
|
match = Code.__RE.match(code_init)
|
||||||
self.__opus, self.__serial = \
|
|
||||||
Code.__RE_PROMO.match(code).groups()
|
|
||||||
self.__rarity = ""
|
|
||||||
|
|
||||||
elif code.startswith("B"):
|
if match is not None:
|
||||||
# card code starts with "B"
|
groups = match.groups()
|
||||||
self.__opus, self.__serial = \
|
opus, serial, rarity = \
|
||||||
Code.__RE_BOSS.match(code).groups()
|
groups[0], int(groups[1]), groups[2]
|
||||||
self.__rarity = ""
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
# card code not recognized
|
# card code not recognized
|
||||||
self.__opus, self.__serial, self.__rarity = \
|
opus, serial, rarity = \
|
||||||
"?", "???", "?"
|
"?", 0, "?"
|
||||||
|
|
||||||
def __str__(self) -> str:
|
object.__setattr__(self, "opus", opus)
|
||||||
return f"{self.__opus}-{self.__serial}{self.__rarity}"
|
object.__setattr__(self, "serial", serial)
|
||||||
|
object.__setattr__(self, "rarity", rarity)
|
||||||
def __repr__(self) -> str:
|
|
||||||
return f"Code({str(self)!r})"
|
|
||||||
|
|
||||||
def __hash__(self) -> hash:
|
|
||||||
return hash(self.short)
|
|
||||||
|
|
||||||
def __eq__(self, other: Code):
|
|
||||||
return self.short == other.short
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def short(self) -> str:
|
def short(self) -> str:
|
||||||
return f"{self.__opus}-{self.__serial}"
|
return f"{self.opus}-{self.serial:03}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def opus(self) -> str:
|
def long(self) -> str:
|
||||||
return self.__opus
|
return f"{self.short}{self.rarity}"
|
||||||
|
|
||||||
@property
|
|
||||||
def serial(self) -> int:
|
|
||||||
return int(self.__serial)
|
|
||||||
|
|
||||||
@property
|
|
||||||
def rarity(self) -> str:
|
|
||||||
return self.__rarity
|
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from PIL import Image
|
|
||||||
|
|
||||||
_Point = tuple[int, int]
|
|
||||||
|
|
||||||
|
|
||||||
class Grid(tuple[int, int]):
|
class Grid(tuple[int, int]):
|
||||||
def __mul__(self, other: Grid) -> Grid:
|
def __mul__(self, other: Grid) -> Grid:
|
||||||
|
@ -21,16 +17,3 @@ class Grid(tuple[int, int]):
|
||||||
def capacity(self):
|
def capacity(self):
|
||||||
# capacity of grid (reserve last space for card back)
|
# capacity of grid (reserve last space for card back)
|
||||||
return self.x * self.y - 1
|
return self.x * self.y - 1
|
||||||
|
|
||||||
def chunks(self, whole: list) -> list:
|
|
||||||
# while there are elements
|
|
||||||
while whole:
|
|
||||||
# get a chunk
|
|
||||||
yield whole[:self.capacity]
|
|
||||||
# remove that chunk
|
|
||||||
whole = whole[self.capacity:]
|
|
||||||
|
|
||||||
def paste(self, page: Image.Image, index: int, card: Image.Image) -> None:
|
|
||||||
w, h = card.size
|
|
||||||
position = (index % self.x) * w, (index // self.x) * h
|
|
||||||
page.paste(card, position)
|
|
||||||
|
|
|
@ -1,43 +1,37 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from dataclasses import dataclass, InitVar, field
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True)
|
||||||
class Language:
|
class Language:
|
||||||
def __init__(self, language: str):
|
short: str = field(init=False)
|
||||||
language = language.lower()
|
short_init: InitVar[str] = field(default="")
|
||||||
|
|
||||||
|
def __post_init__(self, short_init: str):
|
||||||
|
short_init = short_init.lower()
|
||||||
|
|
||||||
# supported languages
|
# supported languages
|
||||||
if language in ["de", "es", "fr", "ja", "it"]:
|
if short_init in ["de", "es", "fr", "ja", "it"]:
|
||||||
self.__short = language
|
object.__setattr__(self, "short", short_init)
|
||||||
else:
|
else:
|
||||||
# everything else is english
|
# everything else is english
|
||||||
self.__short = "en"
|
object.__setattr__(self, "short", "en")
|
||||||
|
|
||||||
def __repr__(self):
|
|
||||||
return f"Language({self.__short!r})"
|
|
||||||
|
|
||||||
def __str__(self):
|
|
||||||
return self.__short
|
|
||||||
|
|
||||||
def __hash__(self) -> hash:
|
|
||||||
return hash(str(self))
|
|
||||||
|
|
||||||
def __eq__(self, other: Language):
|
|
||||||
return str(self) == str(other)
|
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def image_suffix(self):
|
def image_suffix(self):
|
||||||
# supported languages for face URLs
|
# supported languages for face URLs
|
||||||
if self.__short in ["de", "es", "fr", "it"]:
|
if self.short in ["de", "es", "fr", "it"]:
|
||||||
return self.__short
|
return self.short
|
||||||
else:
|
else:
|
||||||
return "eg"
|
return "eg"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def key_suffix(self):
|
def key_suffix(self):
|
||||||
# supported languages for Square API
|
# supported languages for Square API
|
||||||
if self.__short in ["de", "es", "fr", "it"]:
|
if self.short in ["de", "es", "fr", "it"]:
|
||||||
return f"_{self.__short.upper()}"
|
return f"_{self.short.upper()}"
|
||||||
elif self.__short == "ja":
|
elif self.short == "ja":
|
||||||
return ""
|
return ""
|
||||||
else:
|
else:
|
||||||
return "_EN"
|
return "_EN"
|
||||||
|
|
|
@ -17,17 +17,17 @@ class Opus(Cards):
|
||||||
|
|
||||||
params: dict[str, any]
|
params: dict[str, any]
|
||||||
if opus_id.isnumeric():
|
if opus_id.isnumeric():
|
||||||
name = f"Opus {opus_id} ({language})"
|
name = f"Opus {opus_id} ({language.short})"
|
||||||
self.__number = opus_id
|
self.__number = opus_id
|
||||||
params = {"set": [f"Opus {roman.toRoman(int(opus_id)).upper()}"]}
|
params = {"set": [f"Opus {roman.toRoman(int(opus_id)).upper()}"]}
|
||||||
|
|
||||||
elif opus_id == "chaos":
|
elif opus_id == "chaos":
|
||||||
name = f"Boss Deck Chaos ({language})"
|
name = f"Boss Deck Chaos ({language.short})"
|
||||||
self.__number = "B"
|
self.__number = "B"
|
||||||
params = {"set": ["Boss Deck Chaos"]}
|
params = {"set": ["Boss Deck Chaos"]}
|
||||||
|
|
||||||
elif opus_id == "promo":
|
elif opus_id == "promo":
|
||||||
name = f"Promo ({language})"
|
name = f"Promo ({language.short})"
|
||||||
self.__number = "PR"
|
self.__number = "PR"
|
||||||
params = {"rarity": ["pr"]}
|
params = {"rarity": ["pr"]}
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
from .grid import Grid
|
from .grid import Grid
|
||||||
|
|
||||||
# constants
|
# constants
|
||||||
|
@ -33,3 +35,18 @@ def encircle_symbol(symbol: str, negative: bool):
|
||||||
|
|
||||||
symbol_num = ord(symbol) - ord(base_symbols[0])
|
symbol_num = ord(symbol) - ord(base_symbols[0])
|
||||||
return chr(ord(base_symbols[1]) + symbol_num)
|
return chr(ord(base_symbols[1]) + symbol_num)
|
||||||
|
|
||||||
|
|
||||||
|
def chunks(chunk_size: int, whole: list):
|
||||||
|
# while there are elements
|
||||||
|
while whole:
|
||||||
|
# get a chunk
|
||||||
|
yield whole[:chunk_size]
|
||||||
|
# remove that chunk
|
||||||
|
whole = whole[chunk_size:]
|
||||||
|
|
||||||
|
|
||||||
|
def grid_paste(page: Image.Image, index: int, card: Image.Image) -> None:
|
||||||
|
w, h = card.size
|
||||||
|
position = (index % GRID.x) * w, (index // GRID.x) * h
|
||||||
|
page.paste(card, position)
|
||||||
|
|
Loading…
Reference in a new issue