![]() |
Coding RPG Game - Need Fresh Eyes. - Printable Version +- Python Forum (https://python-forum.io) +-- Forum: Python Coding (https://python-forum.io/forum-7.html) +--- Forum: Game Development (https://python-forum.io/forum-11.html) +--- Thread: Coding RPG Game - Need Fresh Eyes. (/thread-32466.html) |
Coding RPG Game - Need Fresh Eyes. - LastStopDEVS - Feb-11-2021 This is a project I have been working on for a couple of months. The project has drastically improved my understanding of Python3 and I have been pretty good about attacking most if not all of the problems so far by myself. However, once the project files start to get a little bigger (this being my largest project thus far) it gets much easier to miss simple mistakes. I think this needs a fresh pair of eyes. Don't worry too much about tweaking it into a more efficient version, I'll get there. I don't mind any critiques picking out obvious no-no's or offering any advice or suggestions unrelated to the main error issue.. With that out of the way, here is the problem I am experiencing that I need looked over. I will post the 2 main files first and then the rest of the project for others to observe and learn from as well. I noticed there aren't as many advanced RPG code examples provided online so I want to put together this project as a open-source text-based PyMUD. One obvious mistake is unnecessary imports but maybe I did right. play.pyfrom uimenu import * from account import * import output if __name__ == '__main__': Account.create(Account) Player.create() Interface.main_menu()uimenu.py from output import * from player import * import maps class Interface: def main_menu(): actions = ['gm', 'revive'] alphas = False asci = False commands = ['target', 'heal', 'n', 'north', 's', 'south', 'e', 'east', 'w', 'west'] esc = ['quit', 'exit', 'q', 'esc'] numeric = False reset = ['cls', 'main', 'menu', 'reset', 'restart'] run = [True, False] spaces = False Output.iFrame(Output, '#', player.location['Name'], '_', player.location['Description'], player.location['Content']) print('\nActions:',actions,'\nCommands:',commands,'\nMenu:',reset,'\nQuit:',esc) while run[0] is True: uin = input(f'\n{player.tag}{player.name}[{player.level}] | Health: {player.health}/{player.mhp} | Experience: {player.exp}/{player.exp2lvl}\n\n>>> ').lower() try: if '.' in uin[:1]: if uin[1:] not in actions: for char in uin[1:]: if char.isspace(): spaces = True elif char.isalpha(): alphas = True elif char.isdigit(): numeric = True elif char.isascii(): asci = True if spaces is True: print('\n|Notice|- The (.)command must not contain any spaces! Please try again.') spaces = False continue elif alphas is True and spaces is False and numeric is False and asci is False: print('\n|Notice|- The (.)command was entered incorrectly or does not exist! Please try again.') alphas = False continue elif asci is True and len(uin) > 1: print('\n|Notice|- Except for (.) Special characters are not valid or accepted in (.)commands! Please try again.') asci = False spaces = False alphas = False numeric = False continue elif numeric is True: print('\n|Notice|- The (.)command must not contain any numerical digits! Please try again.') numeric = False continue elif uin[1:] == '' or alphas is False and numeric is False and asci is False and spaces is False: print('\n|Notice|- No command was entered after the (.)! Please try again.') continue else: try: #Handles all (.)Actions if uin[1:] == actions[0]: #GM-ON if player.tag == '': player.tag = '[GM]-' print('\n|Notice|- You have upgraded your rank to Game Master.') else: if player.tag == '[GM]-': player.tag = '' print('\n|Notice|- You have disabled your rank of Game Master.') elif uin[1:] == actions[1]: if player.tag == '': print('\n|Notice|- You must be a Game Master in order to use the revive command!') if player.health - 100 >= 1: player.health -= 100 else: player.health -= player.health player.death() elif player.tag == '[GM]-': if player.health < 1: print(f'\n|Notice|- You have been revived by Game Master {player.name}!') player.health = 0 player.health += player.mhp else: print('\n|Notice|- You can not be revived if you are alive!') finally: #print('\n|Notice|- You have successfully executed a .command!\n') pass elif uin == '': print('\n|Notice|- There was no command entered! Please try again.') continue elif ' ' in uin: print('\n|Notice|- The command must not contain any spaces! Please try again.') continue elif uin not in esc and uin not in reset: if uin not in commands: for char in uin: if char.isspace(): spaces = True elif char.isalpha(): alphas = True elif char.isdigit(): numeric = True elif char.isascii(): asci = True if alphas is True and spaces is False and numeric is False and asci is False: print('\n|Notice|- The command was entered incorrectly or does not exist! Please try again.') alphas = False continue elif numeric is True: print('\n|Notice|- The command must not contain any numerical digits! Please try again.') numeric = False continue elif asci is True: print('\n|Notice|- The command contains special characters that are not valid or accepted! Please try again.') asci = False numeric = False alphas = False spaces = False continue else: if player.health == 0: print('\n|Notice|- You must first revive before you can enter a command! Only Action Commands work right now.') else: try: #Handles all Commands if uin == commands[0]: player.get_target() elif uin == commands[1]: player.heal() elif uin == commands[2] or uin == commands[3]: if player.location['North'] == '': print('\n|Notice|- You can no longer travel further North. Please pick one of the paths described in your current map\'s description.') else: print(f'\n{player.name} travels North!') player.location = maps.Map.zonemap[player.location['North']] elif uin == commands[4] or uin == commands[5]: if player.location['South'] == '': print('\n|Notice|- You can no longer travel further South. Please pick one of the paths described in your current map\'s description.') else: print(f'\n{player.name} travels South!') player.location = maps.Map.zonemap[player.location['South']] elif uin == commands[6] or uin == commands[7]: if player.location['East'] == '': print('\n|Notice|- You can no longer travel further East. Please pick one of the paths described in your current map\'s description.') else: print(f'\n{player.name} travels East!') player.location = maps.Map.zonemap[player.location['East']] elif uin == commands[8] or uin == commands[9]: if player.location['West'] == '': print('\n|Notice|- You can no longer travel further West. Please pick one of the paths described in your current map\'s description.') else: print(f'\n{player.name} travels West!') player.location = maps.Map.zonemap[player.location['West']] finally: Interface.main_menu() break else: if uin in esc: print('\n|Notice|- Terminating Program Sequence. Good-bye!') run.reverse() Interface.UI_quit() break elif uin in reset: print('\n|Notice|- Restarting Program Sequence. See you soon!') run.reverse() Interface.main_menu() break #except: #print('|Notice|- An error has prevented a section of this code from being executed.\n') finally: pass def UI_quit(): gap = ' '*20 print(' _____ _ _\n'+ '|_ _|| | | |\n'+ ' | | | |__ __ _ _ __ | | __ ___\n'+ ' | | | \'_ \ / _` || \'_ \ | |/ // __|\n'+ ' | | | | | || (_| || | | || < \__ \\\n'+ ' \_/ |_| |_| \__,_||_| |_||_|\_\|___/\n'+gap+ ' _____\n'+gap+ '| ___|\n'+gap+ '| |_ ___ _ __\n'+gap+ '| _|/ _ \ | \'__|\n'+gap+ '| | | (_) || |\n'+gap+ '\_| \___/ |_|\n'+ ' _____ _ _\n'+ '| ___ \| | (_)\n'+ '| |_/ /| | __ _ _ _ _ _ __ __ _\n'+ '| __/ | | / _` || | | || || \'_ \ / _` |\n'+ '| | | || (_| || |_| || || | | || (_| |\n'+ '\_| |_| \__,_| \__, ||_||_| |_| \__, |\n'+ ' __/ | __/ |\n'+ ' |___/ |___/\n')player.py import random, time, os, maps, uimenu from enemy import * from output import * player = None class Player: max_level = 20 exp_mod = 1 def __init__(self, name, mhp, health, level, exp, exp2lvl, target, tag, location): self.name = name self.health = health self.mhp = health self.level = level self.exp = exp self.exp2lvl = exp2lvl self.target = target self.tag = tag self.location = location def __str__(self): if self.health < 0: self.health = 0 return(f'\n[Player Details]\nCharacter: {self.tag}{self.name}\nLevel: {self.level}\nHealth: {self.health}/{self.mhp}\nExperience: {self.exp}/{self.exp2lvl}') def create(): Output.iFrame(Output, '#', 'PyRona RPG v1.10', '_', 'Character Creation Menu', 'We will now create your in-game PyRona RPG character.', 50) name = input('\nCreate Character: ') health = random.randrange(500, 1500, 5) mhp = health level = 1 exp = 0 exp2lvl = 500 * level * (1+level) target = [None] tag = '' location = maps.Map.zonemap['a1'] player = Player(name, mhp, health, level, exp, exp2lvl, target, tag, location) print('\n[Character Details]') time.sleep(random.uniform(0.25, 0.75)) print(f'Name: {player.name}') time.sleep(random.uniform(0.25, 0.75)) print('Location:',player.location['Name']) time.sleep(random.uniform(0.25, 0.75)) print(f'Level: {player.level}') time.sleep(random.uniform(0.25, 0.75)) print(f'Health: {player.health}/{player.mhp}') time.sleep(random.uniform(0.25, 0.75)) print(f'Experience: {player.exp}/{player.exp2lvl}') time.sleep(random.uniform(0.25, 0.75)) while True: input('\n|Notice|- Press the enter key to continue.') break def levelup(self): self.target = [None] if self.exp >= self.exp2lvl: self.health = self.mhp self.level += 1 self.exp2lvl = 500 * self.level * (1+self.level) self.health += (random.randrange(25, 125, 25)) * self.level self.mhp = self.health print(f'Congratulations {self.name}! You have just advanced to level {self.level}!') if self.level < Player.max_level: if self.exp >= self.exp2lvl: self.levelup() else: print(self) else: print('|Notice|- You may no longer gain experience from battles. You have reached the maximum level!') self.exp = self.exp2lvl def get_target(self): try: if self.health == 0: print('\n|Notice|- You are dead and may not obtain a target!') self.death() else: if None in self.target: self.target.clear() #Continue building map spawns by checking if index [0]-[3] in list A, B, C, or D are equal by comparison to the players self.location['Name'] vmaps = ['A1', 'A2', 'A3', 'A4', 'B1', 'B2', 'B3', 'B4', 'C1', 'C2', 'C3', 'C4', 'D1', 'D2', 'D3', 'D4'] if 'A' in self.location['Name']: #65, 30, 5, 0 map_spawn = random.choices(creatures, [65, 30, 5, 0]) enemy = random.choice(map_spawn) self.target = enemy() elif 'B' in self.location['Name']: #25, 42, 32, 1 map_spawn = random.choices(creatures, [25, 42, 32, 1]) enemy = random.choice(map_spawn) self.target = enemy() elif 'C' in self.location['Name']: #5, 60, 30, 5 map_spawn = random.choices(creatures, [5, 60, 30, 5]) enemy = random.choice(map_spawn) self.target = enemy() elif 'D' in self.location['Name']: #0, 25, 45, 30 map_spawn = random.choices(creatures, [0, 25, 45, 30]) enemy = random.choice(map_spawn) self.target = enemy() print(self.target) self.attack() except: print(f'\n|Notice|- You are already attacking a {self.target.name}\n') def attack(self): attacker = ['Enemy', 'Player'] while self.health > 0 and self.target.health > 0: if attacker[0] == 'Player': if self.tag == '[GM]-': self.dmg = round(random.randrange(50, 150, 1)*self.target.level/20*self.level) else: self.dmg = round(random.randrange(1, 5, 1)*self.target.level/2) print(f'\nPlayer attacks target for {self.dmg} points of damage!') self.target.health -= self.dmg attacker.reverse() print(self.target) time.sleep(1) elif attacker[0] == 'Enemy': a = int(self.target.dmg[:2]) b = int(self.target.dmg[5:7]) self.target.damage = random.randrange(a, b, 1) print(f'{self.target.name} attacks player for {self.target.damage} points of damage!') self.health -= self.target.damage attacker.reverse() print(self) time.sleep(1) else: if self.health <= 0: print(f'{self.name} was just slain by a {self.target.name}... Better luck next time!') self.death() elif self.target.health <= 0: if self.tag == '[GM]-': Player.exp_mod = random.randrange(5, 20, 1) exp_gain = (random.randrange(10, 35, 1) * self.target.level)*Player.exp_mod/2 else: Player.exp_mod = 1 exp_gain = (random.randrange(10, 35, 1) * self.target.level)/2+50 if self.level < Player.max_level: print(f'You have just slain a level {self.target.level} \'{self.target.name}\'.\nThis creature\'s type was: {self.target.ctype}\nYou have gained {exp_gain} experience!\nXP Modifier: {Player.exp_mod}') self.exp += exp_gain if self.exp >= self.exp2lvl: self.levelup() self.target = [None] def print_inventory(self): pass def heal(self): if self.health < 1: print('\n|Notice|- You must first be revived by a Game Master before you can heal!') else: if self.health >= self.mhp: self.health = self.mhp print('\n|Notice|- You are already at max health and can no longer receive heals!') else: heals = random.randrange(75, 250, 1) if self.health + heals <= self.mhp: if heals > 175: print(f'\nYou have been healed with a major healing touch for {heals} health points!') else: print(f'\nYou have been healed with a healing touch for {heals} health points!') self.health += heals else: print(f'\nYou have been healed to max health from a healing touch spell.') self.health = self.mhp def death(self): self.target = [None] if self.health < 1: #self.health = 0 print('You are dead... Now you must figure out a way to revive!') else: return('\n[Death]: You are not dead. Who do you think you are? Get outta here!')enemy.py import random, time class Enemy: def __init__(self, name, ctype, level, health, dmg): self.name = name self.ctype = ctype self.level = level self.health = health self.max_health = health self.dmg = dmg def __str__(self): if self.health < 0: self.health = 0 return(f'\n[Enemy Details]\nName: {self.name}\nType: {self.ctype}\nLevel: {self.level}\nHealth: {self.health}/{self.max_health}\nDamage: {self.dmg}\n') def display(self): print(self.__str__()) class Zombie(Enemy): def __init__(self): super().__init__(name='Zombie', ctype='Undead', level=5, health=75, dmg = '05 - 15') class Ghost(Enemy): def __init__(self): super().__init__(name='Ghost', ctype='Apparition', level=10, health=150, dmg = '15 - 30') class Wolf(Enemy): def __init__(self): super().__init__(name='Wolf', ctype='Creature', level=15, health=300, dmg = '30 - 60') class Troll(Enemy): def __init__(self): super().__init__(name='Troll', ctype='Humanoid', level=20, health=500, dmg = '60 - 99') creatures = [Zombie, Ghost, Wolf, Troll]items.py from output import * class Item(): 'The base class for all items' def __init__(self, name, description, value): self.name = name self.description = description self.value = value def __str__(self): return ('Name: {}\nDescription: {}\nValue: {}\n'.format(self.name, self.description, self.value)) class Weapon(Item): 'This is a subclass of Item: A class for all Weapons' def __init__(self, name, description, value, damage): self.damage = damage super().__init__(name, description, value) def __str__(self): return("Name: {}\nDescription: {}\nValue: {}\nDamage: {}\n".format(self.name, self.description, self.value, self.damage)) class Rock(Weapon): 'class for Weapon: Rock' def __init__(self): super().__init__(name = 'Rock', description = 'A fist-sized rock, suitable for bludgeoning.', value = 0, damage = 2.5) class Dagger(Weapon): def __init__(self): super().__init__(name = "Dagger", description = "A dull dagger with some rust. It's better than nothing..", value = 20, damage = 4.0) class Gold(Item): 'This is the \'GoldCoin\' class' def __init__(self, amt): self.amt = amt super().__init__(name = 'Gold', description = 'A loot of \'Gold Coins\'. This is certainly a valuable stash.', value = amt)output.py import random, sys, textwrap, time class Output: def iFrame(self, border='#', header='', lbreak='_', desc='', content=(''), width=50): iFrame = { 'Border': f'{border}'*50, 'Header': f'{border}'+header.center(48)+f'{border}', 'Content': content.center(50), 'Desc': f'{border}'+desc.center(48)+f'{border}', 'Break': f'{border}'+lbreak*48+f'{border}' } print('\r') print(iFrame['Border']), print(iFrame['Header']), print(iFrame['Break']), print(iFrame['Desc']), print(iFrame['Border']+'\n'), Output.content(Output, content, width) def content(self, content, width): '''Formatting content with textwrap.''' wrapper = textwrap.TextWrapper(width) default_content = wrapper.wrap(text = content) for element in default_content: print(element) def text_delay(text, delay): for characters in text: sys.stdout.write(characters) sys.stdout.flush() time.sleep(delay)account.py from output import * import errno, json, os, random, time cache = None account = None class Account: i = 0 data = {'ID': None, 'Username': None, 'Password': None, 'Date': None} def __init__(self, uid, username, password, date): self.uid = uid self.username = username self.password = password self.date = date def __str__(self): return('\n##################################################\n# PyRona RPG v1.10 #\n#________________________________________________#\n# Account Login Menu #\n##################################################') def create(self): global cache global account Output.iFrame(Output, '#', 'PyRona RPG v1.10', '_', 'Account Creation Menu', 'Creating a new account is easy. Enter the following information into the prompt to register your account.') account = Account(Account.i+1, input('\nCreate Username: '), input('Create Password: '), time.ctime()[11:24]) cache = [account.uid, account.username, account.password] print('\nImporting Account {} to Databse @ {}'.format(account.username, time.ctime()[11:24])) Account.data.update({'ID': account.uid, 'Username': account.username, 'Password': '*'*len(account.password), 'Date': time.ctime()[11:24]}) self.write_file() print('Table Updated Successfully @ {}'.format(time.ctime()[11:24])) print(account) account.login(input('\nUsername: '), input('Password: ')) def login(self, username, password): global cache print('\n--[Logging Into Account: {}'.format(self.username)) time.sleep(2) if username == cache[1] and password == cache[2]: print(f'Welcome, {self.username}. You have successfully logged in!') else: print('\nInvalid Login, please try again!\n') self.login(input('Username: '), input('Password: ')) def write_file(): filename = f'\Documents\Game Programming\Python Project Files\Self-Py Projects\SmallGame\Data\{account.username}.txt' if not os.path.exists(os.path.dirname(filename)): try: os.makedirs(os.path.dirname(filename)) except OSError as exc: raise with open(filename, 'w') as file: file.write('\r'+json.dumps(Account.data))maps.py #MAP REFERENCE ''' *Starting on B2 ____________ |A1|A2|A3|A4| |B1|B2|B3|B4| |C1|C2|C3|C4| |D1|D2|D3|D4| ------------- ''' class Map: zonename = 'Name' description = 'Description' content = 'Content' solved = False north = 'North' south = 'South' west = 'West' east = 'East' solved_places = {'a1': False, 'a2': False, 'a3': False, 'a4': False, 'b1': False, 'b2': False, 'b3': False, 'b4': False, 'c1': False, 'c2': False, 'c3': False, 'c4': False, 'd1': False, 'd2': False, 'd3': False, 'd4': False } zonemap = { 'a1': { zonename: 'A1', description: 'Here is the description of A1', content: 'MAP A1: You can travel in the directions South or East!', solved: False, north: '', south: 'b1', west: '', east: 'a2' }, 'a2': { zonename: 'A2', description:'Here is the description of A2', content: 'MAP A2: You can travel in the directions South, East, or West!', solved: False, north: '', south: 'b2', west: 'a1', east: 'a3' }, 'a3': { zonename: 'A3', description: 'Here is the description of A3', content: 'MAP A3: You can travel in the directions South, East, or West!', solved: False, north: '', south: 'b3', west: 'a2', east: 'a4' }, 'a4': { zonename: 'A4', description: 'Here is the description of A4', content: 'Map A4: You can travel in the directions South or West!', solved: False, north: '', south: 'b4', west: 'a3', east: '' }, 'b1': { zonename: 'B1', description: 'Here is the description of B1', content: 'Map B1: You can travel in the directions North, South, or East!', solved: False, north: 'a1', south: 'c1', west: '', east: 'b2' }, 'b2': { zonename: 'B2', description: 'Here is the description of B2', content: 'Map B2: You can travel in the directions North, South, East, and West!', solved: False, north: 'a2', south: 'c2', west: 'b1', east: 'b3' }, 'b3': { zonename: 'B3', description: 'Here is the description of B3', content: 'Map B3: You can travel in the directions North, South, East, and West!', solved: False, north: 'a3', south: 'c3', west: 'b2', east: 'b4' }, 'b4': { zonename: 'B4', description: 'Here is the description of B4', content: 'Map B4: You can travel in the directions North, South, or West!', solved: False, north: 'a4', south: 'c4', west: 'b3', east: '' }, 'c1': { zonename: 'C1', description: 'Here is the description of C1', content: 'Map C1: You can travel in the directions North, South, or East!', solved: False, north: 'b1', south: 'd1', west: '', east: 'c2' }, 'c2': { zonename: 'C2', description: 'Here is the description of C2', content: 'Map C2: You can travel in the directions North, South, East, or West!', solved: False, north: 'b2', south: 'd2', west: 'c1', east: 'c3' }, 'c3': { zonename: 'C3', description: 'Here is the description of C3', content: 'Map C3: You can travel in the directions North, South, East, and West!', solved: False, north: 'b3', south: 'd3', west: 'c2', east: 'c4' }, 'c4': { zonename: 'C4', description: 'Here is the description of C4', content: 'Map C4: You can travel in the directions North, South, or West!', solved: False, north: 'b4', south: 'd4', west: 'c3', east: '' }, 'd1': { zonename: 'D1', description: 'Here is the description of D1', content: 'Map D1: You can travel in the directions North or East!', solved: False, north: 'c1', south: '', west: '', east: 'd2' }, 'd2': { zonename: 'D2', description: 'Here is the description of D2', content: 'Map D2: You can travel in the directions North, East, or West!', solved: False, north: 'c2', south: '', west: 'd1', east: 'd3' }, 'd3': { zonename: 'D3', description: 'Here is the description of D3', content: 'Map D3: You can travel in the directions North, East, or West!', solved: False, north: 'c3', south: '', west: 'd2', east: 'd4' }, 'd4': { zonename: 'D4', description: 'Here is the description of D4', content: 'Map D4: You can travel in the directions North, East, or West!', solved: False, north: 'c4', south: '', west: 'c3', east: '' } } RE: Coding RPG Game - Need Fresh Eyes. - BashBedlam - Feb-11-2021 The assignment player = Player(name, mhp, health, level, exp, exp2lvl, target, tag, location)happens inside of the method Player.Create () and therefor is not the same "player" variable that is being referenced in Interface.main_menu () .You can use a global variable or have Player.Create () return a reference to the created instance of the class Player .
RE: Coding RPG Game - Need Fresh Eyes. - deanhystad - Feb-11-2021 Not just not the same player, but not the same variable. Global variables are bad. Global variables defined in other modules are worse. Global variables from other modules that are imported using a wildcard get you your own special level of hell. RE: Coding RPG Game - Need Fresh Eyes. - nilamo - Feb-11-2021 As mentioned by others, the issue here is relying on global variables. Your Player.create() creates a local player object, that isn't assigned to the global player variable. I have a feeling you'll have a similar issue with the Account. The pattern you're using (kind of) is called the Singleton. You have a Player class, but you don't really expect to create more than one instance of that class. You can read more about this in The Book: https://gameprogrammingpatterns.com/singleton.html As a side note, that entire book is very well written. The code examples are all c++, but all the code is (for the most part) very simple and designed to illustrate the point, and can be understood even if you don't know c++. Normally, these sorts of classes are Builders, that have a GetInstance method. For your player, here's how I'd do it: >>> class Player: ... __players = [] ... def __init__(self, name): ... self.name = name ... Player.__players.append(self) ... @classmethod ... def get(cls, player_id=0): ... if len(cls.__players) > player_id: ... return cls.__players[player_id] ... raise Exception(f"Player {player_id} does not exist.") ... >>> me = Player('george') >>> me <__main__.Player object at 0x0000026450096CD0> >>> me.name 'george' >>> # elsewhere in our codebase... >>> player = Player.get() >>> player.name 'george'This way, the class itself controls the instance, and you just ask it for that instance. |