def _get_saves(self) -> Saves: out = {} for stat_key, save_key in zip(constants.STAT_ABBREVIATIONS, constants.SAVE_NAMES): out[save_key] = Skill(self.character_data['stats'][stat_key]['save'], prof=1 if self.character_data['stats'][stat_key]['saveProficiency'] else 0) return Saves(out)
def get_skills_and_saves(self) -> (Skills, Saves): if self.character_data is None: raise Exception('You must call get_character() first.') character = self.character_data stats = self.get_stats() NAME_SET = set(SKILL_NAMES + SAVE_NAMES) ADV_INT_MAP = {-1: False, 0: None, 1: True} profs = {} effects = collections.defaultdict(lambda: 0) # calculate profs for prof in character.get('proficiencies', []): if prof.get('enabled', False) and not prof.get('removed', False): profs[prof.get('name')] = prof.get('value') \ if prof.get('value') > profs.get(prof.get('name', 'None'), 0) \ else profs[prof.get('name')] # and effects for effect in self.character_data.get('effects', []): if effect.get('stat') in NAME_SET \ and effect.get('enabled', True) \ and not effect.get('removed', False): statname = effect.get('stat') if effect.get('operation') == 'disadvantage': effects[statname] = max(-1, effects[statname] - 1) if effect.get('operation') == 'advantage': effects[statname] = min(1, effects[statname] + 1) # assign skills skills = {} for skill in SKILL_NAMES: prof_mult = profs.get(skill, 0) base_val = floor(stats.get_mod(SKILL_MAP[skill]) + stats.prof_bonus * prof_mult) adv = ADV_INT_MAP.get(effects.get(skill)) if skill not in STAT_NAMES: value = int(self.calculate_stat(skill, base=base_val)) else: value = base_val skills[skill] = Skill( value, prof=prof_mult, adv=adv ) # and saves saves = {} for save in SAVE_NAMES: prof_mult = profs.get(save, 0) base_val = floor(stats.get_mod(SKILL_MAP[save]) + stats.prof_bonus * prof_mult) adv = ADV_INT_MAP.get(effects.get(save)) saves[save] = Skill( int(self.calculate_stat(save, base=base_val)), prof=prof_mult, adv=adv ) return Skills(skills), Saves(saves)
def _get_saves(self) -> Saves: out = {} for stat_key, save_key in zip(constants.STAT_ABBREVIATIONS, constants.SAVE_NAMES): stat_data = self.character_data['stats'][stat_key] adv_type = derive_adv(stat_data['saveAdv'], stat_data['saveDis']) out[save_key] = Skill( stat_data['save'], prof=1 if stat_data['saveProficiency'] else 0, adv=adv_type) return Saves(out)
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_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_skills_and_saves(self): """Returns a dict of all the character's skills.""" if self.character_data is None: raise Exception('You must call get_character() first.') stats = self.get_stats() profBonus = stats.prof_bonus profs = dict() bonuses = dict() advantages = collections.defaultdict(lambda: []) for mod in self.modifiers(): mod['subType'] = mod['subType'].replace("-saving-throws", "Save") if mod['type'] == 'half-proficiency': profs[mod['subType']] = max(profs.get(mod['subType'], 0), 0.5) elif mod['type'] == 'proficiency': profs[mod['subType']] = max(profs.get(mod['subType'], 0), 1) elif mod['type'] == 'expertise': profs[mod['subType']] = 2 elif mod['type'] == 'bonus': if not mod['isGranted']: continue if mod['statId'] is not None: bonuses[mod['subType']] = bonuses.get( mod['subType'], 0) + self.stat_from_id(mod['statId']) else: bonuses[mod['subType']] = bonuses.get( mod['subType'], 0) + (mod['value'] or 0) elif mod['type'] == 'advantage' and not mod[ 'restriction']: # unconditional adv advantages[mod['subType']].append(True) elif mod['type'] == 'disadvantage' and not mod[ 'restriction']: # unconditional dis advantages[mod['subType']].append(False) profs['animalHandling'] = profs.get('animal-handling', 0) profs['sleightOfHand'] = profs.get('sleight-of-hand', 0) advantages['animalHandling'] = advantages['animal-handling'] advantages['sleightOfHand'] = advantages['sleight-of-hand'] def _simplify_adv(adv_list): adv_set = set(adv_list) if len(adv_set) == 1: return adv_set.pop() return None skills = {} for skill in SKILL_NAMES: # add proficiency and bonuses to skills relevantprof = profs.get(skill, 0) relevantbonus = bonuses.get(skill, 0) relevantadv = _simplify_adv(advantages[skill]) if 'ability-checks' in profs and skill != 'initiative': relevantprof = max(relevantprof, profs['ability-checks']) if 'ability-checks' in bonuses and skill != 'initiative': relevantbonus += bonuses['ability-checks'] skills[skill] = Skill(floor( stats.get_mod(SKILL_MAP[skill]) + (profBonus * relevantprof) + relevantbonus), relevantprof, relevantbonus, adv=relevantadv) # saves saves = {} for save in SAVE_NAMES: # add proficiency and bonuses to skills relevantprof = profs.get(save, 0) relevantbonus = bonuses.get(save, 0) relevantadv = _simplify_adv(advantages[save]) if 'saving-throws' in profs: relevantprof = max(relevantprof, profs['saving-throws']) if 'saving-throws' in bonuses: relevantbonus += bonuses['saving-throws'] saves[save] = Skill(floor( stats.get_mod(SKILL_MAP[save]) + (profBonus * relevantprof) + relevantbonus), relevantprof, relevantbonus, adv=relevantadv) # values ignored_ids = set() for charval in self.character_data['characterValues']: if charval['value'] is None: continue if charval['typeId'] == 39: # misc saving throw bonus save_id = SAVE_NAMES[charval['valueId'] - 1] save_bonus = charval['value'] saves[save_id].value += save_bonus saves[save_id].bonus += save_bonus elif charval['valueId'] in HOUSERULE_SKILL_MAP and charval[ 'valueId'] not in ignored_ids: skill_name = HOUSERULE_SKILL_MAP[charval['valueId']] if charval['typeId'] == 23: # override skills[skill_name] = Skill(charval['value']) ignored_ids.add( charval['valueId'] ) # this must be the final value so we stop looking elif charval['typeId'] in { 24, 25 }: # PROBABLY skill magic/misc bonus skills[skill_name].value += charval['value'] skills[skill_name].bonus += charval['value'] elif charval['typeId'] == 26: # proficiency stuff relevantprof = profs.get(skill_name, 0) skills[skill_name].value -= relevantprof * profBonus if charval[ 'value'] == 0: # no prof, don't need to do anything skills[skill_name].prof = 0 elif charval['value'] == 1: # half prof, round down skills[skill_name].value += profBonus // 2 skills[skill_name].prof = 0.5 elif charval['value'] == 2: # half, round up skills[skill_name].value += ceil(profBonus / 2) skills[skill_name].prof = 0.5 elif charval['value'] == 3: # full skills[skill_name].value += profBonus skills[skill_name].prof = 1 elif charval['value'] == 4: # double skills[skill_name].value += profBonus * 2 skills[skill_name].prof = 2 skills = Skills(skills) saves = Saves(saves) return skills, saves
def get_skills_and_saves(self): """Returns a dict of all the character's skills.""" if self.character_data is None: raise Exception('You must call get_character() first.') character = self.character_data stats = self.get_stats() profBonus = stats.prof_bonus profs = dict() bonuses = dict() for modtype in character['modifiers'].values( ): # calculate proficiencies in all skills for mod in modtype: mod['subType'] = mod['subType'].replace( "-saving-throws", "Save") if mod['type'] == 'half-proficiency': profs[mod['subType']] = max(profs.get(mod['subType'], 0), 0.5) elif mod['type'] == 'proficiency': profs[mod['subType']] = max(profs.get(mod['subType'], 0), 1) elif mod['type'] == 'expertise': profs[mod['subType']] = 2 elif mod['type'] == 'bonus': if not mod['isGranted']: continue if mod['statId'] is not None: bonuses[mod['subType']] = bonuses.get( mod['subType'], 0) + self.stat_from_id( mod['statId']) else: bonuses[mod['subType']] = bonuses.get( mod['subType'], 0) + (mod['value'] or 0) profs['animalHandling'] = profs.get('animal-handling', 0) profs['sleightOfHand'] = profs.get('sleight-of-hand', 0) skills = {} for skill in SKILL_NAMES: # add proficiency and bonuses to skills relevantprof = profs.get(skill, 0) relevantbonus = bonuses.get(skill, 0) if 'ability-checks' in profs: relevantprof = max(relevantprof, profs['ability-checks']) if 'ability-checks' in bonuses: relevantbonus += bonuses['ability-checks'] skills[skill] = Skill( floor( stats.get_mod(SKILL_MAP[skill]) + (profBonus * relevantprof) + relevantbonus), relevantprof, relevantbonus) saves = {} for save in SAVE_NAMES: # add proficiency and bonuses to skills relevantprof = profs.get(save, 0) relevantbonus = bonuses.get(save, 0) if 'saving-throws' in profs: relevantprof = max(relevantprof, profs['saving-throws']) if 'saving-throws' in bonuses: relevantbonus += bonuses['saving-throws'] saves[save] = Skill( floor( stats.get_mod(SKILL_MAP[save]) + (profBonus * relevantprof) + relevantbonus), relevantprof, relevantbonus) ignored_ids = set() for charval in self.character_data['characterValues']: if charval['valueId'] in HOUSERULE_SKILL_MAP and charval[ 'valueId'] not in ignored_ids: skill_name = HOUSERULE_SKILL_MAP[charval['valueId']] if charval['typeId'] == 23: # override skills[skill_name] = Skill(charval['value']) ignored_ids.add( charval['valueId'] ) # this must be the final value so we stop looking elif charval['typeId'] in { 24, 25 }: # PROBABLY skill magic/misc bonus skills[skill_name].value += charval['value'] skills[skill_name].bonus += charval['value'] elif charval['typeId'] == 26: # proficiency stuff relevantprof = profs.get(skill_name, 0) skills[skill_name].value -= relevantprof * profBonus if charval[ 'value'] == 0: # no prof, don't need to do anything skills[skill_name].prof = 0 elif charval['value'] == 1: # half prof, round down skills[skill_name].value += profBonus // 2 skills[skill_name].prof = 0.5 elif charval['value'] == 2: # half, round up skills[skill_name].value += ceil(profBonus / 2) skills[skill_name].prof = 0.5 elif charval['value'] == 3: # full skills[skill_name].value += profBonus skills[skill_name].prof = 1 elif charval['value'] == 4: # double skills[skill_name].value += profBonus * 2 skills[skill_name].prof = 2 skills = Skills(skills) saves = Saves(saves) return skills, saves