Esempio n. 1
0
class Portal(Block):
    """Represents a single portal as a block in the maze"""

    P_TYPE_1 = 1
    P_TYPE_2 = 2
    TYPE_1_COLOR = (0, 255, 255)
    TYPE_2_COLOR = (255, 128, 0)

    def __init__(self, screen, x, y, direction, maze, p_type=P_TYPE_1):
        self.screen = screen
        self.maze = maze
        self.direction = direction
        self.p_type = p_type
        if p_type == Portal.P_TYPE_1:
            self.image_manager = ImageManager('blue-portal-bg.png',
                                              sheet=True,
                                              pos_offsets=[(0, 0, 32, 32),
                                                           (32, 0, 32, 32),
                                                           (64, 0, 32, 32),
                                                           (0, 32, 32, 32),
                                                           (32, 32, 32, 32),
                                                           (64, 32, 32, 32),
                                                           (0, 64, 32, 32)],
                                              resize=(self.maze.block_size,
                                                      self.maze.block_size),
                                              animation_delay=250)
            image, _ = self.image_manager.get_image()
        else:
            self.image_manager = ImageManager('orange-portal-bg.png',
                                              sheet=True,
                                              pos_offsets=[(0, 0, 32, 32),
                                                           (32, 0, 32, 32),
                                                           (64, 0, 32, 32),
                                                           (0, 32, 32, 32),
                                                           (32, 32, 32, 32),
                                                           (64, 32, 32, 32),
                                                           (0, 64, 32, 32)],
                                              resize=(self.maze.block_size,
                                                      self.maze.block_size),
                                              animation_delay=250)
            image, _ = self.image_manager.get_image()
        super().__init__(x, y, maze.block_size, maze.block_size, image)

    def get_nearest_col(self):
        """Get the current column location on the maze map"""
        return (self.rect.x -
                (self.screen.get_width() // 5)) // self.maze.block_size

    def get_nearest_row(self):
        """Get the current row location on the maze map"""
        return (self.rect.y -
                (self.screen.get_height() // 12)) // self.maze.block_size

    def update(self):
        """Update the portal animations"""
        self.image = self.image_manager.next_image()

    def blit(self):
        """Blit the portal to the screen"""
        self.screen.blit(self.image, self.rect)
Esempio n. 2
0
class SimpleAnimation(pygame.sprite.Sprite):
    """A class for presenting a basic animation with little to no special logic"""
    def __init__(self,
                 screen,
                 sprite_sheet,
                 sheet_offsets,
                 pos=(0, 0),
                 resize=None,
                 detail=None,
                 frame_delay=None,
                 flip=False):
        super().__init__()
        self.screen = screen
        if not resize:
            resize = (self.screen.get_height() // 10,
                      self.screen.get_height() // 10)
        self.image_manager = ImageManager(sprite_sheet,
                                          sheet=True,
                                          pos_offsets=sheet_offsets,
                                          resize=resize,
                                          animation_delay=frame_delay)
        if flip:
            self.image_manager.flip()
        self.image, self.rect = self.image_manager.get_image()
        if detail:
            self.detail_piece = ImageManager(
                detail, sheet=True, pos_offsets=sheet_offsets,
                resize=resize).all_images()[
                    0]  # grab first image in detail sheet
            if flip:
                self.image_manager.flip()
            self.image.blit(self.detail_piece, (0, 0))  # combine detail
        else:
            self.detail_piece = None
        self.rect.centerx, self.rect.centery = pos

    def update(self):
        """Update to the next image in the animation"""
        self.image = self.image_manager.next_image()
        if self.detail_piece:
            self.image.blit(self.detail_piece, (0, 0))  # combine detail

    def blit(self):
        """Blit the current image to the screen"""
        self.screen.blit(self.image, self.rect)
Esempio n. 3
0
class PacMan(pygame.sprite.Sprite):
    """Represents the player character 'PacMan' and its related logic/control"""
    PAC_YELLOW = (255, 255, 0)
    PAC_AUDIO_CHANNEL = 0

    def __init__(self, screen, maze):
        super().__init__()
        self.screen = screen
        self.radius = maze.block_size // 5
        self.maze = maze
        self.sound_manager = SoundManager(sound_files=['pacman_chomp.wav', 'pacman_eatfruit.wav',
                                                       'pacman_death.wav', 'pacman-portal.wav'],
                                          keys=['chomp', 'eatfruit', 'death', 'portal'],
                                          channel=PacMan.PAC_AUDIO_CHANNEL)
        self.horizontal_images = ImageManager('pacman-horiz.png', sheet=True, pos_offsets=[(0, 0, 32, 32),
                                                                                           (32, 0, 32, 32),
                                                                                           (0, 32, 32, 32),
                                                                                           (32, 32, 32, 32),
                                                                                           (0, 64, 32, 32)],
                                              resize=(self.maze.block_size, self.maze.block_size),
                                              reversible=True)
        self.vertical_images = ImageManager('pacman-vert.png', sheet=True, pos_offsets=[(0, 0, 32, 32),
                                                                                        (32, 0, 32, 32),
                                                                                        (0, 32, 32, 32),
                                                                                        (32, 32, 32, 32),
                                                                                        (0, 64, 32, 32)],
                                            resize=(self.maze.block_size, self.maze.block_size),
                                            reversible=True)
        self.death_images = ImageManager('pacman_death.png', sheet=True, pos_offsets=[(0, 0, 32, 32),
                                                                                      (32, 0, 32, 32),
                                                                                      (0, 32, 32, 32),
                                                                                      (32, 32, 32, 32),
                                                                                      (0, 64, 32, 32),
                                                                                      (32, 64, 32, 32)],
                                         resize=(self.maze.block_size, self.maze.block_size),
                                         animation_delay=150, repeat=False)
        self.flip_status = {'use_horiz': True, 'h_flip': False, 'v_flip': False}
        self.spawn_info = self.maze.player_spawn[1]
        self.tile = self.maze.player_spawn[0]
        self.direction = None
        self.moving = False
        self.speed = maze.block_size // 7
        self.image, self.rect = self.horizontal_images.get_image()
        self.rect.centerx, self.rect.centery = self.spawn_info   # screen coordinates for spawn
        self.dead = False
        self.portal_controller = PortalController(screen, self, maze)   # controller object for portal

        # Keyboard related events/actions/releases
        self.event_map = {pygame.KEYDOWN: self.perform_action, pygame.KEYUP: self.reset_direction}
        self.action_map = {pygame.K_UP: self.set_move_up, pygame.K_LEFT: self.set_move_left,
                           pygame.K_DOWN: self.set_move_down, pygame.K_RIGHT: self.set_move_right,
                           pygame.K_q: self.blue_portal, pygame.K_w: self.orange_portal}

    def clear_portals(self):
        """Remove all portals from play"""
        self.portal_controller.clear_portals()

    def blue_portal(self):
        """Create a blue portal from PacMan"""
        self.sound_manager.play('portal')
        self.portal_controller.fire_b_portal_projectile()

    def orange_portal(self):
        """Create an orange portal from PacMan"""
        self.sound_manager.play('portal')
        self.portal_controller.fire_o_portal_projectile()

    def set_death(self):
        """Set the death flag for PacMan and begin the death animation"""
        self.sound_manager.play('dead')
        self.dead = True
        self.image, _ = self.death_images.get_image()

    def revive(self):
        """Set dead to False and give PacMan a default image"""
        self.dead = False
        self.image, _ = self.horizontal_images.get_image()
        self.death_images.image_index = 0

    def reset_position(self):
        """Reset position back to pre-define spawn location"""
        self.rect.centerx, self.rect.centery = self.spawn_info  # screen coordinates for spawn

    def reset_direction(self, event):
        """Reset the movement direction if key-up on movement keys"""
        if event.key in (pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT, pygame.K_RIGHT):
            self.moving = False

    def perform_action(self, event):
        """Change direction based on the event key"""
        if event.key in self.action_map:
            self.action_map[event.key]()

    def set_move_up(self):
        """Set move direction up"""
        if self.direction != 'u':
            self.direction = 'u'
            if self.flip_status['v_flip']:
                self.vertical_images.flip(False, True)
                self.flip_status['v_flip'] = False
            self.flip_status['use_horiz'] = False
        self.moving = True

    def set_move_left(self):
        """Set move direction left"""
        if self.direction != 'l':
            self.direction = 'l'
            if not self.flip_status['h_flip']:
                self.horizontal_images.flip()
                self.flip_status['h_flip'] = True
            self.flip_status['use_horiz'] = True
        self.moving = True

    def set_move_down(self):
        """Set move direction down"""
        if self.direction != 'd':
            self.direction = 'd'
            if not self.flip_status['v_flip']:
                self.vertical_images.flip(x_bool=False, y_bool=True)
                self.flip_status['v_flip'] = True
            self.flip_status['use_horiz'] = False
        self.moving = True

    def set_move_right(self):
        """Set move direction to right"""
        if self.direction != 'r':
            self.direction = 'r'
            if self.flip_status['h_flip']:
                self.horizontal_images.flip()
                self.flip_status['h_flip'] = False
            self.flip_status['use_horiz'] = True
        self.moving = True

    def get_nearest_col(self):
        """Get the current column location on the maze map"""
        return (self.rect.x - (self.screen.get_width() // 5)) // self.maze.block_size

    def get_nearest_row(self):
        """Get the current row location on the maze map"""
        return (self.rect.y - (self.screen.get_height() // 12)) // self.maze.block_size

    def is_blocked(self):
        """Check if PacMan is blocked by any maze barriers, return True if blocked, False if clear"""
        result = False
        if self.direction is not None and self.moving:
            original_pos = self.rect
            if self.direction == 'u':
                test = self.rect.move((0, -self.speed))
            elif self.direction == 'l':
                test = self.rect.move((-self.speed, 0))
            elif self.direction == 'd':
                test = self.rect.move((0, self.speed))
            else:
                test = self.rect.move((self.speed, 0))
            self.rect = test    # temporarily move self

            # if any collision, result = True
            if pygame.sprite.spritecollideany(self, self.maze.maze_blocks):
                result = True
            elif pygame.sprite.spritecollideany(self, self.maze.shield_blocks):
                result = True
            elif not self.portal_controller.portables_usable():
                result = self.portal_controller.collide_portals(self)
            self.rect = original_pos    # reset position
        return result

    def update(self):
        """Update PacMan's position in the maze if moving, and if not blocked"""
        if not self.dead:
            self.portal_controller.update()
            self.portal_controller.check_portals(self)
            if self.direction and self.moving:
                if self.flip_status['use_horiz']:
                    self.image = self.horizontal_images.next_image()
                else:
                    self.image = self.vertical_images.next_image()
                if not self.is_blocked():
                    if self.direction == 'u':
                        self.rect.centery -= self.speed
                    elif self.direction == 'l':
                        self.rect.centerx -= self.speed
                    elif self.direction == 'd':
                        self.rect.centery += self.speed
                    elif self.direction == 'r':
                        self.rect.centerx += self.speed
                self.tile = (self.get_nearest_row(), self.get_nearest_col())
        else:
            self.image = self.death_images.next_image()

    def blit(self):
        """Blit the PacMan sprite to the screen"""
        self.portal_controller.blit()
        self.screen.blit(self.image, self.rect)

    def eat(self):
        """Eat pellets from the maze and return the score accumulated"""
        score = 0
        fruit_count = 0
        power = None
        collision = pygame.sprite.spritecollideany(self, self.maze.pellets)
        if collision:
            collision.kill()
            score += 10
            self.sound_manager.play('chomp')
        collision = pygame.sprite.spritecollideany(self, self.maze.fruits)
        if collision:
            collision.kill()
            score += 20
            fruit_count += 1
            self.sound_manager.play('eatfruit')
        collision = pygame.sprite.spritecollideany(self, self.maze.power_pellets)
        if collision:
            collision.kill()
            score += 20
            power = True
            self.sound_manager.play('chomp')
        return score, fruit_count, power
Esempio n. 4
0
class Ghost(Sprite):
    """Represents the enemies of PacMan which chase him around the maze"""
    GHOST_AUDIO_CHANNEL = 1

    def __init__(self,
                 screen,
                 maze,
                 target,
                 spawn_info,
                 sound_manager,
                 ghost_file='ghost-red.png'):
        super().__init__()
        self.screen = screen
        self.maze = maze
        self.internal_map = maze.map_lines
        self.target = target
        self.sound_manager = sound_manager
        self.norm_images = ImageManager(ghost_file,
                                        sheet=True,
                                        pos_offsets=[(0, 0, 32, 32),
                                                     (0, 32, 32, 32)],
                                        resize=(self.maze.block_size,
                                                self.maze.block_size),
                                        animation_delay=250)
        self.blue_images = ImageManager('ghost-ppellet.png',
                                        sheet=True,
                                        pos_offsets=[(0, 0, 32, 32),
                                                     (0, 32, 32, 32)],
                                        resize=(self.maze.block_size,
                                                self.maze.block_size),
                                        animation_delay=150)
        self.blue_warnings = ImageManager('ghost-ppellet-warn.png',
                                          sheet=True,
                                          pos_offsets=[(0, 0, 32, 32),
                                                       (0, 32, 32, 32)],
                                          resize=(self.maze.block_size,
                                                  self.maze.block_size),
                                          animation_delay=150)
        self.eyes = ImageManager('ghost-eyes.png',
                                 sheet=True,
                                 pos_offsets=[(0, 0, 32, 32), (32, 0, 32, 32),
                                              (0, 32, 32, 32),
                                              (32, 32, 32, 32)],
                                 resize=(self.maze.block_size,
                                         self.maze.block_size),
                                 keys=['r', 'u', 'd', 'l'])
        self.score_font = sysfont.SysFont(None, 22)
        self.score_image = None
        self.image, self.rect = self.norm_images.get_image()
        self.curr_eye, _ = self.eyes.get_image(
            key='r')  # default eye to looking right
        self.image.blit(self.curr_eye, (0, 0))  # combine eyes and body
        self.return_tile = spawn_info[0]  # spawn tile
        self.return_path = None  # path back to spawn tile
        self.return_delay = 1000  # 1 second delay from being eaten to returning
        self.eaten_time = None  # timestamp for being eaten
        self.start_pos = spawn_info[1]
        self.reset_position()
        self.tile = spawn_info[0]
        self.direction = None
        self.last_position = None
        self.speed = maze.block_size / 10
        self.state = {
            'enabled': False,
            'blue': False,
            'return': False,
            'speed_boost': False
        }
        self.blue_interval = 5000  # 5 second time limit for blue status
        self.blue_start = None  # timestamp for blue status start
        self.blink = False
        self.last_blink = time.get_ticks()
        self.blink_interval = 250

    @staticmethod
    def find_path(maze_map, start, target):
        """Determine a path in the maze map from the start to the target tile"""
        path = []  # path list
        tried = set()  # set for faster membership checks
        done = False
        curr_tile = start
        while not done:
            if curr_tile == target:
                done = True  # if at target tile, we are done
            else:
                options = [  # possible moves
                    (curr_tile[0] + 1, curr_tile[1]),
                    (curr_tile[0] - 1, curr_tile[1]),
                    (curr_tile[0], curr_tile[1] + 1),
                    (curr_tile[0], curr_tile[1] - 1)
                ]
                test = (abs(target[0] - start[0]), abs(target[1] - start[0]))
                prefer = test.index(max(test[0], test[1]))
                if prefer == 0:
                    options.sort(key=lambda x: x[0], reverse=True)
                else:
                    options.sort(key=lambda x: x[1], reverse=True)
                backtrack = True  # assume we must backtrack
                for opt in options:
                    try:
                        if maze_map[opt[0]][opt[1]] not in (
                                'x', ) and opt not in tried:
                            backtrack = False  # if we haven't tried this option before, and it's not blocked
                            path.append(
                                opt
                            )  # then add to the path, and remember that it's been tried
                            tried.add(opt)
                            curr_tile = opt
                            break
                    except IndexError:
                        continue
                if backtrack:  # backtrack to the previous position in the path
                    curr_tile = path.pop()
        return path

    def increase_speed(self):
        """Increase the ghost's speed"""
        self.state['speed_boost'] = True
        self.speed = self.maze.block_size / 8

    def reset_speed(self):
        """Reset the ghost's speed"""
        self.state['speed_boost'] = False
        self.speed = self.maze.block_size / 10

    def reset_position(self):
        """Hard reset the ghost position back to its original location"""
        self.rect.left, self.rect.top = self.start_pos

    def get_dir_from_path(self):
        """Return a new direction based on the next step in the current path"""
        try:
            next_step = self.return_path[0]
            if next_step[0] > self.tile[0]:
                return 'd'  # move up next
            if next_step[0] < self.tile[0]:
                return 'u'  # move down next
            if next_step[1] > self.tile[1]:
                return 'r'  # move right next
            if next_step[1] < self.tile[1]:
                return 'l'  # move left next
        except IndexError as ie:
            print('Error while trying to get new path direction', ie)
            return None

    def set_eaten(self):
        """Begin the ghost's sequence for having been eaten by PacMan"""
        self.state['return'] = True
        self.state['blue'] = False
        self.tile = (self.get_nearest_row(), self.get_nearest_col())
        self.return_path = Ghost.find_path(self.internal_map, self.tile,
                                           self.return_tile)
        self.direction = self.get_dir_from_path()
        self.image = self.score_font.render('200', True, (255, 255, 255))
        self.eaten_time = time.get_ticks()

    def get_direction_options(self):
        """Check if the ghost is blocked by any maze barriers and return all directions possible to move in"""
        tests = {
            'u': self.rect.move((0, -self.speed)),
            'l': self.rect.move((-self.speed, 0)),
            'd': self.rect.move((0, self.speed)),
            'r': self.rect.move((self.speed, 0))
        }
        remove = []
        original_pos = self.rect

        for d, t in tests.items():
            self.rect = t  # temporarily move self
            if spritecollideany(self,
                                self.maze.maze_blocks) and d not in remove:
                remove.append(
                    d)  # if collision, mark this direction for removal
            if spritecollideany(self, self.target.portal_controller.blue_portal
                                ) and d not in remove:
                remove.append(d)
            if spritecollideany(self, self.target.portal_controller.
                                orange_portal) and d not in remove:
                remove.append(d)
        for rem in remove:
            del tests[rem]
        self.rect = original_pos  # reset position
        return list(tests.keys())

    def begin_blue_state(self):
        """Switch the ghost to its blue state"""
        if not self.state['return']:
            self.state['blue'] = True
            self.image, _ = self.blue_images.get_image()
            self.blue_start = time.get_ticks()
            self.sound_manager.stop()
            self.sound_manager.play_loop('blue')

    def change_eyes(self, look_direction):
        """Change the ghosts' eyes to look in the given direction"""
        self.image, _ = self.norm_images.get_image()
        self.curr_eye, _ = self.eyes.get_image(key=look_direction)
        self.image.blit(self.curr_eye, (0, 0))  # combine eyes and body

    def get_chase_direction(self, options):
        """Figure out a new direction to chase in based on the target and walls"""
        pick_direction = None
        target_pos = (self.target.rect.centerx, self.target.rect.centery)
        test = (abs(target_pos[0]), abs(target_pos[1]))
        prefer = test.index(max(test[0], test[1]))
        if prefer == 0:  # x direction
            if target_pos[prefer] < self.rect.centerx:  # to the left
                pick_direction = 'l'
            elif target_pos[prefer] > self.rect.centerx:  # to the right
                pick_direction = 'r'
        else:  # y direction
            if target_pos[prefer] < self.rect.centery:  # upward
                pick_direction = 'u'
            elif target_pos[prefer] > self.rect.centery:  # downward
                pick_direction = 'd'
        if pick_direction not in options:  # desired direction not available
            if 'u' in options:  # pick a direction that is available
                return 'u'
            if 'l' in options:
                return 'l'
            if 'r' in options:
                return 'r'
            if 'd' in options:
                return 'd'
        else:  # desired direction available, return it
            return pick_direction

    def get_flee_direction(self, options):
        """Figure out a new direction to flee in based on the target and walls"""
        pick_direction = None
        target_pos = (self.target.rect.centerx, self.target.rect.centery)
        test = (abs(target_pos[0]), abs(target_pos[1]))
        prefer = test.index(max(test[0], test[1]))
        if prefer == 0:  # x direction
            if target_pos[
                    prefer] < self.rect.centerx:  # to the left, so move right
                pick_direction = 'r'
            elif target_pos[
                    prefer] > self.rect.centerx:  # to the right, so move left
                pick_direction = 'l'
        else:  # y direction
            if target_pos[prefer] < self.rect.centery:  # upward, so move down
                pick_direction = 'd'
            elif target_pos[prefer] > self.rect.centery:  # downward, so move up
                pick_direction = 'u'
        if pick_direction not in options:  # desired direction not available
            if 'u' in options:  # pick a direction that is available
                return 'u'
            if 'l' in options:
                return 'l'
            if 'd' in options:
                return 'd'
            if 'r' in options:
                return 'r'
        else:  # desired direction available, return it
            return pick_direction

    def get_nearest_col(self):
        """Get the current column location on the maze map"""
        return (self.rect.left -
                (self.screen.get_width() // 5)) // self.maze.block_size

    def get_nearest_row(self):
        """Get the current row location on the maze map"""
        return (self.rect.top -
                (self.screen.get_height() // 12)) // self.maze.block_size

    def is_at_intersection(self):
        """Return True if the ghost is at an intersection, False if not"""
        directions = 0
        self.tile = (self.get_nearest_row(), self.get_nearest_col())
        if self.internal_map[self.tile[0] - 1][self.tile[1]] not in ('x', ):
            directions += 1
        if self.internal_map[self.tile[0] + 1][self.tile[1]] not in ('x', ):
            directions += 1
        if self.internal_map[self.tile[0]][self.tile[1] - 1] not in ('x', ):
            directions += 1
        if self.internal_map[self.tile[0]][self.tile[1] + 1] not in ('x', ):
            directions += 1
        return True if directions > 2 else False

    def enable(self):
        """Initialize ghost AI with the first available direction"""
        options = self.get_direction_options()
        self.direction = options[0]
        self.state['enabled'] = True
        self.sound_manager.play_loop('std')

    def disable(self):
        """Disable the ghost AI"""
        self.direction = None  # remove direction
        self.state['enabled'] = False  # reset states
        self.state['return'] = False
        self.return_path = None  # remove path
        if self.state['blue']:
            self.stop_blue_state(resume_audio=False)
        self.image, _ = self.norm_images.get_image()  # reset image
        self.sound_manager.stop()

    def stop_blue_state(self, resume_audio=True):
        """Revert back from blue state"""
        self.state['blue'] = False
        self.state['return'] = False
        self.image, _ = self.norm_images.get_image()
        self.sound_manager.stop()
        if resume_audio:
            self.sound_manager.play_loop('std')

    def check_path_tile(self):
        """Check if the ghost has reached the tile it's looking for in the path,
        and if so remove it from the path"""
        self.tile = (self.get_nearest_row(), self.get_nearest_col())
        if self.return_path and self.tile == self.return_path[0]:
            del self.return_path[0]
            if not len(self.return_path) > 0:
                return '*'  # signal that the path is complete
        return None

    def update_normal(self):
        """Update logic for a normal state"""
        options = self.get_direction_options()
        if self.is_at_intersection() or self.last_position == (
                self.rect.centerx, self.rect.centery):
            self.direction = self.get_chase_direction(options)
        if self.direction == 'u' and 'u' in options:
            self.rect.centery -= self.speed
        elif self.direction == 'l' and 'l' in options:
            self.rect.centerx -= self.speed
        elif self.direction == 'd' and 'd' in options:
            self.rect.centery += self.speed
        elif self.direction == 'r' and 'r' in options:
            self.rect.centerx += self.speed
        self.change_eyes(self.direction
                         or 'r')  # default look direction to right
        self.image = self.norm_images.next_image()

    def update_blue(self):
        """Update logic for blue state"""
        self.image = self.blue_images.next_image()
        options = self.get_direction_options()
        if self.is_at_intersection() or self.last_position == (
                self.rect.centerx, self.rect.centery):
            self.direction = self.get_flee_direction(options)
        # print(self.internal_map[self.tile[0]][self.tile[1]])
        if self.direction == 'u' and 'u' in options:
            self.rect.centery -= self.speed
        elif self.direction == 'l' and 'l' in options:
            self.rect.centerx -= self.speed
        elif self.direction == 'd' and 'd' in options:
            self.rect.centery += self.speed
        elif self.direction == 'r' and 'r' in options:
            self.rect.centerx += self.speed
        if abs(self.blue_start - time.get_ticks()) > self.blue_interval:
            self.stop_blue_state()
        elif abs(self.blue_start - time.get_ticks()) > int(
                self.blue_interval * 0.5):
            if self.blink:
                self.image = self.blue_warnings.next_image()
                self.blink = False
                self.last_blink = time.get_ticks()
            elif abs(self.last_blink - time.get_ticks()) > self.blink_interval:
                self.blink = True

    def update_return(self):
        """Update logic for when returning to ghost spawn"""
        if abs(self.eaten_time - time.get_ticks()) > self.return_delay:
            self.image, _ = self.eyes.get_image(key=self.direction)
            test = self.check_path_tile()
            if test == '*':
                self.state['return'] = False
                self.direction = self.get_chase_direction(
                    self.get_direction_options())
            else:
                self.direction = self.get_dir_from_path()
            if self.direction == 'u':
                self.rect.centery -= self.speed
            elif self.direction == 'l':
                self.rect.centerx -= self.speed
            elif self.direction == 'd':
                self.rect.centery += self.speed
            elif self.direction == 'r':
                self.rect.centerx += self.speed

    def update(self):
        """Update the ghost position"""
        if self.state['enabled']:
            if not self.state['blue'] and not self.state['return']:
                self.update_normal()
            elif self.state['blue']:
                self.update_blue()
            elif self.state['return']:
                self.update_return()
            self.last_position = (self.rect.centerx, self.rect.centery)

    def blit(self):
        """Blit ghost image to the screen"""
        self.screen.blit(self.image, self.rect)