Corrected last bugs in character creation. Now fully complete.

This commit is contained in:
kansaigaijin
2025-07-13 19:16:02 +12:00
parent 92a3d526c5
commit 55030323ad
3 changed files with 125 additions and 101 deletions

View File

@@ -13,11 +13,16 @@ def wait():
def startGame():
print("Welcome stranger, to the world of Kanjin!.")
start = input("Would you like to create your own character or use pre-generated stats?"
"\n 1) Create a new character\n 2) Pre-generated\n >> ")
# Loop for initial choice: Create or Pre-generated
while True:
start = input("Would you like to create your own character or use pre-generated stats?"
"\n 1) Create a new character\n 2) Pre-generated\n >> ")
if start in ("1", "Create"):
while True: # Global check to see if Name, Age, and Gender are correct
# --- Character Basic Info (Name, Gender, Age) ---
name, gender, age = None, None, None # Initialize
# Loop until basic info is confirmed
while True:
# Name
name = input('What is your name?\n >> ').title()
while len(name) < 2:
@@ -25,7 +30,7 @@ def startGame():
# Gender
while True:
gender_select = input("What is your gender?\n"
"1) Male\n2) Female\n3) Other\n >> ")
"1) Male\n2) Female\n3) Other\n >> ").lower()
if gender_select not in ("male", "female", "other", "1", "2", "3"):
print("Sorry I didn't recognise that gender. Please try again.")
continue
@@ -46,26 +51,23 @@ def startGame():
print("Sorry I didn't recognise that age. Please type in a whole number.")
continue
# Double check responses
correct = input(f"Hello, {name}. You are a {age} year old {gender}.\nIs this correct? Y/N\n >> ")
if correct in ['y', 'yes']:
break
elif correct in ['n', 'no']:
continue
correct = input(f"Hello, {name}.\nYou are a {age} year old {gender.lower()}.\nIs this correct? Y/N\n >> ")
if correct.lower() in ['y', 'yes']:
break # Exit basic info confirmation loop
elif correct.lower() in ['n', 'no']:
continue # Restart basic info input
else:
print("Sorry, I didn't catch that.\n")
correct = input(f"You are a {age} year old {gender}. \nIs this correct? Y/N\n >> ")
if correct in ['y', 'yes']:
break
elif correct in ['n', 'no']:
continue
break
print("Sorry, I didn't catch that. Please try again.\n")
continue
# --- Race and Job Selection ---
race = None
job = None
while True: # Global check to see if Race and Job are correct
# Race
# Loop until Race and Job are confirmed
while True:
# Race Selection
selected_race_obj = None
# Loop for selecting and viewing race
while True:
race_choice = input("Please select a race to learn more about it:\n"
"1) Elf\n2) Dwarf\n3) Human\n >> ").title()
@@ -81,19 +83,23 @@ def startGame():
if selected_race_obj:
print(f"\n--- {selected_race_obj.name} ---")
print(selected_race_obj.__repr__()) # Use the __repr__ to get full description and traits
print(selected_race_obj.__repr__())
print('Would you like to proceed with this race or view another?')
proceed = input('1) Proceed\n2) View another race\n >> ')
if proceed in ('1', 'proceed'):
race = selected_race_obj
break
elif proceed in ('2', 'view', 'view another', 'view another race'):
race = None
if proceed.lower() in ('1', 'proceed'):
race = selected_race_obj # Assign race object
break # Exit race selection loop
elif proceed.lower() in ('2', 'view', 'view another', 'view another race'):
continue # Restart race selection
else:
print("Sorry, I didn't catch that. Please try again.\n")
continue
# Job
# Job Selection
selected_job_obj = None
# Loop for selecting and viewing job
while True:
job_choice = input("Please select a race to learn more about it:\n"
job_choice = input("Please select a job to learn more about it:\n"
"1) Barbarian\n2) Cleric\n3) Wizard\n >> ").title()
if job_choice in ['Barbarian', '1']:
selected_job_obj = Barbarian
@@ -107,30 +113,35 @@ def startGame():
if selected_job_obj:
print(f"\n--- {selected_job_obj.name} ---")
print(selected_job_obj.__repr__()) # Use the __repr__ to get full description and traits
print(selected_job_obj.__repr__())
print('Would you like to proceed with this job or view another?')
proceed = input('1) Proceed\n2) View another job\n >> ')
if proceed in ('1', 'proceed'):
job = selected_job_obj # Store the string name
break
elif proceed in ('2', 'view', 'view another', 'view another job'):
job = None
continue
if proceed.lower() in ('1', 'proceed'):
job = selected_job_obj # Assign job object
break # Exit job selection loop
elif proceed.lower() in ('2', 'view', 'view another', 'view another job'):
continue # Restart job selection
else:
print("Sorry, I didn't catch that. Please try again.\n")
continue
# Final check
# Final confirmation for both Race and Job
while True:
correct = input(f"{name}, you are a {race.name_adjective} {job.name}.\nIs this correct? Y/N\n >> ")
if correct in ['y', 'yes']:
# This safeguard should ideally not be hit if inner loops work correctly
if race is None or job is None:
print("Error: Race or Job not selected. Restarting Race/Job selection.")
break # Break out of this inner loop to restart the outer race/job loop
correct = input(f"\n{name}, you are a {race.name_adjective} {job.name}.\nIs this correct? Y/N\n >> ")
if correct.lower() in ['y', 'yes']:
return name, gender, age, race, job # All confirmed, return values
elif correct.lower() in ['n', 'no']:
# If not correct, break this loop to re-enter race/job selection
break
elif correct in ['n', 'no']:
continue
else:
print("Sorry, I didn't catch that. Please try again.\n")
continue
return name, gender, age, race, job
elif start in ("2", "pre-generated"):
print("Please be prepared to enter a name, gender, age, race, and class, from those available in the game."
"\nIf you are unsure what the options are, please go back and create a new character.")
@@ -139,19 +150,40 @@ def startGame():
name = input("Name: ")
gender = input("Gender: ")
age = int(input("Age: "))
race = input("Race: ")
job = input("Job: ")
return name, gender, age, race, job # Return and exit function
race_str = input("Race (Elf, Dwarf, Human): ")
job_str = input("Job (Barbarian, Cleric, Wizard): ")
# Map string inputs to actual Race/Job objects for consistency
race_map = {'elf': Elf, 'dwarf': Dwarf, 'human': Human}
job_map = {'barbarian': Barbarian, 'cleric': Cleric, 'wizard': Wizard}
actual_race = race_map.get(race_str.lower())
actual_job = job_map.get(job_str.lower())
if actual_race and actual_job:
return name, gender, age, actual_race, actual_job
else:
print("Invalid race or job entered for pre-generated character. Please try again.")
# Force restart to character creation choice by setting 'start' to '1'
# and then continue the outermost loop.
start = "1"
continue
elif create in ("n", "no"):
# If 'n', continue the outer while True loop to re-prompt the initial choice.
# If 'n', re-prompt the initial choice.
start = input("Would you like to create your own character or use pre-generated stats?"
"\n 1) Create a new character\n 2) Pre-generated\n >> ")
continue
else:
print("Invalid input. Please enter Y or N.")
# If invalid, continue the outer while True loop to re-prompt the initial choice.
# If invalid, re-prompt the initial choice.
start = input("Would you like to create your own character or use pre-generated stats?"
"\n 1) Create a new character\n 2) Pre-generated\n >> ")
continue
else:
print("Invalid input. Please select '1' or '2'.")
# Continue the outer while True loop to re-prompt the initial choice.
# Re-prompt the initial choice.
start = input("Would you like to create your own character or use pre-generated stats?"
"\n 1) Create a new character\n 2) Pre-generated\n >> ")
continue
def query_equip(player: Player):

19
main.py
View File

@@ -247,25 +247,8 @@ def main_game_loop():
# Character Creation
name, gender, age, race, job = startGame()
# Instantiate Race and Job objects based on user's choice
selected_race = None
if race == "Elf":
selected_race = Elf
elif race == "Dwarf":
selected_race = Dwarf
elif race == "Human":
selected_race = Human
selected_job = None
if job == "Barbarian":
selected_job = Barbarian
elif job == "Cleric":
selected_job = Cleric
elif job == "Wizard":
selected_job = Wizard
# Create player instance
player = Player(name, gender, age, selected_race, selected_job)
player = Player(name, gender, age, race, job)
game_engine.set_player(player) # Set the player in the game engine
# Display initial character summary

View File

@@ -141,14 +141,16 @@ class Inventory:
.replace('RightRing', 'Right Ring'))
return str(slot_val) # Fallback for unexpected types
def add_item(self, item, count=1):
def add_item(self, item, count=1, silent=False):
"""Adds an item to the inventory."""
if item in self.items:
self.items[item]["Count"] += count
print(f"Added {count} more {item.name}. Total: {self.items[item]['Count']}.")
if not silent:
print(f"Added {count} more {item.name}. Total: {self.items[item]['Count']}.")
else:
self.items[item] = {"Count": count, "object": item.itemType}
print(f"Added {count} {item.name} to inventory.")
if not silent:
print(f"Added {count} {item.name} to inventory.")
def remove_item(self, item, count=1):
"""Removes an item from the inventory."""
@@ -168,7 +170,7 @@ class Inventory:
def current_inventory(self):
"""Prints a list of items in your backpack that aren't equipped to your person."""
print(f'In your rucksack you have:')
print(f'You are currently carrying:')
found_un_equipped = False
for item_obj, item_data in self.items.items():
# Check if the item is in the backpack AND not currently equipped in any slot
@@ -182,7 +184,7 @@ class Inventory:
def current_equipment(self):
"""Prints a list of items that you have equipped in your slots."""
print(f'You are currently wearing:')
print(f'You have equipped:')
equipment_found = False
for slot, equipment in self.equipped_items.items():
if equipment is not None and slot != Slots.Attunement: # Don't print the attunement counter
@@ -209,73 +211,80 @@ class Inventory:
print(f"Attunement slots used: {self.equipped_items[Slots.Attunement]}/3")
def equip_item(self, item_obj, slot: Slots):
def equip_item(self, item_obj, slot: Slots, silent=False):
"""Equips an item to a specific slot."""
if item_obj not in self.items or self.items[item_obj]["Count"] == 0:
print(f"You don't have {item_obj.name} to equip.")
if not silent:
print(f"You don't have {item_obj.name} to equip.")
return
# Check attunement limits before equipping (moved up for early exit)
if item_obj.attunement and self.equipped_items[Slots.Attunement] >= 3:
print(f"You cannot equip {item_obj.name}. You have reached your attunement limit (3/3).")
if not silent:
print(f"You cannot equip {item_obj.name}. You have reached your attunement limit (3/3).")
return
# Handle unequipping existing item in the target slot first
if self.equipped_items[slot] is not None:
self.unequip_item(slot)
self.unequip_item(slot) # This will print its own unequip message
# Now, handle equipping based on item type and specific properties
if item_obj.itemType == ItemType.Weapon:
if item_obj.slot == Slots.TwoHanded:
# If equipping a two-handed weapon, unequip anything in main/off-hand
if self.equipped_items[Slots.MainHand] is not None:
self.unequip_item(Slots.MainHand)
self.unequip_item(Slots.MainHand, silent=True) # Silent unequip
if self.equipped_items[Slots.OffHand] is not None:
self.unequip_item(Slots.OffHand)
self.unequip_item(Slots.OffHand, silent=True) # Silent unequip
self.equipped_items[Slots.TwoHanded] = item_obj
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(Slots.TwoHanded)}.")
if not silent:
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(Slots.TwoHanded)}.")
elif item_obj.versatile:
# If equipping a versatile weapon, it goes in MainHand, and potentially affects OffHand
if self.equipped_items[Slots.TwoHanded] is not None:
self.unequip_item(Slots.TwoHanded) # Unequip two-handed if present
self.unequip_item(Slots.TwoHanded, silent=True) # Unequip two-handed if present silently
self.equipped_items[Slots.MainHand] = item_obj
# A versatile weapon can be used two-handed, implying it might occupy the off-hand conceptually
# For simplicity, we'll just equip it to MainHand here. If you want it to occupy off-hand,
# you'd need more complex logic for 1H vs 2H use.
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(Slots.MainHand)}.")
if not silent:
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(Slots.MainHand)}.")
else: # Standard one-handed weapon (or other weapon types not specifically handled above)
self.equipped_items[slot] = item_obj # Equip to the specified slot (MainHand or OffHand)
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(slot)}.")
if not silent:
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(slot)}.")
elif item_obj.itemType == ItemType.Armor:
# For armor, ensure the target slot is appropriate for armor (e.g., Chest, Helm)
# The Armor class now has a 'slot' attribute, so we can use that for validation/assignment
if item_obj.slot == slot: # Ensure the item's intended slot matches the target slot
self.equipped_items[slot] = item_obj
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(slot)}.")
if not silent:
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(slot)}.")
else:
# Corrected this line to use _get_slot_display_name for both slot and item_obj.slot
print(f"Cannot equip {item_obj.name} to {self._get_slot_display_name(slot)}. It belongs in the {self._get_slot_display_name(item_obj.slot)} slot.")
if not silent:
print(f"Cannot equip {item_obj.name} to {self._get_slot_display_name(slot)}. It belongs in the {self._get_slot_display_name(item_obj.slot)} slot.")
return # Exit if slot mismatch
elif item_obj.itemType == ItemType.Item:
# For general items (rings, cloaks, etc.), equip to the specified slot
self.equipped_items[slot] = item_obj
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(slot)}.")
if not silent:
print(f"Equipped {item_obj.name} to {self._get_slot_display_name(slot)}.")
else:
print(f"Cannot equip {item_obj.name}. Unknown item type or invalid slot for this item.")
if not silent:
print(f"Cannot equip {item_obj.name}. Unknown item type or invalid slot for this item.")
return # Exit if item type is not recognized for equipping
self.update_attunement() # Update attunement after successful equip
def unequip_item(self, slot: Slots):
def unequip_item(self, slot: Slots, silent=False):
"""Unequips an item from a specific slot."""
if self.equipped_items[slot] is not None:
unequipped_item = self.equipped_items[slot]
self.equipped_items[slot] = None
print(f"Unequipped {unequipped_item.name} from {self._get_slot_display_name(slot)}.") # Use helper function
if not silent:
print(f"Unequipped {unequipped_item.name} from {self._get_slot_display_name(slot)}.") # Use helper function
self.update_attunement()
else:
print(f"Nothing is equipped in {self._get_slot_display_name(slot)}.") # Use helper function
if not silent:
print(f"Nothing is equipped in {self._get_slot_display_name(slot)}.") # Use helper function
def update_attunement(self):
"""Recalculates the number of attuned items."""
@@ -418,12 +427,12 @@ class Player:
"Wisdom": 0,
"Charisma": 0}
# Initial items and equipment
self.inventory.add_item(rock, 1)
self.inventory.add_item(tornRags, 1)
# Initial items and equipment (now with silent=True)
self.inventory.add_item(rock, 1, silent=True)
self.inventory.add_item(tornRags, 1, silent=True)
# Equip initial items (using the inventory's equip method)
self.inventory.equip_item(rock, Slots.MainHand)
self.inventory.equip_item(tornRags, Slots.Chest)
self.inventory.equip_item(rock, Slots.MainHand, silent=True)
self.inventory.equip_item(tornRags, Slots.Chest, silent=True)
def getModifier(self):
@@ -537,7 +546,7 @@ class Player:
def current_stats(self):
"""Prints a display of the user's current statistics."""
print("Your current stats are:")
print(f'\nYour current stats are:')
print(f'Hit Points: {self.currentHP}/{self.maxHP}')
print(f'Strength: {self.stats["Strength"]} ({self.mods["Strength"]:+})')
print(f'Dexterity: {self.stats["Dexterity"]} ({self.mods["Dexterity"]:+})')
@@ -606,9 +615,9 @@ class Player:
stats.append(str(val))
rolls = [] # Reset for next roll
print(f"Please assign a stat to a selected attribute by entering the number then the attribute.\n"
f"For example, '10 strength' or '10 str' will assign 10 to your strength, if you have a 10 available.\n\n"
f"Your rolled stats are:\n {', '.join(stats)}\n\n"
print(f"\nPlease assign a stat to a selected attribute by entering the number then the attribute.\n"
f"For example: '10 strength' or '10 str' will assign 10 to your strength, if you have a 10 available.\n\n"
f"Your rolled stats are:\n {', '.join(stats)}\n"
f"Your attributes are:\n {', '.join(attributes)}\n")
current_attributes = list(attributes)
@@ -642,7 +651,7 @@ class Player:
else:
print("Error. Input was not recognised. Please try again with 'Number' + 'Attribute'.")
print(f"Your current stats are:\n"
print(f'\nYour current stats are:\n'
f'Strength: {self.stats["Strength"]}\n'
f'Dexterity: {self.stats["Dexterity"]}\n'
f'Constitution: {self.stats["Constitution"]}\n'
@@ -651,7 +660,7 @@ class Player:
f'Charisma: {self.stats["Charisma"]}\n')
select = input("Are you happy with this selection? Y/N?\n"
"WARNING: If you select 'N', your dice will be randomly rolled again. Proceed?\n"
" WARNING: If you select 'N', your dice will be randomly rolled again. Proceed?\n"
" >> ").lower()
if select == "n":
continue # Loop back to re-roll and re-allocate