Feb-11-2021, 02:51 AM
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.
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.
Error:Traceback (most recent call last):
File "D:\Documents\Game Programming\Python Project Files\Self-Py Projects\SmallGame\play.py", line 8, in <module>
Interface.main_menu()
File "D:\Documents\Game Programming\Python Project Files\Self-Py Projects\SmallGame\uimenu.py", line 26, in main_menu
player.location['Name'],
AttributeError: 'NoneType' object has no attribute 'location'
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: '' } }