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 if self._stats is not None: return self._stats try: prof_bonus = int(character.value("H14")) 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.value("C" + str(index))) index += 5 except (TypeError, ValueError): raise MissingAttribute(stat) stats = BaseStats(prof_bonus, **stat_dict) self._stats = stats return stats
def get_stats(self): """Returns a dict of stats.""" if self.character is None: raise Exception('You must call get_character() first.') character = self.character stats = {"name": "", "image": "", "description": "", "strength": 10, "dexterity": 10, "constitution": 10, "wisdom": 10, "intelligence": 10, "charisma": 10, "strengthMod": 0, "dexterityMod": 0, "constitutionMod": 0, "wisdomMod": 0, "intelligenceMod": 0, "charismaMod": 0, "proficiencyBonus": 0} stats['name'] = character.cell("C6").value or "Unnamed" stats['description'] = "The Google sheet does not have a description field." try: stats['proficiencyBonus'] = int(character.cell("H14").value) except (TypeError, ValueError): raise MissingAttribute("Proficiency Bonus") stats['image'] = character.cell("C176").value index = 15 for stat in ('strength', 'dexterity', 'constitution', 'intelligence', 'wisdom', 'charisma'): try: stats[stat] = int(character.cell("C" + str(index)).value) stats[stat + 'Mod'] = int(character.cell("C" + str(index - 2)).value) index += 5 except (TypeError, ValueError): raise MissingAttribute(stat) return stats
def get_levels(self): if self.character_data is None: raise Exception('You must call get_character() first.') try: total_level = int(self.character_data.value("AL6")) self.total_level = total_level except ValueError: raise MissingAttribute("Character level") level_dict = {} if self.additional: for rownum in range(69, 79): # sheet2, C69:C78 namecell = f"C{rownum}" levelcell = f"N{rownum}" classname = self.additional.value(namecell) if classname: classname = re.sub( r'[.$]', '_', classname) # sentry-H7 - invalid class names classlevel = int(self.additional.value(levelcell)) level_dict[classname] = classlevel else: # classes should be top-aligned break levels = Levels(level_dict, total_level) return levels
def get_skills(self): """Returns a dict of all the character's skills.""" if self.character is None: raise Exception('You must call get_character() first.') character = self.character skillslist = ['I25', 'I26', 'I27', 'I28', 'I22', 'I19', 'I29', 'I18', 'I30', 'V12', 'I31', 'I20', 'I32', 'I33', 'I34', 'I35', 'I36', 'I37', 'I38', 'I39', 'I40', 'I41', 'I17', 'I42', 'I21', 'C13', 'C18', 'C23', 'C33', 'C28', 'C38'] skillsMap = ['acrobatics', 'animalHandling', 'arcana', 'athletics', 'charismaSave', 'constitutionSave', 'deception', 'dexteritySave', 'history', 'initiative', 'insight', 'intelligenceSave', 'intimidation', 'investigation', 'medicine', 'nature', 'perception', 'performance', 'persuasion', 'religion', 'sleightOfHand', 'stealth', 'strengthSave', 'survival', 'wisdomSave', 'strength', 'dexterity', 'constitution', 'wisdom', 'intelligence', 'charisma'] skills = {} for index, skill in enumerate(skillslist): try: skills[skillsMap[index]] = int(character.cell(skill).value) except (TypeError, ValueError): raise MissingAttribute(skillsMap[index]) return skills
def get_level(self): if self.character is None: raise Exception('You must call get_character() first.') character = self.character try: level = int(character.cell("AL6").value) except ValueError: raise MissingAttribute("Character level") return level
def _get_sheet(self): """Returns a dict with character sheet data.""" if self.character is None: raise Exception('You must call get_character() first.') character = self.character try: stats = self.get_stats() hp = int(character.cell("U16").value) armor = character.cell("R12").value attacks = self.get_attacks() skills = self.get_skills() level = self.get_level() stats['description'] = self.get_description() spellbook = self.get_spellbook() except ValueError: raise MissingAttribute("Max HP") except: raise saves = {} for key in skills: if 'Save' in key: saves[key] = skills[key] stat_vars = {} stat_vars.update(stats) stat_vars['level'] = int(level) stat_vars['hp'] = hp stat_vars['armor'] = int(armor) stat_vars.update(saves) sheet = {'type': 'google', 'version': 5, # v3: added stat cvars # v4: consumables # v5: spellbook 'stats': stats, 'levels': {'level': int(level)}, 'hp': hp, 'armor': int(armor), 'attacks': attacks, 'skills': skills, 'resist': [], 'immune': [], 'vuln': [], 'saves': saves, 'stat_cvars': stat_vars, 'consumables': {}, 'spellbook': spellbook} embed = self.get_embed(sheet) return {'embed': embed, 'sheet': sheet}
def get_skills_and_saves(self): if self.character_data is None: raise Exception('You must call get_character() first.') character = self.character_data skills = {} saves = {} is_joat = self.version == 2 and bool(character.cell("AR45").value) for cell, skill, advcell in SKILL_CELL_MAP: if isinstance(cell, int): advcell = f"F{cell}" profcell = f"H{cell}" cell = f"I{cell}" else: profcell = None try: value = int(character.cell(cell).value) except (TypeError, ValueError): raise MissingAttribute(skill) adv = None if self.version == 2 and advcell: advtype = character.cell(advcell).value if advtype in {'a', 'adv', 'advantage'}: adv = True elif advtype in {'d', 'dis', 'disadvantage'}: adv = False prof = 0 if "Save" not in skill and is_joat: prof = 0.5 if profcell: proftype = character.cell(profcell).value_unformatted if proftype == 'e': prof = 2 elif proftype and proftype != '0': prof = 1 skl_obj = Skill(value, prof, adv=adv) if "Save" in skill: saves[skill] = skl_obj else: skills[skill] = skl_obj skills = Skills(skills) saves = Saves(saves) return skills, saves
def get_levels(self): if self.character is None: raise Exception('You must call get_character() first.') levels = {} try: levels['level'] = int(self.character.cell("AL6").value) except ValueError: raise MissingAttribute("Character level") if self.additional: for rownum in range(69, 79): # sheet2, C69:C78 namecell = f"C{rownum}" levelcell = f"N{rownum}" classname = self.additional.cell(namecell).value if classname: classlevel = int(self.additional.cell(levelcell).value) levels[f"{classname}Level"] = classlevel else: # classes should be top-aligned break return levels
def get_stats(self): """Returns a dict of stats.""" if self.character is None: raise Exception('You must call get_character() first.') character = self.character stats = { "name": "", "image": "", "description": "", "strength": 10, "dexterity": 10, "constitution": 10, "wisdom": 10, "intelligence": 10, "charisma": 10, "strengthMod": 0, "dexterityMod": 0, "constitutionMod": 0, "wisdomMod": 0, "intelligenceMod": 0, "charismaMod": 0, "proficiencyBonus": 0 } stats['name'] = character.get('CharacterName', "No name") or "Unnamed" stats[ 'description'] = "Description is not supported with the PDF loader." stats['proficiencyBonus'] = int(character.get('ProfBonus')) for stat in ('strength', 'dexterity', 'constitution', 'wisdom', 'intelligence', 'charisma'): try: stats[stat] = int(character.get(stat[:3].upper() + 'score')) stats[stat + 'Mod'] = int( character.get(stat[:3].upper() + 'bonus')) except TypeError: raise MissingAttribute(stat) return stats
def get_skills(self): """Returns a dict of all the character's skills.""" if self.character is None: raise Exception('You must call get_character() first.') character = self.character skills = {} skill_effects = {} for cell, skill, advcell in SKILL_MAP: try: skills[skill] = int(character.cell(cell).value) except (TypeError, ValueError): raise MissingAttribute(skill) if self.version == 2 and advcell: advtype = character.cell(advcell).value if advtype in ('a', 'adv'): skill_effects[skill] = 'adv' elif advtype in ('d', 'dis'): skill_effects[skill] = 'dis' skills = {k: v for k, v in sorted(skills.items())} return skills, skill_effects
def get_hp(self): try: return int(self.character_data.cell("U16").value) except (TypeError, ValueError): raise MissingAttribute("Max HP")
def get_ac(self): try: return int(self.character_data.cell("R12").value) except (TypeError, ValueError): raise MissingAttribute("AC")
def get_skills_and_saves(self): if self.character_data is None: raise Exception('You must call get_character() first.') character = self.character_data skills = {} saves = {} is_joat = False all_check_bonus = 0 if self.version == (2, 0): is_joat = bool(character.value("AR45")) all_check_bonus = int(character.value("AQ26") or 0) elif self.version == (2, 1): is_joat = bool(character.value("AQ59")) all_check_bonus = int(character.value("AR58")) joat_bonus = int(is_joat and self.get_stats().prof_bonus // 2) # calculate str, dex, con, etc checks for cell, skill, advcell in BASE_ABILITY_CHECKS: try: # add bonuses manually since the cell does not include them value = int( character.value(cell)) + all_check_bonus + joat_bonus except (TypeError, ValueError): raise MissingAttribute(skill) prof = 0 if is_joat: prof = 0.5 skl_obj = Skill(value, prof) skills[skill] = skl_obj # read the value of the rest of the skills for cell, skill, advcell in SKILL_CELL_MAP: if isinstance(cell, int): advcell = f"F{cell}" profcell = f"H{cell}" cell = f"I{cell}" else: profcell = None try: value = int(character.value(cell)) except (TypeError, ValueError): raise MissingAttribute(skill) adv = None if self.version >= (2, 0) and advcell: advtype = character.unformatted_value(advcell) if advtype in {'a', 'adv', 'advantage'}: adv = True elif advtype in {'d', 'dis', 'disadvantage'}: adv = False prof = 0 if "Save" not in skill and is_joat: prof = 0.5 if profcell: proftype = character.unformatted_value(profcell) if proftype == 'e': prof = 2 elif proftype and proftype != '0': prof = 1 skl_obj = Skill(value, prof, adv=adv) if "Save" in skill: saves[skill] = skl_obj else: skills[skill] = skl_obj skills = Skills(skills) saves = Saves(saves) return skills, saves
def get_hp(self): try: return int(self.character_data.unformatted_value("U16")) except (TypeError, ValueError): raise MissingAttribute("Max HP")
def _get_sheet(self): """Returns a dict with character sheet data.""" if self.character is None: raise Exception('You must call get_character() first.') character = self.character try: stats = self.get_stats() hp = int(character.cell("U16").value) except ValueError: raise MissingAttribute("Max HP") armor = character.cell("R12").value attacks = self.get_attacks() skills, skill_effects = self.get_skills() levels = self.get_levels() temp_resist = self.get_resistances() resistances = temp_resist['resist'] immunities = temp_resist['immune'] spellbook = self.get_spellbook() saves = {} for key in skills.copy(): if 'Save' in key: saves[key] = skills.pop(key) stat_vars = {} stat_vars.update(stats) stat_vars.update(levels) stat_vars['hp'] = hp stat_vars['armor'] = int(armor) stat_vars.update(saves) # v3: added stat cvars # v4: consumables # v5: spellbook # v6: v2.0 support (level vars, resistances, extra spells/attacks) # v7: race/background (experimental) # v8: skill/save effects sheet = { 'type': 'google', 'version': 7, 'stats': stats, 'levels': levels, 'hp': hp, 'armor': int(armor), 'attacks': attacks, 'skills': skills, 'resist': resistances, 'immune': immunities, 'vuln': [], 'saves': saves, 'stat_cvars': stat_vars, 'skill_effects': skill_effects, 'consumables': {}, 'spellbook': spellbook, 'race': self.get_race(), 'background': self.get_background() } return {'embed': None, 'sheet': sheet}