Following all the good notes I've received from my question, I've edited my code quite a lot, so there are probably even more problems than there was before. Anyway, here are some details of the changes I've made:
Changes
- Added
Hero
andMap
class - Added
Warrior
,Mage
,Healer
subclasses ofHero
- Saved all story/game related text into separate files
- Battles are no longer automated. Characters have options now
- Changed some variables into more appropriate data types
- Removed all global variables
The code is even longer than before (sorry):
import time
import yaml
import pickle
import os
import random
hero_races = ("Human", "Elf", "Orc")
hero_classes = ("Warrior", "Mage", "Healer")
dungeon_map = ("Training Room", "Slime Room", "Chimera Room", "Demon Room")
save_file = "hero_information.txt"
if os.path.exists("TextGameDetails.yaml"):
with open("TextGameDetails.yaml") as story_file:
story_info = yaml.load(story_file)
dungeon_description = story_info["dungeon_description"]
level_complete = story_info["level_complete"]
else:
print("Game files are missing!\nCannot run game")
exit()
class Monster:
def __init__(self, level):
self.health = 100 * level
self.attack = 20 * level
def battle_action(self, hero):
damage = random.randrange(self.attack / 2, self.attack)
if hero.block_stance:
print("Monster attacks you for {} damage"
.format(damage / 2))
hero.health -= damage / 2
else:
print("Monster attacks you for {} damage"
.format(damage))
hero.health -= damage
class Hero:
def __init__(self, hero_name, hero_health, hero_attack, race_choice,
class_choice):
self.health = hero_health
self.attack = hero_attack
self.block_stance = False
self.name = hero_name
self.race = race_choice
self.h_class = class_choice
def attack_enemy(self, monster):
damage = random.randrange(int(self.attack / 2), self.attack)
print(f"You attack the monster for {damage} damage!")
monster.health -= damage
print(f"Monster has only {monster.health} health remaining")
def block(self):
print("You switch to a defensive stance!")
self.block_stance = True
def battle_action(self, monster):
self.block_stance = False
print("1- Attack\t2- Defend")
action_option = int(input("Action: "))
if action_option == 1:
self.attack_enemy(monster)
elif action_option == 2:
self.block()
def display_status(self):
print(f"Welcome back {self.name} the {hero_races[self.race - 1]} "
f"{hero_classes[self.h_class -1]}, you have {self.health} health")
class Warrior(Hero):
def __init__(self, hero_name, hero_health, hero_attack, race_choice,
class_choice):
super(Warrior, self).__init__(hero_name, hero_health, hero_attack,
race_choice, class_choice)
class Mage(Hero):
def __init__(self, hero_name, hero_health, hero_attack, race_choice,
class_choice):
super(Mage, self).__init__(hero_name, hero_health, hero_attack,
race_choice, class_choice)
self.spell = self.attack * 2
def cast_spell(self, monster):
damage = random.randrange(self.spell / 2, self.spell)
print("You attack the monster with magic for {} damage!"
.format(damage))
monster.health -= damage
print("Monster has only {} health remaining"
.format(monster.health))
def battle_action(self, monster):
self.block_stance = False
print("1- Attack\t2- Defend\t3- Cast Spell")
action_option = int(input("Action: "))
if action_option == 1:
self.attack_enemy(monster)
elif action_option == 2:
self.block()
elif action_option == 3:
self.cast_spell(monster)
class Healer(Hero):
def __init__(self, hero_name, hero_health, hero_attack, race_choice,
class_choice):
super(Healer, self).__init__(hero_name, hero_health, hero_attack,
race_choice, class_choice)
def battle_action(self, monster):
self.block_stance = False
print("1- Attack\t2- Defend\t3- Heal Self")
action_option = int(input("Action: "))
if action_option == 1:
self.attack_enemy(monster)
elif action_option == 2:
self.block()
elif action_option == 3:
print("You have restored 50 health points")
self.health += 50
class Map:
def __init__(self, level):
self.level = level
def display_map(self):
print(f"You are at the {dungeon_map[self.level]}")
def map_navigation(self, hero):
while True:
print("1- Proceed\t2- Status\t3- Map\t4- Save\t5- Exit to Main Screen")
choice = int(input("What should I do?... "))
if choice == 1:
self.level += 1
return 0
elif choice == 2:
hero.display_status()
elif choice == 3:
self.display_map()
elif choice == 4:
save_game(hero, self)
elif choice == 5:
return 1
def create_hero():
print("What is your name?")
hero_name = input("My name is: ")
print("Choose a race")
print("1- Human\t\t2- Elf\t\t3- Orc")
race_choice = int(input("My race is: "))
while True:
print("Choose a class.")
print("1- Warrior\t\t2- Mage\t\t3- Healer")
class_choice = int(input("My class is: "))
if class_choice == 1:
hero = Warrior(hero_name, 200, 100, race_choice, class_choice)
break
elif class_choice == 2:
hero = Mage(hero_name, 100, 50, race_choice, class_choice)
break
elif class_choice == 3:
hero = Healer(hero_name, 200, 25, race_choice, class_choice)
break
return hero
def save_game(hero, game_map):
print("Saving Game...\nPlease don't turn off the application.\n...")
time.sleep(3)
with open(save_file, 'wb') as info:
pickle.dump(hero, info)
pickle.dump(game_map, info)
print("Your game has been saved successfully!")
def load_game():
if not os.path.exists(save_file):
return 1, 1
with open(save_file, 'rb') as info:
hero = pickle.load(info)
game_map = pickle.load(info)
return hero, game_map
def encounter(hero, game_map):
print(dungeon_description[game_map.level])
monster = Monster(game_map.level + 1)
battle(hero, monster)
print(level_complete[game_map.level])
def battle(hero, monster):
print("Prepare to battle!\n...")
time.sleep(3)
while monster.health > 0:
print("Prepare for monster attack! ....")
time.sleep(2)
monster.battle_action(hero)
time.sleep(1)
print("Your turn to fight back!\n...")
hero.battle_action(monster)
def main(choices=("New Game", "Load Game", "Exit")):
while True:
active_character = False
for index, choice in enumerate(choices, start=1):
print(f'{index}- {choice}')
start_choice = int(input())
if start_choice == 3:
print("Game Over.")
break
elif start_choice == 1:
hero = create_hero()
active_character = True
game_map = Map(0)
encounter(hero, game_map)
elif start_choice == 2:
hero, game_map = load_game()
if hero == 1 or game_map == 1: # 1 is the return value if there is no save file
print("File save not found")
continue
active_character = True
if active_character:
max_level = len(dungeon_map) - 1
while game_map.level < max_level:
if game_map.map_navigation(hero) == 1:
break
encounter(hero, game_map)
if __name__ == "__main__":
main()
Here's my TextGameDetails.yaml file (copy/paste this into a notepad++ file, file extension should be .yaml, not .yml):
---
dungeon_description:
- "You are standing in the knight's training chambers. In front of you lies your master-at-arms."
- |-
You enter the room. You can barely make out anything from this dark area. However you see a glowing blue light straight ahead of you.
The light burns greater... it begins to make the shape of a creature... a slime!
- |-
As you proceed into the room you hear eerie noises from all across the room followed by a dark shape that moves too fast for you to catch. You stand in the middle of the round room you hear a loud growl behind you.
You turn around to fight a lion no... it's a two-headed creature, both a lion and a giant serpent.
- |-
As you step into the room you find it hard to stand as if an unknown force is pushing your body down into the ground. You start shaking your teeth grinding.
It takes you a moment but you are finally able to regain control of your body. Already tired from your previous trials you push ahead and find an empty throne.
You blink and all of a sudden there is a dark figure sitting on the chair. He no... It smiles and stands up then walks toward you.
.... This is your final challenge.
level_complete:
- |-
After several excruciating blows, your sparring session is over.
Your teacher guides you to the gate where your first true challenge begins to become a full-pledged knight
- |-
you fight it off and you (surprisingly) beat your first real enemy.
Behind the gooey carcass of the now-defeated slime lies another room with the doorknob of a lion
- |-
You fight it off and you barely beat this vicious creature. You reach the edge of the room to find a dark door. Red mist flows through the gaps of the door.
Do you proceed? This may be your final choice
- "With great struggle, you defeat your final challenge. Now you are ready to become a full-fledged knight."
Game files are missing! Cannot run game
Could you provide yourTextGameDetails.yaml
as well? \$\endgroup\$ – Georgy Dec 27 '17 at 21:17