1
0
Fork 0
mirror of https://github.com/ldericher/fftcgtool synced 2025-01-15 15:02:59 +00:00
fftcgtool/fftcg/ttsdeck.py
2021-09-06 04:41:49 +02:00

226 lines
6.4 KiB
Python

from __future__ import annotations
import json
import logging
import os
import re
import requests
from .carddb import CardDB
from .cards import Cards
from .code import Code
from .language import Language
from .utils import CARD_BACK_URL, DECKS_DIR_NAME
class TTSDeck(Cards):
def __init__(self, codes: list[Code], name: str, description: str, face_down: bool):
logger = logging.getLogger(__name__)
super().__init__(name)
self.__description = description
self.__face_down = face_down
# get cards from carddb
carddb = CardDB()
# non-imported cards
codes_invalid = frozenset([
code
for code in codes
if code not in carddb
])
# show errors and remove non-imported cards
for code in codes_invalid:
logger.error(f"Code '{code}' not in CardDB, ignoring!")
while code in codes:
codes.remove(code)
# put existing cards into deck
self.extend([
carddb[code]
for code in codes
])
@property
def file_name(self) -> str:
return f"{super().file_name}.json"
__FFDECKS_API_URL = "https://ffdecks.com/api/deck"
__RE_FFDECKS_ID = re.compile(r"((https?://)?ffdecks\.com(/+api)?/+deck/+)?([0-9]+).*", flags=re.UNICODE)
@classmethod
def from_ffdecks_deck(cls, deck_id: str) -> TTSDeck:
logger = logging.getLogger(__name__)
# check deck id
match = TTSDeck.__RE_FFDECKS_ID.match(deck_id)
if match is None:
logger.error("Malformed Deck ID for FFDecks API!")
return cls([], "", "", True)
else:
# extract deck id from match
deck_id = match.groups()[3]
# api request
res = requests.get(TTSDeck.__FFDECKS_API_URL, params={"deck_id": deck_id})
if not res.ok:
logger.error("Invalid Deck ID for FFDecks API!")
return cls([], "", "", True)
logger.info(f"Importing Deck {deck_id}")
# pre-extract the used data
deck_cards = [{
"code": card["card"]["serial_number"],
"type": card["card"]["type"],
"cost": int(card["card"]["cost"]),
"count": int(card["quantity"]),
} for card in res.json()["cards"]]
# sort cards by type, then by cost
def by_type(data: dict[str, str | int]) -> int:
key_prios = {
"Forward": 1,
"Summon": 2,
"Monster": 3,
"Backup": 5,
}
if data["type"] in key_prios:
return key_prios[data["type"]]
else:
return 4
def by_cost(data: dict[str, str | int]) -> int:
return data["cost"]
deck_cards.sort(key=by_cost)
deck_cards.sort(key=by_type)
# ffdecks quirk: some full-art promos in database
replace_full_arts = {
# line format:
# full-art-id: normal id,
"PR-051": "11-083",
"PR-055": "11-062",
}
# replace with normal-art cards
for card in deck_cards:
if card["code"] in replace_full_arts:
card["code"] = replace_full_arts[card["code"]]
codes = [
# create list of code objects
Code(card["code"])
# for each card
for card in deck_cards
# repeat to meet count
for _ in range(card["count"])
]
# general metadata
name = f"{res.json()['name']}"
description = deck_id
# create deck object
return cls(codes, name, description, True)
def get_tts_object(self, language: Language) -> dict[str, any]:
carddb = CardDB()
# unique face urls used
unique_faces = set([
card[language].face
for card in self
])
# lookup for indices of urls
face_indices = {
url: i + 1
for i, url in enumerate(unique_faces)
}
# build the "CustomDeck" dictionary
custom_deck = {
str(i): {
"NumWidth": "10",
"NumHeight": "7",
"FaceURL": carddb.get_face_url(face),
"BackURL": CARD_BACK_URL,
} for face, i in face_indices.items()
}
# values both in main deck and each contained card
common_dict = {
"Transform": {
"scaleX": 2.17822933,
"scaleY": 1.0,
"scaleZ": 2.17822933,
"rotY": 180.0,
},
"Locked": False,
"Grid": True,
"Snap": True,
"Autoraise": True,
"Sticky": True,
"Tooltip": True,
"GridProjection": False,
}
# cards contained in deck
contained_objects = [
{
"Nickname": card[language].name,
"Description": card[language].text,
"CardID": 100 * face_indices[card[language].face] + card.index,
"Name": "Card",
"Hands": True,
"SidewaysCard": False,
} | common_dict for card in self
]
# extract the card ids
deck_ids = [
contained_object["CardID"]
for contained_object in contained_objects
]
# create the deck dictionary
deck_dict = {"ObjectStates": [
{
"Nickname": self.name,
"Description": self.__description,
"DeckIDs": deck_ids,
"CustomDeck": custom_deck,
"ContainedObjects": contained_objects,
"Name": "Deck",
"Hands": False,
"SidewaysCard": False,
} | common_dict
]}
if self.__face_down:
# flip the deck
deck_dict["ObjectStates"][0]["Transform"]["rotZ"] = 180.0
return deck_dict
def get_json(self, language: Language) -> str:
return json.dumps(self.get_tts_object(language), indent=2)
def save(self, language: Language) -> None:
# only save if the deck contains cards
if self:
if not os.path.exists(DECKS_DIR_NAME):
os.mkdir(DECKS_DIR_NAME)
with open(os.path.join(DECKS_DIR_NAME, self.file_name), "w") as file:
file.write(self.get_json(language))