def list_character_stats(self, character_name):
        try:
            for stat, value in self.characters[character_name].items():
                print(stat + ':', value)
            print('\n')

        except KeyError:
            print('{!s} does not have a character profile saved!'.format(
                character_name))
            input('Press enter to continue...\n')
            main_menu()

        input('Press enter to continue. \n')
    def change_stat(self, character_name, stat, new_stat):
        try:
            self.characters[character_name].update({stat: new_stat})
        except KeyError as ker:
            print('Could not update {!s}'.format(new_stat))
            print(ker)
            main_menu()

        with open('characters.json', 'w') as characters_file:
            characters_file.truncate(0)
            characters_file.write(json.dumps(self.characters))

        print('{!s} {!s} updated to {!s}.'.format(character_name, stat,
                                                  new_stat))

        input('Press enter to continue. \n')
    def do_character_attack(self, damage_type):
        # refresh character/monster profiles so we dont have to reload them manually
        self.__init__(self.character_name, self.monster_name)

        # roll d20
        random.seed()
        char_hit_roll = random.randint(1, 20)
        # attacks are done here
        try:
            if char_hit_roll < 20:
                # compare if roll stats are greater than character's armor, subtract damage from players health
                # perform health check and display new values
                char_attack_hit = char_hit_roll + self.char[
                    'attackHit'] + self.char['proficiency']
                if char_attack_hit > self.mons['armorClass']:
                    print(
                        'Character attack roll: {!s}'.format(char_attack_hit))

                    # calculate attack damage
                    damage_roll = random.randint(
                        1, self.char['damageDice']) + self.char[damage_type]
                    print('{!s} hit {!s} for {!s} damage'.format(
                        self.character_name, self.monster_name, damage_roll))
                    new_mons_health = self.mons['hitPoints'] - damage_roll
                    # failed it
                else:
                    print('{!s} did not hit above {!s}\'s armor class! \n'.
                          format(self.character_name, self.monster_name))

            elif char_hit_roll == 20:
                print('***CRITICAL DAMAGE ROLL***')
                crit_damage_roll = (self.char['damageDice'] *
                                    2) + self.char[damage_type]
                print('Critical damage roll hit for: {!s}'.format(
                    crit_damage_roll))
                # calculate health impacts of monster
                new_mons_health = self.mons['hitPoints'] - crit_damage_roll

            Monsters.change_stat(Monsters(), self.monster_name, 'hitPoints',
                                 new_mons_health)

        # if KeyError, a stat is missing from a profile somewhere
        except KeyError as ker:
            print('A character stat seems to be missing.')
            print(ker)
            main_menu()
    def do_monster_attack(self, aggro_level):
        # refresh character/monster profiles so we dont have to reload them manually
        self.__init__(self.character_name, self.monster_name)

        # roll d20
        random.seed()
        mons_hit_roll = random.randint(1, 20)

        # attacks are done here
        try:
            # compare if roll stats are greater than self.character's armor, subtract damage from players health
            # perform health check and display new values
            monster_attack_hit = mons_hit_roll + self.mons['attackHit']
            if monster_attack_hit > self.char['armorClass']:
                print('Monster attack roll: {!s}'.format(monster_attack_hit))
                attack_roll = random.randint(1, self.mons['damageDice'])
                print('{!s} hit {!s} for {!s} damage'.format(
                    self.monster_name, self.character_name, attack_roll))

                # calculate health impacts and aggro level health
                new_char_health = self.char['hitPoints'] - attack_roll
                Characters.change_stat(Characters(), self.character_name,
                                       'hitPoints', new_char_health)

                if (self.char['hitPoints'] -
                    (self.char['hitPoints'] * aggro_level)) == new_char_health:
                    print(
                        'Character\'s desired aggro level health reached! \n')
                    main_menu()

            # failed it
            else:
                print('{!s} did not hit above {!s}\'s armor class! \n'.format(
                    self.monster_name, self.character_name))

        # if KeyError, a stat is missing from a profile somewhere
        except KeyError as ker:
            print('A monster stat seems to be missing.')
            print(ker)
            main_menu()
    def __init__(self, character_name, monster_name):
        super(Characters)
        super(Monsters)
        self.character_name = character_name
        self.monster_name = monster_name

        try:
            with open('characters.json', 'r') as characters_file:
                characters = json.load(characters_file)
                self.char = characters[character_name]
                self.char_init = int(self.char['initiative'])

                # do a quick health check to ensure it's actually alive
                if self.char['hitPoints'] <= 0:
                    print('{!s} has been killed! \n'.format(
                        self.character_name))
                    input('Press enter to continue... \n')
                    main_menu()

        except KeyError:
            print(
                '{!s} does not exist. Please ensure the spelling/capitalization is '
                'correct or reference the character list'.format(
                    character_name))
            main_menu()

        try:
            with open('monsters.json', 'r') as monsters_file:
                monsters = json.load(monsters_file)
                self.mons = monsters[monster_name]
                self.mons_init = int(self.mons['initiative'])

                # do a quick health check to ensure it's actually alive
                if self.mons['hitPoints'] <= 0:
                    print('{!s} has been killed! \n'.format(self.monster_name))
                    self.reset_monster_health()
                    main_menu()

                    # force reload of __init__ because of an odd condition
                    # where the battle could have been underway
                    self.__init__(character_name, monster_name)

        except KeyError:
            print(
                '{!s} does not exist. Please ensure the spelling/capitalization is '
                'correct or reference the monster list'.format(monster_name))
            main_menu()