import time from functions import startGame, wait, parse, get_instructions, query_equip, error_message, change_equip from random import randint from enum import Enum, auto # pyinstaller --onefile --paths=C:\Users\kasai\PycharmProjects\pythonProject\Kanjin\KanjinEngine.py class Engine(object): def __init__(self, scene_map): # self.name = "name" # self.descrip = "descrip" self.seed = None self.scene_map = scene_map # gets the game's map from instance "mymap" at bottom of this file def play(self): current_scene = self.scene_map.opening_scene() last_scene = self.scene_map.next_scene('finished') while current_scene != last_scene: next_scene_name = current_scene.enter() current_scene = self.scene_map.next_scene(next_scene_name) current_scene.enter() # # note: will throw error if no new scene passed in by next line: # next_scene_name = current_scene.action() # # get the name of the next scene from the action() function that # # runs in the current scene - what it returns # # current_scene = self.scene_map.next_scene(next_scene_name) # # here we use that val returned by current scene to go to # # the next scene, running function in Map class Player: """Character Creation""" # Default Character def __init__(self, name, gender, age, race, job): # Personality self.name = name self.gender = gender self.age = age self.race = race self.job = job # Health self.hitDie = randint(1, 6) self.currentHP = 100 self.maxHP = 100 # Levels self.level = 1 self.exp = 0 self.maxEXP = 100 # Magic self.spells_known = [] self.spells_ready = [] # Equipment self.weapon = rock self.armor = tornRags # Defence self.ac = 0 self.elemental_resistance = {DamageType.FIRE: False, DamageType.WATER: False, DamageType.LIGHTNING: False} self.physical_resistance = {DamageType.SLASHING: False, DamageType.CRUSHING: False, DamageType.PIERCING: False} # Ability Scores self.stats = {"Strength": 0, "Dexterity": 0, "Constitution": 0, "Intelligence": 0, "Wisdom": 0, "Charisma": 0} # Ability Modifiers self.mods = {"Strength": 0, "Dexterity": 0, "Constitution": 0, "Intelligence": 0, "Wisdom": 0, "Charisma": 0} # Held items self.inventory = {rock: {"Count": 1, "object": Weapon, "equipped": True }, paper: {"Count": 1, "object": Weapon, "equipped": False }, tornRags: {"Count": 1, "object": Armor, "equipped": True }, GP1: {"Count": 10, "object": Money, "equipped": False }, } def getModifier(self): self.mods["Strength"] = -5 + self.stats["Strength"] // 2 self.mods["Dexterity"] = -5 + self.stats["Dexterity"] // 2 self.mods["Constitution"] = -5 + self.stats["Constitution"] // 2 self.mods["Intelligence"] = -5 + self.stats["Intelligence"] // 2 self.mods["Wisdom"] = -5 + self.stats["Wisdom"] // 2 self.mods["Charisma"] = -5 + self.stats["Charisma"] // 2 def set_stats_race(self): if self.race.lower() == "human": self.stats["Strength"] += 1, self.stats["Dexterity"] += 1, self.stats["Constitution"] += 1, self.stats["Intelligence"] += 1, self.stats["Wisdom"] += 1, self.stats["Charisma"] += 1 elif self.race.lower() == "elf": self.stats["Dexterity"] += 2 self.stats["Intelligence"] += 1 elif self.race.lower() == "dwarf": self.stats["Constitution"] += 2 self.stats["Wisdom"] += 1 def set_stats_job(self): if self.job.lower() == "warrior": self.hitDie = randint(1, 12) self.maxHP = (12 + self.mods["Constitution"]) self.currentHP = (12 + self.mods["Constitution"]) elif self.job.lower() == "ranger": self.hitDie = randint(1, 10) self.maxHP = (10 + self.mods["Constitution"]) self.currentHP = (10 + self.mods["Constitution"]) elif self.job.lower() == "mage": self.hitDie = randint(1, 8) self.maxHP = (8 + self.mods["Constitution"]) self.currentHP = (8 + self.mods["Constitution"]) def current_stats(self): """Prints a display of the user's current statistics.""" print("Your current stats are:") print(f'Hit Points: {self.currentHP}/{self.maxHP}') print(f'Strength: {self.stats["Strength"]}') print(f'Dexterity: {self.stats["Dexterity"]}') print(f'Constitution: {self.stats["Constitution"]}') print(f'Intelligence: {self.stats["Intelligence"]}') print(f'Wisdom: {self.stats["Wisdom"]}') print(f'Charisma: {self.stats["Charisma"]}') def current_equip(self): """Prints a list of items currently equipped.""" print(f"You have equipped:\n {self.armor.name}\n" f" {self.armor.description}" f"\n{self.weapon.name}\n" f" {self.weapon.description}\n") 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:') for item_key, item_value in self.inventory.items(): # string, dictionary for key, value in item_value.items(): # variable/object, integer if (key == "equipped") and not value: # if equipped = False values = item_value.values() # make a variable of all values values_list = list(values) # turn variable into a list count_value = values_list[0] # take the first value from list - why? print(f'{item_key.name} x {count_value}\n' f' {item_key.description}') def drop_item(self, object1): pass def loot_object(self, object1): for scene, room in Map.scenes.items(): # iterate map scenes if Engine.seed == scene: # set new Engine seed in each room.enter() for list_key, list_value in room.lootable_items.items(): # string, dictionary if object1 in list_key: # if user input is in the lootable items for var, count in list_value.items(): # variable/object, integer if var in self.inventory: # if object is in inventory self.inventory[var]["Count"] += count # increase item count print(f'{var.name} x {count} has been added to your existing stack.') elif var not in self.inventory[var]: # if variable not in inventory self.inventory[var] = {"Count": count, # add it in "object": var.type, "equipped": False} print(f'{var.name} x {count} has been added to your rucksack.') del room.lootable_items[object1] # remove item from lootable list break else: print("You don't see one of those to loot.") break def add_inventory(self, object1): for scene, room in Map.scenes.items(): # iterate map scenes if Engine.seed == scene: # set new Engine seed in each room.enter() for list_key, list_value in room.available_items.items(): # string, dictionary if object1 in list_key: # if user input is in the available items for var, count in list_value.items(): # variable/object, integer if var in self.inventory: # if object is in inventory self.inventory[var]["Count"] += count # increase item count print(f'{var.name} x {count} has been added to your existing stack.') elif var not in self.inventory[var]: # if variable not in inventory self.inventory[var] = {"Count": count, # add it in "object": var.type, "equipped": False} print(f'{var.name} x {count} has been added to your rucksack.') del room.available_items[object1] # remove item from available list break elif object1 not in list_key: print("You don't see any lying around..") break def new_char(self): """Outputs final stats, armour, and inventory""" self.allocation() self.set_stats_race() self.set_stats_job() self.getModifier() self.current_stats() print(' ') time.sleep(1) self.current_equip() print(' ') self.current_inventory() print(' ') def health_check(self): """Print out current/max HP""" print(f'You have {self.currentHP}/{self.maxHP} HP.') def take_damage(self, DMGtype, size): """Define the damage type and dice size""" damage = randint(1, size) if DMGtype in self.elemental_resistance or DMGtype in self.physical_resistance: damage //= 2 self.currentHP -= damage return damage def allocation(self): rolls = [] stats = [] attributes = ["Strength", "Dexterity", "Constitution", "Intelligence", "Wisdom", "Charisma"] while len(stats) != 6: # roll 4d6 for roll in range(4): r = randint(1, 6) if r < 6: r = randint(1, 6) rolls.append(r) # Drop the lowest number and then add to single value del rolls[rolls.index(min(rolls))] val = sum(rolls) stats.append(str(val)) rolls = [] print("Please assign a stat to a selected attribute by entering the number then the attribute.\n" "For example, '10 strength' will assign the stat 10 to your strength, if you have a 10 showing.\n") print("Please select a stat and an attribute.\n\n") print("Your rolled stats are:\n" + ', '.join(stats) + "\n\n") print("Your attributes are:\n" + ', '.join(attributes) + "\n\n") # Allocate while len(stats) != 0: input_text = input(">> ").title() words = input_text.split() if words[0] in stats and words[1] in attributes: self.stats[words[1]] += int(words[0]) stats.remove(words[0]) attributes.remove(words[1]) if len(stats) == 0: print("") break print("The remaining options are:\n") print(', '.join(stats)) print(', '.join(attributes)) else: print("Error. Input was not recognised. Please try again with 'Number' + 'Attribute'.") continue class Scene(object): def __init__(self): self.objects = [] # def enter(self): # print(self.name) # works # print(self.descrip) # works # # this applies to all classes under Scene, but Zed does it differently class DamageType(Enum): SLASHING = auto() CRUSHING = auto() PIERCING = auto() FIRE = auto() WATER = auto() LIGHTNING = auto() class DamageMod(Enum): STRENGTH = auto() DEXTERITY = auto() CONSTITUTION = auto() INTELLIGENCE = auto() WISDOM = auto() CHARISMA = auto() class ItemType(Enum): ARMOR = auto() WEAPON = auto() MONEY = auto() ITEM = auto() class Item: """The base class for all items""" def __init__(self, name, description, value, magical): self.name = name self.description = description self.value = value self.magical = magical self.type = Item self.count = 1 def __repr__(self): return f"{self.name}\n=====\n{self.description}\nValue: {self.value}\n" class Money(Item): """The currency item used in the world of Kanjin""" def __init__(self, name, amt, magical): self.name = name self.amt = amt self.magical = magical self.type = ItemType.MONEY super().__init__(name=self.name, description=f"A small round coin made of {self.name.lower()} " f"with the imperial city logo stamped on the face.", value=self.amt, magical=self.magical) class Weapon(Item): """The base class for all weapons""" def __init__(self, name, description, value, damage1H, damage2H, dmgType, dmgMod, versatile, thrown, magical): self.damage1H = damage1H self.damage2H = damage2H self.dmgType = dmgType self.dmgMod = dmgMod self.versatile = versatile self.thrown = thrown self.type = Weapon super().__init__(name, description, value, magical) def __repr__(self): return f"{self.name}\n=====" \ f"\n{self.description}" \ f"\nValue: {self.value}" \ f"\nDamage: One Handed - {self.damage1H}" \ f"\nDamage: Two Handed - {self.damage2H}" \ f"\nDamage Type: {self.dmgType}" \ f"\nMagical: {self.magical}\n" class Armor(Item): """The base class for all armor""" def __init__(self, name, description, value, grade, ac, disadvantage, dmgRes, magical): self.grade = grade self.ac = ac self.stealthDis = disadvantage self.dmgRes = dmgRes self.type = Armor super().__init__(name, description, value, magical) def __repr__(self): if self.stealthDis: return f"{self.name}\n" \ f"=====\n" \ f"{self.description}\n" \ f"Value: {self.value}\n" \ f"AC: {self.ac}\n" \ f"Magical: {self.magical}" elif not self.stealthDis: return f"{self.name}\n=====" \ f"\n{self.description}\n" \ f"Value: {self.value}\n" \ f"AC: {self.ac}\n" \ f"Disadvantage on Stealth checks: {self.stealthDis}\n" \ f"Magical: {self.magical}\n" def process_command(command, object1): output = None if command == "help": output = get_instructions() elif command == "inventory": user.current_inventory() elif command == "equipment": response = query_equip() if response == "view": user.current_equip() elif response == "change": output = change_equip() elif command == "examine": for item_key, item_value in user.inventory.items(): if object1 == item_key.name.lower(): print(item_key) else: output = "Unable to examine that." elif command == "take": user.add_inventory(object1) elif command == "loot": user.loot_object(object1) elif command == "error": output = error_message() return output # Different types of coins GP1 = Money("Gold", 1, False) SP1 = Money("Silver", 1, False) CP1 = Money("Copper", 1, False) # starting equip rock = Weapon( name="Rock", description="A fist-sized rock, suitable for bludgeoning.", value="No value", damage1H=randint(1, 6), damage2H=0, dmgType=DamageType.CRUSHING, dmgMod=DamageMod.STRENGTH, versatile=False, thrown=True, magical=False ) tornRags = Armor( name="Torn Rags", description="A ripped and worn-out outfit.", value="No Value", grade="Light", ac=0, disadvantage=True, dmgRes="None", magical=False ) paper = Item( name="Paper", description="A blank piece of paper", value=0, magical=False ) user = Player("Jamie", "Male", 29, "Elf", "Mage") # user = Player(*startGame()) # enter() needs to return a map name. class Tutorial(Scene): available_items = { "rock": {rock: 12}, "paper": {paper: 3} } lootable_items = { "chest": {GP1: 5, rock: 1}, "corpse": {GP1: 100} } name = "Tutorial Level" # descrip = ("There are certain commands that will be available almost anytime you are able to type,\n" # "such as viewing your inventory, checking your equipped items, and also changing them.\n" # "You can also view your stats including your current and max hp.\n" # "Give it a go starting with 'Check inventory', 'Check equipment', and 'View stats'.\n" # "See what else you can do, then enter 'continue' to move to the next step.") descrip = f'Scattered around the room you see {available_items["rock"][rock]}x rock.' has_visited = False current_seed = "tutorial" def enter(self): Engine.seed = "tutorial" if not self.has_visited: print("===========") print(self.name) print("===========") print(self.descrip) else: print("Welcome back to the tutorial.") self.has_visited = True action = input(">> ").lower() while action not in ("continue", "scene"): # move scene to parser process_command(*parse(action)) action = input(">> ").lower() else: if action == "continue": return "begin" elif action == "scene": print(self.available_items) return self.current_seed def action(self): pass class Begin(Scene): available_items = { "rock": {rock: 10}, "paper": {paper: 13}} name = "Kanjin - An RPG Text Adventure" descrip = "Welcome to Kanjin.\nThis game is based off of the D&D SRD ruleset, and will see you, our adventurer, " \ "exploring, adventuring, and plundering.\n\nTo play Kanjin is very simple.\nRead the instructions, " \ "understand the scene, and take control.\nAll directional cues will be given and clear but you " \ "can also use specific commands at almost anytime when you give your input.\n" \ "After character creation, type 'Help' to view a list of commands and explanations.\n" has_visited = False current_seed = "begin" def enter(self): Engine.seed = "begin" if not self.has_visited: print("===========") print(self.name) print("===========") print(self.descrip) wait() # user.new_char() else: print("Welcome back to the beginning") self.has_visited = True action = input(">> ").lower() while action not in ("continue", "scene", "back"): process_command(*parse(action)) action = input(">> ").lower() else: if action == "continue": return "cave entrance" elif action == "back": return "tutorial" elif action == "scene": print(self.descrip) return self.current_seed return "Cave entrance" def action(self): pass class CaveEntrance(Scene): available_items = {} name = "Cave Entrance" descrip = "A small broken statue at the mouth of a cave marks the entrance to a dungeon.\n" \ "Beyond the broken statue lies a massive, rugged room, too dark to see into.\n" \ "You make out the silhouette of broken pottery sprawled around.\n" \ "\nBehind you is the forest you traversed through to get here, about 1500m away from the road.\n" \ "To the east, a canyon. Too wide to cross and too deep to see the bottom.\n" \ "On the west is a mountain face. You are not prepared to take that on.\n" descFalse = "You return to the cave entrance with the road behind you, a canyon to the east, " \ "and a mountain face on the right." has_visited = False current_seed = "cave entrance" def enter(self): Engine.seed = "cave entrance" if not self.has_visited: print("===========") print(self.name) print("===========") print(self.descrip) else: print("Welcome back to the cave") self.has_visited = True action = input(">> ").lower() while action not in ("continue", "scene", "back"): process_command(*parse(action)) action = input(">> ").lower() else: if action == "continue": return "begin" elif action == "back": return "begin" elif action == "scene": print(self.descrip) return "Cave entrance" def action(self): pass class A1(Scene): available_items = {} name = "Laser Weapon Armory" descrip = "Shelves and cases line the walls of this room. Weapons of every description " \ "fill the shelves and cases. There is a digital keypad set into the wall." current_seed = "A1" def enter(self): print("===========") print(self.name) print("===========") print(self.descrip) def action(self): pass # class Death(Scene): # # def __init__(self): # self.name = "You have died!" # self.descrip = "Your spirit leaves swiftly as your body collapses." # # quips = [ # "You died. You kinda suck at this.", # "Your Mom would be proud...if she were smarter.", # "Such a loser.", # "I have a small puppy that's better at this.", # "You're worse than your Dad's jokes." # ] # # def enter(self): # print(Death.quips[randint(0, len(self.quips) - 1)]) # exit(1) # # def action(self): # pass # Map tells us where we are and where we can go # it does not make us move - Engine does that class Map(object): scenes = { 'tutorial': Tutorial(), "begin": Begin(), 'cave entrance': CaveEntrance(), 'A1': A1(), # 'death': Death(), # 'finished': Finished() } def __init__(self, start_scene_key): self.start_scene_key = start_scene_key # above we make a local var named start_scene_key # start_scene_key remains unchanged throughout the game @staticmethod def next_scene(scene_name): val = Map.scenes.get(scene_name) # above is how we get value out of the dictionary named scenes return val # this function can be called repeatedly in the game, # unlike opening_scene, which is called only ONCE def opening_scene(self): return self.next_scene(self.start_scene_key) seed = "tutorial" # starts the game myMap = Map(seed) # instantiate a new Map object w/ one arg myGame = Engine(myMap) # instantiate a new Engine object w/ one arg myGame.play() # call function from that Engine instance