Created
August 25, 2023 13:20
-
-
Save DHCross/2a16f2607b4e7221c4349e2cab4d4c1f to your computer and use it in GitHub Desktop.
Python character generator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import random | |
# Define the races | |
races = { | |
"Dwarf": { | |
"base_die_ranks": {"Fortitude": "d4", "Endurance": "d4", "Prowess": "d6", "Melee": "d6"}, | |
"advantages": ["Night Vision", "Strong-willed", "Sense of Direction"] | |
}, | |
"Elf": { | |
"base_die_ranks": {"Competence": "d6", "Wizardry": "+1", "Prowess": "d4", "Agility": "d4", "Reaction": "+1"}, | |
"advantages": ["Night Vision", "Gift of Magic", "Magic Resistance (+1)"] | |
}, | |
"Gnome": { | |
"base_die_ranks": {"Competence": "d6", "Adroitness": "d6", "Perception": "d4", "Perspicacity": "+1"}, | |
"advantages": ["Eidetic Memory", "Low-Light Vision", "Observant"], | |
"flaw": "Restriction: small weapons only" | |
}, | |
"Half-Elf": { | |
"base_die_ranks": {"Competence": "d6", "Agility": "d4", "Reaction": "+1", "Endurance": "d4", "Willpower": "d4"}, | |
"advantages": ["Heightened Senses", "Low-Light Vision", "Magic Resistance (+1)"] | |
}, | |
"Half-Orc": { | |
"base_die_ranks": {"Fortitude": "d8", "Endurance": "d6", "Strength": "d8", "Ferocity": "+1"}, | |
"advantages": ["Low-light Vision", "Intimidation", "Menacing"], | |
"flaw": "Ugliness" | |
}, | |
"Human": { | |
"base_die_ranks": {"Competence": "d6", "Threat": "+1", "Prowess": "d4", "Melee": "d4", "Willpower": "d6"}, | |
"advantages": ["Fortunate", "Survival"] | |
} | |
} | |
# Define the classes | |
classes = { | |
"Adept (Wizardry)": { | |
"base_die_ranks": {"Competence": "d6", "Expertise": "d6", "Wizardry": "+1", "Perception": "d4", "Perspicacity": "+1"}, | |
"advantages": ["Arcanum", "Gift of Magic", "Literacy", "Scholar"], | |
"feats": ["Guile", "Lore", "Magical Artifice", "Quick-witted"] | |
}, | |
"Rogue (Skullduggery)": { | |
"base_die_ranks": {"Competence": "d4", "Skulduggery": "+1", "Perception": "d4", "Prowess": "d8", "Agility": "d8"}, | |
"advantages": ["Expeditious", "Fortunate", "Streetwise", "Underworld Contacts"], | |
"feats": ["Backstab", "Evasion", "Roguish Charm", "Stealth"] | |
}, | |
"Theurgist (Theurgy)": { | |
"base_die_ranks": {"Competence": "d6", "Theurgy": "+1", "Fortitude": "d4", "Willpower": "d4"}, | |
"advantages": ["Gift of Magic", "Magic Defense", "Religion", "Strong-Willed"], | |
"feats": ["Divine Healing", "Path Mastery (Druidry or Hereticism)", "Spiritual Smite", "Supernatural Intervention"] | |
}, | |
"Warrior (Warcraft)": { | |
"base_die_ranks": {"Prowess": "d8", "Melee": "d6", "Threat": "+1", "Fortitude": "d6"}, | |
"advantages": ["Commanding", "Intimidation", "Magic Resistance (+1)", "Tactician"], | |
"feats": ["Battle Savvy", "Maneuvers", "Stunning Reversal", "Sunder Foe"] | |
}, | |
"Assassin (Skullduggery)": { | |
"base_die_ranks": {"Competence": "d4", "Adroitness": "d6", "Perception": "d4", "Agility": "d4", "Vitality": "d6"}, | |
"focuses": {"Finesse +1 or Ranged Finesse +1": "d4"}, | |
"advantages": ["Expeditious", "Heightened Senses (hearing)", "Observant", "Read Emotions"], | |
"feats": ["Death Strike", "Lethal Exploit", "Ranged Ambush", "Shadow Walk"] | |
} | |
} | |
# Define the core abilities and their associated focus areas | |
core_abilities_specialties = { | |
"Competence": ["Adroitness", "Expertise", "Perception"], | |
"Prowess": ["Agility", "Melee", "Ranged"], | |
"Fortitude": ["Endurance", "Strength", "Willpower"] | |
} | |
# Define the specialties and their associated focus areas | |
specialties_focus_areas = { | |
"Adroitness": ["Skulduggery", "Cleverness"], | |
"Expertise": ["Wizardry", "Theurgy"], | |
"Perception": ["Alertness", "Perspicacity"], | |
"Agility": ["Speed", "Reaction"], | |
"Melee": ["Threat", "Finesse"], | |
"Ranged": ["Ranged Threat", "Ranged Finesse"], | |
"Endurance": ["Vitality", "Resilience"], | |
"Strength": ["Ferocity", "Might"], | |
"Willpower": ["Courage", "Resistance"] | |
} | |
# Define utility functions | |
def calculate_max_value(die_rank): | |
if die_rank == "d4": | |
return 4 | |
elif die_rank == "d6": | |
return 6 | |
elif die_rank == "d8": | |
return 8 | |
elif die_rank == "d10": | |
return 10 | |
elif die_rank == "d12": | |
return 12 | |
elif die_rank[0] == "+": | |
return int(die_rank[1:]) | |
else: | |
return 0 | |
def allocate_points_to_ability(character, ability, cps): | |
current_rank = character['base_die_ranks'].get(ability, 'd0') | |
current_value = calculate_max_value(current_rank) | |
target_rank = next_die_rank(current_rank) | |
target_value = calculate_max_value(target_rank) | |
if cps >= target_value - current_value: | |
character['base_die_ranks'][ability] = target_rank | |
remaining_cps = cps - (target_value - current_value) | |
else: | |
character['base_die_ranks'][ability] = f"+{current_value + cps}" | |
remaining_cps = 0 | |
return character, remaining_cps | |
def allocate_points_to_specialization(character, specialization, cps): | |
current_rank = character['base_die_ranks'].get(specialization, 'd0') | |
current_value = calculate_max_value(current_rank) | |
target_rank = next_die_rank(current_rank) | |
target_value = calculate_max_value(target_rank) | |
if cps >= target_value - current_value: | |
character['base_die_ranks'][specialization] = target_rank | |
remaining_cps = cps - (target_value - current_value) | |
else: | |
character['base_die_ranks'][specialization] = f"+{current_value + cps}" | |
remaining_cps = 0 | |
return character, remaining_cps | |
def allocate_points_to_focus_area(character, focus_area, cps): | |
current_value = character['focus_areas'].get(focus_area, 0) | |
target_value = current_value + cps | |
if target_value >= 0: | |
character['focus_areas'][focus_area] = target_value | |
else: | |
character['focus_areas'][focus_area] = 0 | |
return character | |
def allocate_character_points(character, cps): | |
for ability in core_abilities_specialties["Competence"]: | |
character, cps = allocate_points_to_ability(character, ability, cps) | |
for ability in core_abilities_specialties["Prowess"]: | |
character, cps = allocate_points_to_ability(character, ability, cps) | |
for ability in core_abilities_specialties["Fortitude"]: | |
character, cps = allocate_points_to_ability(character, ability, cps) | |
specialties = set() | |
for ability in core_abilities_specialties.values(): | |
specialties.update(ability) | |
for specialty in specialties: | |
character, cps = allocate_points_to_specialization(character, specialty, cps) | |
for focus_area in character['focus_areas']: | |
character = allocate_points_to_focus_area(character, focus_area, cps) | |
return character | |
def next_die_rank(die_rank): | |
if die_rank == "d4": | |
return "d6" | |
elif die_rank == "d6": | |
return "d8" | |
elif die_rank == "d8": | |
return "d10" | |
elif die_rank == "d10": | |
return "d12" | |
elif die_rank[0] == "+": | |
return f"+{int(die_rank[1:]) + 1}" | |
else: | |
return "d4" | |
def allocate_points_for_level(character, level): | |
cps_for_level = { | |
1: 10, | |
2: 150, | |
3: 250, | |
4: 350, | |
5: 450 | |
} | |
cps = cps_for_level.get(level, 10) # Default to 10 if the level is not recognized | |
character = allocate_character_points(character, cps) | |
return character | |
# Generate a character based on user choice | |
def generate_character_choice(): | |
# Select race | |
race_options = list(races.keys()) | |
print("Select a race:") | |
for i, race in enumerate(race_options): | |
print(f"{i + 1}. {race}") | |
race_choice = int(input("Enter the number of the race: ")) | |
selected_race = race_options[race_choice - 1] | |
# Select class | |
class_options = list(classes.keys()) | |
print("\nSelect a class:") | |
for i, class_name in enumerate(class_options): | |
print(f"{i + 1}. {class_name}") | |
class_choice = int(input("Enter the number of the class: ")) | |
selected_class = class_options[class_choice - 1] | |
# Select level | |
level = int(input("\nEnter the character level: ")) | |
# Generate the character | |
character = generate_character(level, selected_race, selected_class) | |
print(f"\nGenerating a level {level} {selected_race} {selected_class} character...\n") | |
print(character) | |
# Generate a level 5 character | |
def generate_character(level, race, class_name): | |
# Randomly select race and class | |
base_die_ranks = races[race]["base_die_ranks"].copy() | |
base_die_ranks.update(classes[class_name].get("base_die_ranks", {})) | |
# Ensure that Competence, Prowess, and Fortitude are at least d4 | |
for attribute in ["Competence", "Prowess", "Fortitude"]: | |
if attribute not in base_die_ranks or calculate_max_value(base_die_ranks[attribute]) < 4: | |
base_die_ranks[attribute] = "d4" | |
# Initialize focus areas with zero bonus | |
focus_areas = {focus_area: 0 for ability in core_abilities_specialties.values() for focus_area in ability} | |
focus_areas.update({focus_area: 0 for specialty in specialties_focus_areas.values() for focus_area in specialty}) | |
character = { | |
"race": race, | |
"class_name": class_name, | |
"base_die_ranks": base_die_ranks, | |
"advantages": races[race].get("advantages", []) + classes[class_name].get("advantages", []), | |
"feats": classes[class_name].get("feats", []), | |
"focus_areas": focus_areas | |
} | |
# Allocate 10 character points to weakest abilities and specialties | |
character = allocate_character_points(character, 10) | |
# Allocate additional points based on the chosen level | |
character = allocate_points_for_level(character, level) | |
# Calculate the Defense Pools | |
active_defense_pool = calculate_max_value(character['base_die_ranks'].get('Prowess', "d0")) + \ | |
calculate_max_value(character['base_die_ranks'].get('Agility', "d0")) + \ | |
calculate_max_value(character['base_die_ranks'].get('Melee', "d0")) | |
passive_defense_pool = calculate_max_value(character['base_die_ranks'].get('Fortitude', "d0")) + \ | |
calculate_max_value(character['base_die_ranks'].get('Endurance', "d0")) + \ | |
calculate_max_value(character['base_die_ranks'].get('Strength', "d0")) | |
spirit_points = calculate_max_value(character['base_die_ranks'].get('Competence', "d0")) + \ | |
calculate_max_value(character['base_die_ranks'].get('Willpower', "d0")) | |
character_info = f"Race: {character['race']}\n" | |
character_info += f"Class: {character['class_name']}\n" | |
for ability in ["Competence", "Prowess", "Fortitude"]: | |
character_info += f"• {ability} {character['base_die_ranks'].get(ability, 'd0')}" | |
if ability in core_abilities_specialties: | |
specialties = core_abilities_specialties[ability] | |
if specialties: | |
character_info += " → " | |
for i, specialty in enumerate(specialties): | |
character_info += f"{specialty} {character['base_die_ranks'].get(specialty, 'd0')}" | |
focus_areas = specialties_focus_areas.get(specialty) | |
if focus_areas: | |
filtered_focus_areas = [ | |
focus_area for focus_area in focus_areas if character['focus_areas'].get(focus_area, 0) > 0 | |
] | |
if filtered_focus_areas: | |
filtered_focus_areas_info = [ | |
f"{focus_area} (+{character['focus_areas'].get(focus_area, 0)})" | |
for focus_area in filtered_focus_areas | |
] | |
character_info += " → " + ", ".join(filtered_focus_areas_info) | |
if i < len(specialties) - 1: | |
character_info += ", " | |
character_info += ". " | |
else: | |
character_info += ".\n" | |
character_info += "Advantages:\n" | |
for advantage in character['advantages']: | |
character_info += f"• {advantage}\n" | |
character_info += "Feats:\n" | |
for feat in character['feats']: | |
character_info += f"• {feat}\n" | |
character_info += f"Active Defense Pool: {active_defense_pool}\n" | |
character_info += f"Passive Defense Pool: {passive_defense_pool}\n" | |
character_info += f"Spirit Points: {spirit_points}\n" | |
return character_info | |
# Generate a level 5 character | |
print(generate_character(5)) | |
# Generate a character based on user choice | |
generate_character_choice() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment