From 55030323adcb8ed42fb4b2abdef0ecd1b65e5244 Mon Sep 17 00:00:00 2001 From: kansaigaijin Date: Sun, 13 Jul 2025 19:16:02 +1200 Subject: [PATCH] Corrected last bugs in character creation. Now fully complete. --- function_list.py | 124 +++++++++++++++++++++++++++++------------------ main.py | 19 +------- player.py | 83 +++++++++++++++++-------------- 3 files changed, 125 insertions(+), 101 deletions(-) diff --git a/function_list.py b/function_list.py index 337bc9d..2364bce 100644 --- a/function_list.py +++ b/function_list.py @@ -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): diff --git a/main.py b/main.py index 2206402..0d526b0 100644 --- a/main.py +++ b/main.py @@ -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 diff --git a/player.py b/player.py index faa2762..4d7ce72 100644 --- a/player.py +++ b/player.py @@ -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