import pprint import re 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") def main(): args = dnd5etools.scripts.argparse.build_argument_parser( "Summarizes spell templates", ).parse_args() db = dnd5etools.db.spells.SpellsDb(args.data_dir) if args.source_code is None: codes = list(db.db_index.source_index.keys()) else: codes = args.source_code 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() 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), (cone_size_re, cones), ]: m = search_re.search(spell.description) if m is not None: dest.add(m.group("size")) found = True break if not found: print(spell) print("spheres", spheres) print("cylinders", cylinders) print("cubes", cubes) print("cones", cones) def is_template_spell(spell: dnd5etools.db.spells.Spell) -> bool: return ( len(spell.area_type) > 0 and has_timed_duration(spell) and area_size_re.search(spell.description) is not None ) def has_timed_duration(spell: dnd5etools.db.spells.Spell) -> bool: for sd in spell.duration: if sd.type != dnd5etools.db.spells.DurationType.Instant: return True return False