From 221cb7141085a25ba3ea752ea3953abd3ebbf007 Mon Sep 17 00:00:00 2001 From: KansaiGaijin <83641841+KansaiGaijin@users.noreply.github.com> Date: Tue, 7 Jul 2026 12:13:43 +1200 Subject: [PATCH] Add Container class with lock/trap detection, corpse drops, and numbered duplicate corpses --- .idea/.gitignore | 8 + .idea/Kanjin-Text-RPG.iml | 8 + .idea/dictionaries/project.xml | 7 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/vcs.xml | 7 + AGENTS.md | 33 ++ __pycache__/class_list.cpython-311.pyc | Bin 0 -> 8594 bytes __pycache__/class_list.cpython-312.pyc | Bin 0 -> 7852 bytes __pycache__/enum_list.cpython-311.pyc | Bin 0 -> 3618 bytes __pycache__/enum_list.cpython-312.pyc | Bin 0 -> 2772 bytes __pycache__/function_list.cpython-311.pyc | Bin 0 -> 33144 bytes __pycache__/function_list.cpython-312.pyc | Bin 0 -> 28999 bytes __pycache__/job_list.cpython-312.pyc | Bin 0 -> 7208 bytes __pycache__/main.cpython-311.pyc | Bin 0 -> 23953 bytes __pycache__/player.cpython-311.pyc | Bin 0 -> 54385 bytes __pycache__/player.cpython-312.pyc | Bin 0 -> 51721 bytes __pycache__/race_list.cpython-311.pyc | Bin 0 -> 9239 bytes __pycache__/race_list.cpython-312.pyc | Bin 0 -> 8616 bytes __pycache__/scene.cpython-311.pyc | Bin 0 -> 11336 bytes __pycache__/scene.cpython-312.pyc | Bin 0 -> 8019 bytes __pycache__/spell_list.cpython-311.pyc | Bin 0 -> 9150 bytes __pycache__/spell_list.cpython-312.pyc | Bin 0 -> 8988 bytes job_list.py => class_list.py | 49 ++- enum_list.py | 24 +- function_list.py | 142 +++++--- main.py | 139 +++++-- player.py | 339 ++++++++++++++++-- race_list.py | 10 +- scene.py | 90 +++-- spell_list.py | 221 ++++++++++++ 32 files changed, 938 insertions(+), 160 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/Kanjin-Text-RPG.iml create mode 100644 .idea/dictionaries/project.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 AGENTS.md create mode 100644 __pycache__/class_list.cpython-311.pyc create mode 100644 __pycache__/class_list.cpython-312.pyc create mode 100644 __pycache__/enum_list.cpython-311.pyc create mode 100644 __pycache__/enum_list.cpython-312.pyc create mode 100644 __pycache__/function_list.cpython-311.pyc create mode 100644 __pycache__/function_list.cpython-312.pyc create mode 100644 __pycache__/job_list.cpython-312.pyc create mode 100644 __pycache__/main.cpython-311.pyc create mode 100644 __pycache__/player.cpython-311.pyc create mode 100644 __pycache__/player.cpython-312.pyc create mode 100644 __pycache__/race_list.cpython-311.pyc create mode 100644 __pycache__/race_list.cpython-312.pyc create mode 100644 __pycache__/scene.cpython-311.pyc create mode 100644 __pycache__/scene.cpython-312.pyc create mode 100644 __pycache__/spell_list.cpython-311.pyc create mode 100644 __pycache__/spell_list.cpython-312.pyc rename job_list.py => class_list.py (79%) create mode 100644 spell_list.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/Kanjin-Text-RPG.iml b/.idea/Kanjin-Text-RPG.iml new file mode 100644 index 0000000..f571432 --- /dev/null +++ b/.idea/Kanjin-Text-RPG.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/project.xml b/.idea/dictionaries/project.xml new file mode 100644 index 0000000..c18d331 --- /dev/null +++ b/.idea/dictionaries/project.xml @@ -0,0 +1,7 @@ + + + + darkvision + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..db8786c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9863d7f --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..8306744 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..6cfaeb9 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,33 @@ +# AGENTS.md + +## Run + +```bash +python main.py +``` + +Pure stdlib. No dependencies, no build, no test/lint tooling. + +## Entrypoints + +- `main.py:main_game_loop()` — game start +- `function_list.py:startGame()` — character creation (returns `name, gender, age, race, class_, pre_allocated_stats`) + +## Play vs. debug + +To skip character creation during playtesting, replace `startGame()` with a pre-generated character and assign stats directly (see `main.py:274-290` and `function_list.py:14-94` for the 3 pre-gens). + +## Quirks + +- `sys.path.append('.')` is required at the top of `main.py`, `scene.py`, `function_list.py` — sibling imports break without it. +- `class` is a reserved keyword, so the project uses `class_` everywhere (parameter/attribute names, e.g. `Player.__init__(self, ..., class_)`). +- Race/class modules export `RACES` and `CLASSES` dicts (e.g. `RACES["Elf"]`, `CLASSES["Wizard"]`). The underlying classes (`Race`, `CharacterClass`) are preserved for instantiation if needed. +- `CharacterClass.__init__` receives `class_` as a parameter — do not confuse with Python's `class` statement. + +## Architecture + +- `scene.py` — a directed graph of `Scene` objects linked via `add_exit(direction, scene)`. The `Scene` class also holds `available_items` (free pickups) and `lootable_items` (container contents). Containers use the `Container` class which has a `description`, `lock_dc` / `trap_dc` (DC for detecting locks/traps via an Intelligence check), and visible `contents`. Locked containers can't be opened (contents hidden); trapped containers trigger if opened without detection/disarming. Use `scene.add_corpse(enemy_name, items_dict)` to drop a lootable corpse when an enemy dies. +- `player.py` — `Player`, `Inventory` (equipped slots, carried items, weight/encumbrance, currency), item hierarchy (`Item` → `Weapon`, `Armor`, `Money`). +- `spell_list.py` — `Spell` objects grouped into `SPELL_LISTS[class_name][spell_level]`. Casting uses an SRD-style slot system with upcasting. +- `enum_list.py` — shared enums (`Slots`, `DamageType`, `ItemType`, `SaveType`, etc.). +- Modifier formula: `mod = -5 + stat // 2` (standard D&D 5e). diff --git a/__pycache__/class_list.cpython-311.pyc b/__pycache__/class_list.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6bffa46e018cabaf6c1ca0cc9a55463a8a88114c GIT binary patch literal 8594 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 literal 0 HcmV?d00001 diff --git a/__pycache__/enum_list.cpython-311.pyc b/__pycache__/enum_list.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5da0e2dd2dc15efab0e7e64b27661dc0f4ce8e2d GIT binary patch literal 3618 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 R;(otLug|13sUDZz{{saCI+*|f literal 0 HcmV?d00001 diff --git a/__pycache__/function_list.cpython-312.pyc b/__pycache__/function_list.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..501d90b64582dd6e7e586714381f1aecd6d1786b GIT binary patch literal 28999 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 HsObL%BzWEA literal 0 HcmV?d00001 diff --git a/__pycache__/job_list.cpython-312.pyc b/__pycache__/job_list.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..487307618a9129f74da8af300f7a18371b3cdbc2 GIT binary patch literal 7208 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 pzjhSH& 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 literal 0 HcmV?d00001 diff --git a/__pycache__/player.cpython-312.pyc b/__pycache__/player.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..179689b7b1ef53d9d2737317084ae635557404f9 GIT binary patch literal 51721 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 z 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 literal 0 HcmV?d00001 diff --git a/__pycache__/race_list.cpython-312.pyc b/__pycache__/race_list.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1c2dc36425ccbd8c555ed146899625daa9014b76 GIT binary patch literal 8616 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 literal 0 HcmV?d00001 diff --git a/__pycache__/scene.cpython-312.pyc b/__pycache__/scene.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c954baf1d85352bf9e936f1532cb5e5f42eefa7b GIT binary patch literal 8019 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 literal 0 HcmV?d00001 diff --git a/__pycache__/spell_list.cpython-311.pyc b/__pycache__/spell_list.cpython-311.pyc new file mode 100644 index 0000000000000000000000000000000000000000..431979d702fad60c9ca101f2a831a69b608db6a2 GIT binary patch literal 9150 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 literal 0 HcmV?d00001 diff --git a/__pycache__/spell_list.cpython-312.pyc b/__pycache__/spell_list.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7e6d830532fc73224749e74d9340d6770ab2e368 GIT binary patch literal 8988 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 literal 0 HcmV?d00001 diff --git a/job_list.py b/class_list.py similarity index 79% rename from job_list.py rename to class_list.py index 4da9939..dbd4354 100644 --- a/job_list.py +++ b/class_list.py @@ -1,5 +1,14 @@ -class Job: - def __init__(self, name, description, hitdie, proficiency, savingthrow, skill, gold): +# NOTE ON NAMING: this represents a D&D "class" (Barbarian, Cleric, Wizard...), but `class` +# is a reserved Python keyword and can't be used as a variable/parameter/attribute name. +# The convention used throughout this project (per PEP 8) is: +# - The type itself is named `CharacterClass` (capitalised type names don't collide with +# the keyword, but "Class" alone reads ambiguously next to Python's own `class` statement). +# - Any variable, parameter, or attribute that holds one of these is named `class_` +# (trailing underscore), e.g. `self.class_` on Player, or `class_=Wizard`. +class CharacterClass: + def __init__(self, name, description, hitdie, proficiency, savingthrow, skill, gold, + spellcasting_ability=None, cantrips_known=0, spell_slots=None, + spellbook_style=None): self.name = name self.description = description self.hitdie = hitdie @@ -8,6 +17,20 @@ class Job: self.skill = skill self.gold = gold + # --- Spellcasting (SRD) --- + # spellcasting_ability: e.g. "Intelligence" for Wizard, "Wisdom" for Cleric. None = non-caster. + self.spellcasting_ability = spellcasting_ability + self.cantrips_known = cantrips_known + # spell_slots: dict of {spell_level: slot_count} at character level 1. + self.spell_slots = spell_slots or {} + # spellbook_style: "spellbook" (Wizard - learns a fixed list, prepares a subset) + # "all_known" (Cleric - knows/can prepare from their entire class list) + self.spellbook_style = spellbook_style + + @property + def is_caster(self): + return self.spellcasting_ability is not None + def __repr__(self) -> str: profs = "" for key, value in self.proficiency.items(): @@ -24,7 +47,7 @@ class Job: + '\n'.join(self.skill[1:]).title() + '\n' -class Barbarian(Job): +class Barbarian(CharacterClass): def __init__(self): name = "Barbarian" description = "A tall human tribesman strides through a blizzard, draped in fur and hefting his axe. " \ @@ -72,7 +95,7 @@ class Barbarian(Job): super().__init__(name, description, hitdie, proficiency, savingthrow, skills, gold) -class Cleric(Job): +class Cleric(CharacterClass): def __init__(self): name = "Cleric" description = "Arms and eyes upraised toward the sun and a prayer on his lips, an elf begins to glow with " \ @@ -111,10 +134,12 @@ class Cleric(Job): "religion" ] gold = [2, 10] - super().__init__(name, description, hitdie, proficiency, savingthrow, skill, gold) + super().__init__(name, description, hitdie, proficiency, savingthrow, skill, gold, + spellcasting_ability="Wisdom", cantrips_known=3, + spell_slots={1: 2}, spellbook_style="all_known") -class Wizard(Job): +class Wizard(CharacterClass): def __init__(self): name = "Wizard" description = "Clad in the silver robes that denote her station, an elf closes her eyes to shut out the " \ @@ -165,9 +190,13 @@ class Wizard(Job): "religion" ] gold = [2, 10] - super().__init__(name, description, hitdie, proficiency, savingthrow, skills, gold) + super().__init__(name, description, hitdie, proficiency, savingthrow, skills, gold, + spellcasting_ability="Intelligence", cantrips_known=3, + spell_slots={1: 2}, spellbook_style="spellbook") -Barbarian = Barbarian() -Cleric = Cleric() -Wizard = Wizard() +CLASSES = { + "Barbarian": Barbarian(), + "Cleric": Cleric(), + "Wizard": Wizard(), +} diff --git a/enum_list.py b/enum_list.py index b869353..ad4e1d3 100644 --- a/enum_list.py +++ b/enum_list.py @@ -47,4 +47,26 @@ class Slots(Enum): LeftRing = auto() RightRing = auto() Other = auto() - Attunement = auto() \ No newline at end of file + Attunement = auto() + + +class SpellSchool(Enum): + Abjuration = auto() + Conjuration = auto() + Divination = auto() + Enchantment = auto() + Evocation = auto() + Illusion = auto() + Necromancy = auto() + Transmutation = auto() + + +class SaveType(Enum): + """Which stat a target rolls to resist a spell's effect.""" + Strength = auto() + Dexterity = auto() + Constitution = auto() + Intelligence = auto() + Wisdom = auto() + Charisma = auto() + NONE = auto() # No saving throw (e.g. Magic Missile) \ No newline at end of file diff --git a/function_list.py b/function_list.py index acc5290..d9c2af0 100644 --- a/function_list.py +++ b/function_list.py @@ -4,8 +4,8 @@ sys.path.append('.') from player import Player # Only import what's necessary from enum_list import ItemType, Slots -from race_list import Elf, Dwarf, Human -from job_list import Barbarian, Cleric, Wizard +from race_list import RACES +from class_list import CLASSES def wait(): @@ -16,8 +16,8 @@ pre_generated_characters = { "name": "Arion", # Example Name "gender": "Male", "age": 25, - "race": Elf, - "job": Wizard, # Based on the High Elf sheet + "race": RACES["Elf"], + "class_": CLASSES["Wizard"], # Based on the High Elf sheet "alignment": "lawful good", "description": "A scholarly High Elf skilled in the arcane arts.", "stats": { @@ -41,8 +41,8 @@ pre_generated_characters = { "name": "Brundle", # Example Name "gender": "Female", "age": 35, - "race": Human, - "job": Barbarian, # Based on the Human sheet + "race": RACES["Human"], + "class_": CLASSES["Barbarian"], # Based on the Human sheet "alignment": "chaotic good", "description": "A robust Human warrior, charging into battle.", "stats": { @@ -67,8 +67,8 @@ pre_generated_characters = { "name": "Drok", # Example Name "gender": "Other", "age": 75, # Dwarves live longer - "race": Dwarf, - "job": Cleric, # Based on the Hill Dwarf sheet (Life Domain implied by "healing" focus) + "race": RACES["Dwarf"], + "class_": CLASSES["Cleric"], # Based on the Hill Dwarf sheet (Life Domain implied by "healing" focus) "alignment": "lawful good", "description": "A stout Hill Dwarf cleric, a pillar of his community.", "stats": { @@ -142,10 +142,10 @@ def startGame(): print("Sorry, I didn't catch that. Please try again.\n") continue - # --- Race and Job Selection --- + # --- Race and Class Selection --- race = None - job = None - # Loop until Race and Job are confirmed + class_ = None + # Loop until Race and Class are confirmed while True: # Race Selection selected_race_obj = None @@ -154,11 +154,11 @@ def startGame(): race_choice = input("Please select a race to learn more about it:\n" "1) Elf\n2) Dwarf\n3) Human\n >> ").title() if race_choice in ['Elf', '1']: - selected_race_obj = Elf + selected_race_obj = RACES["Elf"] elif race_choice in ['Dwarf', '2']: - selected_race_obj = Dwarf + selected_race_obj = RACES["Dwarf"] elif race_choice in ['Human', '3']: - selected_race_obj = Human + selected_race_obj = RACES["Human"] else: print("Sorry I didn't recognise that race. Please select 'Elf', 'Dwarf', or 'Human'.\n") continue @@ -177,48 +177,48 @@ def startGame(): print("Sorry, I didn't catch that. Please try again.\n") continue - # Job Selection - selected_job_obj = None - # Loop for selecting and viewing job + # Class Selection + selected_class_obj = None + # Loop for selecting and viewing class while True: - job_choice = input("Please select a job to learn more about it:\n" + class_choice = input("Please select a class to learn more about it:\n" "1) Barbarian\n2) Cleric\n3) Wizard\n >> ").title() - if job_choice in ['Barbarian', '1']: - selected_job_obj = Barbarian - elif job_choice in ['Cleric', '2']: - selected_job_obj = Cleric - elif job_choice in ['Wizard', '3']: - selected_job_obj = Wizard + if class_choice in ['Barbarian', '1']: + selected_class_obj = CLASSES["Barbarian"] + elif class_choice in ['Cleric', '2']: + selected_class_obj = CLASSES["Cleric"] + elif class_choice in ['Wizard', '3']: + selected_class_obj = CLASSES["Wizard"] else: - print("Sorry I didn't recognise that job. Please select 'Barbarian', 'Cleric', or 'Wizard'.\n") + print("Sorry I didn't recognise that class. Please select 'Barbarian', 'Cleric', or 'Wizard'.\n") continue - if selected_job_obj: - print(f"\n--- {selected_job_obj.name} ---") - print(selected_job_obj.__repr__()) + if selected_class_obj: + print(f"\n--- {selected_class_obj.name} ---") + print(selected_class_obj.__repr__()) - print('Would you like to proceed with this job or view another?') - proceed = input('1) Proceed\n2) View another job\n >> ') + print('Would you like to proceed with this class or view another?') + proceed = input('1) Proceed\n2) View another class\n >> ') 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 + class_ = selected_class_obj # Assign class object + break # Exit class selection loop + elif proceed.lower() in ('2', 'view', 'view another', 'view another class'): + continue # Restart class selection else: print("Sorry, I didn't catch that. Please try again.\n") continue - # Final confirmation for both Race and Job + # Final confirmation for both Race and Class while True: # 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 race is None or class_ is None: + print("Error: Race or Class not selected. Restarting Race/Class selection.") + break # Break out of this inner loop to restart the outer race/class loop + correct = input(f"\n{name}, you are a {race.name_adjective} {class_.name}.\nIs this correct? Y/N\n >> ") if correct.lower() in ['y', 'yes']: - return name, gender, age, race, job, None# All confirmed, return values + return name, gender, age, race, class_, None# All confirmed, return values elif correct.lower() in ['n', 'no']: - # If not correct, break this loop to re-enter race/job selection + # If not correct, break this loop to re-enter race/class selection break else: print("Sorry, I didn't catch that. Please try again.\n") @@ -233,19 +233,19 @@ def startGame(): gender = input("Gender (Male, Female, Other): ") age = int(input("Age: ")) race_str = input("Race (Elf, Dwarf, Human): ") - job_str = input("Job (Barbarian, Cleric, Wizard): ") + class_str = input("Class (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} + # Map string inputs to actual Race/Class objects for consistency + race_map = {'elf': RACES["Elf"], 'dwarf': RACES["Dwarf"], 'human': RACES["Human"]} + class_map = {'barbarian': CLASSES["Barbarian"], 'cleric': CLASSES["Cleric"], 'wizard': CLASSES["Wizard"]} actual_race = race_map.get(race_str.lower()) - actual_job = job_map.get(job_str.lower()) + actual_class = class_map.get(class_str.lower()) - if actual_race and actual_job: - return name, gender, age, actual_race, actual_job, None + if actual_race and actual_class: + return name, gender, age, actual_race, actual_class, None else: - print("Invalid race or job entered for pre-generated character. Please try again.") + print("Invalid race or class entered for pre-generated character. Please try again.") start = "1" continue return None @@ -267,7 +267,7 @@ def startGame(): print("Who would you like to to play?") for key, char_data in pre_generated_characters.items(): print( - f"{key}) {char_data['name']} ({char_data['race'].name_adjective} {char_data['job'].name}, {char_data['alignment']})") + f"{key}) {char_data['name']} ({char_data['race'].name_adjective} {char_data['class_'].name}, {char_data['alignment']})") print("Type 'back' to return to character creation options.") char_choice = input(">> ").strip().lower() @@ -284,7 +284,7 @@ def startGame(): print(f"Gender: {selected_char_data['gender']}") print(f"Age: {selected_char_data['age']}") print(f"Race: {selected_char_data['race'].name_adjective}") - print(f"Class: {selected_char_data['job'].name}") + print(f"Class: {selected_char_data['class_'].name}") print(f"Alignment: {selected_char_data['alignment']}") print(f"Description: {selected_char_data['description']}") print(f"Age: {selected_char_data['age']}") @@ -340,11 +340,11 @@ def startGame(): gender = selected_char_data['gender'] age = selected_char_data['age'] race = selected_char_data['race'] - job = selected_char_data['job'] + class_ = selected_char_data['class_'] pre_allocated_stats = selected_char_data['stats'] - print(f"\nYou have selected: {name}, a {age} year old {race.name_adjective} {job.name}.") - return name, gender, age, race, job, pre_allocated_stats # Exit all loops and function + print(f"\nYou have selected: {name}, a {age} year old {race.name_adjective} {class_.name}.") + return name, gender, age, race, class_, pre_allocated_stats # Exit all loops and function elif confirm_choice in ("2", "no"): print("\nReturning to pre-generated character selection.") @@ -532,7 +532,13 @@ def get_instructions(): "Enter cave | house | room - maybe... TBC\n" "Scene or Location - Replays the current area's details\n" "Look around - lists the available items in your scene without displaying the scene description.\n" - "Examine | Open | Loot *object* - Provides details of an item, opens a container, or takes an object.") + "Examine | Open | Loot *object* - Provides details of an item, opens a container, or takes an object.\n" + "Spells | Spellbook - Lists your cantrips, known/preparable spells, prepared spells, and slots.\n" + "Cast *spell name* - Casts a cantrip or prepared spell.\n" + "Prepare | Unprepare *spell name* - Moves a spell in/out of your prepared list.\n" + "Rest - Long rest: restores HP and spell slots.\n" + "Check weight | carry | encumbrance - Shows carrying weight vs capacity.\n" + "Check currency | coins | gold - Shows coinage held and total gp value.") print(descrip1) time.sleep(3) # Reduced sleep for faster testing print(descrip2) @@ -580,6 +586,24 @@ def parse(input_text): command = "go" object1 = " ".join(words[1:]) return command, object1 + elif words[0] == "cast": + command = "cast" + object1 = " ".join(words[1:]) + return command, object1 + elif words[0] == "prepare": + command = "prepare" + object1 = " ".join(words[1:]) + return command, object1 + elif words[0] == "unprepare": + command = "unprepare" + object1 = " ".join(words[1:]) + return command, object1 + elif words[0] == "check" and words[1] in ("weight", "carry", "encumbrance"): + command = "weight" + return command, object1 + elif words[0] == "check" and words[1] in ("currency", "coins", "gold", "money"): + command = "currency" + return command, object1 # Single-word commands if words[0] == "help": @@ -630,6 +654,14 @@ def parse(input_text): command = "drop" # User needs to specify what to drop object1 = None + elif words[0] in ("spells", "spellbook"): + command = "spells" + elif words[0] == "rest": + command = "rest" + elif words[0] in ("weight", "carry", "encumbrance"): + command = "weight" + elif words[0] in ("currency", "coins", "gold", "money"): + command = "currency" elif words[0] == "quit": command = "quit" else: diff --git a/main.py b/main.py index 1edbd52..09a3ffb 100644 --- a/main.py +++ b/main.py @@ -1,4 +1,5 @@ import sys +from random import randint # Add the current directory to sys.path to allow imports from sibling files sys.path.append('.') from player import Player @@ -36,8 +37,8 @@ class Engine: # Display lootable containers if self.current_scene.lootable_items: print("\nYou also notice:") - for container_name, items_in_container in self.current_scene.lootable_items.items(): - if any(count > 0 for count in items_in_container.values()): # Check if container has any items left + for container_name, container in self.current_scene.lootable_items.items(): + if container.has_items(): print(f" - {container_name.title()}") def look_around(self): @@ -53,8 +54,8 @@ class Engine: # Display lootable containers if self.current_scene.lootable_items: print("\nYou also notice:") - for container_name, items_in_container in self.current_scene.lootable_items.items(): - if any(count > 0 for count in items_in_container.values()): # Check if container has any items left + for container_name, container in self.current_scene.lootable_items.items(): + if container.has_items(): print(f" - {container_name.title()}") @@ -116,19 +117,45 @@ class Engine: if not found_in_scene: # Check lootable containers in the current scene found_in_loot = False - for container_name, items_in_container in self.current_scene.lootable_items.items(): - if object_name.lower() in container_name.lower(): # If user tries to examine the container itself - print(f"\nYou examine the {container_name}. Inside, you see:") - if items_in_container: - for item_obj, count in items_in_container.items(): - if count > 0: - print(f" - {item_obj.name} (x{count})") - else: - print("It appears to be empty.") - found_in_loot = True - break # Exit after examining the container + for container_name, container in self.current_scene.lootable_items.items(): + if object_name.lower() in container_name.lower(): + print(f"\n--- {container.name.title()} ---") + print(container.description) - for item_obj, count in items_in_container.items(): + int_mod = self.player.mods["Intelligence"] + noticed_something = False + + if container.lock_dc: + roll = randint(1, 20) + int_mod + print(f"\nInvestigation check: 1d20{int_mod:+} = {roll} vs DC {container.lock_dc}") + if roll >= container.lock_dc: + print("It appears to be locked.") + noticed_something = True + elif not container.trap_dc: + print("You don't notice anything unusual about it.") + + if container.trap_dc: + roll = randint(1, 20) + int_mod + print(f"\nInvestigation check: 1d20{int_mod:+} = {roll} vs DC {container.trap_dc}") + if roll >= container.trap_dc: + print("You notice a trap mechanism on it.") + container.trap_detected = True + noticed_something = True + elif not container.lock_dc and not noticed_something: + print("You don't notice anything unusual about it.") + + if not container.is_locked: + if container.contents: + print("\nInside, you see:") + for item_obj, count in container.contents.items(): + if count > 0: + print(f" - {item_obj.name} (x{count})") + else: + print("It appears to be empty.") + found_in_loot = True + break + + for item_obj, count in container.contents.items(): if object_name.lower() in item_obj.name.lower() and count > 0: print(f"\n--- {item_obj.name} ---") print(item_obj.description) @@ -185,22 +212,32 @@ class Engine: def loot_container(self, container_name): """Allows the player to loot items from a specified container.""" - container_found = False - for current_container_name, items_in_container in list(self.current_scene.lootable_items.items()): - if current_container_name.lower() == container_name.lower(): - container_found = True - if not items_in_container: + for current_name, container in list(self.current_scene.lootable_items.items()): + if current_name.lower() == container_name.lower(): + if container.is_locked: + print(f"The {container_name} is locked.") + return + + if container.is_trapped and not container.trap_disarmed: + if container.trap_detected: + print(f"The {container_name} is trapped. You'll need to disable the trap first.") + return + else: + print(f"You open the {container_name}. A trap is triggered!") + # TODO: trap damage/effects + return + + if not container.contents: print(f"The {container_name} is empty.") return print(f"You look inside the {container_name}. You see:") - loot_list = list(items_in_container.items()) + loot_list = list(container.contents.items()) while True: for i, (item_obj, count) in enumerate(loot_list, 1): print(f"{i}) {item_obj.name} (x{count})") - take_all_option = len(loot_list) + 1 leave_option = len(loot_list) + 2 @@ -209,13 +246,14 @@ class Engine: choice = input("What would you like to take? (number or 'take all'/'leave')\n>> ").lower().strip() - if choice == "take all" or (choice.isdigit() and int(choice) == take_all_option): - for item_obj, count in list(items_in_container.items()): + for item_obj, count in list(container.contents.items()): self.player.inventory.add_item(item_obj, count) print(f"- Took {count} {item_obj.name}") - del items_in_container[item_obj] + container.remove_item(item_obj, count) print(f"The {container_name} is now empty.") + if not container.has_items(): + del self.current_scene.lootable_items[current_name] return elif choice == "leave" or (choice.isdigit() and int(choice) == leave_option): print(f"You leave the {container_name} untouched.") @@ -239,18 +277,16 @@ class Engine: if 0 < take_count <= current_count: self.player.inventory.add_item(item_obj, take_count) - items_in_container[item_obj] -= take_count + container.remove_item(item_obj, take_count) print(f"You took {take_count} {item_obj.name}.") - if items_in_container[item_obj] <= 0: - del items_in_container[item_obj] - loot_list = list(items_in_container.items()) + if item_obj not in container.contents: + loot_list = list(container.contents.items()) - if not items_in_container: - print(f"The {container_name} is now empty.") - return - elif not loot_list: + if not container.contents: print(f"The {container_name} is now empty.") + if not container.has_items(): + del self.current_scene.lootable_items[current_name] return else: print("Invalid amount or not enough items.") @@ -260,8 +296,7 @@ class Engine: print("Invalid selection. Please enter a number, 'take all', or 'leave'.") return - if not container_found: - print(f"You don't see a '{container_name}' here to loot.") + print(f"You don't see a '{container_name}' here to loot.") # Initialize the game engine @@ -272,10 +307,10 @@ def main_game_loop(): The main loop for the game, handling character creation and continuous gameplay. """ # Character Creation - name, gender, age, race, job, pre_allocated_stats = startGame() + name, gender, age, race, class_, pre_allocated_stats = startGame() # Create player instance - player = Player(name, gender, age, race, job) + player = Player(name, gender, age, race, class_) game_engine.set_player(player) # Set the player in the game engine # Display initial character summary @@ -286,13 +321,16 @@ def main_game_loop(): # For 'Pre-generated Character' game_engine.player.stats = pre_allocated_stats game_engine.player.getModifier() - game_engine.player.set_stats_job() # Still need to set HP based on job and Con modifier + game_engine.player.set_stats_class() # Still need to set HP based on class and Con modifier game_engine.player.current_stats() # Display stats after setting them print(' ') game_engine.player.inventory.current_equipment() print(' ') game_engine.player.inventory.current_inventory() print(' ') + if game_engine.player.class_.is_caster: + game_engine.player.print_spellbook() + print(' ') wait() print("\nYour adventure begins...") @@ -340,6 +378,29 @@ def main_game_loop(): elif command == "go": direction = obj1 game_engine.move_to_scene(direction) + elif command == "spells": + game_engine.player.print_spellbook() + elif command == "cast": + if obj1: + game_engine.player.cast_spell(obj1) + else: + print("What would you like to cast?") + elif command == "prepare": + if obj1: + game_engine.player.prepare_spell(obj1) + else: + print("What would you like to prepare?") + elif command == "unprepare": + if obj1: + game_engine.player.unprepare_spell(obj1) + else: + print("What would you like to unprepare?") + elif command == "rest": + game_engine.player.long_rest() + elif command == "weight": + game_engine.player.carry_report() + elif command == "currency": + game_engine.player.currency_report() elif command == "quit": print("Thanks for playing!") break diff --git a/player.py b/player.py index 2b77059..a5c2039 100644 --- a/player.py +++ b/player.py @@ -1,16 +1,33 @@ +import re from random import randint -from enum_list import DamageType, DamageMod, ItemType, Slots +from enum_list import DamageType, DamageMod, ItemType, Slots, SaveType +from spell_list import SPELL_LISTS, get_spell_by_name + + +def roll_dice(dice_str): + """Rolls a dice string like '1d6', '3d4+3', '2d10-1' and returns the total.""" + if not dice_str: + return 0 + match = re.match(r"(\d+)d(\d+)([+-]\d+)?", dice_str.strip()) + if not match: + return 0 + num, size, modifier = match.groups() + total = sum(randint(1, int(size)) for _ in range(int(num))) + if modifier: + total += int(modifier) + return total # Define Item classes here, as they are fundamental building blocks class Item: """The base class for all items""" - def __init__(self, name, description, value, magical, itemType, attunement): + def __init__(self, name, description, value, magical, itemType, attunement, weight=0): self.name = name self.description = description self.value = value self.magical = magical self.itemType = itemType self.attunement = attunement + self.weight = weight # in pounds, per SRD carrying capacity rules def __repr__(self): return f"{self.name}\n=====\n{self.description}\nValue: {self.value}\n" @@ -29,13 +46,14 @@ class Money(Item): value=self.amt, magical=self.magical, itemType=ItemType.Money, - attunement=False) + attunement=False, + weight=0.02) # SRD: 50 coins weigh 1 lb, regardless of denomination class Weapon(Item): """The base class for all weapons""" def __init__(self, name, description, value, slot, damage1H, damage2H, versatiledmg, light, finesse, - dmgType, dmgMod, versatile, thrown, ItemType, magical, attunement): + dmgType, dmgMod, versatile, thrown, ItemType, magical, attunement, weight=0): self.slot = slot self.damage1H = damage1H self.damage2H = damage2H @@ -47,7 +65,7 @@ class Weapon(Item): self.versatile = versatile self.thrown = thrown self.itemType = ItemType - super().__init__(name, description, value, magical, ItemType, attunement) + super().__init__(name, description, value, magical, ItemType, attunement, weight) def __str__(self): if self.damage2H is None and self.damage1H is not None: @@ -84,14 +102,15 @@ class Weapon(Item): class Armor(Item): """The base class for all armor""" - def __init__(self, name, description, value, slot, grade, ac, disadvantage, dmgRes, ItemType, magical, attunement): + def __init__(self, name, description, value, slot, grade, ac, disadvantage, dmgRes, ItemType, magical, + attunement, weight=0): self.slot = slot self.grade = grade self.ac = ac self.stealthDis = disadvantage self.dmgRes = dmgRes self.itemType = ItemType - super().__init__(name, description, value, magical, ItemType, attunement) + super().__init__(name, description, value, magical, ItemType, attunement, weight) def __repr__(self): if self.stealthDis: @@ -299,6 +318,65 @@ class Inventory: attune_count += 1 self.equipped_items[Slots.Attunement] = attune_count + # ----- Weight / Encumbrance (SRD carrying capacity) ----- + def total_weight(self): + """Sums the weight of everything carried, including equipped gear.""" + return sum(item.weight * data["Count"] for item, data in self.items.items()) + + def carrying_capacity(self, strength_score): + """SRD: your carrying capacity is your Strength score multiplied by 15.""" + return strength_score * 15 + + def encumbrance_status(self, strength_score): + """Returns (status_str, weight, capacity) using the SRD variant encumbrance thresholds.""" + weight = self.total_weight() + capacity = self.carrying_capacity(strength_score) + heavily_encumbered_at = strength_score * 10 + encumbered_at = strength_score * 5 + + if weight > capacity: + status = "Over Capacity! You cannot carry this much." + elif weight > heavily_encumbered_at: + status = "Heavily Encumbered (speed -20 ft, disadvantage on Strength/Dexterity/Constitution checks, attacks, and saves)" + elif weight > encumbered_at: + status = "Encumbered (speed -10 ft)" + else: + status = "Unencumbered" + return status, weight, capacity + + def print_carry_report(self, strength_score): + """Prints a human-readable summary of current carrying weight/capacity.""" + status, weight, capacity = self.encumbrance_status(strength_score) + print(f"Carrying {weight:.2f} lb / {capacity} lb capacity.") + print(f"Status: {status}") + + # ----- Currency ----- + def _coin_counts(self): + """Returns a dict of coin name -> count currently held, e.g. {'Gold': 12}.""" + counts = {} + for item, data in self.items.items(): + if item.itemType == ItemType.Money: + counts[item.name] = data["Count"] + return counts + + def total_currency_in_gp(self): + """Converts all held coinage to a single gold-piece value (SRD: 1gp = 10sp = 100cp).""" + coins = self._coin_counts() + gp = coins.get("Gold", 0) + sp = coins.get("Silver", 0) + cp = coins.get("Copper", 0) + return gp + (sp / 10) + (cp / 100) + + def print_currency(self): + """Prints a breakdown of held coinage and its total gold-piece value.""" + coins = self._coin_counts() + if not coins: + print("You have no coins.") + return + parts = [f"{count} {name}" for name, count in coins.items() if count > 0] + print(f"You are carrying: {', '.join(parts) if parts else 'no coins'}.") + print(f"Total value: {self.total_currency_in_gp():.2f} gp") + # --- Item Instances (Moved here for clarity, but still defined once) --- # Different types of coins @@ -323,7 +401,8 @@ rock = Weapon( thrown=True, magical=False, ItemType=ItemType.Weapon, - attunement=False + attunement=False, + weight=2 ) dagger = Weapon( @@ -342,7 +421,8 @@ dagger = Weapon( thrown=True, magical=False, ItemType=ItemType.Weapon, - attunement=False + attunement=False, + weight=1 ) polearm = Weapon( @@ -361,7 +441,8 @@ polearm = Weapon( thrown=False, # Polearms are not typically thrown magical=False, ItemType=ItemType.Weapon, - attunement=False + attunement=False, + weight=6 ) tornRags = Armor( @@ -375,7 +456,8 @@ tornRags = Armor( dmgRes="None", magical=False, ItemType=ItemType.Armor, - attunement=False + attunement=False, + weight=2 ) paper = Item( @@ -384,19 +466,22 @@ paper = Item( value=None, magical=False, itemType=ItemType.Item, - attunement=False + attunement=False, + weight=0 ) class Player: """Character Creation""" - def __init__(self, name, gender, age, race, job): + def __init__(self, name, gender, age, race, class_): # Identity self.name = name self.gender = gender self.age = age self.race = race - self.job = job + # `class_` (not `class`) because `class` is a reserved Python keyword. + # See the NOTE ON NAMING comment at the top of class_list.py for the full convention. + self.class_ = class_ # Defence self.ac = 0 # This will be calculated based on equipped armor @@ -423,6 +508,19 @@ class Player: # Equipment and Inventory self.inventory = Inventory() # Each player gets their own inventory instance + # Proficiency bonus (SRD: +2 at levels 1-4) + self.proficiency_bonus = 2 + + # --- Spellcasting (SRD) --- + # None of these are populated until initialize_spellcasting() runs, since they + # depend on ability modifiers, which are calculated later during character creation. + self.spellcasting_ability = class_.spellcasting_ability # e.g. "Intelligence", or None + self.cantrips_known = [] # list of Spell objects, always available, no slot cost + self.spells_known = [] # Wizard: spellbook contents. Cleric: full accessible list. + self.spells_prepared = [] # subset of spells_known currently prepared/castable + self.spell_slots_max = dict(class_.spell_slots) # {level: max_slots} + self.spell_slots_current = dict(class_.spell_slots) # {level: slots_remaining} + # Ability Scores self.stats = {"Strength": 0, "Dexterity": 0, @@ -540,22 +638,198 @@ class Player: print("As a Dwarf, your constitution increases by 2.") self.stats["Constitution"] += 2 - def set_stats_job(self): - """Applies job-specific stats like hit points.""" - # Using self.job.name to access the job name from the Job object - if self.job.name.lower() == "barbarian": - self.hitDie = randint(self.job.hitdie[0], self.job.hitdie[1]) # Use job's hitdie range - self.maxHP = (self.job.hitdie[1] + self.mods["Constitution"]) # Use max hitdie value for initial HP + def set_stats_class(self): + """Applies class-specific stats like hit points.""" + # Using self.class_.name to access the class name from the CharacterClass object + if self.class_.name.lower() == "barbarian": + self.hitDie = randint(self.class_.hitdie[0], self.class_.hitdie[1]) # Use class's hitdie range + self.maxHP = (self.class_.hitdie[1] + self.mods["Constitution"]) # Use max hitdie value for initial HP self.currentHP = self.maxHP - elif self.job.name.lower() == "cleric": - self.hitDie = randint(self.job.hitdie[0], self.job.hitdie[1]) - self.maxHP = (self.job.hitdie[1] + self.mods["Constitution"]) + elif self.class_.name.lower() == "cleric": + self.hitDie = randint(self.class_.hitdie[0], self.class_.hitdie[1]) + self.maxHP = (self.class_.hitdie[1] + self.mods["Constitution"]) self.currentHP = self.maxHP - elif self.job.name.lower() == "wizard": - self.hitDie = randint(self.job.hitdie[0], self.job.hitdie[1]) - self.maxHP = (self.job.hitdie[1] + self.mods["Constitution"]) + elif self.class_.name.lower() == "wizard": + self.hitDie = randint(self.class_.hitdie[0], self.class_.hitdie[1]) + self.maxHP = (self.class_.hitdie[1] + self.mods["Constitution"]) self.currentHP = self.maxHP + self.initialize_spellcasting() + + # ----- Magic system (SRD) ----- + def initialize_spellcasting(self): + """Grants starting cantrips and known/preparable spells for casters. Non-casters no-op.""" + if not self.class_.is_caster: + return + + class_name = self.class_.name + class_spells = SPELL_LISTS.get(class_name, {}) + + # Cantrips: granted automatically up to the class's cantrips_known count. + available_cantrips = class_spells.get(0, []) + self.cantrips_known = list(available_cantrips[:self.class_.cantrips_known]) + + # Known/preparable leveled spells depend on the class's spellbook style. + available_level1 = class_spells.get(1, []) + if self.class_.spellbook_style == "spellbook": + # Wizard: the whole curated list forms your starting spellbook. + self.spells_known = list(available_level1) + elif self.class_.spellbook_style == "all_known": + # Cleric: you have access to your entire class list, and choose what to prepare. + self.spells_known = list(available_level1) + + # Auto-prepare up to your max_prepared_spells() limit so the character is playable + # immediately after creation; the player can re-prepare later with 'prepare '. + max_prepared = self.max_prepared_spells() + self.spells_prepared = list(self.spells_known[:max_prepared]) + + def max_prepared_spells(self): + """SRD: ability modifier + character level (minimum 1).""" + if not self.spellcasting_ability: + return 0 + return max(1, self.mods[self.spellcasting_ability] + self.level) + + def spell_save_dc(self): + """SRD: 8 + proficiency bonus + spellcasting ability modifier.""" + if not self.spellcasting_ability: + return None + return 8 + self.proficiency_bonus + self.mods[self.spellcasting_ability] + + def spell_attack_bonus(self): + """SRD: proficiency bonus + spellcasting ability modifier.""" + if not self.spellcasting_ability: + return None + return self.proficiency_bonus + self.mods[self.spellcasting_ability] + + def print_spellbook(self): + """Prints known cantrips, known/preparable spells, prepared spells, and slots.""" + if not self.class_.is_caster: + print(f"{self.class_.name}s don't cast spells.") + return + + print(f"Spellcasting Ability: {self.spellcasting_ability}") + print(f"Spell Save DC: {self.spell_save_dc()}") + print(f"Spell Attack Bonus: {self.spell_attack_bonus():+}") + print(f"\nCantrips Known ({len(self.cantrips_known)}):") + for spell in self.cantrips_known: + print(f" - {spell.name}") + + style_label = "Spellbook" if self.class_.spellbook_style == "spellbook" else "Class Spell List" + print(f"\n{style_label} ({len(self.spells_known)}):") + for spell in self.spells_known: + prepared_tag = " [Prepared]" if spell in self.spells_prepared else "" + print(f" - {spell.name} (Level {spell.level}){prepared_tag}") + + print(f"\nPrepared Spells: {len(self.spells_prepared)}/{self.max_prepared_spells()}") + print("Spell Slots:") + for level in sorted(self.spell_slots_max): + print(f" Level {level}: {self.spell_slots_current.get(level, 0)}/{self.spell_slots_max[level]}") + + def prepare_spell(self, spell_name): + """Moves a spell from your known list into your prepared list, if you have room.""" + spell = next((s for s in self.spells_known if s.name.lower() == spell_name.lower()), None) + if not spell: + print(f"'{spell_name}' isn't in your {'spellbook' if self.class_.spellbook_style == 'spellbook' else 'class spell list'}.") + return + if spell in self.spells_prepared: + print(f"{spell.name} is already prepared.") + return + if len(self.spells_prepared) >= self.max_prepared_spells(): + print(f"You can't prepare any more spells ({self.max_prepared_spells()} max). " + f"Unprepare something first.") + return + self.spells_prepared.append(spell) + print(f"Prepared {spell.name}.") + + def unprepare_spell(self, spell_name): + """Removes a spell from your prepared list.""" + spell = next((s for s in self.spells_prepared if s.name.lower() == spell_name.lower()), None) + if not spell: + print(f"'{spell_name}' isn't currently prepared.") + return + self.spells_prepared.remove(spell) + print(f"Unprepared {spell.name}.") + + def cast_spell(self, spell_name, slot_level=None): + """ + Casts a cantrip (free) or a prepared leveled spell (consumes a slot). + Returns a result dict describing what happened, or None if the cast failed. + """ + # Cantrips are always available and never cost a slot. + cantrip = next((s for s in self.cantrips_known if s.name.lower() == spell_name.lower()), None) + if cantrip: + return self._resolve_spell_effect(cantrip, slot_level=0) + + spell = next((s for s in self.spells_prepared if s.name.lower() == spell_name.lower()), None) + if not spell: + print(f"You don't have '{spell_name}' prepared, and it isn't a cantrip you know.") + return None + + # Default to casting at its base level if no upcast level is specified. + cast_level = slot_level or spell.level + if cast_level < spell.level: + print(f"{spell.name} requires at least a level {spell.level} slot.") + return None + if self.spell_slots_current.get(cast_level, 0) <= 0: + print(f"You have no level {cast_level} spell slots remaining.") + return None + + self.spell_slots_current[cast_level] -= 1 + return self._resolve_spell_effect(spell, slot_level=cast_level) + + def _resolve_spell_effect(self, spell, slot_level): + """Rolls damage/healing for a spell and prints/returns the outcome.""" + result = {"spell": spell.name, "damage": 0, "heal": 0, "dmg_type": spell.dmg_type, + "save": spell.save, "save_dc": self.spell_save_dc()} + + # Determine upcast bonus dice, if any. + extra_levels = max(0, slot_level - spell.level) if spell.level > 0 else 0 + + if spell.damage: + damage = roll_dice(spell.damage) + if spell.scaling_damage: + if "per_slot_level" in spell.scaling_damage and extra_levels: + for _ in range(extra_levels): + damage += roll_dice(spell.scaling_damage["per_slot_level"]) + elif self.level in spell.scaling_damage: + # Cantrip scaling by character level (e.g. Fire Bolt at level 5/11/17) + damage = roll_dice(spell.scaling_damage.get(self.level, spell.damage)) + else: + for threshold in sorted([t for t in spell.scaling_damage if isinstance(t, int)], reverse=True): + if self.level >= threshold: + damage = roll_dice(spell.scaling_damage[threshold]) + break + result["damage"] = damage + + if spell.heal: + heal = roll_dice(spell.heal) + if spell.scaling_damage and "per_slot_level" in spell.scaling_damage and extra_levels: + for _ in range(extra_levels): + heal += roll_dice(spell.scaling_damage["per_slot_level"]) + result["heal"] = heal + + # Print a readable summary. + print(f"\nYou cast {spell.name}!") + if spell.save != SaveType.NONE: + print(f"Target must make a DC {result['save_dc']} {spell.save.name} saving throw.") + elif spell.damage: + atk_bonus = self.spell_attack_bonus() + print(f"Spell attack roll: 1d20{atk_bonus:+} vs target AC.") + if result["damage"]: + print(f"Damage: {result['damage']} {spell.dmg_type.name if spell.dmg_type else ''}".rstrip()) + if result["heal"]: + print(f"Healing: {result['heal']} HP") + if not spell.damage and not spell.heal: + print(spell.description) + + return result + + def long_rest(self): + """Restores HP to max and refills all spell slots.""" + self.currentHP = self.maxHP + self.spell_slots_current = dict(self.spell_slots_max) + print("You take a long rest. HP and spell slots are fully restored.") + def current_stats(self): """Prints a display of the user's current statistics.""" print(f'\nYour current stats are:') @@ -584,18 +858,29 @@ class Player: self.allocation() # 1. Sets base self.stats from rolls. self.set_stats_race() # 2. Applies racial bonuses to self.stats. self.getModifier() # 3. Calculates ALL self.mods based on the FINAL self.stats (base + racial). - self.set_stats_job() # 4. Calculates maxHP using the now-accurate self.mods["Constitution"]. + self.set_stats_class() # 4. Calculates maxHP using the now-accurate self.mods["Constitution"]. self.current_stats() # 5. Displays the final stats. print(' ') self.inventory.current_equipment() # Corrected to call instance method print(' ') self.inventory.current_inventory() print(' ') + if self.class_.is_caster: + self.print_spellbook() + print(' ') def health_check(self): """Print out current/max HP""" print(f'You have {self.currentHP}/{self.maxHP} HP.') + def carry_report(self): + """Print current weight carried vs. carrying capacity, and encumbrance status.""" + self.inventory.print_carry_report(self.stats["Strength"]) + + def currency_report(self): + """Print current coinage and its total gold-piece value.""" + self.inventory.print_currency() + def take_damage(self, DMGtype, size): """Define the damage type and dice size""" damage = randint(1, size) diff --git a/race_list.py b/race_list.py index 5807ffa..7257f3c 100644 --- a/race_list.py +++ b/race_list.py @@ -39,7 +39,7 @@ class Elf(Race): speed = { 'Walking': 30 } - darkvision = [30, 60] + darkvision = 60 language = ["Common", "Elvish"] advantage = [] resistance = [] @@ -155,6 +155,8 @@ class Human(Race): language, advantage, resistance, proficiency, traits) -Elf = Elf() -Dwarf = Dwarf() -Human = Human() +RACES = { + "Elf": Elf(), + "Dwarf": Dwarf(), + "Human": Human(), +} diff --git a/scene.py b/scene.py index 26afcdd..748cb76 100644 --- a/scene.py +++ b/scene.py @@ -5,6 +5,36 @@ sys.path.append('.') from player import Item, Weapon, Armor, rock, dagger, polearm, tornRags, paper, GP1, SP1, CP1 from enum_list import * +class Container: + def __init__(self, name, description, lock_dc=None, trap_dc=None): + self.name = name + self.description = description + self.lock_dc = lock_dc + self.trap_dc = trap_dc + self.trap_disarmed = False + self.contents = {} + + @property + def is_locked(self): + return self.lock_dc is not None + + @property + def is_trapped(self): + return self.trap_dc is not None + + def add_item(self, item_obj, count=1): + self.contents[item_obj] = self.contents.get(item_obj, 0) + count + + def remove_item(self, item_obj, count=1): + if item_obj in self.contents: + self.contents[item_obj] -= count + if self.contents[item_obj] <= 0: + del self.contents[item_obj] + + def has_items(self): + return bool(self.contents) + + class Scene: """ Represents a single location or area in the game. @@ -45,15 +75,12 @@ class Scene: return self.exits.get(direction.lower()) def add_lootable_item(self, container_name, item_obj, count=1): - """ - Adds an item to a lootable container within the scene. - :param container_name: The name of the container (e.g., "chest", "goblin corpse"). - :param item_obj: The Item object to add. - :param count: The quantity of the item. - """ if container_name not in self.lootable_items: - self.lootable_items[container_name] = {} - self.lootable_items[container_name][item_obj] = self.lootable_items[container_name].get(item_obj, 0) + count + self.lootable_items[container_name] = Container(container_name, "") + self.lootable_items[container_name].add_item(item_obj, count) + + def add_container(self, container): + self.lootable_items[container.name] = container def add_available_item(self, item_obj, count=1): """ @@ -75,18 +102,31 @@ class Scene: del self.available_items[item_obj] def remove_lootable_item(self, container_name, item_obj, count=1): + if container_name in self.lootable_items: + self.lootable_items[container_name].remove_item(item_obj, count) + if not self.lootable_items[container_name].has_items(): + del self.lootable_items[container_name] + + def add_corpse(self, enemy_name, items): + """Creates a lootable corpse from a defeated enemy. + Multiple corpses of the same enemy are numbered: 'spider corpse', 'spider corpse 2', etc. + items: dict of {item_obj: count} or iterable of (item_obj, count) pairs. """ - Removes a specified count of an item from a lootable container in the scene. - :param container_name: The name of the container. - :param item_obj: The Item object to remove. - :param count: The quantity to remove. - """ - if container_name in self.lootable_items and item_obj in self.lootable_items[container_name]: - self.lootable_items[container_name][item_obj] -= count - if self.lootable_items[container_name][item_obj] <= 0: - del self.lootable_items[container_name][item_obj] - if not self.lootable_items[container_name]: # If container is empty, remove it - del self.lootable_items[container_name] + base_name = f"{enemy_name.lower()} corpse" + name = base_name + counter = 1 + while name in self.lootable_items: + counter += 1 + name = f"{base_name} {counter}" + + corpse = Container(name, f"The lifeless body of a {enemy_name}.") + if isinstance(items, dict): + for item_obj, count in items.items(): + corpse.add_item(item_obj, count) + else: + for item_obj, count in items: + corpse.add_item(item_obj, count) + self.lootable_items[name] = corpse # --- Default Scenes --- @@ -112,9 +152,9 @@ tutorial = Scene( ) tutorial.add_available_item(rock, 1) tutorial.add_available_item(paper, 2) -tutorial.add_lootable_item("suspicious tree trunk", Item("Test Item", - "This is for testing purposes", None, - False, ItemType.Item, False), 1) +tutorial_trunk = Container("suspicious tree trunk", "A hollow tree trunk with a small gap in the bark.") +tutorial_trunk.add_item(Item("Test Item", "This is for testing purposes", None, False, ItemType.Item, False), 1) +tutorial.add_container(tutorial_trunk) # Scene 1: Starting Clearing starting_clearing = Scene( @@ -125,8 +165,10 @@ starting_clearing = Scene( "You notice a small, weathered wooden chest near a fallen log." ) starting_clearing.add_available_item(rock, 3) # Rocks lying on the ground -starting_clearing.add_lootable_item("small wooden chest", GP1, 15) -starting_clearing.add_lootable_item("small wooden chest", SP1, 20) +starting_clearing_chest = Container("small wooden chest", "A small, weathered wooden chest with rusty iron bands.") +starting_clearing_chest.add_item(GP1, 15) +starting_clearing_chest.add_item(SP1, 20) +starting_clearing.add_container(starting_clearing_chest) # Scene 2: Forest Path diff --git a/spell_list.py b/spell_list.py new file mode 100644 index 0000000..b05e974 --- /dev/null +++ b/spell_list.py @@ -0,0 +1,221 @@ +from enum_list import SpellSchool, SaveType, DamageType + + +class Spell: + """Base class for all spells, built from the D&D 5e SRD.""" + def __init__(self, name, level, school, casting_time, range_, components, + duration, description, classes, damage=None, dmg_type=None, + scaling_damage=None, save=SaveType.NONE, heal=None, + ritual=False, concentration=False): + self.name = name + self.level = level # 0 = cantrip + self.school = school + self.casting_time = casting_time + self.range = range_ + self.components = components + self.duration = duration + self.description = description + self.classes = classes # list of class names that can cast this, e.g. ["Wizard"] + self.damage = damage # base damage dice string e.g. "1d10" + self.dmg_type = dmg_type + self.scaling_damage = scaling_damage # dice added per extra slot level/character level, if any + self.save = save + self.heal = heal # healing dice string, e.g. "1d8" + self.ritual = ritual + self.concentration = concentration + + def __repr__(self): + level_str = "Cantrip" if self.level == 0 else f"Level {self.level}" + tags = [] + if self.ritual: + tags.append("Ritual") + if self.concentration: + tags.append("Concentration") + tag_str = f" ({', '.join(tags)})" if tags else "" + return (f"{self.name} - {level_str} {self.school.name}{tag_str}\n" + f"=====\n" + f"Casting Time: {self.casting_time}\n" + f"Range: {self.range}\n" + f"Components: {self.components}\n" + f"Duration: {self.duration}\n\n" + f"{self.description}") + + +# ----- Wizard Cantrips ----- +fire_bolt = Spell( + name="Fire Bolt", level=0, school=SpellSchool.Evocation, + casting_time="1 action", range_="120 feet", components="V, S", + duration="Instantaneous", + description="You hurl a mote of fire at a creature or object within range. Make a ranged " + "spell attack against the target. On a hit, the target takes fire damage. " + "A flammable object hit by this spell ignites if it isn't being worn or carried.", + classes=["Wizard"], damage="1d10", dmg_type=DamageType.Fire, + scaling_damage={5: "2d10", 11: "3d10", 17: "4d10"} +) + +ray_of_frost = Spell( + name="Ray of Frost", level=0, school=SpellSchool.Evocation, + casting_time="1 action", range_="60 feet", components="V, S", + duration="Instantaneous", + description="A frigid beam of blue-white light streaks toward a creature within range. " + "Make a ranged spell attack against the target. On a hit, it takes cold " + "damage and its speed is reduced by 10 feet until the start of your next turn.", + classes=["Wizard"], damage="1d8", dmg_type=DamageType.Cold, + scaling_damage={5: "2d8", 11: "3d8", 17: "4d8"} +) + +mage_hand = Spell( + name="Mage Hand", level=0, school=SpellSchool.Conjuration, + casting_time="1 action", range_="30 feet", components="V, S", + duration="1 minute", + description="A spectral, floating hand appears at a point you choose within range. " + "The hand lasts for the duration and can manipulate objects, open unlocked " + "containers, and carry up to 10 pounds.", + classes=["Wizard"] +) + +prestidigitation = Spell( + name="Prestidigitation", level=0, school=SpellSchool.Transmutation, + casting_time="1 action", range_="10 feet", components="V, S", + duration="Up to 1 hour", + description="This spell is a minor magical trick: create a harmless sensory effect, " + "light or snuff a small flame, clean or soil an object, chill/warm/flavor " + "a small amount of nonliving material, or make a small mark or symbol appear.", + classes=["Wizard"] +) + +# ----- Wizard Level 1 Spells ----- +magic_missile = Spell( + name="Magic Missile", level=1, school=SpellSchool.Evocation, + casting_time="1 action", range_="120 feet", components="V, S", + duration="Instantaneous", + description="You create three glowing darts of magical force. Each dart hits a creature " + "of your choice that you can see within range, dealing force damage. The " + "darts all strike simultaneously and automatically hit - no attack roll needed.", + classes=["Wizard"], damage="3d4+3", dmg_type=DamageType.Force, + scaling_damage={"per_slot_level": "1d4+1"} +) + +shield = Spell( + name="Shield", level=1, school=SpellSchool.Abjuration, + casting_time="1 reaction", range_="Self", components="V, S", + duration="1 round", + description="An invisible barrier of magical force appears and protects you. Until the " + "start of your next turn, you have a +5 bonus to AC and you take no damage " + "from Magic Missile.", + classes=["Wizard"] +) + +burning_hands = Spell( + name="Burning Hands", level=1, school=SpellSchool.Evocation, + casting_time="1 action", range_="Self (15-foot cone)", components="V, S", + duration="Instantaneous", + description="A thin sheet of flames shoots forth from your outstretched fingertips. Each " + "creature in a 15-foot cone must make a Dexterity saving throw. A creature " + "takes fire damage on a failed save, or half as much on a successful one.", + classes=["Wizard"], damage="3d6", dmg_type=DamageType.Fire, save=SaveType.Dexterity, + scaling_damage={"per_slot_level": "1d6"} +) + +detect_magic = Spell( + name="Detect Magic", level=1, school=SpellSchool.Divination, + casting_time="1 action", range_="Self", components="V, S", + duration="Concentration, up to 10 minutes", + description="For the duration, you sense the presence of magic within 30 feet of you. " + "If you sense magic in this way, you can use your action to see a faint " + "aura around any visible creature or object that bears magic.", + classes=["Wizard", "Cleric"], ritual=True, concentration=True +) + +# ----- Cleric Cantrips ----- +sacred_flame = Spell( + name="Sacred Flame", level=0, school=SpellSchool.Evocation, + casting_time="1 action", range_="60 feet", components="V, S", + duration="Instantaneous", + description="Flame-like radiance descends on a creature that you can see within range. " + "The target must succeed on a Dexterity saving throw or take radiant damage. " + "The target gains no benefit from cover for this saving throw.", + classes=["Cleric"], damage="1d8", dmg_type=DamageType.Radiant, save=SaveType.Dexterity, + scaling_damage={5: "2d8", 11: "3d8", 17: "4d8"} +) + +guidance = Spell( + name="Guidance", level=0, school=SpellSchool.Divination, + casting_time="1 action", range_="Touch", components="V, S", + duration="Concentration, up to 1 minute", + description="You touch one willing creature. Once before the spell ends, the target " + "can roll a d4 and add the number rolled to one ability check of its choice.", + classes=["Cleric"], concentration=True +) + +spare_the_dying = Spell( + name="Spare the Dying", level=0, school=SpellSchool.Necromancy, + casting_time="1 action", range_="Touch", components="V, S", + duration="Instantaneous", + description="You touch a living creature that has 0 hit points. The creature becomes " + "stable. This spell has no effect on undead or constructs.", + classes=["Cleric"] +) + +# ----- Cleric Level 1 Spells ----- +cure_wounds = Spell( + name="Cure Wounds", level=1, school=SpellSchool.Evocation, + casting_time="1 action", range_="Touch", components="V, S", + duration="Instantaneous", + description="A creature you touch regains a number of hit points.", + classes=["Cleric"], heal="1d8", scaling_damage={"per_slot_level": "1d8"} +) + +bless = Spell( + name="Bless", level=1, school=SpellSchool.Enchantment, + casting_time="1 action", range_="30 feet", components="V, S, M", + duration="Concentration, up to 1 minute", + description="You bless up to three creatures of your choice within range. Whenever a " + "target makes an attack roll or a saving throw before the spell ends, the " + "target can add 1d4 to the attack roll or saving throw.", + classes=["Cleric"], concentration=True +) + +guiding_bolt = Spell( + name="Guiding Bolt", level=1, school=SpellSchool.Evocation, + casting_time="1 action", range_="120 feet", components="V, S", + duration="1 round", + description="A flash of light streaks toward a creature of your choice within range. " + "Make a ranged spell attack against the target. On a hit, the target takes " + "radiant damage, and the next attack roll made against this target before " + "the end of your next turn has advantage.", + classes=["Cleric"], damage="4d6", dmg_type=DamageType.Radiant, + scaling_damage={"per_slot_level": "1d6"} +) + +healing_word = Spell( + name="Healing Word", level=1, school=SpellSchool.Evocation, + casting_time="1 bonus action", range_="60 feet", components="V", + duration="Instantaneous", + description="A creature of your choice that you can see within range regains hit points.", + classes=["Cleric"], heal="1d4", scaling_damage={"per_slot_level": "1d4"} +) + + +# Master lookup: {class_name: {spell_level: [Spell, ...]}} +SPELL_LISTS = { + "Wizard": { + 0: [fire_bolt, ray_of_frost, mage_hand, prestidigitation], + 1: [magic_missile, shield, burning_hands, detect_magic], + }, + "Cleric": { + 0: [sacred_flame, guidance, spare_the_dying], + 1: [cure_wounds, bless, guiding_bolt, healing_word, detect_magic], + }, +} + + +def get_spell_by_name(name): + """Looks up a Spell object across all class lists by (case-insensitive) name.""" + name = name.lower().strip() + for class_spells in SPELL_LISTS.values(): + for level_spells in class_spells.values(): + for spell in level_spells: + if spell.name.lower() == name: + return spell + return None