diff --git a/dnd5etools/scripts/spell_templates.py b/dnd5etools/scripts/spell_templates.py index 723a006..cb4b1e0 100644 --- a/dnd5etools/scripts/spell_templates.py +++ b/dnd5etools/scripts/spell_templates.py @@ -1,14 +1,31 @@ +import collections import pprint import re +import typing import dnd5etools.db.spells import dnd5etools.scripts.argparse -area_size_re = re.compile(r"\d+-foot") -sphere_size_re = re.compile(r"(?P\d+)-foot-radius sphere") -cylinder_size_re = re.compile(r"(?P\d+)-foot-radius, \d+-foot-high cylinder") -cube_size_re = re.compile(r"(?P\d+)-foot cube") -cone_size_re = re.compile(r"(?P\d+)-foot cone") +area_size_re = re.compile(r"\d+-foot", flags=re.IGNORECASE) +light_size_re = re.compile(r"(light|light\|\w+\}) in a \d+-foot", flags=re.IGNORECASE) +circle_size_re = re.compile( + r"(?P\d+)-foot-radius[ -](sphere|circle)", flags=re.IGNORECASE +) +radius_size_re = re.compile(r"(?P\d+)-foot radius", flags=re.IGNORECASE) +circle_diam_size_re = re.compile( + r"(?P\d+)-foot-diameter[ -](sphere|circle)", flags=re.IGNORECASE +) +cylinder_size_re = re.compile( + r"(?P\d+)-foot-radius, \d+-foot[ -](high|tall) cylinder", flags=re.IGNORECASE +) +alt_cylinder_size_re = re.compile( + r"\d+-foot[ -](high|tall), (?P\d+)-foot-radius cylinder", flags=re.IGNORECASE +) +square_size_re = re.compile(r"(?P\d+)-foot[ -](cube|square)", flags=re.IGNORECASE) +cone_size_re = re.compile(r"(?P\d+)-foot cone", flags=re.IGNORECASE) +line_size_re = re.compile( + r"\d+-foot-wide, (?P\d+)-foot[ -]long line", flags=re.IGNORECASE +) def main(): @@ -23,38 +40,63 @@ def main(): all_template_spells = [] for code in codes: all_template_spells += filter(is_template_spell, db.get_spell_list(code).spells) - # pprint.pprint(all_template_spells) - spheres = set() - cylinders = set() - cubes = set() - cones = set() + num_matches = 0 + circles = collections.defaultdict(list) + squares = collections.defaultdict(list) + cones = collections.defaultdict(list) + lines = collections.defaultdict(list) for spell in all_template_spells: found = False for search_re, dest in [ - (sphere_size_re, spheres), - (cylinder_size_re, cylinders), - (cube_size_re, cubes), + (line_size_re, lines), + (circle_size_re, circles), + (cylinder_size_re, circles), + (circle_diam_size_re, circles), + (alt_cylinder_size_re, circles), + (square_size_re, squares), (cone_size_re, cones), + (radius_size_re, circles), ]: m = search_re.search(spell.description) if m is not None: - dest.add(m.group("size")) + size = int(m.group("size")) + if search_re not in ( + cone_size_re, + square_size_re, + circle_diam_size_re, + line_size_re, + ): + size *= 2 + dest[size].append(spell.name) found = True + num_matches += 1 break - if not found: - print(spell) - print("spheres", spheres) - print("cylinders", cylinders) - print("cubes", cubes) - print("cones", cones) + print(f"{num_matches} matched from {len(all_template_spells)}") + print("circles/diameter") + print_sizes(circles, " ") + print("squares/size") + print_sizes(squares, " ") + print("cones/size") + print_sizes(cones, " ") + print("lines/length") + print_sizes(lines, " ") def is_template_spell(spell: dnd5etools.db.spells.Spell) -> bool: + num_area_sizes = 0 + for _ in area_size_re.finditer(spell.description): + num_area_sizes += 1 + num_light_sizes = 0 + for _ in light_size_re.finditer(spell.description): + num_light_sizes += 1 return ( len(spell.area_type) > 0 + and not "Wall" in spell.name and has_timed_duration(spell) - and spell.range.type != dnd5etools.db.spells.SpellRangeType.Emanation - and area_size_re.search(spell.description) is not None + and spell.range.type != dnd5etools.db.spells.SpellRangeType.Emanation + and spell.range.distance_type != "self" + and num_area_sizes > 0 + and num_light_sizes < num_area_sizes ) @@ -63,3 +105,9 @@ def has_timed_duration(spell: dnd5etools.db.spells.Spell) -> bool: if sd.type != dnd5etools.db.spells.DurationType.Instant: return True return False + + +def print_sizes(sizes_dict: typing.Mapping[int, int], indent: str) -> None: + for s in sorted(sizes_dict.items(), key=lambda kv: len(kv[1]), reverse=True): + print(f"{indent} {s[0]}ft => {len(s[1])}") + print(f"{indent}{indent}{s[1]}")