Exemplo n.º 1
0
def parse_spellcasting(monster):
    if 'trait' not in monster:
        monster['trait'] = []
    known_spells = []
    usual_dc = (0, 0)  # dc, number of spells using dc
    usual_sab = (0, 0)  # same thing
    caster_level = 1
    for cast_type in monster['spellcasting']:
        trait = {
            'name': cast_type['name'],
            'text': render(cast_type['headerEntries'])
        }
        type_dc = re.search(r'\(spell save DC (\d+)',
                            '\n'.join(cast_type['headerEntries']))
        type_sab = re.search(r'{@?hit (\d+)}',
                             '\n'.join(cast_type['headerEntries']))
        type_caster_level = re.search(r'(\d+)[stndrh]{2}-level',
                                      '\n'.join(cast_type['headerEntries']))
        type_spells = []
        if 'will' in cast_type:
            type_spells.extend(extract_spell(s) for s in cast_type['will'])
            spells = render(', '.join(cast_type['will']))
            trait['text'] += f"\nAt will: {spells}"
        if 'daily' in cast_type:
            for times_per_day, spells in cast_type['daily'].items():
                each = ' each' if times_per_day.endswith('e') else ''
                times_per_day = times_per_day.rstrip('e')
                type_spells.extend(extract_spell(s) for s in spells)
                spells = render(', '.join(spells))
                trait['text'] += f"\n{times_per_day}/day{each}: {spells}"
        if 'spells' in cast_type:
            for level, level_data in cast_type['spells'].items():
                spells = level_data['spells']
                level_text = get_spell_level(level)
                slots = f"{level_data.get('slots', 'unknown')} slots" if level != '0' else "at will"
                type_spells.extend(extract_spell(s) for s in spells)
                spells = render(', '.join(spells))
                trait['text'] += f"\n{level_text} ({slots}): {spells}"
        trait['text'] = render(trait['text'])
        monster['trait'].append(trait)
        known_spells.extend(type_spells)
        if type_dc and (len(type_spells) > usual_dc[1] or not usual_dc[0]):
            usual_dc = (int(type_dc.group(1)), len(type_spells))
        if type_sab and (len(type_spells) > usual_sab[1] or not usual_sab[0]):
            usual_sab = (int(type_sab.group(1)), len(type_spells))
        if type_caster_level:
            caster_level = int(type_caster_level.group(1))
    dc = usual_dc[0]
    sab = usual_sab[0]
    monster['spellcasting'] = {
        'spells': known_spells,
        'dc': dc,
        'attackBonus': sab,
        'casterLevel': caster_level
    }  # overwrite old
    log.info(
        f"Lvl {caster_level}; DC: {dc}; SAB: {sab}; Spells: {known_spells}")
Exemplo n.º 2
0
def object_actions(objects):
    for object in objects:
        if 'actionEntries' in object:
            object['entries'].append('__Actions__')
            object['entries'].append(render(object['actionEntries']))
            del object['actionEntries']  # also unparsed
    return objects
Exemplo n.º 3
0
def parse_invocations():
    out = []
    optfeats = get_data('optionalfeatures.json')['optionalfeature']
    invocs = [i for i in optfeats if i['featureType'] == 'EI']

    for invoc in invocs:
        log.info(f"Parsing invocation {invoc['name']}")
        text = render(invoc['entries'])
        if 'prerequisite' in invoc:
            prereqs = []
            for prereq in invoc['prerequisite']:
                if prereq['type'] == 'prereqPact':
                    prereqs.append(f"Pact of the {prereq['entry']}")
                elif prereq['type'] == 'prereqPatron':
                    prereqs.append(f"Patron: {prereq['entry']}")
                elif prereq['type'] == 'prereqLevel':
                    prereqs.append(f"Level {prereq['level']}")
                elif prereq['type'] == 'prereqSpell':
                    prereqs.append(f"*{', '.join(prereq['entries'])}* spell")
                else:
                    log.warning(f"Unknown prereq type: {prereq['type']}")
            text = f"*Prerequisite: {', '.join(prereqs)}*\n{text}"
        inv = {
            'name': f"Warlock: Eldritch Invocation: {invoc['name']}",
            'text': text,
            'srd': invoc['source'] == 'PHB'
        }
        out.append(inv)
    return out
Exemplo n.º 4
0
def parse(data):
    processed = []
    for spell in data:
        log.info(f"Parsing {spell['name']}...")
        parsetime(spell)
        parserange(spell)
        parsecomponents(spell)
        parseduration(spell)
        parseclasses(spell)
        srdfilter(spell)

        ritual = spell.get('meta', {}).get('ritual', False)
        desc = render(spell['entries'])
        if 'entriesHigherLevel' in spell:
            higherlevels = render(spell['entriesHigherLevel']) \
                .replace("**At Higher Levels**: ", "")
        else:
            higherlevels = None

        if NEW_AUTOMATION:
            automation = get_automation(spell)
        else:
            automation = get_automation_from_old(spell)

        newspell = {
            "name": spell['name'],
            "level": spell['level'],
            "school": spell['school'],
            "casttime": spell['time'],
            "range": spell['range'],
            "components": spell['components'],
            "duration": spell['duration'],
            "description": desc,
            "classes": spell['classes'],
            "subclasses": spell['subclasses'],
            "ritual": ritual,
            "higherlevels": higherlevels,
            "source": spell['source'],
            "page": spell.get('page', '?'),
            "concentration": spell['concentration'],
            "automation": automation,
            "srd": spell['srd']
        }
        processed.append(recursive_tag(newspell))

    processed = ensure_ml_order(processed)
    return processed
Exemplo n.º 5
0
def parse_traits(raw):
    traits = []
    for entry in raw['entries']:
        if entry['type'] == 'list':
            for item in entry['items']:
                trait = {
                    'name': item['name'],
                    'text': render(item.get('entry') or item.get('entries'))
                }
                traits.append(trait)
        elif entry['type'] in ('entries', 'section'):
            trait = {'name': entry['name'], 'text': render(entry['entries'])}
            traits.append(trait)
        else:
            log.warning(f"Unknown entry: {entry}")
            continue
    return traits
Exemplo n.º 6
0
def render_variant_eqs(entries, inherits):
    for i, entry in enumerate(entries):
        if isinstance(entry, str):
            entries[i] = re.sub(r"{=(\w+)}",
                                lambda m: inherits.get(m.group(1), m.group(0)),
                                entry)
        else:
            entries[i] = render(entry)
    return entries
Exemplo n.º 7
0
def parse_ac(data):
    for monster in data:
        log.info(f"Parsing {monster['name']} AC")
        if isinstance(monster['ac'][0], int):
            monster['ac'] = {'ac': int(monster['ac'][0])}
        elif isinstance(monster['ac'][0], dict):
            monster['ac'] = {'ac': int(monster['ac'][0]['ac']),
                             'armortype': render(monster['ac'][0].get('from', []), join_char=', ')}
        else:
            log.warning(f"Unknown AC type: {monster['ac']}")
            raise Exception
    return data
Exemplo n.º 8
0
def monster_render(data):
    for monster in data:
        log.info(f"Rendering {monster['name']}")
        for t in ('trait', 'action', 'reaction', 'legendary'):
            log.info(f"  Rendering {t}s")
            if t in monster:
                temp = []
                for entry in monster[t]:
                    text = render(entry['entries'])
                    temp.append({'name': entry.get('name', ''), 'text': text})
                monster[t] = temp

        if 'spellcasting' in monster:
            parse_spellcasting(monster)
    return data
Exemplo n.º 9
0
def prerender(data):
    for item in data:
        if 'entries' in item:
            item['desc'] = render(item['entries'])
            del item['entries']
        else:
            item['desc'] = ""

        if 'additionalEntries' in item:
            item['desc'] += f"\n\n{render(item['additionalEntries'])}"
        item['desc'] = item['desc'].strip()

        for k, v in item.items():
            item[k] = recursive_tag(v)
    return data
Exemplo n.º 10
0
def prerender(data):
    out = []
    for feat in data:
        log.debug(feat['name'])
        desc = render(feat['entries'])
        prereq = parse_prereq(feat)
        ability = parse_ability(feat)

        new_feat = {
            "name": feat['name'],
            "prerequisite": prereq,
            "source": feat['source'],
            "page": feat['page'],
            "desc": desc,
            "ability": ability
        }
        out.append(new_feat)
    return out
Exemplo n.º 11
0
def parse_classfeats(data):
    out = []
    for _class in data:
        log.info(f"Parsing classfeats for class {_class['name']}...")
        for level in _class.get('classFeatures', []):
            for feature in level:
                fe = {
                    'name': f"{_class['name']}: {feature['name']}",
                    'text': render(feature['entries']),
                    'srd': _class['srd']
                }
                log.info(f"Found feature: {fe['name']}")
                out.append(fe)
                options = [
                    e for e in feature['entries']
                    if isinstance(e, dict) and e.get('type') == 'options'
                ]
                for option in options:
                    for opt_entry in option.get('entries', []):
                        fe = {
                            'name':
                            f"{_class['name']}: {feature['name']}: {_resolve_name(opt_entry)}",
                            'text': f"{render(opt_entry['entries'])}",
                            'srd': _class['srd']
                        }
                        log.info(f"Found option: {fe['name']}")
                        out.append(fe)
                subentries = [
                    e for e in feature['entries']
                    if isinstance(e, dict) and e.get('type') == 'entries'
                ]
                for opt_entry in subentries:
                    fe = {
                        'name':
                        f"{_class['name']}: {feature['name']}: {_resolve_name(opt_entry)}",
                        'text': f"{render(opt_entry['entries'])}",
                        'srd': _class['srd']
                    }
                    log.info(f"Found subentry: {fe['name']}")
                    out.append(fe)
        for subclass in _class.get('subclasses', []):
            log.info(f"Parsing classfeats for subclass {subclass['name']}...")
            for level in subclass.get('subclassFeatures', []):
                for feature in level:
                    options = [
                        f for f in feature.get('entries', [])
                        if isinstance(f, dict) and f['type'] == 'options'
                    ]  # battlemaster only
                    for option in options:
                        for opt_entry in option.get('entries', []):
                            fe = {
                                'name': f"{_class['name']}: {option['name']}: "
                                f"{_resolve_name(opt_entry)}",
                                'text': render(opt_entry['entries']),
                                'srd': subclass.get('srd', False)
                            }
                            log.info(f"Found option: {fe['name']}")
                            out.append(fe)
                    for entry in feature.get('entries', []):
                        if not isinstance(entry, dict): continue
                        if not entry.get('type') == 'entries': continue
                        fe = {
                            'name':
                            f"{_class['name']}: {subclass['name']}: {entry['name']}",
                            'text': render(entry['entries']),
                            'srd': subclass.get('srd', False)
                        }
                        log.info(f"Found feature: {fe['name']}")
                        out.append(fe)
                        options = [
                            e for e in entry['entries'] if isinstance(e, dict)
                            and e.get('type') == 'options'
                        ]
                        for option in options:
                            for opt_entry in option.get('entries', []):
                                fe = {
                                    'name':
                                    f"{_class['name']}: {subclass['name']}: {entry['name']}: "
                                    f"{_resolve_name(opt_entry)}",
                                    'text':
                                    render(opt_entry['entries']),
                                    'srd':
                                    subclass.get('srd', False)
                                }
                                log.info(f"Found option: {fe['name']}")
                                out.append(fe)
                        subentries = [
                            e for e in entry['entries'] if isinstance(e, dict)
                            and e.get('type') == 'entries'
                        ]
                        for opt_entry in subentries:
                            fe = {
                                'name':
                                f"{_class['name']}: {subclass['name']}: {entry['name']}: "
                                f"{_resolve_name(opt_entry)}",
                                'text':
                                f"{render(opt_entry['entries'])}",
                                'srd':
                                _class['srd']
                            }
                            log.info(f"Found subentry: {fe['name']}")
                            out.append(fe)
    return out