517 lines
17 KiB
Plaintext
517 lines
17 KiB
Plaintext
import time
|
|
from random import randint
|
|
from functions import startGame, typingPrint
|
|
from enum import Enum, auto
|
|
|
|
# pyinstaller --onefile --paths=C:\Users\kasai\PycharmProjects\pythonProject\Kanjin\KanjinEngine.py
|
|
|
|
|
|
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
|
|
},
|
|
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":
|
|
print("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":
|
|
print("Elf")
|
|
self.stats["Dexterity"] += 2
|
|
self.stats["Intelligence"] += 1
|
|
|
|
elif self.race.lower() == "dwarf":
|
|
print("Dwarf")
|
|
self.stats["Constitution"] += 2
|
|
self.stats["Wisdom"] += 1
|
|
|
|
def set_stats_job(self):
|
|
if self.job.lower() == "warrior":
|
|
print("Warrior")
|
|
self.hitDie = randint(1, 12)
|
|
self.maxHP = (12 + self.mods["Constitution"])
|
|
self.currentHP = (12 + self.mods["Constitution"])
|
|
|
|
elif self.job.lower() == "ranger":
|
|
print("Ranger")
|
|
self.hitDie = randint(1, 10)
|
|
self.maxHP = (10 + self.mods["Constitution"])
|
|
self.currentHP = (10 + self.mods["Constitution"])
|
|
|
|
elif self.job.lower() == "mage":
|
|
print("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."""
|
|
typingPrint("Your current stats are:", time)
|
|
typingPrint(f'Hit Points: {self.currentHP}/{self.maxHP}', time)
|
|
typingPrint(f'Strength: {self.stats["Strength"]}', time)
|
|
typingPrint(f'Dexterity: {self.stats["Dexterity"]}', time)
|
|
typingPrint(f'Constitution: {self.stats["Constitution"]}', time)
|
|
typingPrint(f'Intelligence: {self.stats["Intelligence"]}', time)
|
|
typingPrint(f'Wisdom: {self.stats["Wisdom"]}', time)
|
|
typingPrint(f'Charisma: {self.stats["Charisma"]}', time)
|
|
|
|
def current_equip(self):
|
|
"""Prints a list of items currently equipped."""
|
|
typingPrint(f"You have equipped:\n {self.armor.name}\n"
|
|
f" {self.armor.description}"
|
|
f"\n{self.weapon.name}\n"
|
|
f" {self.weapon.description}\n", time)
|
|
|
|
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(' ')
|
|
# 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)
|
|
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 = []
|
|
|
|
typingPrint("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",
|
|
time)
|
|
typingPrint("Please select a stat and an attribute.\n\n", time)
|
|
typingPrint("Your rolled stats are:\n" + ', '.join(stats) + "\n\n", time)
|
|
typingPrint("Your attributes are:\n" + ', '.join(attributes) + "\n\n", time)
|
|
|
|
# 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
|
|
typingPrint("The remaining options are:\n", time)
|
|
typingPrint(', '.join(stats), time)
|
|
typingPrint(', '.join(attributes), time)
|
|
else:
|
|
typingPrint("Error. Input was not recognised. Please try again with 'Number' + 'Attribute'.", time)
|
|
continue
|
|
|
|
|
|
class Scene(object):
|
|
|
|
def enter(self):
|
|
print(self.name) # works
|
|
print(self.descrip) # works
|
|
# this applies to all classes under Scene, but Zed does it differently
|
|
|
|
|
|
class Engine(object):
|
|
|
|
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')
|
|
# see the Map object: runs function named opening_scene()
|
|
# this runs only once
|
|
# this STARTS THE GAME
|
|
|
|
# this (below) is supposed to be an infinite loop (but mine is not)
|
|
while not last_scene:
|
|
next_scene_name = current_scene.enter()
|
|
current_scene = self.scene_map.next_scene(next_scene_name)
|
|
print("\n--------")
|
|
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 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 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
|
|
|
|
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
|
|
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, vers, thrown, magical):
|
|
self.damage1H = damage1H
|
|
self.damage2H = damage2H
|
|
self.dmgType = dmgType
|
|
self.dmgMod = dmgMod
|
|
self.vers = vers
|
|
self.thrown = thrown
|
|
super().__init__(name, description, value, magical)
|
|
|
|
def __repr__(self):
|
|
return f"{self.name}\n=====\n{self.description}\nValue: {self.value}" \
|
|
f"\nDamage: One Handed - {self.damage1H}" \
|
|
f"\nDamage: Two Handed - {self.damage2H}\n" \
|
|
f"Damage Type: {self.dmgType}\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
|
|
super().__init__(name, description, value, magical)
|
|
|
|
def __repr__(self):
|
|
if self.stealthDis:
|
|
return f"{self.name}\n=====\n{self.description}\nValue: {self.value}\n" \
|
|
f"AC: {self.ac}\nMagical: {self.magical}"
|
|
elif not self.stealthDis:
|
|
return f"{self.name}\n=====\n{self.description}\nValue: {self.value}\n" \
|
|
f"AC: {self.ac}\nDisadvantage on Stealth checks: {self.stealthDis}\nMagical: {self.magical}"
|
|
|
|
|
|
# Different types of coins
|
|
GP1 = Money("Gold", 1, False)
|
|
SP1 = Money("Silver", 1, False)
|
|
CP1 = Money("Copper", 1, False)
|
|
|
|
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,
|
|
vers=False,
|
|
thrown=True,
|
|
magical=False
|
|
)
|
|
tornRags = Armor(
|
|
name="Torn Rags",
|
|
description="A ripped and worn-out outfit.",
|
|
value=0,
|
|
grade="Light",
|
|
ac=0,
|
|
disadvantage=True,
|
|
dmgRes="None",
|
|
magical=False
|
|
)
|
|
|
|
# user = Player("Jamie", "Male", 29, "Elf", "Mage")
|
|
user = Player(*startGame())
|
|
|
|
|
|
class Begin(Scene):
|
|
name = "Kanjin - An RPG Text Adventure"
|
|
desc = "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 queues 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."
|
|
|
|
def enter(self):
|
|
# user.new_char()
|
|
pass
|
|
|
|
|
|
class CaveEntrance(Scene):
|
|
name = "Cave Entrance"
|
|
descTrue = "You are standing at the entryway to a cave."
|
|
descFalse = "You return to the cave entrance."
|
|
has_visited = False
|
|
|
|
def enter(self):
|
|
typingPrint(f'\n===========\n'
|
|
f'{self.name}', time)
|
|
if not self.has_visited:
|
|
typingPrint(self.descTrue, time)
|
|
else:
|
|
typingPrint(self.descTrue, time)
|
|
|
|
action = input(">> ")
|
|
|
|
if action == "shoot!":
|
|
return "death"
|
|
elif action == "dodge":
|
|
return "pod"
|
|
else:
|
|
print("error")
|
|
return "central corridor"
|
|
|
|
# def action(self):
|
|
# pass
|
|
|
|
|
|
class A1(Scene):
|
|
|
|
def __init__(self):
|
|
self.name = "Laser Weapon Armory"
|
|
self.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."
|
|
|
|
def enter(self):
|
|
print(f'\n===========\n'
|
|
f'{self.name}'
|
|
f'\n===========\n'
|
|
f'{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
|
|
#
|
|
#
|
|
# class TheBridge(Scene):
|
|
#
|
|
# def __init__(self):
|
|
# self.name = "The Bridge"
|
|
# self.descrip = "Clearly this is a central command station of the spaceship. A wide view screen shows the" \
|
|
# " stars against a black curtain of empty space."
|
|
#
|
|
# def enter(self):
|
|
# print(f'\n===========\n'
|
|
# f'{self.name}'
|
|
# f'\n===========\n'
|
|
# f'{self.descrip}')
|
|
#
|
|
# def action(self):
|
|
# pass
|
|
|
|
|
|
class Tutorial(Scene):
|
|
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")
|
|
|
|
def enter(self):
|
|
print(f'\n===========\n'
|
|
f'{self.name}'
|
|
f'\n===========\n'
|
|
f'{self.descrip}')
|
|
action = input(">> ")
|
|
|
|
if action == "shoot!":
|
|
print("result")
|
|
return "death"
|
|
elif action == "dodge":
|
|
print("result")
|
|
return "pod"
|
|
else:
|
|
print("error")
|
|
return "central corridor"
|
|
|
|
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 = {
|
|
'Cave entrance': CaveEntrance(),
|
|
'A1': A1(),
|
|
# 'death': Death(),
|
|
# 'bridge': TheBridge(),
|
|
'Tutorial': Tutorial(),
|
|
"begin": Begin()
|
|
}
|
|
|
|
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
|
|
|
|
def next_scene(self, 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)
|
|
# this function exists only for starting, using the first
|
|
# string we passed in ('corridor')
|
|
# it combines the previous 2 functions and is called only once
|
|
# (called in Engine)
|
|
|
|
|
|
# print("\nType one of the following words: pod, corridor, bridge, armory, death.")
|
|
seed = "begin" # or input("> ")
|
|
# this is for testing only - get the "seed" for the map
|
|
|
|
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
|