Example #1
0
def parse_critterdb_traits(data, key):
    traits = []
    attacks = []
    for trait in data['stats'][key]:
        name = trait['name']
        raw = trait['description']

        overrides = list(AVRAE_ATTACK_OVERRIDES_RE.finditer(raw))
        raw_atks = list(ATTACK_RE.finditer(raw))
        raw_damage = list(JUST_DAMAGE_RE.finditer(raw))

        filtered = AVRAE_ATTACK_OVERRIDES_RE.sub('', raw)
        desc = markdownify(filtered).strip()

        if overrides:
            for override in overrides:
                if override.group('simple'):
                    attacks.append(
                        Attack.from_dict({
                            'name': override.group(2) or name,
                            'attackBonus': override.group(3) or None,
                            'damage': override.group(4) or None,
                            'details': desc
                        }))
                elif (freeform_override := override.group('freeform')):
                    try:
                        attack_yaml = yaml.safe_load(freeform_override)
                    except yaml.YAMLError:
                        raise ExternalImportError(
                            f"Monster had an invalid automation YAML ({data['name']}: {name})"
                        )
                    if not isinstance(attack_yaml, list):
                        attack_yaml = [attack_yaml]
                    for atk in attack_yaml:
                        if isinstance(atk, dict):
                            atk['name'] = atk_name = atk.get('name') or name
                            try:
                                attacks.append(Attack.from_dict(atk))
                            except Exception:
                                raise ExternalImportError(
                                    f"An automation YAML contained an invalid attack ({data['name']}: {atk_name})"
                                )
                        else:
                            raise ExternalImportError(
                                f"An automation YAML contained an invalid attack ({data['name']}: {name})"
                            )
                # else: empty override, so skip this attack.
        elif raw_atks:
            for atk in raw_atks:
                attack_bonus = atk.group('attackBonus').lstrip('+')

                # Bonus damage
                bonus = ""
                if (bonus_damage_type := atk.group('damageTypeBonus')) and \
                        (bonus_damage := atk.group('damageBonusInt') or atk.group('damageBonusDice')):
Example #2
0
                attack_bonus = atk.group('attackBonus').lstrip('+')

                # Bonus damage
                bonus = ""
                if (bonus_damage_type := atk.group('damageTypeBonus')) and \
                        (bonus_damage := atk.group('damageBonusInt') or atk.group('damageBonusDice')):
                    bonus = f" + {bonus_damage} [{bonus_damage_type}]"

                # Versatile Attacks
                if (vers_damage_type := atk.group('damageTypeVers')) and \
                        (verse_damage := atk.group('damageIntVers') or atk.group('damageDiceVers')):
                    damage = f"{verse_damage} [{vers_damage_type}]" + bonus
                    attacks.append(
                        Attack.from_dict({
                            'name': f"2 Handed {name}",
                            'attackBonus': attack_bonus,
                            'damage': damage,
                            'details': desc
                        }))

                # Ranged Attacks
                if (ranged_damage_type := atk.group('damageTypeRanged')) and \
                        (ranged_damage := atk.group('damageRangedInt') or atk.group('damageRangedDice')):  # ranged
                    damage = f"{ranged_damage}[{ranged_damage_type}]" + bonus
                    attacks.append(
                        Attack.from_dict({
                            'name': f"Ranged {name}",
                            'attackBonus': attack_bonus,
                            'damage': damage,
                            'details': desc
                        }))