def generate_description(attrs, weapons, armors):
    desc_attrs = misc.get_attr_and_weights(attrs, 'Description')
    x = 0
    while x < 5:
        x += 1
        desc = roll.one_with_weights(desc_attrs)

        tag_dict = desc.get_tag_dict()
        tag_fills = {}
        for key, vals in tag_dict.items():
            if key != 'weapon_name_req' or key != 'armor_name_req':
                tag_fills[key] = roll.one(vals)
            elif key == 'weapon':
                tag_fills[key] = roll.one(weapons)
            elif key == 'armor':
                tag_fills[key] = roll.one(armors)

        if 'weapon_name_req' in tag_dict.keys():
            weapon = roll.from_list_if_list(tag_dict['weapon_name_req'],
                                            weapons)
            if weapon is None:
                continue
            else:
                tag_fills['weapon'] = weapon.lower()

        if 'armor_name_req' in tag_dict.keys():
            armor = roll.from_list_if_list(tag_dict['armor_name_req'], armors)
            if armor is None:
                continue
            else:
                tag_fills['armor'] = armor.lower()

        return desc.value.format_map(tag_fills)
    else:
        return 'They have a blank expression on their face'
def generate_caster(archetype, level, high_stat, second_high_stat, stats):
    # Two things these need to capture
    # spells known and the number of slots available
    # full caster, wizard, cleric, sorceror, bard
    # Full casters: smart characters or faithful with WIS as highest stat
    if roll.one_with_weights([(0, 10), (1, 4 + (level**2))]) == 1 and (
            archetype.value == 'Smart' or
        (archetype.value == 'Faithful' and stats['WIS'] > stats['CHA']) or
        (archetype.value == 'Crafty' and
         (high_stat == 'CHA' or second_high_stat == 'CHA'))):
        cantrips_known = floor(3 + (level / 10))
        spell_cap = 4 + level
        spells_known = roll.one(list(range(int(spell_cap / 2), spell_cap)))

        if archetype == 'Smart':
            cast_stat = high_stat
        elif archetype == 'Crafty':
            cast_stat = 'CHA'
        else:
            cast_stat = 'WIS'

        return 'Full Caster', cantrips_known, spells_known, cast_stat
        # return pick_spells(archetype, level, spells_known, cantrips_known, high_stat)

    # half caster: faithful characters with WIS as second highest stat, crafty characters with INT or CHA as high stat
    # only after level 2 - SHOULD BE WARLOCKS, RANGERS, AND PALADINS
    if roll.one_with_weights([(0, 20), (1, min(
            20, 2 * level))]) == 1 and level >= 2 and (
                (archetype.value == 'Crafty' and
                 (high_stat == 'WIS' or second_high_stat == 'WIS')) or
                (archetype.value == 'Faithful')):
        cantrips_known = 0
        spell_cap = floor(3 + ((level - 1) / 2))
        spells_known = roll.one(list(range(int(spell_cap / 2), spell_cap)))
        if archetype == 'Faithful':
            cast_stat = 'CHA'
        else:
            cast_stat = 'WIS'

        return 'Half Caster', cantrips_known, spells_known, cast_stat
        # return pick_spells(archetype, level, spells_known, cantrips_known, cast_stat)

    # 1/3 caster: Bulky characters with INT/WIS/CHA as one of top 2 stats
    # or crafty characters with INT or CHA as second high stat
    # only after level 3
    if roll.one_with_weights([(0, 20), (1, min(
            10, level))]) == 1 and level >= 3 and (
                (archetype.value == 'Bulky' and
                 (high_stat == 'INT' or second_high_stat == 'INT')) or
                (archetype.value == 'Crafty' and
                 (high_stat == 'INT' or second_high_stat == 'INT'))):
        cantrips_known = floor(1 + (level / 10))
        spell_cap = floor(4 + ((level - 1) / 3))
        spells_known = roll.one(list(range(int(spell_cap / 2), spell_cap)))

        return 'Third Caster', cantrips_known, spells_known, 'INT'
        # return pick_spells(archetype, level, spells_known, cantrips_known, cast_stat)

    return False, 0, 0, None
def generate_skills(attrs, archetype, high_stat):
    skill_attrs = misc.get_attrs(attrs, 'Skill')

    class_skills = misc.get_attrs_by_tag(skill_attrs, 'archetype',
                                         archetype.value)

    trained_skills = []
    select = 3
    if high_stat is not 'CON':
        stat_skills = misc.get_attrs_by_tag(skill_attrs, 'skill_stat',
                                            high_stat)
        stat_skill = roll.one(stat_skills)
        trained_skills.append(stat_skill)
        if stat_skill in class_skills:
            select = 4

    skill_pool = roll.many(skill_attrs, select, selection=class_skills)

    skill_count = roll.one_with_weights([(3, 6), (4, 2), (5, 1)])
    for i in range(skill_count):
        skill_choice = roll.one_with_removal(skill_pool, trained_skills)
        if skill_choice is not None:
            trained_skills.append(skill_choice)

    return trained_skills
 def get_lowest_stat(self):
     """
     Gives you a list of the lowest stats. Generally this is one, but it could be more!
     :return: the lowest score
     :rtype: list
     """
     lowest = 100
     small_stat = []
     for stat, value in self.stats.items():
         if value < lowest:
             lowest = value
             small_stat = [stat]
         elif value == lowest:
             small_stat.append(stat)
     try:
         return one(small_stat)
     except IndexError:
         return one(list(self.stats.keys()))
 def get_highest_stat(self):
     """
     Gives you a list of the highest stats. If it's a tie, choose one value at random
     :return: the highest stats
     :rtype: list
     """
     highest = 0
     big_stat = []
     for stat, value in self.stats.items():
         if value > highest:
             highest = value
             big_stat = [stat]
         elif value == highest:
             big_stat.append(stat)
     # This part breaks sometimes. If it does, just return a random stat. Gives the NPC a rogue element.
     try:
         return one(big_stat)
     except IndexError:
         return one(list(self.stats.keys()))
def generate_armor(attrs, archetype, skills, stats):
    armor_attrs = misc.get_attrs(attrs, 'Armor')
    arch_armors = misc.get_attrs_by_tag(armor_attrs, 'archetype',
                                        archetype.value)
    arch_armors = misc.remove_attrs_by_tag(arch_armors, 'armor_type', 'shield')

    if 'Stealth' in [x.value for x in skills]:
        arch_armors = misc.remove_attrs_by_tag(arch_armors, 'stealth_dis',
                                               True)
    if stats['STR'] < 15:
        arch_armors = misc.remove_attrs_by_tag(arch_armors, 'req_str', '15')
    elif stats['STR'] > 15:
        arch_armors = misc.remove_attrs_by_tag(arch_armors, 'req_str', '13')
    if stats['DEX'] > 2:
        arch_armors = misc.get_attrs_by_tag(arch_armors, 'armor_type', 'light')

    return [roll.one(arch_armors)]
def generate_name(attrs):
    name_attrs = misc.get_attrs(attrs, 'Name')
    return roll.one(name_attrs)