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)
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)
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')
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()
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
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