From 63bb0f135a2265770c4eac737f49727783967768 Mon Sep 17 00:00:00 2001 From: Mickey Lyle Date: Wed, 4 Jan 2023 03:01:04 -0800 Subject: [PATCH 1/2] Mickey's refactor. --- README.md | 5 + character.py | 155 ++++++++++++++++++++++++ character_dnd.py | 299 ----------------------------------------------- dice.py | 3 + entity.py | 18 +++ equipment.py | 61 ++++++++++ main.py | 42 ++----- monster.py | 51 ++++++++ 8 files changed, 305 insertions(+), 329 deletions(-) create mode 100644 character.py delete mode 100644 character_dnd.py create mode 100644 dice.py create mode 100644 entity.py create mode 100644 equipment.py mode change 100644 => 100755 main.py create mode 100644 monster.py diff --git a/README.md b/README.md index f009f13..90ac613 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,8 @@ # Python-Character-Creator Python for a DnD character creator. Code is supposed to receive inputs from a user so they may be walked through the creation of a DnD character. + +TODO: +- critical hits +- critical damage +- more content diff --git a/character.py b/character.py new file mode 100644 index 0000000..bd68a14 --- /dev/null +++ b/character.py @@ -0,0 +1,155 @@ +import math +from random import randint +from dataclasses import dataclass + +from entity import Entity +from equipment import armors, weapons +from dice import roll + + +CHARACTER_ATTRIBUTES = ("Strength", "Dexterity", "Constitution", "Intelligence", "Wisdom", "Charisma") + + +@dataclass +class PlayerClass: + name: str + starting_hp: int + hit_die: int + + +classes = list() +classes.append(PlayerClass("Fighter", 6, 10)) + + +class Character(Entity): + def __init__(self): + Entity.__init__(self, input("Name your character: ")) + self.level = 1 + self.picker("class", classes) + self.reset_attributes() + self.pick_attributes(26) + self.max_hp = self.player_class.starting_hp + \ + self.get_attribute_mod(CHARACTER_ATTRIBUTES[2]) * self.level + self.current_hp = self.max_hp + print(self.get_attrs_str()) + self.picker("armor", armors) + self.picker("weapon", weapons) + + + def get_attribute_mod(self, attribute): + return math.floor((self.attributes[attribute] - 10) / 2) + + + def get_attribute_mod_cost(self, attribute): + a = self.attributes[attribute] + if a < 14: + return 1 + elif a == 14: + return 2 + elif a > 14: + return "MAX" + + + def reset_attributes(self): + self.attributes = dict() + for a in CHARACTER_ATTRIBUTES: + self.attributes[a] = 8 + + + def pick_attributes(self, points): + numbers = range(1, len(CHARACTER_ATTRIBUTES) + 1) + error = "" + + while points > 0: + print(" Attribute\tValue\tModifier\tCost") + for n, a in zip(numbers, CHARACTER_ATTRIBUTES): + print(f"{n}: {a}\t{self.attributes[a]}\t{self.get_attribute_mod(a)}\t{self.get_attribute_mod_cost(a)}") + print() + print(f"Attribute Points: {points}") + print(error) + choice = int(input("Choose an attribute to increase: ")) + print() + if choice in numbers: + choice -= 1 + a = CHARACTER_ATTRIBUTES[choice] + cost = self.get_attribute_mod_cost(a) + if cost == "MAX": + error = f"{CHARACTER_ATTRIBUTES[choice]} is at maximum" + elif cost <= points: + self.attributes[a] += 1 + points -= cost + error = "" + else: + error = f"Not enough points to increase {a}" + else: + error = f"Please pick a number between {min(numbers)} and {max(numbers)}" + + + def get_attack_bonus(self): + return self.get_attack_attr_bonus() + self.get_proficiency_bonus() + + + def get_attack_attr_bonus(self): + return max(self.get_attribute_mod["Strength"], + self.get_attribute_mod["Dexterity"]) \ + if self.weapon.finesse else self.get_attribute_mod("Strength") + + + def get_proficiency_bonus(self): + return math.floor(2 + ((self.level - 1) * 0.25)) + + + def roll_weapon_damage(self): + return roll(*self.weapon.damage) + self.get_attack_attr_bonus() + + + def __str__(self): + return f"{self.name} is a level {self.level} {self.player_class.name}\n" \ + f"{self.get_health_str()}\n" \ + f"Armor: {self.armor.name} AC: {self.armor.armor_class} Weapon: {self.weapon.name}" \ + f"{self.get_attrs_str()}" + + + def get_attrs_str(self): + return ''.join([f"{a}: {self.attributes[a]}\n" for a in CHARACTER_ATTRIBUTES]) + + + def picker(self, pick_type, pick_list): + choices = [g.name for g in pick_list] + numbers = range(1, len(pick_list) + 1) + + for i, name in zip(numbers, choices): + print(f"{i}: {name}") + + prompt = f"Which {pick_type} would you like? " + + while True: + choice = int(input(prompt)) + if choice in numbers: + break + else: + prompt = f"Please pick a number between {min(numbers)} and {max(numbers)}: " + + if pick_type == "armor": + self.armor = armors[choice - 1] + print(f"You chose {self.armor.name}.\n") + elif pick_type == "weapon": + self.weapon = weapons[choice - 1] + print(f"You chose {self.weapon.name}.\n") + elif pick_type == "class": + self.player_class= classes[choice - 1] + print(f"You chose {self.player_class.name}.\n") + + + def attack(self, enemy): + attack_roll = roll(1, 20) + self.get_attack_bonus() + print(f"{self.name} swings their {self.weapon.name} at the {enemy.name}!") + if enemy.armor_class < attack_roll: + print(f"{self.name} smacks the {enemy.name} a good one! {attack_roll} vs {enemy.get_ac()} AC") + enemy.take_damage(self.roll_weapon_damage()) + else: + print(f"{self.name} wiffs!") + + + def get_ac(self): + return self.armor.ac + min(self.armor.max_dex_bonus, self.attributes["Dexterity"]) diff --git a/character_dnd.py b/character_dnd.py deleted file mode 100644 index 79e60d7..0000000 --- a/character_dnd.py +++ /dev/null @@ -1,299 +0,0 @@ -import math -from random import randint - - -# Class to represent a base D&D character. -class Character_DnD: - def __init__(self, name, species="unknown", job="nobody", level=1, strength=8, dexterity=8, constitution=8, - intelligence=8, wisdom=8, charisma=8, weapon="none", weapon_value=0, armor="none"): - self.name = name - self.species = species - self.job = job - self.level = level - self.strength = strength - self.strength_mod = int(math.floor((strength - 10) / 2)) - self.dexterity = dexterity - self.dexterity_mod = int(math.floor((dexterity - 10) / 2)) - self.constitution = constitution - self.constitution_mod = int(math.floor((constitution - 10) / 2)) - self.intelligence = intelligence - self.intelligence_mod = int(math.floor((intelligence - 10) / 2)) - self.wisdom = wisdom - self.wisdom_mod = int(math.floor((wisdom - 10) / 2)) - self.charisma = charisma - self.charisma_mod = int(math.floor((charisma - 10) / 2)) - self.is_dead = False - self.hit_die = 6 - self.hit_points = ((self.hit_die / 2) + self.constitution_mod) * self.level - self.armor = armor - self.armor_bonus = 0 - self.armor_class = 10 + self.dexterity_mod + self.armor_bonus - self.weapon = weapon - self.weapon_value = weapon_value - self.attribute_points = 26 - self.attack_bonus = 0 - self.spell_save_dc = 0 - self.proficiency_modifier = 2 - - # Report back character name, species, job, stats, and equipment. - def __repr__(self): - return f"\n{self.name} is a level {self.level} {self.species} {self.job} with these stats:" \ - f"\n{self.strength} Strength \n{self.dexterity} Dexterity \n{self.constitution} Constitution " \ - f"\n{self.intelligence} Intelligence \n{self.wisdom} Wisdom \n{self.charisma} Charisma \nThey attack" \ - f" using {self.weapon} and have an armor class of {self.armor_class}" - - # Report back character is dead. - def is_dead(self): - self.is_dead = True - if self.hit_points <= 0: - print(f"{self.name} is at zero hit points and is dead.") - - # Display character's stats. - def stat_display(self): - return f"Your stats are as follows:\nStrength: {self.strength}\nDexterity: {self.dexterity}\n" \ - f"Constitution: {self.constitution}\nIntelligence: {self.intelligence}\nWisdom: {self.wisdom}\n" \ - f"Charisma{self.charisma}" - - # Change attribute points based on what stat was increased in stat_increase method. - def change_attribute_points(self): - while self.attribute_points > 0: - print(f"\nYou have {self.attribute_points} points remaining. and the following stats:\n\nStrength: " - f"{self.strength}\nDexterity: {self.dexterity}\nConstitution: {self.constitution}\nIntelligence: " - f"{self.intelligence}\nWisdom: {self.wisdom}\nCharisma: {self.charisma}\n") - which_stat = input("Which stat would you like to increase? Please select from strength, dexterity, " - "constitution, intelligence, wisdom, and Charisma. ") - if which_stat.lower() not in ["strength", "dexterity", "constitution", "intelligence", "wisdom", - "charisma"]: - print(f"\nWhoops, '{which_stat}' wasn't an option. Please select from Strength, Dexterity, " - f"Constitution, Intelligence,Wisdom, or Charisma.") - else: - self.stat_increase(which_stat) - - # Increase a specific stat and ensure any attributes reliant on that stat recalculate. - def stat_increase(self, stat): - while self.attribute_points > 0: - if stat.lower() == "strength" and self.strength < 15: - self.strength += 1 - self.strength_mod = int(math.floor((self.strength - 10) / 2)) - if self.strength in range(9, 14): - self.attribute_points -= 1 - elif self.attribute_points <= 1: - self.strength -= 1 - self.strength_mod = int(math.floor((self.strength - 10) / 2)) - print( - f"\nYou only have 1 point remaining and changing {stat} from {self.strength} to " - f"{self.strength + 1} requires 2 points. ") - else: - self.attribute_points -= 2 - self.change_attribute_points() - elif stat.lower() == "dexterity" and self.dexterity < 15: - self.dexterity += 1 - self.dexterity_mod = int(math.floor((self.dexterity - 10) / 2)) - self.armor_class = 10 + self.dexterity_mod + self.armor_bonus - if self.dexterity in range(9, 14): - self.attribute_points -= 1 - elif self.attribute_points <= 1: - self.dexterity -= 1 - self.dexterity_mod = int(math.floor((self.dexterity - 10) / 2)) - self.armor_class = 10 + self.dexterity_mod + self.armor_bonus - print( - f"\nYou only have 1 point remaining and changing {stat} from {self.dexterity} to " - f"{self.dexterity + 1} requires 2 points. ") - else: - self.attribute_points -= 2 - elif stat.lower() == "constitution" and self.constitution < 15: - self.constitution += 1 - self.constitution_mod = int(math.floor((self.constitution - 10) / 2)) - self.hit_points = ((self.hit_die / 2) + self.constitution_mod) * self.level - if self.constitution in range(9, 14): - self.attribute_points -= 1 - elif self.attribute_points <= 1: - self.constitution -= 1 - self.constitution_mod = int(math.floor((self.constitution - 10) / 2)) - self.hit_points = ((self.hit_die / 2) + self.constitution_mod) * self.level - print( - f"\nYou only have 1 point remaining and changing {stat} from {self.constitution} to " - f"{self.constitution + 1} requires 2 points. ") - else: - self.attribute_points -= 2 - elif stat.lower() == "intelligence" and self.intelligence < 15: - self.intelligence += 1 - self.intelligence_mod = int(math.floor((self.intelligence - 10) / 2)) - self.spell_save_dc = 8 + self.intelligence_mod + self.proficiency_modifier - if self.intelligence in range(9, 14): - self.attribute_points -= 1 - elif self.attribute_points <= 1: - self.intelligence -= 1 - self.intelligence_mod = int(math.floor((self.intelligence - 10) / 2)) - self.spell_save_dc = 8 + self.intelligence_mod + self.proficiency_modifier - print( - f"\nYou only have 1 point remaining and changing {stat} from {self.intelligence} to " - f"{self.intelligence + 1} requires 2 points. ") - else: - self.attribute_points -= 2 - elif stat.lower() == "wisdom" and self.wisdom < 15: - self.wisdom += 1 - self.wisdom_mod = int(math.floor((self.wisdom - 10) / 2)) - if self.wisdom in range(9, 14): - self.attribute_points -= 1 - elif self.attribute_points <= 1: - self.wisdom -= 1 - self.wisdom_mod = int(math.floor((self.wisdom - 10) / 2)) - print( - f"\nYou only have 1 point remaining and changing {stat} from {self.wisdom} to " - f"{self.wisdom + 1} requires 2 points. ") - else: - self.attribute_points -= 2 - elif stat.lower() == "charisma" and self.charisma < 15: - self.charisma += 1 - self.charisma_mod = int(math.floor((self.charisma - 10) / 2)) - if self.charisma in range(9, 14): - self.attribute_points -= 1 - elif self.attribute_points <= 1: - self.charisma -= 1 - self.charisma_mod = int(math.floor((self.charisma - 10) / 2)) - print( - f"\nYou only have 1 point remaining and changing {stat} from {self.charisma} to " - f"{self.charisma + 1} requires 2 points. ") - else: - self.attribute_points -= 2 - else: - print(f"\n{stat} is already at 15 and cannot be increased further") - break - - # User selects from two weapon / magic options based on job. - def weapon_choice(self): - if self.job.lower() == "fighter": - weapon_choice = input( - "\nWould you like to use a Greatsword (1) for 1d12 damage or a Flail and Shield (2) for " - " 1d10 damage and +2 Armor Class? Please input 1 or 2. ") - if weapon_choice == 1: - self.weapon = "a Greatsword" - self.weapon_value = (randint(1, 12) + self.strength_mod) - self.attack_bonus = self.strength_mod - else: - self.weapon = "a Flail and Shield" - self.weapon_value = randint(1, 8) - self.armor_bonus += 2 - self.armor_class = self.armor_class = 10 + self.dexterity_mod + self.armor_bonus - elif self.job.lower() == "rogue": - weapon_choice = input( - "\nWould you like to use a Shortsword (1) for 1d6 damage or a Dagger (2) for " - " 1d4 damage? Please input 1 or 2. ") - if weapon_choice == 1: - self.weapon = "a Shortsword" - self.weapon_value = randint(1, 6) - else: - self.weapon = "a Dagger" - self.weapon_value = randint(1, 4) - elif self.job.lower() == "wizard": - weapon_choice = input( - "\nWould you like to use a Firebolt (1) for 1d10 damage or a Frostbite (2) for " - " 1d6 cold damage and cause a Con save for disadvantage on the next attack roll? Please input 1 or 2. ") - if weapon_choice == 1: - self.weapon = "Firebolt" - self.weapon_value = randint(1, 10) - else: - self.weapon = "Frostbite" - self.weapon_value = randint(1, 6) - - # Lets the user select different armors, except the wizard. They just get Mage Armor. - def armor_choice(self): - if self.job.lower() == "fighter": - armor_choice = input("\nWould you like Scale Mail (1) for 14 armor class plus your dexterity modifier " - "(maximum +2) or Studded Leather (2) for 12 armor class plus your dexterity modifier?" - "Please input 1 or 2. ") - if armor_choice == 1: - self.armor = "Scale Mail" - self.armor_bonus += 4 - self.armor_class = 10 + self.dexterity_mod + self.armor_bonus - if self.dexterity_mod > 2: - self.armor_class = 10 + 2 + self.armor_bonus - else: - self.armor = "Studded Leather" - self.armor_bonus += 2 - self.armor_class = 10 + self.dexterity_mod + self.armor_bonus - elif self.job.lower() == "rogue": - armor_choice = input("\nWould you like Leather Armor (1) for 11 armor class plus your dexterity modifier or" - " Studded Leather (2) for 12 armor class and no maximum dexterity modifier? Please " - "input 1 or 2. ") - if armor_choice == 1: - self.armor = "Leather Armor" - self.armor_bonus += 1 - self.armor_class = 10 + self.dexterity_mod + self.armor_bonus - else: - self.armor = "Studded Leather" - self.armor_bonus += 2 - self.armor_class = 10 + self.dexterity_mod + self.armor_bonus - if self.job.lower() == "wizard": - self.armor = "Mage Armor" - self.armor_class = 10 + self.dexterity_mod + self.armor_bonus + self.intelligence_mod - - def player_attack(self, monster): - attack_roll = (randint(1, 20)) - save_roll = (randint(1, 20)) - print(f"\n{self.name} attempts to attack the {monster.name} with {self.weapon}.") - if self.weapon.lower() == "frostbite": - print(f"\n{monster.name} rolls a constitution saving throw of {monster.constitution_save + save_roll} " - f"compared to a spell save DC of {self.spell_save_dc}.") - if (monster.constituion_save + save_roll) < self.spell_save_dc: - print(f"\n{monster.name} has failed their save and takes {self.weapon_value} points of damage and " - f"has disadvantage on their next attack roll.") - else: - print(f"\n{monster.name} has succeeded their save and suffers no ill effects.") - else: - print(f"\n{self.name} rolls an attack roll of {self.attack_bonus + attack_roll} compared to an AC of " - f"{monster.armor_class}. ") - if monster.armor_class < self.attack_bonus + attack_roll: - attack_damage = self.weapon_value - print(f"\n{self.name} has landed a hit for {attack_damage} points of damage.") - monster.lose_hit_points(attack_damage) - else: - print(f"\n{monster.name} has successfully evaded the attack.") - - def lose_hit_points(self, amount): - self.hit_points -= amount - if self.hit_points <= 0: - self.hit_points = 0 - print(f"\n{self.name} has died.") - else: - print(f"\n{self.name} takes {amount} damage and has {self.hit_points} hit points remaining.") - - -# Class represents a monter in DnD. -class Monster_DnD: - def __init__(self, name, hit_points, armor_class, weapon, weapon_value, challenge_rating, constitution_save, - attack_bonus): - self.name = name - self.hit_points = hit_points - self.armor_class = armor_class - self.weapon = weapon - self.weapon_value = weapon_value - self.challenge_rating = challenge_rating - self.is_dead = False - self.constitution_save = constitution_save - self.attack_bonus = attack_bonus - - def __repr__(self): - return (f"\n {self.name} is a challenge rating {self.challenge_rating} monster with {self.hit_points} hit " - f"points, {self.armor_class} armor class and that wields {self.weapon}. ") - - def monster_attack(self, player): - attack_roll = (randint(1, 20)) - print(f"\n{self.name.title()} attempts to attack {player.name.title()} with {self.weapon}.") - print(f"\n{self.name.title()} rolls an attack roll of {self.attack_bonus + attack_roll} compared to an AC of " - f"{player.armor_class}. ") - if player.armor_class < self.attack_bonus + attack_roll: - attack_damage = self.weapon_value - print(f"\n{self.name.title()} has landed a hit for {attack_damage} points of damage.") - player.lose_hit_points(attack_damage) - else: - print(f"\n{player.name} has successfully evaded the attack.") - - def lose_hit_points(self, amount): - self.hit_points -= amount - if self.hit_points <= 0: - self.hit_points = 0 - print(f"\n{self.name} has died.") - else: - print(f"\n{self.name} takes {amount} damage and has {self.hit_points} hit points remaining.") diff --git a/dice.py b/dice.py new file mode 100644 index 0000000..4cfe191 --- /dev/null +++ b/dice.py @@ -0,0 +1,3 @@ +from random import randint + +roll = lambda a, b: sum([randint(1, b) for i in range(a)]) diff --git a/entity.py b/entity.py new file mode 100644 index 0000000..94db88d --- /dev/null +++ b/entity.py @@ -0,0 +1,18 @@ +class Entity: + def __init__(self, name): + self.name = name + self.max_hp = 0 + self.current_hp = 0 + + + def get_hp_str(self): + return f"{self.name} has {self.current_hp} out of {self.max_hp}" + + + def take_damage(self, amount): + if self.current_hp <= amount: + self.current_hp= 0 + print(f"{self.name}'s lifeless form crumples to the floor.") + else: + self.current_hp -= amount + print(f"{self.name} takes {amount} damage and has {self.current_hp} hp remaining of {self.max_hp}.") diff --git a/equipment.py b/equipment.py new file mode 100644 index 0000000..dbad812 --- /dev/null +++ b/equipment.py @@ -0,0 +1,61 @@ +from dataclasses import dataclass + + +# THIS NEEDS TO BE FIXED +MAX_INT = 99999 + + +@dataclass +class Armor: + name: str + proficiency: str + cost_gp: int + ac: int + max_dex_bonus: int + min_str: int + stealth_disadvantage: bool + weight: int + + +armors = list() +armors.append(Armor("Scale Mail", "medium", 50, 14, 2, 0, True, 45)) +armors.append(Armor("Studded Leather", "light", 10, 11, MAX_INT, 0, False, 13)) + + +@dataclass +class Weapon: + name: str + martial: bool + ranged: bool + cost: int + damage: (int, int) + damage_type: str + finesse: bool + heavy: bool + light: bool + loading: bool + range_distance: (int, int) + reach: bool + thrown: bool + two_handed: bool + versatile: bool + versatile_damage: (int, int) + + +weapons = list() +weapons.append(Weapon("Club", + False, + False, + 1, + (1, 4), + "bludgeoning", + False, + False, + False, + False, + (0, 0), + False, + False, + False, + False, + (0, 0))) diff --git a/main.py b/main.py old mode 100644 new mode 100755 index 92a9b95..522cabc --- a/main.py +++ b/main.py @@ -1,35 +1,17 @@ -from character_dnd import Character_DnD -from character_dnd import Monster_DnD -from random import randint +#!/usr/bin/env python3 -# Getting input on character name. -character_name = input( - "Please type what you would like your character name to be? ") -# Getting input on character species. -character_species = input("Please type what you would like your character species to be? Please choose from Human, " - "Elf, or Dwarf. Then hit enter. ") +from character import Character +from monster import monsters -while character_species.lower() not in ["human", "elf", "dwarf"]: - character_species = input("Whoops, looks like you didn't choose one of the previous species. Please choose from " - "Human, Elf, or Dwarf. Then hit enter. ") -# Getting input on character job. -character_job = input("Please type what you would like your character's job to be? Please choose from Fighter, Rogue, " - "or Wizard. Then hit enter. ") -while character_job.lower() not in ["fighter", "rogue", "wizard"]: - character_job = input("Whoops, looks like you didn't choose one of the previous jobs. PLease choose from Fighter, " - "Rogue, or Wizard. Then hit enter. ") +def fight(p1, p2): + while p1.current_hp > 0 and p2.current_hp > 0: + p1.attack(p2) + if p2.current_hp > 0: + p2.attack(p1) -my_character = Character_DnD(character_name, character_species, character_job) -Kobold = Monster_DnD("Kobold", 5, 12, "a dagger", randint(1, 4), "1/8", -1, 4) -red_dragon = Monster_DnD("the Ancient Red Dragon", 546, 22, "a Bite", randint(2, 10) + 24, 24, 9, 17) -my_character.change_attribute_points() -my_character.weapon_choice() -my_character.armor_choice() -my_character.player_attack(Kobold) -my_character.player_attack(red_dragon) -red_dragon.monster_attack(my_character) -Kobold.monster_attack(my_character) -# print(my_character.attribute_points) -# print(my_character) +if __name__ == "__main__": + foo = Character() + bar = monsters["Kobold"] + fight(foo, bar) diff --git a/monster.py b/monster.py new file mode 100644 index 0000000..6868342 --- /dev/null +++ b/monster.py @@ -0,0 +1,51 @@ +from entity import Entity +from dice import roll + + +class Monster(Entity): + def __init__(self, + name, + hit_points, + armor_class, + attack_name, + attack_bonus, + damage_roll, + damage_bonus): + Entity.__init__(self, name) + self.name = name + self.max_hp = hit_points + self.current_hp = self.max_hp + self.armor_class = armor_class + self.attack_name = attack_name + self.attack_bonus = attack_bonus + self.damage_roll = damage_roll + self.damage_bonus = damage_bonus + + + def __repr__(self): + return (f"\n {self.name} is a challenge rating {self.challenge_rating} monster with {self.hit_points} hit " + f"points, {self.armor_class} armor class and that wields {self.weapon}. ") + + + def attack(self, enemy): + attack_roll = roll(1, 20) + self.attack_bonus + print(f"{self.name} swings their {self.attack_name} at the {enemy.name}!") + if enemy.get_ac() < attack_roll: + print(f"{self.name} smacks {enemy.name} a good one! {attack_roll} vs {enemy.get_ac()} AC") + enemy.take_damage(roll(self.damage_roll) + self.damage_bonus) + else: + print(f"{self.name} wiffs!") + + + def get_ac(self): + return self.armor_class + + +monsters = dict() +monsters["Kobold"] = Monster("Kobold", + 5, + 12, + "dagger", + 0, + (1, 4), + 0) From b6e380f93d845693be6c052158089f04e00d6aad Mon Sep 17 00:00:00 2001 From: Mickey Lyle Date: Wed, 4 Jan 2023 14:25:12 -0800 Subject: [PATCH 2/2] Implemented RollResult and Greatsword. --- character.py | 20 ++++++++++++++------ dice.py | 31 ++++++++++++++++++++++++++++++- equipment.py | 51 +++++++++++++++++++++++++++++++++++---------------- monster.py | 11 +++++++---- 4 files changed, 86 insertions(+), 27 deletions(-) diff --git a/character.py b/character.py index bd68a14..5d8e0eb 100644 --- a/character.py +++ b/character.py @@ -67,7 +67,12 @@ def pick_attributes(self, points): print() print(f"Attribute Points: {points}") print(error) - choice = int(input("Choose an attribute to increase: ")) + try: + choice = int(input("Choose an attribute to increase: ")) + except ValueError: + error = f"Please pick a number between {min(numbers)} and {max(numbers)}" + continue + print() if choice in numbers: choice -= 1 @@ -100,7 +105,7 @@ def get_proficiency_bonus(self): def roll_weapon_damage(self): - return roll(*self.weapon.damage) + self.get_attack_attr_bonus() + return roll(self.weapon.damage[0], self.weapon.damage[1], self.get_attack_attr_bonus()) def __str__(self): @@ -142,11 +147,14 @@ def picker(self, pick_type, pick_list): def attack(self, enemy): - attack_roll = roll(1, 20) + self.get_attack_bonus() + attack_roll = roll(1, 20, self.get_attack_bonus()) print(f"{self.name} swings their {self.weapon.name} at the {enemy.name}!") - if enemy.armor_class < attack_roll: - print(f"{self.name} smacks the {enemy.name} a good one! {attack_roll} vs {enemy.get_ac()} AC") - enemy.take_damage(self.roll_weapon_damage()) + print(attack_roll) + if enemy.armor_class < attack_roll.result: + print(f"{self.name} smacks the {enemy.name} a good one! {attack_roll.result} vs {enemy.get_ac()} AC") + damage_roll = self.roll_weapon_damage() + print(damage_roll) + enemy.take_damage(damage_roll.result) else: print(f"{self.name} wiffs!") diff --git a/dice.py b/dice.py index 4cfe191..d471923 100644 --- a/dice.py +++ b/dice.py @@ -1,3 +1,32 @@ from random import randint -roll = lambda a, b: sum([randint(1, b) for i in range(a)]) +class RollResult: + def __init__(self, roll, modifier, result, string): + self.roll = roll + self.modifier = modifier + self.result = result + self.string = string + + def __str__(self): + return self.string + +def roll(count, sides, modifier): + roll = [randint(1, sides) for i in range(count)] + roll_string = "(" + ", ".join([str(n) for n in roll]) + ")" + result = sum(roll) + modifier + string = f"Rolled {count}d{sides} + {modifier}: {result} {roll_string}" + return RollResult(roll, modifier, result, string) + +def roll_advantage(modifier): + roll = [randint(1, 20) for i in range(2)] + roll_string = "(" + ", ".join([str(n) for n in roll]) + ")" + result = max(roll) + modifier + string = f"Rolled 1d20 + {modifier} with advantage: {result} {roll_string}" + return RollResult(roll, modifier, result, string) + +def roll_disadvantage(modifier): + roll = [randint(1, 20) for i in range(2)] + roll_string = "(" + ", ".join([str(n) for n in roll]) + ")" + result = min(roll) + modifier + string = f"Rolled 1d20 + {modifier} with disadvantage: {result} {roll_string}" + return RollResult(roll, modifier, result, string) diff --git a/equipment.py b/equipment.py index dbad812..3f403e0 100644 --- a/equipment.py +++ b/equipment.py @@ -30,6 +30,7 @@ class Weapon: cost: int damage: (int, int) damage_type: str + weight: int finesse: bool heavy: bool light: bool @@ -43,19 +44,37 @@ class Weapon: weapons = list() -weapons.append(Weapon("Club", - False, - False, - 1, - (1, 4), - "bludgeoning", - False, - False, - False, - False, - (0, 0), - False, - False, - False, - False, - (0, 0))) +weapons.append(Weapon("Club", # name + False, # martial + False, # ranged + 1, # cost + (1, 4), # damage + "bludgeoning", # damage type + 2, # weight + False, # finesse + False, # heavy + False, # light + False, # loading + (0, 0), # range distance + False, # reach + False, # thrown + False, # two handed + False, # versatile + (0, 0))) # versatile damage +weapons.append(Weapon("Greatsword", # name + True, # martial + False, # ranged + 50, # cost + (2, 6), # damage + "slashing", # damage type + 6, # weight + False, # finesse + True, # heavy + False, # light + False, # loading + (0, 0), # range distance + False, # reach + False, # thrown + True, # two handed + False, # versatile + (0, 0))) # versatile damage diff --git a/monster.py b/monster.py index 6868342..79983f2 100644 --- a/monster.py +++ b/monster.py @@ -28,11 +28,14 @@ def __repr__(self): def attack(self, enemy): - attack_roll = roll(1, 20) + self.attack_bonus + attack_roll = roll(1, 20, self.attack_bonus) print(f"{self.name} swings their {self.attack_name} at the {enemy.name}!") - if enemy.get_ac() < attack_roll: - print(f"{self.name} smacks {enemy.name} a good one! {attack_roll} vs {enemy.get_ac()} AC") - enemy.take_damage(roll(self.damage_roll) + self.damage_bonus) + print(attack_roll) + if enemy.get_ac() < attack_roll.result: + print(f"{self.name} smacks {enemy.name} a good one! {attack_roll.result} vs {enemy.get_ac()} AC") + damage_roll = roll(self.damage_roll[0], self.damage_roll[1], self.damage_bonus) + print(damage_roll) + enemy.take_damage(damage_roll.result) else: print(f"{self.name} wiffs!")