Files
Kanjin/Kanjin-Engine
2022-02-09 18:00:14 +13:00

677 lines
24 KiB
Plaintext

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