I am creating a rogue-like game with python 2.7, using pygame and libtcodpy libraries. It is a tile based game, and when I tried to implement a function called 'take turn' that would move the enemy one space to the left every time the player moved - it applied the same function to the player and the result was extremely buggy and made "PLAYER" move left one space as well. The error occurs in line 101. I was seeing if somebody could point out the flaw. I've tried to solve it using this advice, but can't figure out where he wants me to put the line of code:
"Even after I put self.ai = ai
outside of the if-statement I still get the error that NoneType
doesn't have a function called take_turn
.
I suspect an error around anything related to the 'ai' and also 'take_turn' functions.
I solved it by creating a new ai called ai_Player and in the take_turn function I just put return, so it does nothing. Works like a charm. (Don't forget to add the AI to the player)"
my code as follows (stars indicate where I removed code and made the game run again):
class obj_Actor:
def __init__(self, x, y, name_object, sprite, creature = None, ai = None):
self.x = x #map address
self.y = y #map address
self.sprite = sprite
self.creature = creature
if creature:
creature.owner = self
**self.ai = ai
if ai:
ai.owner = self**
def draw(self):
SURFACE_MAIN.blit(self.sprite, (self.x*constants.CELL_WIDTH, self.y*constants.CELL_HEIGHT))
def move(self, dx, dy):
if GAME_MAP[self.x + dx][self.y + dy].block_path == False:
self.x += dx
self.y += dy
**class ai_Test:
def take_turn(self):
self.owner.move(-1, 0)**
def game_main_loop():
game_quit = False
#player action definition
player_action = 'no-action'
while not game_quit:
#handle player input
player_action = game_handle_keys()
if player_action == 'QUIT':
game_quit == True
**if player_action != 'no-action':
for obj in GAME_OBJECTS:
obj.ai.take_turn()**
#TODO draw the game
draw_game()
#quit the game
pygame.quit()
exit()
def game_initialize():
#This function, initializes main window, and pygame
global SURFACE_MAIN, GAME_MAP, PLAYER, ENEMY, GAME_OBJECTS
#initialize pygame
pygame.init()
SURFACE_MAIN = pygame.display.set_mode( (constants.GAME_WIDTH,constants.GAME_HEIGHT) )
GAME_MAP = map_create()
creature_com1 = com_Creature('Greg')
PLAYER = obj_Actor(0, 0, "Human", constants.S_PLAYER, creature = creature_com1)
creature_com2 = com_Creature('Skeletor')
**ai_com = ai_Test()**
ENEMY = obj_Actor(15, 15, 'Skeleton', constants.S_ENEMY, **ai = ai_com)**
GAME_OBJECTS = [PLAYER, ENEMY]
#execute game
def game_handle_keys():
#gets player input
events_list = pygame.event.get()
#process player input
for event in events_list:
if event.type == pygame.QUIT:
return 'QUIT'
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
PLAYER.move(0, -1)
return 'player-moved'
if event.key == pygame.K_DOWN:
PLAYER.move(0, 1)
return 'player-moved'
if event.key == pygame.K_LEFT:
PLAYER.move(-1, 0)
return 'player-moved'
if event.key == pygame.K_RIGHT:
PLAYER.move (1, 0)
return 'player-moved'
return "no-action"
if __name__ == '__main__':
game_initialize()
game_main_loop()
Traceback (most recent call last):
File "/Users/wills/Desktop/my-pygame/my_pygame.py", line 152, in <module>
game_main_loop()
File "/Users/wills/Desktop/my-pygame/my_pygame.py", line 93, in game_main_loop
obj.ai.take_turn()
AttributeError: obj_Actor instance has no attribute 'ai'
I think your problem is here:
I thought by not assigning it a specific ai, PLAYER which is initialized in the initialize function would ignore the "take turn" function.
If you set something to None
, it doesn't ignore everything you call on it, it raises an AttributeError
any time you try to call anything.
One option is to just check the value before you call it:
for obj in GAME_OBJECTS:
if obj.ai:
obj.ai.take_turn()
If this is definitely the only place you'll ever use that ai
attribute, this is probably the best answer.
But if you're going to use it in multiple places, it's way too easy to do the check in 7 places but forget the 8th and get mysterious errors that only happen in weird circumstances. In that case, you really do want it to act the way you were expecting it to act, and the way to do that is to assign some "dummy" object that automatically ignores the "take turn" function.
The simplest version is:
class DummyPlayer:
def take_turn(self):
pass
And then, when you were assigning the player to None
, assign it to a DummyPlayer
instance. I think that's here:
class obj_Actor:
def __init__(self, x, y, name_object, sprite, creature = None, ai = None):
# etc.
if ai:
ai.owner = self
else:
ai = DummyPlayer()
self.ai = ai
# etc.
Now, self.ai
will always be something with a take_turn
method—either a real AI, or a DummyPlayer
—so it's always safe to just call that method without checking.