WLED/usermods/pixels_dice_tray/generate_roll_info.py

231 wiersze
5.9 KiB
Python

'''
File for generating roll labels and info text for the InfoMenu.
Uses a very limited markdown language for styling text.
'''
import math
from pathlib import Path
import re
from textwrap import indent
# Variables for calculating values in info text
CASTER_LEVEL = 9
SPELL_ABILITY_MOD = 6
BASE_ATK_BONUS = 6
SIZE_BONUS = 1
STR_BONUS = 2
DEX_BONUS = -1
# TFT library color values
TFT_BLACK =0x0000
TFT_NAVY =0x000F
TFT_DARKGREEN =0x03E0
TFT_DARKCYAN =0x03EF
TFT_MAROON =0x7800
TFT_PURPLE =0x780F
TFT_OLIVE =0x7BE0
TFT_LIGHTGREY =0xD69A
TFT_DARKGREY =0x7BEF
TFT_BLUE =0x001F
TFT_GREEN =0x07E0
TFT_CYAN =0x07FF
TFT_RED =0xF800
TFT_MAGENTA =0xF81F
TFT_YELLOW =0xFFE0
TFT_WHITE =0xFFFF
TFT_ORANGE =0xFDA0
TFT_GREENYELLOW =0xB7E0
TFT_PINK =0xFE19
TFT_BROWN =0x9A60
TFT_GOLD =0xFEA0
TFT_SILVER =0xC618
TFT_SKYBLUE =0x867D
TFT_VIOLET =0x915C
class Size:
def __init__(self, w, h):
self.w = w
self.h = h
# Font 1 6x8
# Font 2 12x16
CHAR_SIZE = {
1: Size(6, 8),
2: Size(12, 16),
}
SCREEN_SIZE = Size(128, 128)
# Calculates distance for short range spell.
def short_range() -> int:
return 25 + 5 * CASTER_LEVEL
# Entries in markdown language.
# Parameter 0 of the tuple is the roll name
# Parameter 1 of the tuple is the roll info.
# The text will be shown when the roll type is selected. An error will be raised
# if the text would unexpectedly goes past the end of the screen. There are a
# few styling parameters that need to be on their own lines:
# $COLOR - The color for the text
# $SIZE - Sets the text size (see CHAR_SIZE)
# $WRAP - By default text won't wrap and generate an error. This enables text wrapping. Lines will wrap mid-word.
ENTRIES = [
tuple(["Barb Chain", f'''\
$COLOR({TFT_RED})
Barb Chain
$COLOR({TFT_WHITE})
Atk/CMD {BASE_ATK_BONUS + SPELL_ABILITY_MOD}
Range: {short_range()}
$WRAP(1)
$SIZE(1)
Summon {1 + math.floor((CASTER_LEVEL-1)/3)} chains. Make a melee atk 1d6 or a trip CMD=AT. On a hit make Will save or shaken 1d4 rnds.
''']),
tuple(["Saves", f'''\
$COLOR({TFT_GREEN})
Saves
$COLOR({TFT_WHITE})
FORT 8
REFLEX 8
WILL 9
''']),
tuple(["Skill", f'''\
Skill
''']),
tuple(["Attack", f'''\
Attack
Melee +{BASE_ATK_BONUS + SIZE_BONUS + STR_BONUS}
Range +{BASE_ATK_BONUS + SIZE_BONUS + DEX_BONUS}
''']),
tuple(["Cure", f'''\
Cure
Lit 1d8+{min(5, CASTER_LEVEL)}
Mod 2d8+{min(10, CASTER_LEVEL)}
Ser 3d8+{min(15, CASTER_LEVEL)}
''']),
tuple(["Concentrate", f'''\
Concentrat
+{CASTER_LEVEL + SPELL_ABILITY_MOD}
$SIZE(1)
Defensive 15+2*SP_LV
Dmg 10+DMG+SP_LV
Grapple 10+CMB+SP_LV
''']),
]
RE_SIZE = re.compile(r'\$SIZE\(([0-9])\)')
RE_COLOR = re.compile(r'\$COLOR\(([0-9]+)\)')
RE_WRAP = re.compile(r'\$WRAP\(([0-9])\)')
END_HEADER_TXT = '// GENERATED\n'
def main():
roll_info_file = Path(__file__).parent / 'roll_info.h'
old_contents = open(roll_info_file, 'r').read()
end_header = old_contents.index(END_HEADER_TXT)
with open(roll_info_file, 'w') as fd:
fd.write(old_contents[:end_header+len(END_HEADER_TXT)])
for key, entry in enumerate(ENTRIES):
size = 2
wrap = False
y_loc = 0
results = []
for line in entry[1].splitlines():
if line.startswith('$'):
m_size = RE_SIZE.match(line)
m_color = RE_COLOR.match(line)
m_wrap = RE_WRAP.match(line)
if m_size:
size = int(m_size.group(1))
results.append(f'tft.setTextSize({size});')
elif m_color:
results.append(
f'tft.setTextColor({int(m_color.group(1))});')
elif m_wrap:
wrap = bool(int(m_wrap.group(1)))
else:
print(f'Entry {key} unknown modifier "{line}".')
exit(1)
else:
max_chars_per_line = math.floor(
SCREEN_SIZE.w / CHAR_SIZE[size].w)
if len(line) > max_chars_per_line:
if wrap:
while len(line) > max_chars_per_line:
results.append(
f'tft.println("{line[:max_chars_per_line]}");')
line = line[max_chars_per_line:].lstrip()
y_loc += CHAR_SIZE[size].h
else:
print(f'Entry {key} line "{line}" too long.')
exit(1)
if len(line) > 0:
y_loc += CHAR_SIZE[size].h
results.append(f'tft.println("{line}");')
if y_loc > SCREEN_SIZE.h:
print(
f'Entry {key} line "{line}" went past bottom of screen.')
exit(1)
result = indent('\n'.join(results), ' ')
fd.write(f'''\
static void PrintRoll{key}() {{
{result}
}}
''')
results = []
for key, entry in enumerate(ENTRIES):
results.append(f'''\
case {key}:
return "{entry[0]}";''')
cases = indent('\n'.join(results), ' ')
fd.write(f'''\
static const char* GetRollName(uint8_t key) {{
switch (key) {{
{cases}
}}
return "";
}}
''')
results = []
for key, entry in enumerate(ENTRIES):
results.append(f'''\
case {key}:
PrintRoll{key}();
return;''')
cases = indent('\n'.join(results), ' ')
fd.write(f'''\
static void PrintRollInfo(uint8_t key) {{
tft.setTextColor(TFT_WHITE);
tft.setCursor(0, 0);
tft.setTextSize(2);
switch (key) {{
{cases}
}}
tft.setTextColor(TFT_RED);
tft.setCursor(0, 60);
tft.println("Unknown");
}}
''')
fd.write(f'static constexpr size_t NUM_ROLL_INFOS = {len(ENTRIES)};\n')
main()