diff options
Diffstat (limited to 'parser/utils.py')
-rwxr-xr-x | parser/utils.py | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/parser/utils.py b/parser/utils.py new file mode 100755 index 0000000..1798621 --- /dev/null +++ b/parser/utils.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 + +import json +import re +from fractions import Fraction + +skillsByAbility = {'str': ['Athletics'], 'dex': ['Acrobatics', 'Sleight of Hand', 'Stealth'], 'con': [], 'int': ['Arcana', 'History', 'Investigation', 'Nature', 'Religion'], 'wis': ['Animal Handling', 'Insight', 'Medicine', 'Perception', 'Survival'], 'cha': ['Deception', 'Intimidation', 'Performance', 'Persuasion']} + +armorByType = {'light': [('padded', 11), ('leather', 11), ('studded leather', 12)], 'medium': [('hide', 12), ('chain shirt', 13), ('scale mail', 14), ('breastplate', 14), ('half plate', 15)], 'heavy': [('ring mail', 14), ('chain mail', 16), ('splint', 17), ('plate', 18)], 'misc': [('shield', 2), ('ring of protection', 1)]} + +def procSkill(skillStr): + skill = skillStr.strip() + skillName = re.search('[^\+]*?(?= \+)', skill).group(0) + skillBonus = int(re.search('\+(\d+)', skill).group(1)) + ability = '' + for a in skillsByAbility: + if skillName in skillsByAbility[a]: + ability = a + if not ability: + print('Could not find ability for skill {}'.format(skillName)) + return skillName, skillBonus, ability + +def guessProficiency(desc): + if desc['cr'] <= 1: + return 2 + def getBonus(ability): + return (desc['stats'][ability] - 10) // 2 + # Guess proficiency based on saves, skills, and attacks + if desc['saves']: + for save in desc['saves'].split(','): + save = save.strip() + ability = save.split(' ')[0].lower() + saveBonus = int(save.split('+')[1]) + return saveBonus - getBonus(ability) # This is the answer. + skillGuesses = [] + if desc['skills']: + for skill in desc['skills'].split(','): + skillName, skillBonus, ability = procSkill(skill) + skillGuesses.append(skillBonus - getBonus(ability)) + attackGuesses = [] + for action in desc['actions'].values(): + if re.match('.*Attack:', action): + toHit = int(re.search('\+(\d+) to hit', action).group(1)) + dmgBonusMatch = re.search('\d+d\d+ (\+|−) (\d+)\)', action) + if dmgBonusMatch: + dmgBonus = int(dmgBonusMatch.group(2)) + if dmgBonusMatch.group(1) == '−': + #print('We match here for the {}!'.format(desc['name'])) + dmgBonus *= -1 + else: + dmgBonus = 0 + if toHit - dmgBonus > 1: + attackGuesses.append(toHit - dmgBonus) + if not skillGuesses and not attackGuesses: + print('We got here for the {}!'.format(desc['name'])) + return 2 + else: + profGuesses = skillGuesses + attackGuesses + if min(profGuesses) != 0 and any(guess % min(profGuesses) != 0 for guess in profGuesses): + print('We had conflicting guesses for {}: {}'.format(desc['name'], profGuesses)) + best = (0, 0) + for guess in profGuesses: + numHappy = sum(1 for other in profGuesses if other % guess == 0) + if numHappy > best[1]: + best = (guess, numHappy) + return best[0] + +def cost2copper(cost): + amnt = int(cost.split(' ')[0].replace(',', '')) + den = cost.split(' ')[1] + if den == 'pp': + return amnt * 1000 + elif den == 'gp': + return amnt * 100 + elif den == 'ep': + return amnt * 50 + elif den == 'sp': + return amnt * 10 + elif den == 'cp': + return amnt + +def getArmor(): + with open('../../5thSRD/docs/adventuring/equipment/armor.md') as f: + data = f.read() + tables = re.search('(?sm)(?<=## Armor Table).*?(?=##)', data).group(0) + armors = [] + header = '' + for armor in re.findall('\| (.*) \| (.*) \| (.*) \| (.*) \| (.*) \| (.*) \|', tables): + armor = [part.strip().lower() for part in armor] + if armor[1] == 'cost': + header = armor[0] + else: + armors.append({'name': armor[0], 'cost': cost2copper(armor[1]), 'ac': int(armor[2].split(' ')[0]), 'strength': int(armor[3].replace('-', '0').split(' ')[-1]), 'disadvantage': armor[4] == 'Disadvantage', 'weight': float(armor[5].split(' ')[0]), 'type': header.split(' ')[0]}) + return armors + +weapons = [] +def getWeapons(): + global weapons + if weapons: + return weapons + with open('../../5thSRD/docs/adventuring/equipment/weapons.md') as f: + data = f.read() + special = {} + for s in ['Lance', 'Net']: + special[s.lower()] = re.search('(?<=\*\*{}.\*\*).*'.format(s), data).group(0).strip() + tables = re.search('(?sm)## Weapons Table.*', data).group(0) + weapons = [] + header = '' + for weapon in re.findall('\| (.*) \| (.*) \| (.*) \| (.*) \| (.*) \|', tables): + weapon = [part.strip().lower() for part in weapon] + if weapon[1] == 'cost': + header = weapon[0] + else: + name = weapon[0] + if ',' in name: + parts = name.split(', ') + name = parts[1] + ' ' + parts[0] + if weapon[2] == '-': + weapon[2] = '0d0 -' + damage = {'dmg_type': weapon[2].split(' ')[1]} + if 'd' in weapon[2].split(' ')[0]: + damage['dmg_die_count'] = int(weapon[2].split('d')[0]) + damage['dmg_die_sides'] = int(weapon[2].split(' ')[0].split('d')[1]) + else: + damage['dmg_die_count'] = 1 + damage['dmg_die_sides'] = 1 + rang = [0, 0] + reach = 5 + proporties = [] + if weapon[4] != '-': + proporties = weapon[4].split(', ') + for i, p in enumerate(list(proporties)): + if 'versatile' in p: + proporties[i] = 'versatile' + elif 'range' in p: + proporties[i] = p.split(' (')[0] + rang = [int(r) for r in p.split(' ')[-1][:-1].split('/')] + if 'ammunition' in p: + reach = 0 + elif 'reach' in p: + reach += 5 + if name in special: + proporties.append(special[name]) + weapons.append({'name': name, 'cost': cost2copper(weapon[1]), 'damage': damage, 'weight': float(Fraction(weapon[3].split(' ')[0].replace('-', '0'))), 'range': rang, 'reach': reach, 'properties': proporties, 'type': header, 'text': 'Provided from PHB'}) + return weapons + +def formatWeapon(name, rangeShort, rangeLong, reach, dmgType, dmgCount, dmgSides, text): + baseWeapon = {'cost': 0, 'weight': 0.0, 'properties': [], 'type': 'unknown'} + for weapon in weapons: + if weapon['name'] == name: + baseWeapon = weapon + return {'name': name, 'cost': baseWeapon['cost'], 'damage': {'dmg_type': dmgType, 'dmg_die_count': dmgCount, 'dmg_die_sides': dmgSides}, 'weight': baseWeapon['weight'], 'range': [rangeShort, rangeLong], 'reach': reach, 'properties': baseWeapon['properties'], 'type': baseWeapon['type'], 'text': text} |