我收到一个类型错误,因为 NoneType 对象不可下标。如何使其可下标?



我正在尝试通过Phillip Johnson的一本名为Make Your Own Python Text Adventure的书为基于文本的RPG编写代码。我无法确定错误在我的代码中的位置。我已经经历了几次,似乎无法弄清楚。我尝试摆弄不同的东西,但到目前为止它只是给出了不同的错误。我将发布我的代码和回溯。

Traceback (most recent call last):
File "C:UsersTimothy HallDesktopYradel_GameYradel_Game5.py", line 99, in <module>
main()
File "C:UsersTimothy HallDesktopYradel_GameYradel_Game5.py", line 31, in main
player = Player()
File "C:UsersTimothy HallDesktopYradel_Gameplayer.py", line 11, in __init__
self.x = world.start_tile_location[0]
TypeError: 'NoneType' object is not subscriptable

Yradel_Game5.py

# this program is a text based rpg
#import the OrderedDict
from collections import OrderedDict
# import the player module
from player import Player
# import the world module
import world
def main():
# display Yradellian name examples
print("Some examples of female Yradellian names include Nelsi, Drew, Casey, Ilya, etc.")
print("Some examples of male Yradellian names include Toreth, Daren, Attel, Mayes, etc.")
# get the character name from the user
yesName = False
while yesName == False:
charName = input("What can I call you? ")
nameInput = input("Your name is, " + charName + "? Type Yes or No: ")
if nameInput == "Yes":
yesName = True
else:
print("Sorry...")
# call the parse world function
world.parse_world_dsl()
# create a player object
player = Player()
# display the welcome message
print()
player.say_hello(charName)
# display current inventory
print()
print("Here's your starting game Inventory:")
player.print_inventory()
while True:
# display the intro text for each tile
print()
room = world.tile_at(player.x, player.y)
print(room.intro_text())
# modify the player depending on the tile type
room.modify_player(player)
# get the action input from the user
print()
choose_action(room, player)
# create a funtion for available actions
def get_available_actions(room, player):
actions = OrderedDict()
print("Choose what to do...")
if player.inventory:
action_adder(actions, "i", player.print_inventory, "Print inventory")
if isinstance(room, world.TraderTile):
action_adder(actions, "t", player.trade, "Trade")
if isinstance(room, world.EnemyTile) and room.enemy.is_alive():
action_adder(actions, "a", player.attack, "Attack")
else:
if world.tile_at(room.x, room.y - 1):
action_adder(actions, "n", player.move_north, "Go North!")
if world.tile_at(room.x + 1, room.y):
action_adder(actions, "e", player.move_east, "Go East!")
if world.tile_at(room.x, room.y + 1):
action_adder(actions, "s", player.move_south, "Go South!")
if world.tile_at(room.x - 1, room.y):
action_adder(actions, "w", player.move_west, "Go West!")
if player.hp < 100:
action_adder(actions, "h", player.heal, "Heal")
return actions
# create the action adder function
def action_adder(action_dict, hotkey, action, name):
action_dict[hotkey.lower()] = action
action_dict[hotkey.upper()] = action
print("{}: {}".format(hotkey, name))
# create a function to utilize the action dictionary
def choose_action(room, player):
action = None
while not action:
available_actions = get_available_actions(room, player)
action_input = input("Action: (Type a letter) ")
action = available_actions.get(action_input)
if action:
action()
else:
print("That is invalid input.")
# call the main function
main()

world.py

# import the enemies module
import enemies
# import the npc module
import npc
# import the random module
import random
# create a parent class for the map tiles
class MapTile:
def __init__(self, x, y):
self.x = x
self.y = y
def intro_text(self):
raise NotImplementedError("Create a subclass instead!")
def modify_player(self, player):
pass
# create the tile subclasses
class StartTile(MapTile):
def intro_text(self):
return """You find yourself in a forest, sunlight trickling through the leaves overhead. Your feet crunch over the underbrush. You can see four paths through the trees.n"""
class BoringTile(MapTile):
def intro_text(self):
return """The trees all look the same here...n"""
class CityTile(MapTile):
def intro_text(self):
return """You made it out of the forest into a small town known as Burenburg. The people greet you warmly and you are filled with a sense of accomplishment.n"""
class EnemyTile(MapTile):
# have enemies randomly appear
def __init__(self, x, y):
r = random.random()
if r < 0.50:
self.enemy = enemies.Wolf()
self.alive_text = "A lone Wolf approaches you baring its fangs."
self.dead_text = "The Wolf keels over, dead before you."
else:
self.enemy = enemies.Goblin()
self.alive_text = "A Goblin tries to steal your gold, you must defend yourself against his blade."
self.dead_text = "The Goblin sticks its tongue out at you as it falls over dead."
super().__init__(x, y)
# display their alive/dead message
def intro_text(self):
text = self.alive_text if self.enemy.is_alive() else self.dead_text
return text
# have enemies attack the player
def modify_player(self, player):
if self.enemy.is_alive():
player.hp = player.hp - self.enemy.damage
print("Enemy does {} damage. You have {} HP remaining.".format(self.enemy.damage, player.hp))
class TraderTile(MapTile):
def __init__(self, x, y):
self.trader = npc.Trader()
super().__init__(x, y)
# create a method to trade between a buyer and seller
def trade(self, buyer, seller):
for i, item in enumerate(seller.inventory, 1):
print("{}. {} - {} Gold".format(i, item.name, item.value))
while True:
user_input = input("Choose an item or press Q to exit: ")
if user_input in ["Q", "q"]:
return
else:
try:
choice = int(user_input)
to_swap = seller.inventory[choice - 1]
self.swap(seller, buyer, to_swap)
except ValueError:
print("Invalid choice!")
# create a method to swap items and gold between the buyer and seller
def swap(self, seller, buyer, item):
if item.value > buyer.gold:
print("That's too expensive")
return
seller.inventory.remove(item)
buyer.inventory.append(item)
seller.gold = seller.gold + item.value
buyer.gold = buyer.gold - item.value
print("Trade complete!")
# create a method to accept user input about who is the buyer and seller
def check_if_trade(self, player):
while True:
print("Would you like to (B)uy, (S)ell or (Q)uit?")
user_input = input()
if user_input in ["Q", "q"]:
return
elif user_input in ["B", "b"]:
print("Here's what's available to buy: ")
self.trade(buyer = player, seller = self.trader)
elif user_input in ["S", "s"]:
print("Here's what's available to sell: ")
self.trade(buyer = self.trader, seller = player)
else:
print("Invalid choice!")
# create intro text for this room
def intro_text(self):
return """A man in a brown robe awaits you, willing to trade."""
class GoldTile(MapTile):
def __init__(self, x, y):
self.gold = random.randint(1, 50)
self.gold_claimed = False
super().__init__(x, y)
def modify_player(self, player):
if not self.gold_claimed:
self.gold_claimed = True
player.gold = player.gold + self.gold
print("+{} Gold added.".format(self.gold))
def intro_text(self):
if self.gold_claimed:
return """Another clearing in the forest with nothing in it but cute animals and underbrush."""
else:
return """Someone must've dropped some gold! You pick it up."""
# create the basic world map
world_dsl = """
| |E|C|E| |
|B|B| |B| |
|T| |G|E|B|
|G| |E| |G|
|E|B|S|T|B|
| | |E| |G|
| | |G|B|E|
"""
# create a function to validate the dsl
def is_dsl_valid(dsl):
if dsl.count("|S|") != 1:
return False
if dsl.count("|C|") == 0:
return False
lines = dsl.splitlines()
lines = [l for l in lines if l]
pipe_counts = [line.count("|") for line in lines]
for count in pipe_counts:
if count != pipe_counts[0]:
return False
return True
# define a dictionary that maps dsl abreviations to tile types
tile_type_dict = {"C": CityTile,
"E": EnemyTile,
"S": StartTile,
"B": BoringTile,
"G": GoldTile,
"T": TraderTile,
" ": None}
world_map = []
start_tile_location = None
# create the function to parse the dsl
def parse_world_dsl():
if not is_dsl_valid(world_dsl):
raise SyntaxError("DSL is invalid!")
dsl_lines = world_dsl.splitlines()
dsl_lines = [x for x in dsl_lines if x]
# iterate over each line in the dsl
# instead of i, the variable y is used because we're working with an X-Y grid.
for y, dsl_row in enumerate(dsl_lines):
# create an object to store the tiles
row = []
# split the line into abbreviations using the "split" method
dsl_cells = dsl_row.split("|")
# the split method includes the beginning and end of the line
# so we need to remove those nonexistent cells
dsl_cells = [ c for c in dsl_cells if c]
# iterate over each cell in the dsl line
# instead of j, the variable x is used becuase we're working with an X-Y grid
for x, dsl_cell in enumerate(dsl_cells):
# look up the abbreviation in the dictionary
tile_type = tile_type_dict[dsl_cell]
# if the dictionary returned a valid type, create a new
# tile object, pass it the X-Y coordinates as required
# by the tile__init__(), and add it to the row object. If
# None was found in the dictionary, we just add None.
row.append(tile_type(x, y) if tile_type else None)
# add the whole row to the world_map
world_map.append(row)
# create a function that locates the tile at a specific coordinate
def tile_at(x, y):
if x < 0 or y < 0:
return None
try:
return world_map[y][x]
except IndexError:
return None

player.py

# import the items module
import items
# import the world module
import world
# create a class for Player
class Player:
# define the inventory
def __init__(self):
self.inventory = [items.Dagger(), items.BreadRoll(), items.Potion()]
self.x = world.start_tile_location[0]
self.y = world.start_tile_location[1]
self.hp = 100
self.gold = 100
# print the inventory and the best weapon
def print_inventory(self):
print("Inventory:")
for item in self.inventory:
print("* " + str(item))
print("Gold: {}".format(self.gold))
best_weapon = self.most_powerful_weapon()
print("Your best weapon is your {}".format(best_weapon))
# create a function to heal the player
def heal(self):
consumables = [item for item in self.inventory if isinstance(item, items.Consumable)]
if not consumables:
print("You don't have any items to heal you!")
return
for i, item in enumerate(consumables, 1):
print("Choose an item to use to heal: ")
print("{}. {}".format(i, item))
valid = False
while not valid:
choice = input("")
try:
to_eat = consumables[int(choice) - 1]
self.hp = min(100, self.hp + to_eat.healing_value)
self.inventory.remove(to_eat)
print("Current HP: {}".format(self.hp))
valid = True
except (ValueError, IndexError):
print("Invalid choice, try again.")
# welcome the user to the game
def say_hello(self, name):
print("Hello, " + name, ", welcome to the world of Yradel.")
# create a function to determine the best weapon
def most_powerful_weapon(self):
max_damage = 0
best_weapon = None
for item in self.inventory:
try:
if item.damage > max_damage:
best_weapon = item
max_damage = item.damage
except AttributeError:
pass
return best_weapon
# create functions to let the player move
def move(self, dx, dy):
self.x += dx
self.y += dy
def move_north(self):
self.move(dx = 0, dy = -1)
def move_east(self):
self.move(dx = 1, dy = 0)
def move_south(self):
self.move(dx = 0, dy = 1)
def move_west(self):
self.move(dx = -1, dy = 0)
# create a function to attack the enemy
def attack(self):
best_weapon = self.most_powerful_weapon()
room = world.tile_at(self.x, self.y)
enemy = room.enemy
print("You use {} against the {}!".format(best_weapon.name, enemy.name))
enemy.hp -= best_weapon.damage
if not enemy.is_alive():
print("You killed {}!".format(enemy.name))
else:
print("{} HP is {}.".format(enemy.name, enemy.hp))
# create a method to allow trade
def trade(self):
room = world.tile_at(self.x, self.y)
room.check_if_trade(self)

看起来您忘记在此函数中设置start_tile_location

start_tile_location = None
# create the function to parse the dsl
def parse_world_dsl():
if not is_dsl_valid(world_dsl):
raise SyntaxError("DSL is invalid!")
dsl_lines = world_dsl.splitlines()
dsl_lines = [x for x in dsl_lines if x]
# iterate over each line in the dsl
# instead of i, the variable y is used because we're working with an X-Y grid.
for y, dsl_row in enumerate(dsl_lines):
# create an object to store the tiles
row = []
# split the line into abbreviations using the "split" method
dsl_cells = dsl_row.split("|")
# the split method includes the beginning and end of the line
# so we need to remove those nonexistent cells
dsl_cells = [ c for c in dsl_cells if c]
# iterate over each cell in the dsl line
# instead of j, the variable x is used becuase we're working with an X-Y grid
for x, dsl_cell in enumerate(dsl_cells):
# look up the abbreviation in the dictionary
tile_type = tile_type_dict[dsl_cell]
# if the dictionary returned a valid type, create a new
# tile object, pass it the X-Y coordinates as required
# by the tile__init__(), and add it to the row object. If
# None was found in the dictionary, we just add None.
row.append(tile_type(x, y) if tile_type else None)
if "S" == dsl_cell:
start_tile_location = (x, y)
# add the whole row to the world_map
world_map.append(row)

最新更新