Example #1
0
class LevelTransition:
    """Displays a level transition"""
    TRANSITION_CHANNEL = 4

    def __init__(self, screen, score_controller, transition_time=5000):
        self.screen = screen
        self.score_controller = score_controller
        self.sound = SoundManager(['GetLoud.wav'],
                                  keys=['transition'],
                                  channel=LevelTransition.TRANSITION_CHANNEL,
                                  volume=0.6)
        self.font = pygame.font.Font('fonts/LuckiestGuy-Regular.ttf', 32)
        self.ready_msg = self.font.render('Get Ready!', True,
                                          ScoreBoard.SCORE_WHITE)
        self.ready_msg_rect = self.ready_msg.get_rect()
        ready_pos = screen.get_width() // 2, int(screen.get_height() * 0.65)
        self.ready_msg_rect.centerx, self.ready_msg_rect.centery = ready_pos
        self.level_msg = None
        self.level_msg_rect = None
        self.transition_time = transition_time  # total time to wait until the transition ends
        self.transition_begin = None
        self.transition_show = False

    def prep_level_msg(self):
        """Prepare a message for the current level number"""
        text = 'level ' + str(self.score_controller.level)
        self.level_msg = self.font.render(text, True, ScoreBoard.SCORE_WHITE)
        self.level_msg_rect = self.level_msg.get_rect()
        level_pos = self.screen.get_width() // 2, self.screen.get_height() // 2
        self.level_msg_rect.centerx, self.level_msg_rect.centery = level_pos

    def set_show_transition(self):
        """Begin the sequence for displaying the transition"""
        self.prep_level_msg()
        self.transition_begin = pygame.time.get_ticks()
        self.transition_show = True
        self.sound.play('transition')

    def draw(self):
        """Display the level transition to the screen"""
        if abs(self.transition_begin -
               pygame.time.get_ticks()) > self.transition_time:
            self.transition_show = False
        else:
            self.screen.fill((0, 0, 0))
            self.screen.blit(self.level_msg, self.level_msg_rect)
            if abs(self.transition_begin -
                   pygame.time.get_ticks()) >= self.transition_time // 2:
                self.screen.blit(self.ready_msg, self.ready_msg_rect)
Example #2
0
class LevelTransition:
    TRANSITION_CHANNEL = 4

    def __init__(self, screen, score_controller, transition_time=5000):
        self.screen = screen
        self.score_controller = score_controller
        self.sound = SoundManager(['pacman-beginning.wav'],
                                  keys=['transition'],
                                  channel=LevelTransition.TRANSITION_CHANNEL,
                                  volume=0.6)
        self.font = pygame.font.Font('fonts/LuckiestGuy-Regular.ttf', 32)
        self.ready_msg = self.font.render('Get Ready!', True, ScoreBoard.WHITE)
        self.ready_msg_rect = self.ready_msg.get_rect()
        ready_pos = screen.get_width() // 2, int(screen.get_height() * 0.65)
        self.ready_msg_rect.centerx, self.ready_msg_rect.centery = ready_pos
        self.level_msg = None
        self.level_msg_rect = None
        self.transition_time = transition_time
        self.transition_begin = None
        self.transition_show = False

    def prep_level_msg(self):
        text = 'level ' + str(self.score_controller.level)
        self.level_msg = self.font.render(text, True, ScoreBoard.WHITE)
        self.level_msg_rect = self.level_msg.get_rect()
        level_pos = self.screen.get_width() // 2, self.screen.get_height() // 2
        self.level_msg_rect.centerx, self.level_msg_rect.centery = level_pos

    def set_show_transition(self):
        self.prep_level_msg()
        self.transition_begin = pygame.time.get_ticks()
        self.transition_show = True
        self.sound.play('transition')

    def draw(self):
        if abs(self.transition_begin -
               pygame.time.get_ticks()) > self.transition_time:
            self.transition_show = False
        else:
            self.screen.fill((0, 0, 0))
            self.screen.blit(self.level_msg, self.level_msg_rect)
            if abs(self.transition_begin -
                   pygame.time.get_ticks()) >= self.transition_time // 2:
                self.screen.blit(self.ready_msg, self.ready_msg_rect)
Example #3
0
class GameState:
    def __init__(self):
        self.twitter_feed = TwitterFeed()
        self.io = IOHandler()
        self.sound_manager = SoundManager()

    def update(self):
        if not self.io.active:
            self.io.was_active = False
            self.twitter_feed.reset()
            return

        if not self.io.was_active:
            self.io.was_active = True
            self.display_help()

        option = self.io.read()
        if option == '*':
            self.sound_manager.play('*')
            self.sound_manager.enqueue('load_more')
            self.twitter_feed.load_more()
        elif option == '#':
            self.display_help()
            return
        elif option != None:
            self.sound_manager.play(str(option))
            self.sound_manager.enqueue('tweets/{0}'.format(self.twitter_feed.get_tweet(int(option))))

        self.sound_manager.update()

    def display_help(self):
        self.sound_manager.play('intro')
Example #4
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
        self.maze = maze
        self.sound_manager = SoundManager(sound_files=['pacman-pellet-eat.wav', 'Eaten1.wav',
                                                       'Eaten4.wav'],
                                          keys=['eat', 'fruit', 'dead'],
                                          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

        # 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,
                           }




    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
            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:
            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.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('eat')
        collision = pygame.sprite.spritecollideany(self, self.maze.fruits)
        if collision:
            collision.kill()
            score += 20
            fruit_count += 1
            self.sound_manager.play('fruit')
        collision = pygame.sprite.spritecollideany(self, self.maze.power_pellets)
        if collision:
            collision.kill()
            score += 20
            power = True
            self.sound_manager.play('eat')
        return score, fruit_count, power
class PortalController:
    """Manages portals and their related functionality within the game"""
    PORTAL_AUDIO_CHANNEL = 3

    def __init__(self, screen, user, maze):
        self.screen = screen
        self.maze = maze
        self.user = user
        self.sound_manager = SoundManager(
            sound_files=['portal-open.wav', 'portal-travel.wav'],
            keys=['open', 'travel'],
            channel=PortalController.PORTAL_AUDIO_CHANNEL)
        self.blue_portal = pygame.sprite.GroupSingle(
        )  # portals as GroupSingle, which only allows one per group
        self.blue_projectile = None
        self.orange_portal = pygame.sprite.GroupSingle()
        self.orange_projectile = None
        # converter for projectile direction to portal direction
        self.portal_directions = {'l': 'r', 'r': 'l', 'u': 'd', 'd': 'u'}

    def clear_portals(self):
        """Remove all portals and projectiles"""
        self.blue_portal.empty()
        self.orange_portal.empty()
        self.blue_projectile = None
        self.orange_projectile = None

    def fire_b_portal_projectile(self):
        """Create a projectile for generating a blue portal"""
        if self.user.direction is not None:
            self.blue_projectile = PortalProjectile(
                screen=self.screen,
                source=self.user,
                direction=self.user.direction,
                p_type=Portal.P_TYPE_1)

    def fire_o_portal_projectile(self):
        """Create a projectile for generating an orange portal"""
        if self.user.direction is not None:
            self.orange_projectile = PortalProjectile(
                screen=self.screen,
                source=self.user,
                direction=self.user.direction,
                p_type=Portal.P_TYPE_2)

    def create_blue_portal(self, x, y, direction):
        """Create a blue portal, replacing the location it originally took up with a normal maze block"""
        if self.blue_portal:
            old_x, old_y = self.blue_portal.sprite.rect.x, self.blue_portal.sprite.rect.y
            self.maze.maze_blocks.add(
                Block(old_x, old_y, self.maze.block_size, self.maze.block_size,
                      self.maze.block_image))
        self.blue_portal.add(
            Portal(screen=self.screen,
                   x=x,
                   y=y,
                   direction=direction,
                   maze=self.maze,
                   p_type=Portal.P_TYPE_1))

    def create_orange_portal(self, x, y, direction):
        """Create a blue portal, replacing the location it originally took up with a normal maze block"""
        if self.orange_portal:
            old_x, old_y = self.orange_portal.sprite.rect.x, self.orange_portal.sprite.rect.y
            self.maze.maze_blocks.add(
                Block(old_x, old_y, self.maze.block_size, self.maze.block_size,
                      self.maze.block_image))
        self.orange_portal.add(
            Portal(screen=self.screen,
                   x=x,
                   y=y,
                   direction=direction,
                   maze=self.maze,
                   p_type=Portal.P_TYPE_2))

    def update(self):
        """Update the portal controller's display parts and tracking"""
        self.blue_portal.update()
        self.orange_portal.update()
        if self.blue_projectile:
            self.blue_projectile.update()  # update projectile
            # erase projectile if it hits a portal
            if pygame.sprite.spritecollideany(self.blue_projectile, self.blue_portal) or \
                    pygame.sprite.spritecollideany(self.blue_projectile, self.orange_portal):
                self.blue_projectile = None
                return
            collision = pygame.sprite.spritecollideany(self.blue_projectile,
                                                       self.maze.maze_blocks)
            if collision:  # if projectile hits a block, replace it with a blue portal
                x, y = collision.rect.x, collision.rect.y
                collision.kill()  # Replace the block with a portal
                direction = self.portal_directions[
                    self.blue_projectile.direction]
                self.blue_projectile = None  # remove the projectile
                self.create_blue_portal(x, y, direction)
                self.sound_manager.play('open')
            # remove if projectile off screen
            elif self.blue_projectile.is_off_screen():
                self.blue_projectile = None
        if self.orange_projectile:
            self.orange_projectile.update()
            # erase projectile if it hits a portal
            if pygame.sprite.spritecollideany(self.orange_projectile, self.blue_portal) or \
                    pygame.sprite.spritecollideany(self.orange_projectile, self.orange_portal):
                self.orange_projectile = None
                return
            collision = pygame.sprite.spritecollideany(self.orange_projectile,
                                                       self.maze.maze_blocks)
            if collision:  # if projectile hits a block, replace it with an orange portal
                x, y = collision.rect.x, collision.rect.y
                collision.kill()  # Replace the block with a portal
                direction = self.portal_directions[
                    self.orange_projectile.direction]
                self.orange_projectile = None  # remove the projectile
                self.create_orange_portal(x, y, direction)
                self.sound_manager.play('open')
            elif self.orange_projectile.is_off_screen():
                self.orange_projectile = None

    def portables_usable(self):
        """Return True if the portables are usable (i.e. there are two of them)"""
        return self.blue_portal and self.orange_portal

    def collide_portals(self, other):
        """Return True if the sprite is colliding with any portal"""
        return pygame.sprite.spritecollideany(other, self.blue_portal) or \
            pygame.sprite.spritecollideany(other, self.orange_portal)

    def check_portals(self, *args):
        """Check if other sprites have come into contact with the portals, and if so move them"""
        for arg in args:
            if pygame.sprite.spritecollideany(
                    arg, self.blue_portal) and self.orange_portal:
                # get i, j as it relates to the maze map
                i, j = self.orange_portal.sprite.get_nearest_row(
                ), self.orange_portal.sprite.get_nearest_col()
                # move i or j based on portal direction
                if self.orange_portal.sprite.direction == 'l':
                    j -= 1
                elif self.orange_portal.sprite.direction == 'r':
                    j += 1
                elif self.orange_portal.sprite.direction == 'u':
                    i -= 1
                else:
                    i += 1
                # convert i, j to x, y coordinates using same formula as maze class
                x, y = ((self.screen.get_width()) // 5 + (j * self.maze.block_size)), \
                       ((self.screen.get_height()) // 12 + (i * self.maze.block_size))
                arg.rect.x, arg.rect.y = x, y
                self.sound_manager.play('travel')
            elif pygame.sprite.spritecollideany(
                    arg, self.orange_portal) and self.blue_portal:
                # get i, j as it relates to the maze map
                i, j = self.blue_portal.sprite.get_nearest_row(
                ), self.blue_portal.sprite.get_nearest_col()
                # move i or j based on portal direction
                if self.blue_portal.sprite.direction == 'l':
                    j -= 1
                elif self.blue_portal.sprite.direction == 'r':
                    j += 1
                elif self.blue_portal.sprite.direction == 'u':
                    i -= 1
                else:
                    i += 1
                # convert i, j to x, y coordinates using same formula as maze class
                x, y = ((self.screen.get_width() // 5) + (j * self.maze.block_size)), \
                       ((self.screen.get_height() // 12) + (i * self.maze.block_size))
                arg.rect.x, arg.rect.y = x, y
                self.sound_manager.play('travel')

    def blit(self):
        """Blit the portal controller's display components to the screen"""
        if self.blue_projectile:
            self.blue_projectile.blit()
        if self.orange_projectile:
            self.orange_projectile.blit()
        if self.blue_portal:
            self.blue_portal.sprite.blit()
        if self.orange_portal:
            self.orange_portal.sprite.blit()
Example #6
0
class TetrisGame:
    def __init__(self):
        pg.init()
        pg.font.init()
        pg.display.set_caption("TETRIS")

        self.sound_manager = SoundManager()
        pg.mixer.music.load("Sounds/Music.mp3")
        pg.mixer.music.set_volume(0.3)
        pg.mixer.music.play(-1)

        self.main_surface = pg.display.set_mode((WINDOW_WIDTH, WINDOW_HEIGHT))
        self.playground_surf = pg.Surface(
            (GRID_SIZE[0] * CELL_SIZE * UI_SCALE,
             GRID_SIZE[1] * CELL_SIZE * UI_SCALE))
        self.next_surf = pg.Surface((220 * UI_SCALE, 220 * UI_SCALE))

        self.font = pg.font.Font("Fonts/ARCADECLASSIC.TTF",
                                 int(FONT_SIZE * UI_SCALE))

        if "top.scr" in os.listdir():
            with open("top.scr") as file:
                self.top = int(file.read())
        else:
            self.top = 0

        self.score = 0
        self.lines = 0

        self.colors = np.zeros(GRID_SIZE, dtype=object)
        self.grid_colliders = np.zeros(GRID_SIZE)

        self.cur_shape = Shape(self, 5, -1)
        self.next_shape = Shape(self, 5, -1)

        self.game_over = False

    def __del__(self):
        with open("top.scr", "w") as file:
            file.write(str(self.top))

    def check_events(self):
        for event in pg.event.get():
            if event.type == pg.QUIT:
                exit()
            if event.type == pg.KEYDOWN:
                if event.key == pg.K_SPACE:
                    self.cur_shape.rotate()
                if event.key == pg.K_d:
                    self.cur_shape.move(1)
                if event.key == pg.K_a:
                    self.cur_shape.move(-1)

    def draw_surfaces(self):
        # Playground
        playground_pos = ((PLAYGROUND_POS[0] + UI_HORIZONTAL_OFFSET) *
                          UI_SCALE,
                          (PLAYGROUND_POS[1] + UI_VERTICAL_OFFSET) * UI_SCALE)
        self.main_surface.blit(self.playground_surf, playground_pos)

        # Next
        next_surf_pos = ((PLAYGROUND_POS[0] + GRID_SIZE[0] * CELL_SIZE +
                          UI_HORIZONTAL_OFFSET + INFO_OFFSET) * UI_SCALE,
                         (PLAYGROUND_POS[1] + GRID_SIZE[1] * CELL_SIZE - 220 +
                          UI_VERTICAL_OFFSET) * UI_SCALE)
        self.main_surface.blit(self.next_surf, next_surf_pos)

        self.playground_surf.fill(BG_COLOR)
        self.next_surf.fill(BG_COLOR)

    def draw_ui(self):
        # Playground
        play_rect = ((PLAYGROUND_POS[0] + UI_HORIZONTAL_OFFSET) * UI_SCALE,
                     (PLAYGROUND_POS[1] + UI_VERTICAL_OFFSET) * UI_SCALE,
                     GRID_SIZE[0] * CELL_SIZE * UI_SCALE,
                     GRID_SIZE[1] * CELL_SIZE * UI_SCALE)
        pg.draw.rect(self.main_surface, LINE_COLOR, play_rect, 3)

        # Top
        top_headline = self.font.render("TOP", False, TEXT_COLOR)
        top_headline_pos = ((PLAYGROUND_POS[0] + GRID_SIZE[0] * CELL_SIZE +
                             INFO_OFFSET + UI_HORIZONTAL_OFFSET) * UI_SCALE,
                            (PLAYGROUND_POS[1] + UI_VERTICAL_OFFSET) *
                            UI_SCALE)
        self.main_surface.blit(top_headline, top_headline_pos)
        top = self.font.render(str(self.top).zfill(6), False, TEXT_COLOR)
        top_pos = (
            (PLAYGROUND_POS[0] + GRID_SIZE[0] * CELL_SIZE + INFO_OFFSET +
             UI_HORIZONTAL_OFFSET) * UI_SCALE,
            (PLAYGROUND_POS[1] + 0.75 * FONT_SIZE + UI_VERTICAL_OFFSET) *
            UI_SCALE)
        self.main_surface.blit(top, top_pos)

        # Score
        score_headline = self.font.render("SCORE", False, TEXT_COLOR)
        score_headline_pos = (
            (PLAYGROUND_POS[0] + GRID_SIZE[0] * CELL_SIZE + INFO_OFFSET +
             UI_HORIZONTAL_OFFSET) * UI_SCALE,
            (PLAYGROUND_POS[1] + 2.25 * FONT_SIZE + UI_VERTICAL_OFFSET) *
            UI_SCALE)
        self.main_surface.blit(score_headline, score_headline_pos)
        score = self.font.render(str(self.score).zfill(6), False, TEXT_COLOR)
        score_pos = ((PLAYGROUND_POS[0] + GRID_SIZE[0] * CELL_SIZE +
                      INFO_OFFSET + UI_HORIZONTAL_OFFSET) * UI_SCALE,
                     (PLAYGROUND_POS[1] + 3 * FONT_SIZE + UI_VERTICAL_OFFSET) *
                     UI_SCALE)
        self.main_surface.blit(score, score_pos)

        # Lines
        lines_headline = self.font.render("LINES", False, TEXT_COLOR)
        lines_headline_pos = (
            (PLAYGROUND_POS[0] + GRID_SIZE[0] * CELL_SIZE + INFO_OFFSET +
             UI_HORIZONTAL_OFFSET) * UI_SCALE,
            (PLAYGROUND_POS[1] + 4.5 * FONT_SIZE + UI_VERTICAL_OFFSET) *
            UI_SCALE)
        self.main_surface.blit(lines_headline, lines_headline_pos)
        lines = self.font.render(str(self.lines).zfill(6), False, TEXT_COLOR)
        lines_pos = (
            (PLAYGROUND_POS[0] + GRID_SIZE[0] * CELL_SIZE + INFO_OFFSET +
             UI_HORIZONTAL_OFFSET) * UI_SCALE,
            (PLAYGROUND_POS[1] + 5.25 * FONT_SIZE + UI_VERTICAL_OFFSET) *
            UI_SCALE)
        self.main_surface.blit(lines, lines_pos)

        # Next
        next_headline = self.font.render("NEXT", False, TEXT_COLOR)
        next_headline_pos = ((PLAYGROUND_POS[0] + GRID_SIZE[0] * CELL_SIZE +
                              INFO_OFFSET + UI_HORIZONTAL_OFFSET) * UI_SCALE,
                             (PLAYGROUND_POS[1] + GRID_SIZE[1] * CELL_SIZE -
                              220 - FONT_SIZE + UI_VERTICAL_OFFSET) * UI_SCALE)
        self.main_surface.blit(next_headline, next_headline_pos)
        next_rect = ((PLAYGROUND_POS[0] + GRID_SIZE[0] * CELL_SIZE +
                      UI_HORIZONTAL_OFFSET + INFO_OFFSET) * UI_SCALE,
                     (PLAYGROUND_POS[1] + GRID_SIZE[1] * CELL_SIZE - 220 +
                      UI_VERTICAL_OFFSET) * UI_SCALE, 220 * UI_SCALE,
                     220 * UI_SCALE)
        pg.draw.rect(self.main_surface, LINE_COLOR, next_rect, 3)

    def draw_shapes(self):
        for rect_x, rect_y in self.cur_shape.get_positions():
            self.fill_cell(rect_x, rect_y, self.cur_shape.color)

        for y in range(GRID_SIZE[1]):
            for x in range(GRID_SIZE[0]):
                if self.grid_colliders[x][y]:
                    self.fill_cell(x, y, self.colors[x][y])

        # Grid
        for x in range(1, GRID_SIZE[0]):
            start_pos = x * CELL_SIZE * UI_SCALE, 0
            end_pos = x * CELL_SIZE * UI_SCALE, GRID_SIZE[
                1] * CELL_SIZE * UI_SCALE
            pg.draw.line(self.playground_surf,
                         tuple(map(lambda c: int(c / 2), LINE_COLOR)),
                         start_pos, end_pos)

        for y in range(1, GRID_SIZE[1]):
            start_pos = 0, y * CELL_SIZE * UI_SCALE
            end_pos = GRID_SIZE[
                0] * CELL_SIZE * UI_SCALE, y * CELL_SIZE * UI_SCALE
            pg.draw.line(self.playground_surf,
                         tuple(map(lambda c: int(c / 2), LINE_COLOR)),
                         start_pos, end_pos)

    def draw_next(self):
        offset_y = -0.5 if int(
            self.next_shape.shape[0][0]) != self.next_shape.shape[0][0] else 0
        for pos in self.next_shape.shape:
            center_x, center_y = map(lambda s: s / 2 / CELL_SIZE,
                                     self.next_surf.get_size())
            self.fill_cell(pos[0] + center_x - 0.5,
                           pos[1] + center_y - 0.5 + offset_y,
                           self.next_shape.color, self.next_surf)

    def fill_cell(self, x, y, color, surface=None):
        rect = (x * CELL_SIZE * UI_SCALE, y * CELL_SIZE * UI_SCALE,
                round(CELL_SIZE * UI_SCALE), round(CELL_SIZE * UI_SCALE))

        if not surface:
            pg.draw.rect(self.playground_surf, color, rect)
        else:
            pg.draw.rect(surface, color, rect)

    def check_lines(self):
        lines = []
        for y, *line in enumerate(self.grid_colliders.T):
            if line[0].all():
                lines.append(y)

        if len(lines):
            asyncio.run(self.remove_lines(lines))

    async def remove_lines(self, lines, color=(0, 255, 0)):
        self.sound_manager.play(line_effect)
        half_bright_color = tuple(map(lambda c: int(c / 2), color))
        for i in range(5):
            for y in lines:
                if i % 2:
                    self.colors[:, y] = [color for _ in range(GRID_SIZE[0])]
                else:
                    self.colors[:, y] = [
                        half_bright_color for _ in range(GRID_SIZE[0])
                    ]
                self.render()
            await asyncio.sleep(0.1)

        for y in lines:
            self.grid_colliders = np.delete(self.grid_colliders, y, 1)
            self.grid_colliders = np.insert(self.grid_colliders, 0, 0, 1)
            self.colors = np.delete(self.colors, y, 1)
            self.colors = np.insert(self.colors, 0, 0, 1)

            self.score += 10 * GRID_SIZE[0]
            self.top = self.score if self.score > self.top else self.top
            self.lines += 1
        self.score += (len(lines) - 1) * 25

    def logic(self, time_delta):
        if not self.game_over:
            speed_scale = 10 if pg.key.get_pressed()[pg.K_s] else 1
            self.cur_shape.update(time_delta * speed_scale)
            self.check_lines()
        else:
            self.restart()

    def restart(self):
        pg.mixer.music.rewind()
        self.score = 0
        self.lines = 0

        self.colors = np.zeros(GRID_SIZE, dtype=object)
        self.grid_colliders = np.zeros(GRID_SIZE)

        self.cur_shape = Shape(self, 5, -1)
        self.next_shape = Shape(self, 5, -1)

        self.game_over = False

    def render(self):
        self.main_surface.fill(BG_COLOR)

        self.draw_shapes()
        self.draw_next()
        self.draw_surfaces()
        self.draw_ui()

        pg.display.update()

    def mainloop(self):
        time_delta = 0
        while True:
            start = time()
            self.check_events()
            self.logic(time_delta)
            self.render()
            time_delta = time() - start
Example #7
0
class PacMan(pygame.sprite.Sprite):
    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-pellet-eat.wav', 'pacman-fruit-eat.wav',
            'pacman-killed.wav'
        ],
                                          keys=['eat', 'fruit', 'dead'],
                                          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

        # 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
        }

    def set_death(self):
        self.sound_manager.play('dead')
        self.dead = True
        self.image, _ = self.death_images.get_image()

    def revive(self):
        self.dead = False
        self.image, _ = self.horizontal_images.get_image()
        self.death_images.image_index = 0

    def reset_position(self):
        self.rect.centerx, self.rect.centery = self.spawn_info  # screen coordinates for spawn

    def reset_direction(self, event):
        if event.key in (pygame.K_UP, pygame.K_DOWN, pygame.K_LEFT,
                         pygame.K_RIGHT):
            self.moving = False

    def perform_action(self, event):
        if event.key in self.action_map:
            self.action_map[event.key]()

    def set_move_up(self):
        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):
        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):
        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):
        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):
        return (self.rect.x -
                (self.screen.get_width() // 5)) // self.maze.block_size

    def get_nearest_row(self):
        return (self.rect.y -
                (self.screen.get_height() // 12)) // self.maze.block_size

    def is_blocked(self):
        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

            if pygame.sprite.spritecollideany(self, self.maze.maze_blocks):
                result = True
            elif pygame.sprite.spritecollideany(self, self.maze.shield_blocks):
                result = True
            self.rect = original_pos
        return result

    def update(self):
        if not self.dead:
            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):
        self.screen.blit(self.image, self.rect)

    def eat(self):
        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('eat')
        collision = pygame.sprite.spritecollideany(self, self.maze.fruits)
        if collision:
            collision.kill()
            score += 20
            fruit_count += 1
            self.sound_manager.play('fruit')
        collision = pygame.sprite.spritecollideany(self,
                                                   self.maze.power_pellets)
        if collision:
            collision.kill()
            score += 20
            power = True
            self.sound_manager.play('eat')
        return score, fruit_count, power