Files
Kanjin/Kanjin-Engine
2022-02-09 20:21:57 +13:00

727 lines
26 KiB
Plaintext

import time
from functions import wait, startGame, query_equip, change_equip, get_instructions
from functions import error_message, parse
from random import randint
from enum import Enum, auto
# pyinstaller --onefile --paths=C:\Users\kasai\PycharmProjects\pythonProject\Kanjin\KanjinEngine.py
class Engine(object):
seed = None
def __init__(self, scene_map):
# self.name = "name"
# self.descrip = "descrip"
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):
x = None
for list_key, list_value in self.inventory.items(): # string, dictionary
if object1 in list_key.name.lower(): # if user input is in the inventory list
x = self.inventory.copy()
del x[list_key]
print("Did I drop it?")
return x
def update_inventory(self, x):
self.inventory = x
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
for var, count in list_value.items(): # variable/object, integer
if object1 in list_key: # if user input is in the lootable items
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.')
room.lootable_items[object1][var] = 0 # set item to 0
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
for var, count in list_value.items(): # variable/object, integer
if object1 in list_key: # if user input is in the available items
response = input(f'There are {room.available_items[object1][var]}.\n'
f'How many will you take?\n>> ').lower()
if response in ("0", "none"):
break
elif response == "all":
if var in self.inventory:
self.inventory[var]["Count"] += count # increase item count
print(f'{var.name} x {count} has been added to your existing stack.')
room.available_items[object1][var] = 0
elif var not in self.inventory: # if objects 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.')
room.available_items[object1][var] = 0
elif int(response) <= count:
if var in self.inventory:
self.inventory[var]["Count"] += int(response) # increase item count
print(f'{var.name} x {response} has been added to your existing stack.')
room.available_items[object1][var] = \
room.available_items[object1][var] - int(response)
elif var not in self.inventory: # if objects not in inventory
self.inventory[var] = {"Count": response, # add it in
"object": var.type,
"equipped": False}
print(f'{var.name} x {response} has been added to your rucksack.')
room.available_items[object1][var] = \
room.available_items[object1][var]-int(response)
else:
print("You can't take more than exist.")
break
else:
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()
# if command == "scene":
# output = read_room()
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()
elif command == "drop":
user.update_inventory(user.drop_item(object1))
return output
def read_room():
print("Glancing around the room you see:")
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
for var, count in list_value.items(): # variable/object, integer
available_items = f'{room.available_items[list_key][var]}x {list_key}.'
print(available_items)
if room.available_items[list_key][var] == 0:
del room.available_items[list_key]
# 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=None,
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=None,
grade="Light",
ac=0,
disadvantage=True,
dmgRes="None",
magical=False
)
paper = Item(
name="Paper",
description="A blank piece of paper",
value=None,
magical=False
)
user = Player("Jamie", "Male", 29, "Elf", "Mage")
# user = Player(*startGame())
# enter() needs to return a map name.
class Tutorial(Scene):
available_items = {
"paper": {paper: 3},
"rock": {rock: 12}
}
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.")
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 != "continue": # move scene to parser
if action == "scene":
read_room()
action = input(">> ").lower()
else:
process_command(*parse(action))
action = input(">> ").lower()
else:
if action == "continue":
return "begin"
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