Nov-07-2017, 06:05 PM
Hello everyone,
I've been working on the StackSkills Python video course, and completed the RPG system video tutorial a while back. I have one particular issue with one of the functions presented in the video produces an error after playing the game for a while.
What's a bummer is that the author of the video series never fixed this issue, and I would like to take the ideas in this battle system, and make a cool text RPG project for fun on the side.
I'm getting this error after running this a few times (ignore the character names because I was seeing how many characters I can fit within the current system):
Traceback (most recent call last): File "main.py", line 122, in enemies[enemy].take_damage(dmg) IndexError: list index out of range exited with non-zero status
Since it's been a week or so, just looking at this code is a bit harsh since it was a good 20 or so video tutorial playlist on this course, and I basically followed along. I understand it somewhat well in the fact that the events in the game are based on the text inputs of the player, and all of these events eventually call different methods from the game, magic, and inventory Python files which contain relevant classes.
For this error, my biggest hunch is that the line 'del players[player]' should be: 'del players[target]' instead.
Let me know what you think guys :)
Here's the main.py file:
I've been working on the StackSkills Python video course, and completed the RPG system video tutorial a while back. I have one particular issue with one of the functions presented in the video produces an error after playing the game for a while.
What's a bummer is that the author of the video series never fixed this issue, and I would like to take the ideas in this battle system, and make a cool text RPG project for fun on the side.
I'm getting this error after running this a few times (ignore the character names because I was seeing how many characters I can fit within the current system):
Traceback (most recent call last): File "main.py", line 122, in enemies[enemy].take_damage(dmg) IndexError: list index out of range exited with non-zero status
Since it's been a week or so, just looking at this code is a bit harsh since it was a good 20 or so video tutorial playlist on this course, and I basically followed along. I understand it somewhat well in the fact that the events in the game are based on the text inputs of the player, and all of these events eventually call different methods from the game, magic, and inventory Python files which contain relevant classes.
For this error, my biggest hunch is that the line 'del players[player]' should be: 'del players[target]' instead.
Let me know what you think guys :)
Here's the main.py file:
from game import Person, bcolors from magic import Spell from inventory import Item import random # Parameters for Person Class: (self, hp, mp, atk, df, magic) # Create Black Magic # Parameters for Spell() class: name, cost, dmg, type fire = Spell("Fire", 25, 600, "black") thunder = Spell("Thunder", 25, 600, "black") blizzard = Spell("Blizzard", 25, 600, "black") meteor = Spell("Meteor", 40, 1200, "black") quake = Spell("Quake", 14, 140, "black") # Create White Magic cure = Spell("Cure", 25, 620, "white") cura = Spell("Cura", 32, 1500, "white") curaga = Spell("Curaga", 50, 6000, "white") # Create Some Items # Parameters for Item() class: name, type, description, prop potion = Item("Potion", "potion", "Heals 50 HP", 50) hipotion = Item("Hi-Potion", "potion", "Heals 100 HP", 100) superpotion = Item("Super Potion", "potion", "Heals 1000 HP", 1000) elixer = Item("Elixer", "elixer", "Full restores HP/MP of one party member", 9999) megaelixer = Item("MegaElixer", "elixer", "Fully restores party's HP/MP", 9999) grenade = Item("Grenade", "attack", "Deals 500 damage", 500) enemy_spells = [fire, meteor, curaga] player_spells = [fire, thunder, blizzard, meteor, cure, cura] player_items = [{"item": potion, "quantity": 15}, {"item": hipotion, "quantity": 5}, {"item": superpotion, "quantity": 5}, {"item": elixer, "quantity": 5}, {"item": megaelixer, "quantity": 2}, {"item": grenade, "quantity": 5}] # Title Screen # print("********************") # print("\n") # print(" W E L C O M E") # print(" T O ") # print(" S T R A Y") # print(" ~ A Cat RPG ~") # print("\n") # print("********************") # Character Creation # characterEntry = True # while characterEntry: # heroCat = input("Name Your Hero Cat (10 Character Maximum:)") # if len(heroCat) > 10: # characterEntry = False # heroCat = input("More Than 10 Characters Entered, Please Name Your Hero Cat (10 Character Maximum)") # elif heroCat == "": # characterEntry = False # heroCat = input("Less Than 10 Characters Entered, Please Name Your Hero Cat (10 Character Maximum)") # elif len(heroCat) > 0 and len(heroCat) < 10: # characterEntry = True #print("heroCat = ", heroCat) # We are instantiating the Person() class twice to make a player and an enemy object # Parameters for Person Class: (self, name, hp, mp, atk, df, magic, items) player1 = Person("Tiggerabcd:", 3460, 165, 300, 34, player_spells, player_items) player2 = Person("Tiggerabcd:", 4460, 185, 311, 34, player_spells, player_items) player3 = Person("Tiggerabcd:", 3460, 175, 288, 34, player_spells, player_items) enemy1 = Person("Minion", 1250, 130, 560, 325, enemy_spells, []) enemy2 = Person("Ulfric", 11200, 265, 525, 25, enemy_spells, []) enemy3 = Person("Minion", 1250, 130, 560, 325, enemy_spells, []) enemies = [enemy1, enemy2, enemy3] players = [player1, player2, player3] # This calls the generate_damage() class to generate an attack # print("Player generates damage with", player.generate_damage(), "atk pts") # print("Player generates damage with", player.generate_damage(), "atk pts") # print("Player generates damage with", player.generate_damage(), "atk pts") # This calls the generate_spell_damage to generate a magic spell by giving a parameter index value for a specific spell in the 'magic' dictionary # print("Player casts Fire spell for", player.generate_spell_damage(0), "atk pts") # print("Player casts Thunder spell for", player.generate_spell_damage(1), "atk pts") running = True print(bcolors.FAIL + bcolors.BOLD + "AN ENEMY ATTACKS!" + bcolors.ENDC) while running: print("===================") print("\n\n") print("NAME HP MP") for player in players: player.get_stats() print("\n") for enemy in enemies: enemy.get_enemy_stats() for player in players: player.choose_action() choice = input(" Choose Action:") # Index is a variable that takes the value of choice and subtracts it by 1 to give the program the correct index value for each of the lists # This is because computers start counting lists from position "0" index = int(choice) - 1 # print("You chose", player.get_spell_name(int(index))) if index == 0: dmg = player.generate_damage() # This calls the choose_target() function in the Player class to choose an enemy in the available Enemy array enemy = player.choose_target(enemies) enemies[enemy].take_damage(dmg) print("You attacked " + enemies[enemy].name.replace(" ", "") + " for", dmg, "points of damage.") # This checks to see if the enemy is dead, and if so, delete it from the enemies list if enemies[enemy].get_hp() == 0: print(enemies[enemy].name.replace(" ", "") + " has died.") del enemies[enemy] elif index == 1: player.choose_magic() # This combined statement allows you to index into the correct magic spell choice since computers start counting from 0 onward magic_choice = int(input("Choose magic:"))-1 # This allows you to go back into the previous menu with the use of the number choice, '0' if magic_choice == -1: continue # This indexes into the magic list based on the user's number choice spell = player.magic[magic_choice] # This calls the generate_damage() class from the magic class magic_dmg = spell.generate_damage() current_mp = player.get_mp() if spell.cost > current_mp: # \n: this creates a line break within a print statement in Python print(bcolors.FAIL + "\nNot enough MP\n" + bcolors.ENDC) continue # This reduces the player's mp based on the mp cost of the spell player.reduce_mp(spell.cost) if spell.type == "white": player.heal(magic_dmg) print(bcolors.OKBLUE + "\n" + spell.name + " heals for", str(magic_dmg), "HP." + bcolors.ENDC) elif spell.type == "black": # This calls the choose_target() function in the Player class to choose an enemy in the available Enemy array enemy = player.choose_target(enemies) enemies[enemy].take_damage(magic_dmg) print(bcolors.OKBLUE + "\n" + spell.name + " deals", str(magic_dmg), "points of damage to " + enemies[enemy].name.replace(" ", "") + bcolors.ENDC) # This checks to see if the enemy is dead, and if so, delete it from the enemies list if enemies[enemy].get_hp() == 0: print(enemies[enemy].name.replace(" ", "") + " has died.") del enemies[enemy] elif index == 2: player.choose_items() item_choice = int(input("Choose item: ")) - 1 # This allows you to go back into the previous menu with the use of the number choice, '0' if item_choice == -1: continue item = player.items[item_choice]["item"] if player.items[item_choice]["quantity"] == 0: print(bcolors.FAIL + "\n" + "No " + item.name + "s left..." + bcolors.ENDC) continue player.items[item_choice]["quantity"] -= 1 if item.type == "potion": player.heal(item.prop) print(bcolors.OKGREEN + "\n" + item.name + " heals for", str(item.prop), "HP" + bcolors.ENDC) elif item.type == "elixer": if item.name == "MegaElixer": for i in players: i.hp = i.maxhp i.mp = i.maxmp else: player.hp = player.maxhp player.mp = player.maxmp print(bcolors.OKGREEN + "\n" + item.name + " fully restores HP/MP" + bcolors.ENDC) elif item.type == "attack": # This calls the choose_target() function in the Player class to choose an enemy in the available Enemy array enemy = player.choose_target(enemies) enemies[enemy].take_damage(item.prop) print(bcolors.FAIL + "\n" + item.name + " deals", str(item.prop), "points of damage to " + enemies[enemy].name + bcolors.ENDC) # This checks to see if the enemy is dead, and if so, delete it from the enemies list if enemies[enemy].get_hp() == 0: # The .replace() function strips a whitespace each time an enemy dies print(enemies[enemy].name.replace(" ", "") + " has died.") del enemies[enemy] # Check if battle is over defeated_enemies = 0 defeated_players = 0 for enemy in enemies: if enemy.get_hp() == 0: defeated_enemies += 1 for player in players: if player.get_hp() == 0: defeated_players += 1 # Check if Player Won: if defeated_enemies == 2: print(bcolors.OKGREEN + "You win!" + bcolors.ENDC) running = False # Check if Enemy Won: elif defeated_players == 2: print(bcolors.FAIL + "Your enemy has defeated you!" + bcolors.ENDC) running = False print("\n") # Enemy Attack Phase: for enemy in enemies: enemy_choice = random.randrange(0, 2) if enemy_choice == 0: # Enemy chooses player to attack target = random.randrange(0, 3) # This allows the first enemey to start the attack enemy_dmg = enemy.generate_damage() players[target].take_damage(enemy_dmg) # The .replace() function replaces all spaces print(enemy.name.replace(" ", "") + " attacks " + players[target].name.replace(" ", "") + " for", enemy_dmg) elif enemy_choice == 1: spell, magic_dmg = enemy.choose_enemy_spell() enemy.reduce_mp(spell.cost) if spell.type == "white": enemy.heal(magic_dmg) print(bcolors.OKBLUE + spell.name + " heals " + enemy.name + "for", str(magic_dmg), "HP." + bcolors.ENDC) elif spell.type == "black": # This calls the choose_target() function in the Player class to choose an enemy in the available Enemy array target = random.randrange(0, 3) players[target].take_damage(magic_dmg) print(bcolors.OKBLUE + "\n" + enemy.name.replace(" ", "") + "'s " + spell.name + " deals", str(magic_dmg), "points of damage to " + players[target].name.replace(" ", "") + bcolors.ENDC) # This checks to see if the enemy is dead, and if so, delete it from the enemies list if players[target].get_hp() == 0: print(players[target].name.replace(" ", "") + " has died.") del players[player] # print("Enemy chose", spell, "damage is", magic_dmg) # Fix issue: File "main.py", line 131, in <module>Here's the Game.py file:
# NOTES: # This would normally be in a classes directory # This would normally be called "game.py" if this were in a proper IDE import random from magic import Spell import pprint class bcolors: HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' class Person: def __init__(self, name, hp, mp, atk, df, magic, items): # Max HP value self.maxhp = hp # Current HP self.hp = hp self.maxmp = mp self.mp = mp # We set the low and high attack power +-10 self.atkl = atk - 10 self.atkh = atk + 10 self.df = df # This will be a dictionary of magic spells and their MP cost self.magic = magic self.items = items self.actions = ["Attack", "Magic", "Items"] self.name = name def generate_damage(self): return random.randrange(self.atkl, self.atkh) def take_damage(self, dmg): self.hp -= dmg if self.hp < 0: self.hp = 0 return self.hp # These are utility classes to get HP and MP information to calculate remaining MP and HP points def heal(self, dmg): self.hp += dmg # This makes sure you don't heal above your max hp if self.hp > self.maxhp: self.hp = self.maxhp def get_hp(self): return self.hp def get_max_hp(self): return self.maxhp def get_mp(self): return self.mp def get_max_mp(self): return self.maxmp def reduce_mp(self, cost): self.mp -= cost def choose_action(self): i = 1 print("\n" + " " + bcolors.BOLD + self.name + bcolors.ENDC) print(bcolors.OKBLUE + bcolors.BOLD + " ACTIONS" + bcolors.ENDC) for item in self.actions: print(" " + str(i) + ".", item) i += 1 def choose_magic(self): i = 1 print("\n" + bcolors.OKBLUE + bcolors.BOLD + " MAGIC:" + bcolors.ENDC) for spell in self.magic: print(" " + str(i) + ".", spell.name, "(cost:", str(spell.cost) + ")") i += 1 def choose_items(self): i = 1 print("\n" + bcolors.OKGREEN + bcolors.BOLD + " ITEMS:" + bcolors.ENDC) # This prints out the items available in the items dictionary: for item in self.items: print(" " + str(i) + ".", item["item"].name, ":", item["item"].description, " (x" + str(item["quantity"]) + ")") i += 1 def choose_target(self, enemies): i = 1 print("\n" + bcolors.FAIL + bcolors.BOLD + " TARGET:" + bcolors.ENDC) # This prints the enemies on screen, and then lets the player decide which enemy to attack for enemy in enemies: if enemy.get_hp() != 0: print(" " + str(i) + ".", enemy.name) i += 1 choice = int(input(" Choose target:")) - 1 return choice def get_enemy_stats(self): hp_bar = "" # Enemy HP bar will be 50 characters long bar_ticks = (self.hp / self.maxhp) * 100 / 2 while bar_ticks > 0: hp_bar += "█" bar_ticks -= 1 while len(hp_bar) < 50: hp_bar += " " hp_string = str(self.hp) + "/" + str(self.maxhp) current_hp = "" # This makes sure that the enemy's hp bar is only 11 characters long if len(hp_string) < 11: decreased = 11 - len(hp_string) while decreased > 0: current_hp += " " decreased -= 1 current_hp += hp_string else: current_hp = hp_string print(" __________________________________________________") print(bcolors.BOLD + self.name + "|" + current_hp + " |" + bcolors.FAIL + hp_bar + bcolors.ENDC + "|") def get_stats(self): # Health Bar String hp_bar = "" hp_ticks = (self.hp / self.maxhp) * 100 / 4 mp_bar = "" mp_ticks = (self.mp / self.maxmp) * 100 / 10 while hp_ticks > 0: hp_bar += "█" hp_ticks -= 1 while len(hp_bar) < 25: hp_bar += " " while mp_ticks > 0: mp_bar += "█" mp_ticks -= 1 while len(mp_bar) < 10: mp_bar += " " hp_string = str(self.hp) + "/" + str(self.maxhp) current_hp = "" if len(hp_string) < 9: decreased = 9 - len(hp_string) while decreased > 0: current_hp += " " decreased -= 1 current_hp += hp_string else: current_hp = hp_string mp_string = str(self.mp) + "/" + str(self.maxmp) current_mp = "" if len(mp_string) < 7: decreased = 7 - len(mp_string) while decreased > 0: current_mp += " " decreased -= 1 current_mp += mp_string else: current_mp = mp_string print(" _________________________ __________") print(bcolors.BOLD + self.name + "|" + current_hp + " |" + bcolors.OKGREEN + hp_bar + bcolors.ENDC + "|" + current_mp + " |" + bcolors.OKBLUE + mp_bar + bcolors.ENDC + "|") def choose_enemy_spell (self): # This generates a random number based on the amount of spells available in the magic spell list magic_choice = random.randrange(0, len(self.magic)) spell = self.magic[magic_choice] magic_dmg = spell.generate_damage() # This checks to see if the enemy still has MP remaining and then recalls the function recursively pct = self.hp / self.maxhp * 100 # If the enemy does not have any MP or if the enemy has over 50 HP, then they're not going to use white magic to cure themselves if self.mp < spell.cost or spell.type == "white" and pct > 50: spell, magic_dmg = self.choose_enemy_spell() return spell, magic_dmg else: return spell, magic_dmgHere's the magic.py file:
import random class Spell: def __init__(self, name, cost, dmg, type): # self: instance of the class self.name = name self.cost = cost self.dmg = dmg self.type = type def generate_damage(self): low = self.dmg - 15 high = self.dmg + 15 return random.randrange(low, high)Here's the inventory.py file:
class Item: def __init__(self, name, type, description, prop): self.name = name self.type = type self.description = description self.prop = prop