"""Module for providing the parts in the template.html file""" import csv from pathlib import Path import markdown from bs4 import BeautifulSoup as soup from .helpers import ( CHARACTERISTICS, FORMS, TECHNIQUES, enumerate_helper, repeat_template, ) from .translations import translation_attrs, translation_attrs_setup # Xp helper def xp( name: str, *, suffix="_exp", adv_suffix="_advancementExp", tot_suffix="_totalExp" ) -> str: """ Generate the HTML for the Xp parts of arts & abilities """ return f"""[//]""" def alert(title: str, text: str, *, level: str = "warning", ID: str = None): """ Generate the HTML to display a banner that can be permanently hidden This is used to inform player of important changes in updates. Arguments: text: Main text of the banner title: Title of the banner type: On of "warning", "info". The aspect of the banner ID: optional string ID of this banner, if you need to check if it is open/closed somewhere. Do NOT use numbers """ if not level in ("info", "warning"): raise ValueError("Level must be among 'info', 'warning'") if ID is None: alert_id = alert.numid alert.numid += 1 else: alert_id = str(ID) alert.strid.append(alert_id) return f"""

{level.title()} - {title}

{text}
""" # python supports attributes on function # we use that to store the internal global variable used by the function alert.numid = 0 alert.strid = [] def disable_old_alerts(marker: str): lines = ",\n ".join( f'"alert-{i}": 1' for i in list(range(alert.numid)) + alert.strid ) return f"""setAttrs({{ "{marker}": 1, {lines} }}); """ # Add new parts to this dictionary # parts can be defined in other modules and imported if the generating # code is long GLOBALS = { # makes the module available "markdown": markdown, # makes those function available in the HTML "xp": xp, "alert": alert, "disable_old_alerts": disable_old_alerts, # Makes those values available in the HTML "translation_attrs": translation_attrs, "translation_attrs_setup": translation_attrs_setup, "html_header": "", "css_header": "/* DO NOT MODIFY !\nThis file is automatically generated from a tempalte. Any change will be overwritten\n*/", } # Personality traits GLOBALS["personality_trait_rows"] = repeat_template( """
""", range(1, 7), ) # Reputations GLOBALS["reputation_rows"] = repeat_template( """
""", range(1, 7), ) # Characteristics definitions characteristic_roll = "(@{%(Char)s_Score}) [@{%(char)s_i18n}] + (@{wound_total}) [@{wounds_i18n}] + ([[floor(@{Fatigue})]]) [@{fatigue_i18n}] + (?{@{circumstantial_i18n}|0}) [@{circumstances_i18n}]" GLOBALS["characteristic_rows"] = repeat_template( """ %(Char)s
""".replace( "$characteristic_roll$", characteristic_roll ), CHARACTERISTICS, str_key="char", ) # Characteristic options GLOBALS["characteristic_score_options"] = repeat_template( """""", CHARACTERISTICS, str_key="char", ) GLOBALS["characteristic_score_ask"] = ( "?{@{characteristic_i18n}|" + "| ".join( "@{%(char)s_i18n}, @{%(Char)s_Score} [@{%(char)s_i18n}]" % {"char": char, "Char": char.capitalize()} for char in CHARACTERISTICS ) + "}" ) GLOBALS["characteristic_name_options"] = repeat_template( """""", CHARACTERISTICS, str_key="char", ) GLOBALS["characteristic_name_ask_attr"] = ( "?{@{characteristic_i18n}|" + "| ".join( "@{%(char)s_i18n},@{%(char)s_Score} [@{%(char)s_i18n}]" % {"char": char} for char in CHARACTERISTICS ) + "}" ) # Abilities ability_roll_template = "&{template:ability} {{name=@{character_name}}} {{label0=@{Ability_name}}} {{banner=@{Ability_Speciality}}} {{label1=^{rank}}} {{result1= [[ @{Ability_Score} + @{Ability_Puissant} ]]}} {{label2=@{Ability_CharacName}}} {{result2=[[@{sys_at}@{character_name}@{sys_pipe}@{Ability_CharacName}_Score@{sys_rbk}]]}} {{label3=^{weakness-m}}} {{result3=[[ ([[floor(@{Fatigue})]]) [@{fatigue_i18n}] + (@{wound_total}) [@{wounds_i18n}] ]]}} {{label4=^{circumstances-m}}} {{result4=[[(?{@{circumstantial_i18n}|0})]]}} {{result0=%(roll)s}} {{botch-button=[@{botch_i18n}!](~@{character_name}|botch)}} {{crit-button=[@{critical_i18n}!](~@{character_name}|critical)}}" ability_roll = "[[ %(die)s + (@{Ability_Score} + @{Ability_Puissant}) [@{Ability_name}] + (@{sys_at}@{character_name}@{sys_pipe}@{Ability_CharacName}_Score@{sys_rbk}) [@{sys_at}@{character_name}@{sys_pipe}@{Ability_CharacName}_i18n@{sys_rbk}] + (@{wound_total}) [@{wounds_i18n}] + ([[floor(@{Fatigue})]]) [@{fatigue_i18n}] + (?{@{circumstantial_i18n}|0}) [@{circumstances_i18n}] ]]" GLOBALS["ability_roll_simple"] = ability_roll_template % { "roll": ability_roll % {"die": "@{simple-die}"} } GLOBALS["ability_roll_stress"] = ( ability_roll_template % {"roll": ability_roll % {"die": "@{stress-die}"}} ) + " {{stress=1}}" # Technique definitions GLOBALS["technique_definitions"] = repeat_template( """ %(Tech)s """ + xp("%(Tech)s") + """ """, TECHNIQUES, str_key="tech", ) # Technique options GLOBALS["technique_score_options"] = repeat_template( """""", TECHNIQUES, str_key="tech", ) GLOBALS["technique_score_options_unlabeled"] = repeat_template( """""", TECHNIQUES, str_key="tech", ) GLOBALS["technique_name_options"] = repeat_template( """""", TECHNIQUES, str_key="tech", ) GLOBALS["technique_enumerated_options"] = repeat_template( """""", enumerate_helper(TECHNIQUES, [str.capitalize], start=1), tuple_keys=("index", "tech", "Tech"), ) # Form definitions form_template = ( """ %(Form)s """ + xp("%(Form)s") + """ """ ) GLOBALS["form_definitions_1"] = repeat_template( form_template, FORMS[:5], str_key="form" ) GLOBALS["form_definitions_2"] = repeat_template( form_template, FORMS[5:], str_key="form" ) # Form options GLOBALS["form_score_options"] = repeat_template( """""", FORMS, str_key="form", ) GLOBALS["form_score_options_unlabeled"] = repeat_template( """""", FORMS, str_key="form", ) GLOBALS["form_name_options"] = repeat_template( """""", FORMS, str_key="form", ) GLOBALS["form_enumerated_options"] = repeat_template( """""", enumerate_helper(FORMS, [str.capitalize], start=1), tuple_keys=("index", "form", "Form"), ) # Casting rolls ## Magic tab spontaneous_roll_template = "&{template:arcane} {{label0=^{spontaneous} ^{casting}}} {{result0=%(roll)s}} {{label1=^{aura}}} {{result1=@{aura}}} {{label2=^{weakness-m}}} {{result2=[[ @{wound_total}[@{wounds_i18n}] + [[floor(@{fatigue})]][@{fatigue_i18n}] ]]}} {{label3=^{circumstances-m}}} {{result3=?{@{modifiers_i18n}|0}}} {{botch-button=[@{botch_i18n}!](~@{character_name}|botch)}} {{crit-button=[@{critical_i18n}!](~@{character_name}|critical-spontaneous)}}" spontaneous_roll = "[[(%(die)s + @{Spontaneous1_Technique} + @{Spontaneous1_Form} + ([[@{Spontaneous1_Focus}]]) [@{focus_i18n}] + (@{gestures}) + (@{words}) + (@{Stamina_Score}) [@{stamina_i18n}] + (@{aura}) [@{aura_i18n}] + ([[floor(@{Fatigue})]]) [@{fatigue_i18n}] + (@{wound_total}) [@{wounds_i18n}] + (?{@{modifiers_i18n}|0}) [@{modifiers_i18n}] )/2 ]]" GLOBALS["spontaneous_roll_simple"] = spontaneous_roll_template % { "roll": spontaneous_roll % {"die": "@{simple-die}"} } GLOBALS["spontaneous_roll_stress"] = ( spontaneous_roll_template % {"roll": spontaneous_roll % {"die": "@{stress-die}"}} ) + " {{stress=1}}" ceremonial_roll_template = "&{template:arcane} {{label0=^{ceremonial} ^{casting}}} {{result0= %(roll)s }} {{label1=^{aura}}} {{result1=@{aura}}} {{label2=^{weakness-m}}} {{result2=[[@{wound_total}[@{wounds_i18n}] + [[floor(@{fatigue})]][@{fatigue_i18n}] ]]}} {{label3=^{circumstances-m}}} {{result3=?{@{modifiers_i18n}|0}}} {{botch-button=[@{botch_i18n}!](~@{character_name}|botch)}} {{crit-button=[@{critical_i18n}!](~@{character_name}|critical-spontaneous)}}" ceremonial_roll = "[[(%(die)s+ @{Ceremonial_Technique} + @{Ceremonial_Form} + ([[@{Ceremonial_Focus}]]) [@{focus_i18n}] + (@{gestures}) + (@{words}) + (@{Stamina_Score}) [@{stamina_i18n}] + (@{aura}) [@{aura_i18n}] + ([[floor(@{Fatigue})]]) [@{fatigue_i18n}] + (@{wound_total}) [@{wounds_i18n}] + (@{Ceremonial_Artes_Lib}) [@{artes_i18n}] + (@{Ceremonial_Philos}) [@{philos_i18n}] + (?{@{modifiers_i18n}|0}) [@{modifiers_i18n}] )/2 ]]" GLOBALS["ceremonial_roll_simple"] = ceremonial_roll_template % { "roll": ceremonial_roll % {"die": "@{simple-die}"} } GLOBALS["ceremonial_roll_stress"] = ( ceremonial_roll_template % {"roll": ceremonial_roll % {"die": "@{stress-die}"}} ) + " {{stress=1}}" formulaic_roll_template = "&{template:arcane} {{label0=^{formulaic} ^{casting}}} {{result0= %(roll)s }} {{label1=^{aura}}} {{result1=@{aura}}} {{label2=^{weakness-m}}} {{result2=[[@{wound_total}[@{wounds_i18n}] + [[floor(@{fatigue})]][@{fatigue_i18n}] ]]}} {{label3=^{circumstances-m}}} {{result3=?{@{modifiers_i18n}|0}}} {{botch-button=[@{botch_i18n}!](~@{character_name}|botch)}} {{crit-button=[@{critical_i18n}!](~@{character_name}|critical)}}" formulaic_roll = "[[%(die)s + @{Formulaic_Technique} + @{Formulaic_Form} + ([[@{Formulaic_Focus}]]) [@{focus_i18n}] + (@{gestures}) + (@{words}) + (@{Stamina_Score}) [@{stamina_i18n}] + (@{aura}) [@{aura_i18n}] + ([[floor(@{Fatigue})]]) [@{fatigue_i18n}] + (@{wound_total}) [@{wounds_i18n}] + (?{@{modifiers_i18n}|0}) [@{modifiers_i18n}] ]]" GLOBALS["formulaic_roll_simple"] = formulaic_roll_template % { "roll": formulaic_roll % {"die": "@{simple-die}"} } GLOBALS["formulaic_roll_stress"] = ( formulaic_roll_template % {"roll": formulaic_roll % {"die": "@{stress-die}"}} ) + " {{stress=1}}" ritual_roll_template = "&{template:arcane} {{label0=^{ritual} ^{casting}}} {{result0= %(roll)s }} {{label1=^{aura}}} {{result1=@{aura}}} {{label2=^{weakness-m}}} {{result2=[[ @{wound_total}[@{wounds_i18n}] + [[floor(@{fatigue})]][@{fatigue_i18n}] ]]}} {{label3=^{circumstances-m}}} {{result3=?{@{modifiers_i18n}|0}}} {{botch-button=[@{botch_i18n}!](~@{character_name}|botch)}} {{crit-button=[@{critical_i18n}!](~@{character_name}|critical)}}" ritual_roll = "[[%(die)s + @{Ritual_Technique} + @{Ritual_Form} + ([[@{Ritual_Focus}]]) [@{focus_i18n}] + (@{Stamina_Score}) [@{stamina_i18n}] + (@{aura}) [@{aura_i18n}] + (@{Ritual_Artes_Lib}) [@{artes_i18n}] + (@{Ritual_Philos}) [@{philos_i18n}] + (@{wound_total}) [@{wounds_i18n}] + ([[floor(@{fatigue})]]) [@{fatigue_i18n}] + (?{@{modifiers_i18n}|0}) [@{modifiers_i18n}] ]]" GLOBALS["ritual_roll_simple"] = ritual_roll_template % { "roll": ritual_roll % {"die": "@{simple-die}"} } GLOBALS["ritual_roll_stress"] = ( ritual_roll_template % {"roll": ritual_roll % {"die": "@{stress-die}"}} ) + " {{stress=1}}" ## Spells # Deferred attribute access to get the spell's technique value spell_tech_value = "(@{sys_at}@{character_name}@{sys_pipe}@{spell_tech_name}_Score@{sys_rbk} + @{sys_at}@{character_name}@{sys_pipe}@{spell_tech_name}_Puissant@{sys_rbk}) [@{sys_at}@{character_name}@{sys_pipe}@{spell_tech_name}_i18n@{sys_rbk}]" spell_form_value = "(@{sys_at}@{character_name}@{sys_pipe}@{spell_form_name}_Score@{sys_rbk} + @{sys_at}@{character_name}@{sys_pipe}@{spell_form_name}_Puissant@{sys_rbk}) [@{sys_at}@{character_name}@{sys_pipe}@{spell_form_name}_i18n@{sys_rbk}]" # Export the deferred attribute access for use in the HTML since the focus depends on them GLOBALS["spell_tech_value"] = spell_tech_value GLOBALS["spell_form_value"] = spell_form_value spell_roll_template = "&{template:spell} {{spell= @{spell_name}}} {{character= @{character_name} }} {{sigil=@{sigil}}} {{roll= %(roll)s }} {{range= @{spell_range} }} {{duration= @{spell_duration} }} {{target= @{spell_target} }} {{effect= @{spell_note} }} {{mastery= @{spell_note-2} }} {{Technique= @{sys_at}@{character_name}@{sys_pipe}@{spell_tech_name}_i18n@{sys_rbk} }} {{Form= @{sys_at}@{character_name}@{sys_pipe}@{spell_form_name}_i18n@{sys_rbk} }} {{Level= @{spell_level} }} {{botch-button=[@{botch_i18n}!](~@{character_name}|botch)}} {{crit-button=[@{critical_i18n}!](~@{character_name}|critical)}}" spell_roll = ( "[[%(die)s + (@{Stamina_Score}) [@{stamina_i18n}] + " + spell_tech_value + " + " + spell_form_value + "+ ([[@{spell_Focus}]]) [@{focus_i18n}] + (@{spell_bonus}) [@{bonus_i18n}] + (@{gestures}) + (@{words}) + (@{aura}) [@{aura_i18n}] + ([[floor(@{Fatigue})]]) [@{fatigue_i18n}] + (@{wound_total}) [@{wounds_i18n}] + (?{@{modifiers_i18n}|0}) [@{modifiers_i18n}] ]]" ) GLOBALS["spell_roll_simple"] = spell_roll_template % { "roll": spell_roll % {"die": "@{simple-die}"} } GLOBALS["spell_roll_stress"] = ( spell_roll_template % {"roll": spell_roll % {"die": "@{stress-die}"}} ) + " {{stress=1}}" # Botch formula GLOBALS["botch_separate"] = ( "&{template:botch} {{roll= " + ( "?{@{botch_num_i18n} | " + "|".join( f"{n} {'Die' if n==1 else 'Dice'}," + " ".join(["[[1d10cf10cs0]]"] * n) for n in range(1, 9) ) + "}" ) + " }} {{type=Grouped}}" ) # Fatigue add_fatigue_lvl_num = 10 GLOBALS["fatigue_levels_options"] = repeat_template( """""", range(0, add_fatigue_lvl_num + 1) ) GLOBALS["additional_fatigue_levels"] = repeat_template( """ 0 2 min. Winded """, [(str(i), str(i / 1000)) for i in range(1, add_fatigue_lvl_num + 1)], tuple_keys=("num", "value"), ) GLOBALS["fatigue_level_css"] = "\n".join( ( # IF the fatigue selector is not on a value for which the level is visible "".join( ':not(.sheet-fatigue-proxy[value="%s"])' % val for val in range(lvl, add_fatigue_lvl_num + 1) ) # THEN hide the level + (" + table tr.sheet-addfatigue-%s" % lvl) + " {\n display: none;\n}" ) for lvl in range(1, add_fatigue_lvl_num + 1) ) # Documentation with open(Path(__file__).parents[1] / "documentation.md") as f: html = markdown.markdown("".join(f)) html = soup(html, "html.parser") for i in range(1, 10): for tag in html.find_all(f"h{i}"): tag.attrs["class"] = tag.get("class", "") + " heading_label" GLOBALS["documentation"] = html.prettify() # Rolltemplate ## Custom rolltemplate colors with open(Path(__file__).parent / "css_colors.csv", newline="") as f: reader = csv.DictReader(f) css_rules = [] for color_def in reader: # Base CSS rules lines_header = [ f".sheet-rolltemplate-custom .sheet-crt-container.sheet-crt-color-{color_def['color']} {{", f" --header-bg-color: {color_def['hex']};", ] lines_rolls = [ f".sheet-rolltemplate-custom .sheet-crt-container.sheet-crt-rlcolor-{color_def['color']} .inlinerollresult {{", f" --roll-bg-color: {color_def['hex']};", ] lines_buttons = [ f".sheet-rolltemplate-custom .sheet-crt-container.sheet-crt-btcolor-{color_def['color']} a {{", f" --button-bg-color: {color_def['hex']};", ] # Adapt text color to background color hex = color_def["hex"].lstrip("#") r, g, b = tuple(int(hex[2 * i : 2 * i + 2], 16) / 255 for i in range(3)) # Assuming sRGB -> Luma # may need fixing, color spaces are confusing luma = 0.2126 * r + 0.7152 * g + 0.0722 * b if luma > 0.5: # arbitrary threshold # switch to black text if luma is high enough lines_header.append(" --header-text-color: #000;") lines_buttons.append(" --button-text-color: #000;") if luma < 0.5: lines_rolls.append(" --roll-text-color: #FFF;") # Build the rules for lines in (lines_header, lines_rolls, lines_buttons): lines.append("}") css_rules.append("\n".join(lines)) GLOBALS["custom_rt_color_css"] = "\n".join(css_rules)