def _parse_haunt(statblock, haunt): haunt_source = haunt["lines"].pop(0) caster = None if len(haunt["lines"]) > 0: caster = haunt["lines"].pop(0) if len(haunt["lines"]) > 0: raise Exception("Don't know how to parse haunt: %s" % haunt) haunt = statblock.setdefault("haunt", create("haunt")) if haunt_source.find("(") > -1: haunt_source, notes = haunt_source.split("(") notes = notes.strip().replace(")", "") haunt["notes"] = notes parts = haunt_source.split() align = parts.pop(0) if not validate_alignment(align): raise Exception("Don't recognize alignment: %s" % align) haunt["alignment"] = create("alignment", value=align) haunt["haunt_type"] = " ".join(parts) if caster: if caster.startswith("Caster Level "): caster = caster.strip() caster = caster[13:-2] haunt["caster_level"] = create("caster_level", value=int(caster)) else: raise Exception( "I don't know how to handle this caster level: %s" % caster)
def _handle_saves(statblock, token, data): content = " ".join([l.strip() for l in data["lines"]]) saves = statblock.setdefault("saves", {}) parts = [s.strip() for s in split(content, char=";")] saves_source = [s.strip() for s in split(parts.pop(0))] modifiers = ";".join(parts) for save in saves_source: m = re.match("^(.*) ([-+]?\d+)$", save) if m: name, value = m.groups() name = name.lower() value = int(value) if name == "ref": name = "reflex" elif name == "fort": name = "fortitude" saves[name] = create("save", name=name, value=value) add_situational_modifiers(saves[name], modifiers) continue m = re.match("^(.*) ([-+]?\d+) \((.*)\)$", save) if m: name, value, specific_modifiers = m.groups() name = name.lower() value = int(value) if name == "ref": name = "reflex" elif name == "fort": name = "fortitude" saves[name] = create("save", name=name, value=int(m.groups()[1])) add_situational_modifiers(saves[name], modifiers) add_situational_modifiers(saves[name], specific_modifiers, value) continue raise Exception("Don't know how to parse saves: %s" % content)
def _handle_cl(section, lines): line = lines.pop(0) m = re.match("\((CL .*?)\)", line) if m: cl = m.groups()[0] pieces = split(cl, char=";") for piece in pieces: piece = piece.strip() m = re.match("CL (\d+)[snrt][tdh]", piece) if m: section["caster_level"] = create("caster_level", value=int(m.groups()[0])) continue m = re.match("(\d+)% spell failure", piece) if m: section["spell_failure"] = create("spell_failure", value=int(m.groups()[0])) continue m = re.match("concentration ([+-]?\d+)", piece) if m: section["concentration"] = create("concentration", value=int(m.groups()[0])) continue section["notes"] = line parts = line.split(")") parts.pop(0) line = ")".join(parts).strip() if line == "": return lines else: lines.insert(0, line) return lines raise Exception("Spell block has unexpected format: %s" % line)
def _handle_cl(section, lines): line = lines.pop(0) m = re.match("\((CL .*?)\)", line) if m: cl = m.groups()[0] pieces = split(cl, char=";") for piece in pieces: piece = piece.strip() m = re.match("CL (\d+)[snrt][tdh]", piece) if m: section["caster_level"] = create( "caster_level", value=int(m.groups()[0])) continue m = re.match("(\d+)% spell failure", piece) if m: section["spell_failure"] = create( "spell_failure", value=int(m.groups()[0])) continue m = re.match("concentration ([+-]?\d+)", piece) if m: section["concentration"] = create( "concentration", value=int(m.groups()[0])) continue section["notes"] = line parts = line.split(")") parts.pop(0) line = ")".join(parts).strip() if line == "": return lines else: lines.insert(0, line) return lines raise Exception("Spell block has unexpected format: %s" % line)
def _handle_attack(statblock, token, data): lines = data['lines'] content = " ".join([line.strip() for line in lines]) fields = [field.strip() for field in split(content, char=";")] for field in fields: m = re.match("(.*?) ([+-]?[0-9]+) \((.*?)\)", field) if m: name = m.groups()[0] name, subtype, abbrev = _get_attack_fields(name) bonus = int(m.groups()[1]) modifiers = m.groups()[2] section = create( "attack", subtype, name=name, value=bonus, abbrev=abbrev) add_situational_modifiers(section, modifiers, bonus) statblock[name] = section continue m = re.match("(.*?) ([+-]?[0-9]+)", field) if m: name = m.groups()[0] name, subtype, abbrev = _get_attack_fields(name) bonus = int(m.groups()[1]) statblock[name] = create( "attack", subtype, name=name, value=bonus, abbrev=abbrev) m = re.match("(.*?) -", field) if m: name = m.groups()[0] name, subtype, abbrev = _get_attack_fields(name) statblock[name] = create( "attack", subtype, name=name, value=None, abbrev=abbrev)
def parse_special_abilities(lines): retlines = [] for statblock in yield_statblocks(lines, retlines): source = statblock.setdefault("source", {}) sa_source = source.setdefault("special abilities", {}) sa = None ability = {} sa_types = {"Ex": "Extraordinary", "Su": "Supernatural", "Sp": "Spell-Like"} for line in sa_source.get("lines", []): m = re.match("([A-Z ]*): (.*)", line.strip()) if m: name = cap_name(m.groups()[0]) description = m.groups()[1].strip() sa = statblock.setdefault("special_abilities", []) ability = create( "special_ability", name=name, description=description) sa.append(ability) continue m = re.match(u"([A-Za-z \u2019-]*) \(([SE][uxp])\) (.*)", line.strip()) if m: name = cap_name(m.groups()[0]) sa_type = m.groups()[1] description = m.groups()[2].strip() sa = statblock.setdefault("special_abilities", []) ability = create( "special_ability", name=name, description=description, ability_type=sa_types[sa_type], ability_type_abbrev=sa_type) sa.append(ability) continue ability["description"] = " ".join( [ability["description"], line.strip()]) del source["special abilities"] return retlines
def _handle_skills(statblock, token, data): lines = data['lines'] content = " ".join([line.strip() for line in lines]).split(";") skills_line = content.pop(0) skills = {} for skill_line in split(skills_line): skill_line = skill_line.strip() name = None bonus = None m = re.match("^([A-Za-z ]*) ([+-]?[0-9]+)$", skill_line) if m: name = m.groups()[0] skill_name = name.lower().replace(" ", "_") bonus = int(m.groups()[1]) skills[skill_name] = create("skill", value=bonus, name=name) continue m = re.match("^([A-Za-z ]*) \((.*?)\) ([+-]?[0-9]+)$", skill_line) if m: name = m.groups()[0] skill_name = name.lower().replace(" ", "_") subtype = m.groups()[1] bonus = int(m.groups()[2]) subtype = subtype.replace(", and ", ", ").replace(" or ", ", ") for st in subtype.split(","): skills["%s_%s" % (skill_name, st.strip().replace(" ", "_"))] = create( "skill", value=bonus, name=name, subskill=st.strip()) continue m = re.match("^([A-Za-z ]*) ([+-]?[0-9]+) \((.*?)\)$", skill_line) if m: name = m.groups()[0] skill_name = name.lower().replace(" ", "_") bonus = int(m.groups()[1]) skill = create("skill", value=bonus, name=name) skills[skill_name] = skill modifiers = m.groups()[2] add_situational_modifiers(skill, modifiers, bonus) continue for line in content: if line.strip().startswith("Racial Modifiers "): line = line.replace("Racial Modifiers ", "") bonuses = split(line) for bonus in bonuses: m = re.match("([+-]?[0-9]+) (.*)", bonus.strip()) if m: b = int(m.groups()[0]) skill_name = m.groups()[1] skill = skills.setdefault(skill_name, create("skill", name=name)) add_modifier(skill, create_modifier("racial", value=b)) else: raise Exception("Can't parse: %s" % line) else: raise Exception("Unknown skill subsection: %s" % line) statblock["skills"] = skills
def _handle_defensive_abilities(statblock, data): abilities = [s.strip() for s in split(data[20:])] da_s = statblock.setdefault("defensive abilities", {}) for ability in abilities: m = re.match("^(.*) ([-+]?\d+)$", ability) if m: name = m.groups()[0] bonus = int(m.groups()[1]) da_s["name"] = create("defensive_ability", name=name, value=bonus) else: da_s["name"] = create("defensive_ability", name=ability)
def _handle_languages(statblock, token, data): lines = data['lines'] content = " ".join([line.strip() for line in lines]) fields = split(content, ";") languages = split(fields[0]) final = [] for language in languages: final.append(create("language", name=language.strip())) if len(fields) > 1: others = split(fields[1]) for other in others: final.append(create("language", "other", name=other.strip())) elif len(fields) > 2: raise Exception("I don't know how to handle this language list: %s" % json.dumps(content)) statblock["languages"] = final
def _handle_healing(hp, healing): for h in healing: m = re.match("(.*) (\d+) \((.*)\)", h) if m: name, value, notes = m.groups() heal_section = hp.setdefault("healing", []) heal_section.append(create( "healing_ability", name=name, value=int(value), notes=notes)) continue m = re.match("(.*) (\d+)", h) if m: name, value = m.groups() heal_section = hp.setdefault("healing", []) heal_section.append(create( "healing_ability", name=name, value=int(value)))
def _add_spells(line, section, level=None, freq=None, freq_period=None): parts = line.split("-") parts.pop(0) spells = [s.strip() for s in split("-".join(parts))] spell = create("spell") spell_list = create("spell_list", spells=[]) if level != None: spell_list["level"] = level spell["level"] = level if freq: spell_list["frequency"] = freq if freq_period: spell_list["frequency period"] = freq_period for spell_source in spells: _parse_spell(spell_source, spell, spell_list) section["spell_lists"].append(spell_list)
def _handle_dr(statblock, data): drs = [s.strip() for s in data[3:].split(', DR')] for dr in drs: value, overcome = dr.split("/") dr_section = statblock.setdefault("damage_reduction", []) dr_section.append(create( "damage_reduction", value=int(value), overcome=overcome))
def _handle_dr(statblock, data): drs = [s.strip() for s in data[3:].split(', DR')] for dr in drs: value, overcome = dr.split("/") dr_section = statblock.setdefault("damage_reduction", []) dr_section.append( create("damage_reduction", value=int(value), overcome=overcome))
def _damage_get_plus(weapon, damage): m = re.match("^([+-]\d+).*", damage) if m: plus = m.groups()[0] weapon["damage"]["plus"] = create("damage_plus", value=int(plus)) return damage[len(plus):] return damage
def _handle_gear(statblock, token, data): lines = data['lines'] content = " ".join([line.strip() for line in lines]) gear = statblock.setdefault("gear", {}) items = split(content) subgear = gear.setdefault(token, []) for item in items: subgear.append(create("gear", name=item.strip()))
def create_size(size): sizes = [ "fine", "diminutive", "tiny", "small", "medium", "large", "huge", "gargantuan", "colossal"] if size.lower() in sizes: return create("creature_size", value=size) else: raise Exception("Size unrecognized: %s" % size)
def _handle_resist(statblock, data): resists = [s.strip() for s in split(data[7:])] for resist in resists: m = re.match("(.*) (\d+) \((.*)\)", resist) if m: name, value, notes = m.groups() resist_section = statblock.setdefault("resistances", []) resist_section.append(create( "resistance", name=name, value=int(value), notes=notes)) continue m = re.match("(.*) (\d+)", resist) if m: name, value = m.groups() resist_section = statblock.setdefault("resistances", []) resist_section.append(create( "resistance", name=name, value=int(value))) continue raise Exception("Don't know how to parse resist: %s" % data)
def _handle_healing(hp, healing): for h in healing: m = re.match("(.*) (\d+) \((.*)\)", h) if m: name, value, notes = m.groups() heal_section = hp.setdefault("healing", []) heal_section.append( create("healing_ability", name=name, value=int(value), notes=notes)) continue m = re.match("(.*) (\d+)", h) if m: name, value = m.groups() heal_section = hp.setdefault("healing", []) heal_section.append( create("healing_ability", name=name, value=int(value)))
def create_size(size): sizes = [ "fine", "diminutive", "tiny", "small", "medium", "large", "huge", "gargantuan", "colossal" ] if size.lower() in sizes: return create("creature_size", value=size) else: raise Exception("Size unrecognized: %s" % size)
def _handle_resist(statblock, data): resists = [s.strip() for s in split(data[7:])] for resist in resists: m = re.match("(.*) (\d+) \((.*)\)", resist) if m: name, value, notes = m.groups() resist_section = statblock.setdefault("resistances", []) resist_section.append( create("resistance", name=name, value=int(value), notes=notes)) continue m = re.match("(.*) (\d+)", resist) if m: name, value = m.groups() resist_section = statblock.setdefault("resistances", []) resist_section.append( create("resistance", name=name, value=int(value))) continue raise Exception("Don't know how to parse resist: %s" % data)
def _handle_sr(statblock, data): sr_s = [s.strip() for s in split(data[3:])] for sr in sr_s: m = re.match("(\d+) \((.*)\)", sr) if m: value, notes = m.groups() sr_section = statblock.setdefault("spell_resistances", []) sr_section.append( create("spell_resistance", value=int(value), notes=notes)) continue m = re.match("(\d+) (.*)", sr) if m: value, notes = m.groups() sr_section = statblock.setdefault("spell_resistances", []) sr_section.append( create("spell_resistance", value=int(value), notes=notes)) continue resist_section = statblock.setdefault("spell_resistances", []) resist_section.append(create("spell_resistance", value=int(sr)))
def _handle_initiative(statblock, parts): retlines = [] while len(parts) > 0: part = parts.pop() if part.strip().startswith("Init "): value = int(part[5:]) statblock["initiative"] = create("initiative", value=value) else: retlines.append(part) return retlines
def _handle_sr(statblock, data): sr_s = [s.strip() for s in split(data[3:])] for sr in sr_s: m = re.match("(\d+) \((.*)\)", sr) if m: value, notes = m.groups() sr_section = statblock.setdefault("spell_resistances", []) sr_section.append(create( "spell_resistance", value=int(value), notes=notes)) continue m = re.match("(\d+) (.*)", sr) if m: value, notes = m.groups() sr_section = statblock.setdefault("spell_resistances", []) sr_section.append(create( "spell_resistance", value=int(value), notes=notes)) continue resist_section = statblock.setdefault("spell_resistances", []) resist_section.append(create("spell_resistance", value=int(sr)))
def parse_special_abilities(lines): retlines = [] for statblock in yield_statblocks(lines, retlines): source = statblock.setdefault("source", {}) sa_source = source.setdefault("special abilities", {}) sa = None ability = {} sa_types = { "Ex": "Extraordinary", "Su": "Supernatural", "Sp": "Spell-Like" } for line in sa_source.get("lines", []): m = re.match("([A-Z ]*): (.*)", line.strip()) if m: name = cap_name(m.groups()[0]) description = m.groups()[1].strip() sa = statblock.setdefault("special_abilities", []) ability = create("special_ability", name=name, description=description) sa.append(ability) continue m = re.match(u"([A-Za-z \u2019-]*) \(([SE][uxp])\) (.*)", line.strip()) if m: name = cap_name(m.groups()[0]) sa_type = m.groups()[1] description = m.groups()[2].strip() sa = statblock.setdefault("special_abilities", []) ability = create("special_ability", name=name, description=description, ability_type=sa_types[sa_type], ability_type_abbrev=sa_type) sa.append(ability) continue ability["description"] = " ".join( [ability["description"], line.strip()]) del source["special abilities"] return retlines
def _handle_spells(statblock, token, data): section = statblock.setdefault(token, create("spell_source")) lines = data["lines"] lines = _generate_spell_fields(lines) lines = _split_spell_fields(lines) lines = _handle_cl(section, lines) section["spell_lists"] = [] lines = _handle_spell_notes(section, lines) lines = _split_spells(section, lines) if len(lines) > 0: raise Exception("Unparsed spell data: %s" % json.dumps(lines, indent=2))
def _handle_initiative(statblock, parts): retlines = [] while len(parts) > 0: part = parts.pop() if part.strip().startswith("Init "): value = int(part[5:]) statblock["initiative"] = create( "initiative", value=value) else: retlines.append(part) return retlines
def _handle_senses(statblock, parts): retlines = [] while len(parts) > 0: part = parts.pop() if part.strip().startswith("Senses "): content = part[7:] sections = [p.strip() for p in split(content)] statblock["senses"] = [create("sense", name=s) for s in sections] else: retlines.append(part) return retlines
def _handle_attributes(statblock, token, data): lines = data['lines'] content = " ".join([line.strip() for line in lines]) for field in split(content): field = field.strip() m = re.match("^([A-Za-z]+) ([-0-9]+)$", field) if m: stat = m.groups()[0] stat, abbrev = _get_attribute_name(stat) value = m.groups()[1] if value == "-": value = None else: value = int(value) mod = "%s_mod" % abbrev mod_value = None if value: mod_value = int(value - 10) / 2 statblock[abbrev] = create( "attribute", name=stat, value=value, abbrev=abbrev, mod=create( "attribute_modifier", name=mod, value=mod_value)) else: raise Exception("Cannot parse Attributes: %s" % json.dumps(content))
def _parse_alignment(statblock, data): content = " ".join([l.strip() for l in data["lines"]]) if content.find("(") > -1: content, subtypes = content.split("(") subtypes = subtypes[:-1] statblock["creature_subtypes"] = [s.strip() for s in split(subtypes)] parts = content.split() align = parts.pop(0) if not validate_alignment(align): raise Exception("Don't recognize alignment: %s" % align) statblock["alignment"] = create("alignment", value=align) statblock["size"] = create_size(parts.pop(0)) creature_type = " ".join(parts) if not validate_creature_type(creature_type): raise Exception("Creature Type unrecognized: %s" % creature_type) statblock["creature_type"] = creature_type
def _parse_classes(creature, parts): class_parts = [] while parts[-1][0].isdigit(): class_parts.insert(0, parts.pop()) if len(class_parts) > 0: class_parts.insert(0, parts.pop()) if parts[-1] == "of": class_parts.insert(0, parts.pop()) class_parts.insert(0, parts.pop()) class_source = " ".join(class_parts) classes = [s.strip() for s in split(class_source, char="/")] cc = None for pc_class in classes: m = re.match("^(.*) (\d+)$", pc_class) if m: name, value = m.groups() cc = creature.setdefault("classes", []) cc.append(create("class", name=name, value=int(value))) else: raise Exception("I don't recognize class: %s" % pc_class)
def _handle_hp(statblock, token, data): content = " ".join([l.strip() for l in data["lines"]]) parts = [s.strip() for s in split(content, char=";")] hp_source = parts.pop(0) hp = statblock.setdefault("hit_points", create("hit_points")) hp_source = hp_source.replace(" each", "") m = re.match("(\d+) \((.*)\)", hp_source) if m: hit_points, hit_dice = m.groups() hp["value"] = int(hit_points) m = re.match("(\d+) HD; ([0-9d+]+)([-+]\d+)", hit_dice) if m: hd, dice, plus = m.groups() hp["hit_dice"] = create("hit_dice", value=int(hd)) hp["dice"] = dice hp["plus"] = create("hp_plus", value=int(plus)) else: m = re.match("([0-9]+)d([0-9]+)([-+]\d+)", hit_dice) if m: count, d_type, plus = m.groups() hp["hit_dice"] = create("hit_dice", value=int(count)) hp["dice"] = "%sd%s" % (count, d_type) hp["plus"] = create("hp_plus", value=int(plus)) else: m = re.match("([0-9]+)d([0-9]+)", hit_dice) if m: count, d_type = m.groups() hp["hit_dice"] = create("hit_dice", value=int(count)) hp["dice"] = "%sd%s" % (count, d_type) else: raise Exception( "Don't know how to parse hit dice: %s" % json.dumps(content)) else: raise Exception( "Don't know how to parse hp: %s" % json.dumps(content)) if len(parts) > 0: healing = [h.strip() for h in split(parts.pop(0))] _handle_healing(hp, healing) if len(parts) > 0: raise Exception( "Don't know how to parse hp line: %s" % json.dumps(content))
def _handle_hp(statblock, token, data): content = " ".join([l.strip() for l in data["lines"]]) parts = [s.strip() for s in split(content, char=";")] hp_source = parts.pop(0) hp = statblock.setdefault("hit_points", create("hit_points")) hp_source = hp_source.replace(" each", "") m = re.match("(\d+) \((.*)\)", hp_source) if m: hit_points, hit_dice = m.groups() hp["value"] = int(hit_points) m = re.match("(\d+) HD; ([0-9d+]+)([-+]\d+)", hit_dice) if m: hd, dice, plus = m.groups() hp["hit_dice"] = create("hit_dice", value=int(hd)) hp["dice"] = dice hp["plus"] = create("hp_plus", value=int(plus)) else: m = re.match("([0-9]+)d([0-9]+)([-+]\d+)", hit_dice) if m: count, d_type, plus = m.groups() hp["hit_dice"] = create("hit_dice", value=int(count)) hp["dice"] = "%sd%s" % (count, d_type) hp["plus"] = create("hp_plus", value=int(plus)) else: m = re.match("([0-9]+)d([0-9]+)", hit_dice) if m: count, d_type = m.groups() hp["hit_dice"] = create("hit_dice", value=int(count)) hp["dice"] = "%sd%s" % (count, d_type) else: raise Exception("Don't know how to parse hit dice: %s" % json.dumps(content)) else: raise Exception("Don't know how to parse hp: %s" % json.dumps(content)) if len(parts) > 0: healing = [h.strip() for h in split(parts.pop(0))] _handle_healing(hp, healing) if len(parts) > 0: raise Exception("Don't know how to parse hp line: %s" % json.dumps(content))
def _handle_speed(statblock, token, data): content = " ".join([d.strip() for d in data["lines"]]) statblock["speed"] = [ create("movement", name=c.strip()) for c in split(content) ]
def _handle_speed(statblock, token, data): content = " ".join([d.strip() for d in data["lines"]]) statblock["speed"] = [create("movement", name=c.strip()) for c in split(content)]