import colorsys import json import io import logging import textwrap from collections import namedtuple import flask import lxml.etree as xmltree import mermaid from . import core, gamedata, jsonizer, util pth_upgrade_schema = core.path_appdir / "basepak/rules/schemas/upgrades.xsd" doc_upgrade_schema = xmltree.parse(pth_upgrade_schema.as_posix()) upgrade_schema = xmltree.XMLSchema(doc_upgrade_schema) upgrade_schema_parser = xmltree.XMLParser(schema=upgrade_schema) core.schemas.append((upgrade_schema, "rules/upgrades/**.xml")) UpgradeData = namedtuple("UpgradeData", ["id", "name", "desc", "requires", "food", "shinies"]) ## \brief Renders an upgrade tree. # \param tree The upgrade tree to retrieve. # \api{GET} /upgrades/`` # \return An SVG. @core.app.route("/upgrades/") def get_upgrade_tree(tree): buf_mmd = io.StringIO() rulefile = xmltree.parse(gamedata.vfs.open(f"/rules/upgrades/{tree}.xml"), upgrade_schema_parser) ruleset = rulefile.getroot() unlocked = [] if len(flask.request.query_string) > 0: for upgrade in flask.request.args.get("unlocked", "").split(","): if len(upgrade) > 0: unlocked.append(upgrade) hnd_treedata = ruleset.xpath("./upgrades:TreeData", namespaces=core.xml_namespaces)[0] hnd_name = hnd_treedata.xpath("./upgrades:Name", namespaces=core.xml_namespaces)[0] hnd_primary_color = hnd_treedata.xpath("./upgrades:PrimaryColor", namespaces=core.xml_namespaces)[0] buf_mmd.write(textwrap.dedent(f""" --- title: {hnd_name.text} securityLevel: loose --- flowchart LR """).lstrip()) rgb_primary_color = util.hex_to_rgb(hnd_primary_color.text) hsv_primary_color = colorsys.rgb_to_hsv(*rgb_primary_color) print(f"{rgb_primary_color} ----> {hsv_primary_color}") hsv_fill_open_color = (hsv_primary_color[0], hsv_primary_color[1] / 2, hsv_primary_color[2]) rgb_fill_open_color = colorsys.hsv_to_rgb(*hsv_fill_open_color) hsv_stroke_unlock_color = (hsv_primary_color[0], 1.0, 55) rgb_stroke_unlock_color = colorsys.hsv_to_rgb(*hsv_stroke_unlock_color) hsv_fill_lock_color = (hsv_primary_color[0], 0.1, min(hsv_primary_color[2] * 1.5, 255)) rgb_fill_lock_color = colorsys.hsv_to_rgb(*hsv_fill_lock_color) print(f"{hsv_fill_lock_color}, {rgb_fill_lock_color}") hsv_stroke_lock_color = (hsv_primary_color[0], 0.1, 55) rgb_stroke_lock_color = colorsys.hsv_to_rgb(*hsv_stroke_lock_color) buf_mmd.write(f" classDef open fill:{util.rgb_to_hex(*rgb_fill_open_color)},stroke:{util.rgb_to_hex(*rgb_primary_color)}\n") buf_mmd.write(f" classDef unlocked fill:{util.rgb_to_hex(*rgb_primary_color)},stroke:{util.rgb_to_hex(*rgb_stroke_unlock_color)}\n") buf_mmd.write(f" classDef locked fill:{util.rgb_to_hex(*rgb_fill_lock_color)},stroke:{util.rgb_to_hex(*rgb_stroke_lock_color)}\n") tree_upgrades = [] for hnd_upgrade in ruleset.iter("{seagull:rules/upgrades}Upgrade"): hnd_id = hnd_upgrade.xpath("./upgrades:Id", namespaces=core.xml_namespaces)[0] hnd_upgrade_name = hnd_upgrade.xpath("./upgrades:Name", namespaces=core.xml_namespaces)[0] hnd_desc = hnd_upgrade.xpath("./upgrades:Desc", namespaces=core.xml_namespaces)[0] try: hnd_requires = hnd_upgrade.xpath("./upgrades:Requirements", namespaces=core.xml_namespaces)[0] require_list = [elem.text for elem in hnd_requires.iter("{seagull:rules/upgrades}Require")] try: hnd_food = hnd_requires.xpath("./upgrades:Food", namespaces=core.xml_namespaces)[0] except IndexError: hnd_food = None try: hnd_shinies = hnd_requires.xpath("./upgrades:Shinies", namespaces=core.xml_namespaces)[0] except IndexError: hnd_shinies = None except IndexError: require_list = [] hnd_food = None hnd_shinies = None upgrade = UpgradeData( id=hnd_id.text, name=hnd_upgrade_name.text, desc=hnd_desc.text, requires=require_list, food=int(hnd_food.text) if hnd_food is not None else 0, shinies=int(hnd_shinies.text) if hnd_shinies is not None else 0 ) tree_upgrades.append(upgrade) tiers = {} dependency_lines = [] for upgrade in tree_upgrades: deptier = 0 upgrade_food_costs = f"🍏{upgrade.food}" if upgrade.food > 0 else "" upgrade_shinies_costs = f"🪙{upgrade.shinies}" if upgrade.shinies > 0 else "" buf_mmd.write(f" {upgrade.id}@{{label: \"{upgrade.name}\n{upgrade_food_costs}{upgrade_shinies_costs}\"}}\n") collected_tiers = [] for require in upgrade.requires: if require in tiers: collected_tiers.append(tiers[require]) dependency_lines.append(f" {require} --> {upgrade.id}\n") if len(collected_tiers) > 0: deptier = max(collected_tiers) tier = deptier + 1 tiers[upgrade.id] = tier all_requirements_met = True if tier > 1: while all_requirements_met: for require in upgrade.requires: all_requirements_met = require in unlocked if not all_requirements_met: buf_mmd.write(f" class {upgrade.id} locked\n") elif upgrade in unlocked: buf_mmd.write(f" class {upgrade.id} unlocked\n") else: buf_mmd.write(f" class {upgrade.id} open\n") buf_mmd.write(f" click {upgrade.id} call purchase_upgrade('{tree}','{upgrade.id}')\n") for line in dependency_lines: buf_mmd.write(line) buf_mmd.seek(0) mmd_str = buf_mmd.read() mmd_upgradetree = mermaid.Mermaid(mmd_str, height=400) #print(mmd_str) return mmd_upgradetree.svg_response.content @core.app.route("/upgrades//") def get_upgrade_data(tree, upgrade): rulefile = xmltree.parse(gamedata.vfs.open(f"/rules/upgrades/{tree}.xml"), upgrade_schema_parser) ruleset = rulefile.getroot() target_upgrade = None hnd_id = None for hnd_upgrade in ruleset.iter("{seagull:rules/upgrades}Upgrade"): hnd_id = hnd_upgrade.xpath("./upgrades:Id", namespaces=core.xml_namespaces)[0] if hnd_id.text == upgrade: target_upgrade = hnd_upgrade break hnd_name = hnd_upgrade.xpath("./upgrades:Name", namespaces=core.xml_namespaces)[0] hnd_desc = hnd_upgrade.xpath("./upgrades:Desc", namespaces=core.xml_namespaces)[0] try: hnd_requires = hnd_upgrade.xpath("./upgrades:Requirements", namespaces=core.xml_namespaces)[0] require_list = [elem.text for elem in hnd_requires.iter("{seagull:rules/upgrades}Require")] try: hnd_food = hnd_requires.xpath("./upgrades:Food", namespaces=core.xml_namespaces)[0] except IndexError: hnd_food = None try: hnd_shinies = hnd_requires.xpath("./upgrades:Shinies", namespaces=core.xml_namespaces)[0] except IndexError: hnd_shinies = None except IndexError: hnd_requires = None hnd_shinies = None hnd_food = None require_list = [] return flask.make_response(json.dumps({ "id": hnd_id.text, "name": hnd_name.text, "desc": hnd_desc.text, "food": int(hnd_food.text) if hnd_food else 0, "shinies": int(hnd_shinies.text) if hnd_shinies else 0, "requires": require_list }, cls=jsonizer.JSONizer), 200) def get_upgrade_tree_mmd(tree): if not gamedata.vfs.exists(f"upgrades/{tree}.mmd"): return flask.make_response("No Upgrade Tree", 404) with gamedata.vfs.open(f"upgrades/{tree}.mmd") as fd_upgradetree: mmd_upgradetree = mermaid.Mermaid(fd_upgradetree.read(), height=400) return mmd_upgradetree.svg_response.content