#!/usr/bin/env python3 """Generate latex or markdown from yaml card definitions""" import argparse import pathlib from pathlib import Path from typing import MutableMapping, Tuple import yaml import generate_card_hover_links BABEL_LANGS = {'en': 'english', 'de': 'ngerman'} CARD_TEMPLATE = \ """\\documentclass{{standalone}} \\usepackage[{lang}]{{babel}} \\input{{common.tex}} \\begin{{document}} \\begin{{tikzpicture}} {content} \\cardborder \\end{{tikzpicture}} \\end{{document}}""" SET_SYMBOLS = { 'base': 'base', 'magic': 'magic', 'equipments': 'equipments', 'nautics': 'nautics', 'misc': '', 'kraken': 'kraken', 'tyrant': 'tyrant', 'exp1': 'exp1', 'potions': 'potions', } SCRIPT_PATH = Path(__file__).parent GAME_ROOT = SCRIPT_PATH.parent ASSETS_DIR = GAME_ROOT / 'assets' # all keys known by the rules KEYS = [ 'type', 'token', 'buy', 'upkeep', 'play', 'ai', 'health', 'movement', 'attack', 'full_action', 'effect', 'use' ] FORMATTED_KEYS = { 'en': { 'ai': 'AI', 'full_action': 'Full Action' }, 'de': { 'ai': 'KI', 'full_action': 'Volle-Aktion', 'type': 'Typ', 'buy': 'Kauf-Kosten', 'upkeep': 'Unterhaltskosten', 'play': 'Spielkosten', 'health': 'Leben', 'movement': 'Bewegung', 'attack': 'Angriff', 'effect': 'Effekt', 'durability': 'Haltbarkeit', 'target': 'Ziel', 'use': 'Gebrauch', } } def file_for_card(card_name: str, suffix='.tex') -> pathlib.Path: """Return the corresponding latex file name for a card""" return pathlib.Path(f'{card_name.lower().replace(" ", "_")}{suffix}') def get_field(card: MutableMapping, field: str, language='en') -> str: """Return the internationalized field value from a card""" value = card[field] if isinstance(value, dict): value = value.get(language, value['en']) return value def get_formatted_key(key: str, language='en') -> str: """Return the internationalized formatted version for key""" return FORMATTED_KEYS[language].get(key, key.capitalize()) def get_latex_field(card: MutableMapping, field: str, language='en', fmt_prefix='') -> Tuple[str, bool]: """Return the latex_ prefixed field from card or the field itself""" normal_value = card[field] latex_field = f'latex_{field}' has_latex = latex_field in card if has_latex: latex_value = card[latex_field] # internationalize if isinstance(normal_value, dict): normal_value = normal_value.get(language, normal_value['en']) if has_latex: latex_value = latex_value.get(language, normal_value) # NOTE: normal and latex_ fields must have the same data type # (both string or both list of strings). if has_latex: assert isinstance(latex_value, type(normal_value)) # If the fetched value is not a list we are done here if has_latex: if not isinstance(latex_value, list): return latex_value, True else: if not isinstance(normal_value, list): return f'{fmt_prefix}{normal_value}', False # Transform a list of values into a single string. # normal fields are joined using '\\ ' if not has_latex: assert len(normal_value) > 1 return '\\\\ '.join(normal_value), False # latex_* values are just concatenated. # NOTE: it is allowed for a latex_ field to be sparse. # Missing entries in a latex_ field list are populated from the corresponding # entry of the normal field assert len(latex_value) > 1 for i, l_val in enumerate(latex_value): if l_val is None: latex_value[i] = f'{fmt_prefix}{normal_value[i]}// ' return ''.join(latex_value), True def generate_markdown(card: MutableMapping, language='en', indentation=3): """Output a markdown enumeration""" name = get_field(card, 'name', language) print(f'{"#" * indentation} {name}') # always use the englisch name as file name file_name = file_for_card(get_field(card, 'name', 'en'), "") yml_card_path = f'../cards-data/{card["set"]}/{file_name}.yml' built_card_path = f'../latex-build/{language}/{card["set"]}/{file_name}' gen_png_link_text = lambda _: 'png' gen_png_link_target = lambda c, l: f'{built_card_path}.png' png_hover_link = generate_card_hover_links.gen_hoverable_link( str(file_name), gen_png_link_text, gen_png_link_target, '../latex-build', language) print( f'\n[pdf]({built_card_path}.pdf) {png_hover_link} [yml]({yml_card_path})\n' ) for key in KEYS: if key in card: formatted_key = get_formatted_key(key, language) value = get_field(card, key, language) if isinstance(value, list): value_txt = '\n' for v in value: value_txt += f' * {v}\n' value = value_txt[:-1] print(f'* **{formatted_key}**: {value}') def generate_latex(card: MutableMapping, language='en'): """Generate a standalone tikz picture with the card data""" card_content = "" name, _ = get_latex_field(card, 'name', language) card_file = file_for_card(name) for ext in ['.jpg', '.png']: image_file = ASSETS_DIR / card_file.with_suffix(ext) if image_file.exists(): card_content += f'\\cardbackground{{{image_file.name}}}\n' break card_content += f'\\cardtype{card["type"].capitalize()}' card_content += f'{"Token" if "token" in card else ""}{{{name}}}\n' if 'buy' in card: card_content += f'\\cardbuycost{{{card["buy"]}}}\n' if card['type'] == 'unit': unit_stats = [] abilities = [] for key in ['ai', 'health', 'movement', 'attack']: if key in card: formatted_key = get_formatted_key(key, language) value, _ = get_latex_field(card, key, language) unit_stats.append(f'{formatted_key}: {value}') if 'effect' in card: effect, _ = get_latex_field(card, 'effect', language) abilities.append(effect) if 'full_action' in card: full_action, _ = get_latex_field(card, 'full_action', language, '\\faRedo: ') abilities.append(full_action) unit_stats_str = '\\\\'.join(unit_stats) ability_block = '\\\\ \\vspace{0.2cm} '.join(abilities) if unit_stats and abilities: card_content += f'\\cardsplitcontent{{{unit_stats_str}}}{{{ability_block}}}' elif unit_stats: card_content += f'\\cardcontent{{\\textit{{{unit_stats_str}}}}}' card_content += '\n' if 'upkeep' in card: card_content += f'\\cardplaycost{{{card["upkeep"]}}}\n' elif card['type'] in ['spell', 'artifact']: effect, _ = get_latex_field(card, 'effect', language) assert effect is not None card_content += f'\\cardcontent{{{effect}}}\n' elif card['type'] == 'equipment': formatted_durability = get_formatted_key('durability', language) durability = get_field(card, 'durability', language) equipment_stats = f'{formatted_durability}: {durability}' effect, _ = get_latex_field(card, 'effect', language) card_content += f'\\cardsplitcontent{{{equipment_stats}}}{{{effect}}}\n' elif card['type'] == 'potion': formatted_use = get_formatted_key('use', language) use, _ = get_latex_field(card, 'use', language) card_content += f'\\cardcontent{{\\textit{{{formatted_use}}} - {use}}}\n' elif card['type'] == 'intention': formatted_target = get_formatted_key('target', language) target = get_field(card, 'target', language) target_block = f'{formatted_target}: {target}' if not 'effect' in card: card_content += f'\\cardcontent{{\\textit{{{target_block}}}}}\n' else: effect, _ = get_latex_field(card, 'effect', language) card_content += f'\\cardsplitcontent{{{target_block}}}{{{effect}}}\n' else: print(f'WARNING: unknown card type {card["type"]}!') if 'play' in card: card_content += f'\\cardplaycost{{{card["play"]}}}\n' if card['set'] in SET_SYMBOLS: card_content += f'\\cardmodule{{{SET_SYMBOLS[card["set"]]}}}' else: print(f'WARNING unknown set: {card["set"]}') print( CARD_TEMPLATE.format(lang=BABEL_LANGS.get(language, language), content=card_content)) def main(): """Generate one standalone tikz picture for each card in the input data""" parser = argparse.ArgumentParser( description='generate latex standalone cards') parser.add_argument( 'data', help= 'yaml file or directory containing yaml files defining the cards to generate' ) parser.add_argument('--format', choices=['latex', 'markdown'], default='latex', help='output format') parser.add_argument('--language', choices=['en', 'de'], default='en', help='the language of the card text') args = parser.parse_args() data_files = [args.data] data_path = pathlib.Path(args.data) if data_path.is_dir(): data_files = data_path.glob('*.yml') card_set = data_path.stem else: card_set = data_path.parent.stem for data_file in data_files: with open(data_file, 'r', encoding="utf8") as yaml_file: card = yaml.full_load(yaml_file) card['set'] = card_set if args.format == 'latex': generate_latex(card, language=args.language) elif args.format == 'markdown': generate_markdown(card, language=args.language) if __name__ == '__main__': main()