Esempio n. 1
0
    def get_stats(self) -> BaseStats:
        """Returns a dict of stats."""
        if self.character_data is None:
            raise Exception('You must call get_character() first.')
        if self.stats: return self.stats
        character = self.character_data

        profByLevel = floor(self.get_levels().total_level / 4 + 1.75)
        prof_bonus = self.get_stat('proficiency-bonus', base=int(profByLevel))

        stat_dict = {}
        for i, stat in enumerate(('strength', 'dexterity', 'constitution',
                                  'intelligence', 'wisdom', 'charisma')):
            base = next(s for s in character['stats']
                        if s['id'] == i + 1)['value']
            bonus = next(s for s in character['bonusStats']
                         if s['id'] == i + 1)['value'] or 0
            override = next(s for s in character['overrideStats']
                            if s['id'] == i + 1)['value']
            stat_dict[stat] = override or self.get_stat(f"{stat}-score",
                                                        base=base + bonus)

        stats = BaseStats(prof_bonus, **stat_dict)

        self.stats = stats
        return stats
Esempio n. 2
0
    def __init__(self, owner: str, upstream: str, active: bool,
                 sheet_type: str, import_version: int, name: str,
                 description: str, image: str, stats: dict, levels: dict,
                 attacks: list, skills: dict, resistances: dict, saves: dict,
                 ac: int, max_hp: int, hp: int, temp_hp: int, cvars: dict,
                 options: dict, overrides: dict, consumables: list,
                 death_saves: dict, spellbook: dict, live, race: str,
                 background: str, **kwargs):
        if kwargs:
            log.warning(f"Unused kwargs: {kwargs}")
        # sheet metadata
        self._owner = owner
        self._upstream = upstream
        self._active = active
        self._sheet_type = sheet_type
        self._import_version = import_version

        # main character info
        self.name = name
        self._description = description
        self._image = image
        self.stats = BaseStats.from_dict(stats)
        self.levels = Levels.from_dict(levels)
        self._attacks = [Attack.from_dict(atk) for atk in attacks]
        self.skills = Skills.from_dict(skills)
        self.resistances = Resistances.from_dict(resistances)
        self.saves = Saves.from_dict(saves)

        # hp/ac
        self.ac = ac
        self.max_hp = max_hp
        self._hp = hp
        self._temp_hp = temp_hp

        # customization
        self.cvars = cvars
        self.options = CharOptions.from_dict(options)
        self.overrides = ManualOverrides.from_dict(overrides)

        # ccs
        self.consumables = [
            CustomCounter.from_dict(self, cons) for cons in consumables
        ]
        self.death_saves = DeathSaves.from_dict(death_saves)

        # spellbook
        spellbook = Spellbook.from_dict(spellbook)
        super(Character, self).__init__(spellbook)

        # live sheet integrations
        self._live = live
        integration = INTEGRATION_MAP.get(live)
        if integration:
            self._live_integration = integration(self)
        else:
            self._live_integration = None

        # misc research things
        self.race = race
        self.background = background
Esempio n. 3
0
 def from_bestiary(cls, data):
     for key in ('traits', 'actions', 'reactions', 'legactions'):
         data[key] = [Trait(**t) for t in data.pop(key)]
     data['spellcasting'] = Spellbook.from_dict(data.pop('spellbook'))
     data['saves'] = Saves.from_dict(data['saves'])
     data['skills'] = Skills.from_dict(data['skills'])
     data['ability_scores'] = BaseStats.from_dict(data['ability_scores'])
     return cls(**data)
Esempio n. 4
0
    def from_data(cls, data):
        # print(f"Parsing {data['name']}")
        _type = parse_type(data['type'])
        alignment = parse_alignment(data['alignment'])
        speed = parse_speed(data['speed'])
        ac = data['ac']['ac']
        armortype = data['ac'].get('armortype') or None
        if not 'special' in data['hp']:
            hp = data['hp']['average']
            hitdice = data['hp']['formula']
        else:
            hp = 0
            hitdice = data['hp']['special']
        scores = BaseStats(0, data['str'] or 10, data['dex'] or 10, data['con'] or 10, data['int'] or 10,
                           data['wis'] or 10, data['cha'] or 10)
        if isinstance(data['cr'], dict):
            cr = data['cr']['cr']
        else:
            cr = data['cr']

        vuln = parse_resists(data['vulnerable']) if 'vulnerable' in data else None
        resist = parse_resists(data['resist']) if 'resist' in data else None
        immune = parse_resists(data['immune']) if 'immune' in data else None
        condition_immune = data.get('conditionImmune', []) if 'conditionImmune' in data else None

        raw_resists = {
            "vuln": parse_resists(data['vulnerable'], False) if 'vulnerable' in data else [],
            "resist": parse_resists(data['resist'], False) if 'resist' in data else [],
            "immune": parse_resists(data['immune'], False) if 'immune' in data else []
        }

        languages = data.get('languages', '').split(', ') if 'languages' in data else None

        traits = [Trait(t['name'], t['text']) for t in data.get('trait', [])]
        actions = [Trait(t['name'], t['text']) for t in data.get('action', [])]
        legactions = [Trait(t['name'], t['text']) for t in data.get('legendary', [])]
        reactions = [Trait(t['name'], t['text']) for t in data.get('reaction', [])]

        skills = Skills.default(scores)
        skills.update(data['skill'])

        saves = Saves.default(scores)
        saves.update(data['save'])

        source = data['source']
        proper = bool(data.get('isNamedCreature') or data.get('isNPC'))

        attacks = data.get('attacks', [])
        spellcasting = data.get('spellcasting', {})
        spells = [SpellbookSpell(s) for s in spellcasting.get('spells', [])]
        spellbook = Spellbook({}, {}, spells, spellcasting.get('dc'), spellcasting.get('attackBonus'),
                              spellcasting.get('casterLevel', 1))

        return cls(data['name'], parsesize(data['size']), _type, alignment, ac, armortype, hp, hitdice,
                   speed, scores, cr, xp_by_cr(cr), data['passive'], data.get('senses', ''),
                   vuln, resist, immune, condition_immune, saves, skills, languages, traits,
                   actions, reactions, legactions, 3, data.get('srd', False), source, attacks,
                   spellcasting=spellbook, page=data.get('page'), proper=proper, raw_resists=raw_resists)
Esempio n. 5
0
def migrate_monster(old_monster):
    def spaced_to_camel(spaced):
        return re.sub(r"\s+(\w)", lambda m: m.group(1).upper(), spaced.lower())

    for old_key in ('raw_saves', 'raw_skills'):
        if old_key in old_monster:
            del old_monster[old_key]

    if 'spellcasting' in old_monster and old_monster['spellcasting']:
        old_spellcasting = old_monster.pop('spellcasting')
        old_monster['spellbook'] = Spellbook({}, {}, [SpellbookSpell(s) for s in old_spellcasting['spells']],
                                             old_spellcasting['dc'], old_spellcasting['attackBonus'],
                                             old_spellcasting['casterLevel']).to_dict()
    else:
        old_monster['spellbook'] = Spellbook({}, {}, []).to_dict()


    base_stats = BaseStats(
        0, old_monster.pop('strength'), old_monster.pop('dexterity'), old_monster.pop('constitution'),
        old_monster.pop('intelligence'), old_monster.pop('wisdom'), old_monster.pop('charisma')
    )
    old_monster['ability_scores'] = base_stats.to_dict()

    old_saves = old_monster.pop('saves')
    saves = Saves.default(base_stats)
    save_updates = {}
    for save, value in old_saves.items():
        if value != saves[save]:
            save_updates[save] = value
    saves.update(save_updates)
    old_monster['saves'] = saves.to_dict()

    old_skills = old_monster.pop('skills')
    skills = Skills.default(base_stats)
    skill_updates = {}
    for skill, value in old_skills.items():
        name = spaced_to_camel(skill)
        if value != skills[name]:
            skill_updates[name] = value
    skills.update(skill_updates)
    old_monster['skills'] = skills.to_dict()

    new_monster = Monster.from_bestiary(old_monster)
    return new_monster
Esempio n. 6
0
    def get_stats(self):
        """Returns a dict of stats."""
        if self.character_data is None: raise Exception('You must call get_character() first.')
        character = self.character_data
        try:
            prof_bonus = int(character.cell("H14").value)
        except (TypeError, ValueError):
            raise MissingAttribute("Proficiency Bonus")

        index = 15
        stat_dict = {}
        for stat in ('strength', 'dexterity', 'constitution', 'intelligence', 'wisdom', 'charisma'):
            try:
                stat_dict[stat] = int(character.cell("C" + str(index)).value)
                index += 5
            except (TypeError, ValueError):
                raise MissingAttribute(stat)

        stats = BaseStats(prof_bonus, **stat_dict)
        return stats
Esempio n. 7
0
    def get_stats(self) -> BaseStats:
        if self.character_data is None:
            raise Exception('You must call get_character() first.')
        if self.stats:
            return self.stats
        self.get_levels()

        stat_dict = {
            'proficiencyBonus': int(self.calculate_stat('proficiencyBonus'))
        }

        for stat in ('strength', 'dexterity', 'constitution', 'wisdom',
                     'intelligence', 'charisma'):
            stat_dict[stat] = int(self.calculate_stat(stat))
            stat_dict[stat + 'Mod'] = int(stat_dict[stat]) // 2 - 5
        self.evaluator.names.update(stat_dict)

        stats = BaseStats(stat_dict['proficiencyBonus'], stat_dict['strength'],
                          stat_dict['dexterity'], stat_dict['constitution'],
                          stat_dict['intelligence'], stat_dict['wisdom'],
                          stat_dict['charisma'])

        self.stats = stats
        return stats
Esempio n. 8
0
    def from_critterdb(cls, data):
        ability_scores = BaseStats(0, data['stats']['abilityScores']['strength'] or 10,
                                   data['stats']['abilityScores']['dexterity'] or 10,
                                   data['stats']['abilityScores']['constitution'] or 10,
                                   data['stats']['abilityScores']['intelligence'] or 10,
                                   data['stats']['abilityScores']['wisdom'] or 10,
                                   data['stats']['abilityScores']['charisma'] or 10)
        cr = {0.125: '1/8', 0.25: '1/4', 0.5: '1/2'}.get(data['stats']['challengeRating'],
                                                         str(data['stats']['challengeRating']))
        num_hit_die = data['stats']['numHitDie']
        hit_die_size = data['stats']['hitDieSize']
        con_by_level = num_hit_die * ability_scores.get_mod('con')
        hp = floor(((hit_die_size + 1) / 2) * num_hit_die) + con_by_level
        hitdice = f"{num_hit_die}d{hit_die_size} + {con_by_level}"

        proficiency = data['stats']['proficiencyBonus']
        if proficiency is None:
            raise errors.ExternalImportError(f"Monster's proficiency bonus is nonexistent ({data['name']}).")

        skills = Skills.default(ability_scores)
        skill_updates = {}
        for skill in data['stats']['skills']:
            name = spaced_to_camel(skill['name'])
            if skill['proficient']:
                mod = skills[name].value + proficiency
            else:
                mod = skill.get('value')
            if mod is not None:
                skill_updates[name] = mod
        skills.update(skill_updates)

        saves = Saves.default(ability_scores)
        save_updates = {}
        for save in data['stats']['savingThrows']:
            name = save['ability'].lower() + 'Save'
            if save['proficient']:
                mod = saves.get(name).value + proficiency
            else:
                mod = save.get('value')
            if mod is not None:
                save_updates[name] = mod
        saves.update(save_updates)

        traits = parse_critterdb_traits(data, 'additionalAbilities')
        actions = parse_critterdb_traits(data, 'actions')
        reactions = parse_critterdb_traits(data, 'reactions')
        legactions = parse_critterdb_traits(data, 'legendaryActions')

        attacks = []
        for atk_src in (traits, actions, reactions, legactions):
            for trait in atk_src:
                attacks.extend(trait.attacks)

        resists = {
            "resist": data['stats']['damageResistances'],
            "immune": data['stats']['damageImmunities'],
            "vuln": data['stats']['damageVulnerabilities']
        }

        spellcasting = parse_critterdb_spellcasting(traits)

        return cls(data['name'], data['stats']['size'], data['stats']['race'], data['stats']['alignment'],
                   data['stats']['armorClass'], data['stats']['armorType'], hp, hitdice, data['stats']['speed'],
                   ability_scores, cr, data['stats']['experiencePoints'], None,
                   ', '.join(data['stats']['senses']), data['stats']['damageVulnerabilities'],
                   data['stats']['damageResistances'], data['stats']['damageImmunities'],
                   data['stats']['conditionImmunities'], saves, skills,
                   data['stats']['languages'], traits, actions, reactions, legactions,
                   data['stats']['legendaryActionsPerRound'], True, 'homebrew', attacks,
                   data['flavor']['nameIsProper'], data['flavor']['imageUrl'], raw_resists=resists,
                   spellcasting=spellcasting)