Browse Source

Refactored and modularized several methods.

main
suzan 2 weeks ago
parent
commit
e18048a0a7
  1. 240
      creation_engine.py
  2. 9
      main.py

240
creation_engine.py

@ -1,9 +1,7 @@
import random
import json
# TODO AC? Or Gold Start no AC?
# TODO Feats?
# Constants Lists
VOWELS = ["A", "E", "I", "O", "U"]
SKILL_LIST = ["acrobatics", "animal_handling", "arcana", "athletics", "deception", "history", "insight", "intimidation",
@ -12,24 +10,13 @@ SKILL_LIST = ["acrobatics", "animal_handling", "arcana", "athletics", "deception
ATTRIBUTE_LIST = ["strength", "dexterity", "wisdom", "constitution", "intelligence", "charisma"]
CLASS_LIST = ["Artificer", "Barbarian", "Bard", "Cleric", "Druid", "Fighter", "Monk", "Paladin",
JOB_LIST = ["Artificer", "Barbarian", "Bard", "Cleric", "Druid", "Fighter", "Monk", "Paladin",
"Ranger", "Rogue", "Sorcerer", "Warlock", "Wizard", "Blood Hunter"]
BACKGROUND_LIST = ["Acolyte", "Charlatan", "Criminal / Spy", "Entertainer", "Folk Hero", "Gladiator",
"Guild Artisan / Guild Merchant", "Hermit", "Knight", "Noble", "Outlander", "Pirate", "Sage",
"Sailor", "Soldier", "Urchin", ]
instrument_list = ["Bagpipes", "Birdpipes", "Clarinet", "Drum", "Dulcimer", "Fiddle", "Flute", "Glaur", "Hand Drum",
"Harp", "Horn", "Longhorn", "Lute", "Lyre", "Pan Flute", "Shawm", "Songhorn", "Tantan", "Thelarr",
"Tocken", "Trumpet", "Viol", "Wargong", "Yarting", "Zulkoon"]
languages = ["Aarakocra", "Abyssal", "Aquan", "Auran", "Celestial", "Deep Speech", "Draconic", "Dwarvish", "Elvish",
"Giant",
"Gith", "Gnomish", "Goblin", "Halfling", "Infernal", "Orc", "Primordial", "Sylvan", "Undercommon",
"Vedalken"]
known_language_list = ["Common"]
GAMING_SETS = ["Bowls", "Darts", "Dice Set", "Dragonchess Set", "Playing Card Set", "Quoits", "Three-Dragon Ante Set"]
ARTISANS_TOOLS = ["Alchemist's Supplies", "Brewer's Supplies", "Calligrapher's Supplies", "Carpenter's Tools",
@ -48,21 +35,33 @@ WIZARD_SUBCLASSES = ["Bladesinging", "Chronurgy Magic", "Graviturgy Magic", "Ord
"School of Conjuration", "School of Divination", "School of Enchantment", "School of Evocation",
"School of Illusion", "School of Necromancy", "School of Transmutation", "War Magic"]
# Variable/Functional Lists
instrument_list = ["Bagpipes", "Birdpipes", "Clarinet", "Drum", "Dulcimer", "Fiddle", "Flute", "Glaur", "Hand Drum",
"Harp", "Horn", "Longhorn", "Lute", "Lyre", "Pan Flute", "Shawm", "Songhorn", "Tantan", "Thelarr",
"Tocken", "Trumpet", "Viol", "Wargong", "Yarting", "Zulkoon"]
languages = ["Aarakocra", "Abyssal", "Aquan", "Auran", "Celestial", "Deep Speech", "Draconic", "Dwarvish", "Elvish",
"Giant", "Gith", "Gnomish", "Goblin", "Halfling", "Infernal", "Orc", "Primordial", "Sylvan", "Undercommon",
"Vedalken"]
known_language_list = ["Common"]
# Begin the class for creation
class HeroCreation:
def __init__(self):
self.name_generation()
self.race_selection()
self.race = self.race_string["Race"]
self.race = self.race_dict["Race"]
# 'job' is used in place of 'class' to avoid a function conflict
self.job = random.choice(CLASS_LIST)
self.job = random.choice(JOB_LIST)
self.job_characteristics()
self.background = random.choice(BACKGROUND_LIST)
self.stat_rolls()
self.racial_bonuses()
self.speed = self.race_string["Speed"]
self.article = self.grammar()
self.speed = self.race_dict["Speed"]
self.skill_generation()
self.skill_proficiency()
#Initial configuration of tool proficiencies. These are left alone if none are selected.
self.instruments = "None"
self.artisan_tools = "None"
self.additional_tools = "None"
@ -72,51 +71,29 @@ class HeroCreation:
self.background_proficiencies()
self.finalize_languages()
self.apply_skill_modifiers("dexterity", "initiative")
self.whoami()
self.stat_block = (f"Stat Block\n----------\nCharisma: {self.charisma}\nConstitution: {self.constitution}\n"
f"Dexterity: {self.dexterity}\nIntelligence: {self.intelligence}\n"
f"Strength: {self.strength}\nWisdom: {self.wisdom}\n")
self.saves = (f"Saving Throws\n----------\nCharisma Save: {self.charisma_save}\n"
f"Constitution Save: {self.constitution_save}\n"
f"Dexterity Save: {self.dexterity_save}\n"
f"Intelligence Save: {self.intelligence_save}\n"
f"Strength Save: {self.strength_save}\n"
f"Wisdom Save: {self.wisdom_save}\n")
self.additional_stats = (
f"Additional Stats\n----------\nStarting HP: {self.starting_hp}\nHit Die: {self.hit_die}\n"
f"Initiative Bonus: {self.initiative}\nSpeed: {self.speed}\n")
self.skill_bonuses = (
f"Skill Bonuses\n----------\nAcrobatics: {self.acrobatics}\nAnimal Handling: {self.animal_handling}"
f"\nArcana: {self.arcana}\nAthletics: {self.athletics}\nDeception: {self.deception}"
f"\nHistory: {self.history}\nInsight: {self.insight}\nIntimidation: {self.intimidation}"
f"\nInvestigation: {self.investigation}\nMedicine: {self.medicine}\nNature: {self.nature}"
f"\nPerception: {self.perception}\nPerformance: {self.performance}\nPersuasion: {self.persuasion}"
f"\nReligion: {self.religion}\nSleight of Hand: {self.sleight_of_hand}"
f"\nStealth: {self.stealth}\nSurvival: {self.survival}\n")
self.other_proficiencies = (f"Additional Proficiencies\n----------\n"
f"Languages: {self.languages}\n"
f"Instruments: {self.instruments}\n"
f"Artisan's Tools: {self.artisan_tools}\nAdditional Tools: "
f"{self.additional_tools}\n"
f"Weapon Proficiencies: {self.weapon_proficiency}\n"
f"Armor Proficiencies: {self.armor_proficiency}\n")
# Assign the correct a/an article in the 'whoami' attr
def grammar(self):
if self.race[0] in VOWELS:
return "an"
else:
return "a"
self.print_generator()
self.output()
# Selects two random entries from a large text file containing fantasy names.
def name_generation(self):
with open("names.txt", "r") as f:
name_list = f.read().splitlines()
first_name = random.choice(name_list)
sur_name = random.choice(name_list)
combined_name = f"{first_name} {sur_name}"
setattr(self, "name", combined_name)
# Chooses race at random. Each race has a dictionary with other assigned values/proficiencies
# Things like attribute bonuses or language proficiencies come from this dictionary
def race_selection(self):
with open('races.json') as json_file:
race_data = json.load(json_file)
chosen_race = random.choice(race_data)
setattr(self, "race_string", chosen_race)
setattr(self, "race_dict", chosen_race)
# Rolls the 6 main attributes in the style of roll for d6, drop the lowest.
# Configured to "Re-roll" 1s. To include 1s, increase the randint range to 1,6
def stat_rolls(self):
# Configured to "Re-roll" 1s
# To include 1s, increase the randint range to 1,6
for attribute in ATTRIBUTE_LIST:
roll_list = []
for each in range(4):
@ -125,22 +102,14 @@ class HeroCreation:
stat_total = sum(roll_list)
setattr(self, f"{attribute}", stat_total)
# Assign racial stat bonuses
# Assign racial stat bonuses from the race_dict pulled from races.json
def racial_bonuses(self):
self.charisma += self.race_string["Charisma"]
self.constitution += self.race_string["Constitution"]
self.dexterity += self.race_string["Dexterity"]
self.intelligence += self.race_string["Intelligence"]
self.strength += self.race_string["Strength"]
self.wisdom += self.race_string["Wisdom"]
def name_generation(self):
with open("names.txt", "r") as f:
name_list = f.read().splitlines()
first_name = random.choice(name_list)
sur_name = random.choice(name_list)
combined_name = f"{first_name} {sur_name}"
setattr(self, "name", combined_name)
self.charisma += self.race_dict["Charisma"]
self.constitution += self.race_dict["Constitution"]
self.dexterity += self.race_dict["Dexterity"]
self.intelligence += self.race_dict["Intelligence"]
self.strength += self.race_dict["Strength"]
self.wisdom += self.race_dict["Wisdom"]
# Created skill attributes and save attributes with a base value of +0 to the roll
def skill_generation(self):
@ -162,6 +131,8 @@ class HeroCreation:
setattr(self, save_name, 0)
self.apply_skill_modifiers(attribute, save_name)
# Receives input for all skills and saves and applies a modifier to the roll based on the associated
# attribute value
def apply_skill_modifiers(self, attribute_name, skill_name):
attribute = getattr(self, attribute_name)
if attribute == 1:
@ -191,27 +162,32 @@ class HeroCreation:
setattr(self, skill_name, modifier)
def skill_proficiency(self):
# Pulls the available skills that each background is proficient in and applies a +2 proficiency modifier
with open('backgrounds.json') as json_file:
background_data = json.load(json_file)
# Is this actually a dict?
background_dict = background_data[self.background]["Skills"]
for skill in background_dict:
background_list = background_data[self.background]["Skills"]
for skill in background_list:
current_score = getattr(self, skill)
new_score = str(current_score + 2)
setattr(self, skill, f"{new_score} (Prof)")
# Pulls the available skills that each job can choose to be proficient in
with open('jobs.json') as json_file:
job_data = json.load(json_file)
# Is this actually a dict?
job_dict = job_data[self.job]
# Pulls the number of proficiencies able to be chosen
proficiencies = job_dict["Proficiencies"]
# Pulls available skills to be proficient in
job_skill_list = job_dict["Skills"]
for skill in background_dict:
# Removes any skills chosen by background proficiencies, so they can not be chosen as double proficient.
for skill in background_list:
if skill in job_skill_list:
job_skill_list.remove(skill)
# rogue list for expertise calculation
# Rogue list for expertise calculation (Rogues get one double proficiency at level 1)
rogue_proficient_list = []
# Choose proficient skills and add a +2 modifier for the level 1 proficiency bonus.
for each in range(proficiencies):
proficient_skill = random.choice(job_skill_list)
# Chooses rogue proficiencies and saves them to a list for expertise selection
if self.job == "Rogue":
rogue_proficient_list.append(proficient_skill)
job_skill_list.remove(proficient_skill)
@ -219,12 +195,14 @@ class HeroCreation:
setattr(self, f"{proficient_skill}_base_value", current_score)
new_score = str(current_score + 2)
setattr(self, proficient_skill, f"{new_score} (Prof)")
# Selects one proficient skill to be chosen for expertise for rogues.
if self.job == "Rogue":
expertise_skill = random.choice(rogue_proficient_list)
score_value = getattr(self, f"{expertise_skill}_base_value")
new_score = score_value + 4
setattr(self, expertise_skill, f"{new_score} (Expertise)")
# Assigns additional stats from the job dictionary
def job_characteristics(self):
with open('jobs.json') as json_file:
job_data = json.load(json_file)
@ -233,9 +211,11 @@ class HeroCreation:
starting_hp_value = job_data[self.job]["Starting HP"]
setattr(self, "starting_hp", starting_hp_value)
#Assigns language and tool proficiencies based on job
def job_proficiencies(self):
with open('jobs.json') as json_file:
job_data = json.load(json_file)
# Monk receives a choice of instrument tool or artisan tool. This selects one at random.
if self.job == "Monk":
tool_choice = random.randint(0, 1)
if tool_choice == 0:
@ -243,6 +223,7 @@ class HeroCreation:
else:
artisan_tool_value = 1
else:
# Attempt to pull number of proficiencies for each job. Some have none in these values.
try:
instrument_value = job_data[self.job]["Instruments"]
except KeyError:
@ -251,8 +232,10 @@ class HeroCreation:
artisan_tool_value = job_data[self.job]["Artisan's Tools"]
except KeyError:
pass
# Empty list creation for tools and instruments. May not be populated.
instrument_prof_list = []
artisan_prof_list = []
# Attempts to assign random tool values, assuming the job has any proficiencies available
try:
for value in range(0, instrument_value):
new_instrument = random.choice(instrument_list)
@ -277,25 +260,30 @@ class HeroCreation:
setattr(self, "additional_tools", str(additional_tool_string))
except KeyError:
pass
# Set Armor proficiencies and update the attribute
armor_prof_list = job_data[self.job]["Armor Proficiency"]
if not armor_prof_list:
setattr(self, "armor_proficiency", "None")
else:
armor_prof_string = ", ".join(armor_prof_list)
setattr(self, "armor_proficiency", armor_prof_string)
# Set Weapon proficiencies and update the attribute
weapon_prof_list = job_data[self.job]["Weapon Proficiency"]
weapon_prof_string = ", ".join(weapon_prof_list)
setattr(self, "weapon_proficiency", weapon_prof_string)
#Add proficiency modifier of +2 to both saving throws that each job is proficient with.
for attribute in job_data[self.job]["Saving Throws"]:
current_score = getattr(self, f"{attribute}_save")
new_score = str(current_score + 2)
setattr(self, f"{attribute}_save", f"{new_score} (Prof)")
# Assigns tool and instrument proficiencies based on background
def background_proficiencies(self):
# Importing list that starts with only "Common" and will be added to
global known_language_list
with open('backgrounds.json') as json_file:
background_data = json.load(json_file)
# background instrument proficiencies
# Attempt instrument proficiencies (if any are available)
try:
instrument_value = background_data[self.background]["Additional Proficiencies"]["Instrument"]
except KeyError or UnboundLocalError:
@ -307,7 +295,7 @@ class HeroCreation:
setattr(self, "instruments", f"{new_instrument}")
else:
setattr(self, "instruments", current_instruments + f", {new_instrument}")
# background artisan's tools proficiencies
# Attempt artisan's tool proficiencies (if any are available)
try:
tool_value = background_data[self.background]["Additional Proficiencies"]["Artisan's Tool"]
except KeyError or UnboundLocalError:
@ -319,7 +307,7 @@ class HeroCreation:
setattr(self, "artisan_tools", f"{new_tool}")
else:
setattr(self, "artisan_tools", current_tools + f", {new_tool}")
# language proficiencies
# Attempt language proficiencies (if any are available)
try:
language_value = background_data[self.background]["Additional Proficiencies"]["Languages"]
except UnboundLocalError:
@ -331,7 +319,7 @@ class HeroCreation:
languages.remove(new_language)
except IndexError:
pass
# additional tool proficiencies
# Attempt additional tool proficiencies (if any are available)
additional_tool_list = background_data[self.background]["Additional Proficiencies"]["Additional Tools"]
if not additional_tool_list:
pass
@ -347,17 +335,20 @@ class HeroCreation:
new_tool_list = getattr(self, "additional_tools") + f", {tool}"
setattr(self, "additional_tools", new_tool_list)
# Assigns tool and instrument proficiencies based on race
def race_proficiencies(self):
# Importing list that starts with only "Common" and will be added to
global known_language_list
# Adds racial language proficiencies
try:
known_languages = self.race_string["Languages"]
known_languages = self.race_dict["Languages"]
for language in known_languages:
known_language_list.append(language)
print(language)
languages.remove(language)
except KeyError:
pass
for value in range(0, self.race_string["AdditionalLanguages"]):
# Attempt additional language proficiencies (if the race is able to learn additional languages)
for value in range(0, self.race_dict["AdditionalLanguages"]):
try:
new_language = random.choice(languages)
known_language_list.append(new_language)
@ -365,30 +356,89 @@ class HeroCreation:
except IndexError:
pass
# Creates and joins a string of languages using the known_language_list updated by background and race
def finalize_languages(self):
global known_language_list
language_string = ", ".join(known_language_list)
setattr(self, "languages", language_string)
def whoami(self):
# Creates attributes for the different text blocks that will be printed as an output
def print_generator(self):
# Grammar check for using "a" or "an" when describing which race was chosen
if self.race[0] in VOWELS:
setattr(self,"article","an")
else:
setattr(self,"article","a")
# Chooses subclass for Cleric, as they get one at level 1. Creates the "whoami" attribute
if self.job == "Cleric":
subclass = random.choice(CLERIC_SUBCLASSES)
setattr(self,"subclass",subclass)
print(f"Your new character is {self.name}, {self.article} {self.race} {self.job}, with the "
f"{self.background} background and the {self.subclass} subclass.\n")
setattr(self, "subclass", subclass)
setattr(self, "whoami",
f"Your new character is {self.name}, {self.article} {self.race} {self.job}, with the "
f"{self.background} background and the {self.subclass} subclass.\n")
# Chooses subclass for Sorcerer, as they get one at level 1. Creates the "whoami" attribute
elif self.job == "Sorcerer":
subclass = random.choice(SORCERER_SUBCLASSES)
setattr(self, "subclass", subclass)
print(f"Your new character is {self.name}, {self.article} {self.race} {self.job}, with the "
f"{self.background} background and the {self.subclass} subclass.\n")
setattr(self, "whoami",
f"Your new character is {self.name}, {self.article} {self.race} {self.job}, with the "
f"{self.background} background and the {self.subclass} subclass.\n")
# Chooses subclass for Wizard, as they get one at level 1. Creates the "whoami" attribute
elif self.job == "Wizard":
subclass = random.choice(WIZARD_SUBCLASSES)
setattr(self, "subclass", subclass)
print(f"Your new character is {self.name}, {self.article} {self.race} {self.job}, with the "
f"{self.background} background and the {self.subclass} subclass.\n")
setattr(self, "whoami",
f"Your new character is {self.name}, {self.article} {self.race} {self.job}, with the "
f"{self.background} background and the {self.subclass} subclass.\n")
# For all other classes, creates the "whoami" attribute
else:
print(f"Your new character is {self.name}, {self.article} {self.race} {self.job}, with the "
f"{self.background} background.\n")
setattr(self, "whoami", f"Your new character is {self.name}, {self.article} {self.race} {self.job}, with the "
f"{self.background} background.\n")
# Create the stat_block attribute
setattr(self,"stat_block", f"Stat Block\n----------\nCharisma: {self.charisma}\nConstitution: {self.constitution}\n"
f"Dexterity: {self.dexterity}\nIntelligence: {self.intelligence}\n"
f"Strength: {self.strength}\nWisdom: {self.wisdom}\n")
# Create the saving throw attribute
setattr(self, "saves", "Saving Throws\n----------\nCharisma Save: {self.charisma_save}\n"
f"Constitution Save: {self.constitution_save}\n"
f"Dexterity Save: {self.dexterity_save}\n"
f"Intelligence Save: {self.intelligence_save}\n"
f"Strength Save: {self.strength_save}\n"
f"Wisdom Save: {self.wisdom_save}\n")
# Create the additional stats attribute
setattr(self, "additional_stats", f"Additional Stats\n----------\nStarting HP: {self.starting_hp}\n"
f"Hit Die: {self.hit_die}\n"
f"Initiative Bonus: {self.initiative}\nSpeed: {self.speed}\n")
# Create the skill bonuses attribute
setattr(self, "skill_bonuses", f"Skill Bonuses\n----------\nAcrobatics: {self.acrobatics}\n"
f"Animal Handling: {self.animal_handling}"
f"\nArcana: {self.arcana}\nAthletics: {self.athletics}\nDeception: "
f"{self.deception}"
f"\nHistory: {self.history}\nInsight: {self.insight}\nIntimidation: "
f"{self.intimidation}"
f"\nInvestigation: {self.investigation}\nMedicine: {self.medicine}\nNature: "
f"{self.nature}"
f"\nPerception: {self.perception}\nPerformance: "
f"{self.performance}\nPersuasion: {self.persuasion}"
f"\nReligion: {self.religion}\nSleight of Hand: {self.sleight_of_hand}"
f"\nStealth: {self.stealth}\nSurvival: {self.survival}\n")
# Create the additional proficiencies attribute
setattr(self, "additional_proficiencies", f"Additional Proficiencies\n----------\n"
f"Languages: {self.languages}\n"
f"Instruments: {self.instruments}\n"
f"Artisan's Tools: {self.artisan_tools}\nAdditional Tools: "
f"{self.additional_tools}\n"
f"Weapon Proficiencies: {self.weapon_proficiency}\n"
f"Armor Proficiencies: {self.armor_proficiency}\n")
# Prints all stats, bonuses, and character info
def output(self):
print(self.whoami)
print(self.stat_block)
print(self.saves)
print(self.additional_stats)
print(self.skill_bonuses)
print(self.additional_proficiencies)

9
main.py

@ -1,10 +1,3 @@
import creation_engine
hero = creation_engine.HeroCreation()
print(hero.stat_block)
print(hero.saves)
print(hero.additional_stats)
print(hero.skill_bonuses)
print(hero.other_proficiencies)
print(hero.job)
hero = creation_engine.HeroCreation()
Loading…
Cancel
Save