class PacManPortalGame: game_color = (0, 0, 0) event_start = pygame.USEREVENT + 1 event_remake = pygame.USEREVENT + 2 event_next_level = pygame.USEREVENT + 3 def __init__(self): pygame.init() pygame.mixer.music.load('sounds/bg-music.wav') self.game_screen = pygame.display.set_mode((800, 600)) pygame.display.set_caption('PacMan Portal') self.tick_component = pygame.time.Clock() self.hold_score = ScoreController( screen=self.game_screen, sb_pos=((self.game_screen.get_width() // 5), (self.game_screen.get_height() * 0.965)), items_image='apple.png', itc_pos=(int(self.game_screen.get_width() * 0.6), self.game_screen.get_height() * 0.965)) self.map_layout = Maze(screen=self.game_screen, maze_map_file='maze_map.txt') self.lives_left = PacManCounter( screen=self.game_screen, ct_pos=((self.game_screen.get_width() // 3), (self.game_screen.get_height() * 0.965)), images_size=(self.map_layout.block_size, self.map_layout.block_size)) self.next_level = LevelTransition(screen=self.game_screen, score_controller=self.hold_score) self.lost_game = True self.pause_game = False self.pacman_player = PacMan(screen=self.game_screen, maze=self.map_layout) self.pacman_ghosts = pygame.sprite.Group() self.pacman_ghosts_sound = SoundManager( sound_files=['ghost-blue.wav', 'ghost-eaten.wav', 'ghost-std.wav'], keys=['blue', 'eaten', 'std'], channel=Ghost.ghost_audio) self.pacman_ghosts_interval_active = 2500 self.pacman_ghosts_begin_chase = None self.pacman_ghosts_blinky = None self.pacman_ghosts_others = [] self.blit_ghosts() self.game_actions = { PacManPortalGame.event_start: self.init_ghosts, PacManPortalGame.event_remake: self.rebuild_maze, PacManPortalGame.event_next_level: self.next_level } def play_game(self): event_iterator = EventLoop(loop_running=True, actions={ **self.pacman_player.event_map, **self.game_actions }) self.next_level.set_show_transition() self.lost_game = False if self.pacman_player.dead: self.pacman_player.revive() self.hold_score.reset_level() self.lives_left.reset_counter() self.rebuild_maze() while event_iterator.loop_running: self.tick_component.tick(60) event_iterator.check_events() self.update_screen() if self.lost_game: pygame.mixer.stop() self.hold_score.reset_level() event_iterator.loop_running = False def run(self): play_menu = Menu(self.game_screen) high_score_menu = HighScoreScreen(self.game_screen, self.hold_score) intro_ghost_chase = Intro(self.game_screen) event_iterator = EventLoop( loop_running=True, actions={pygame.MOUSEBUTTONDOWN: play_menu.check_buttons}) while event_iterator.loop_running: self.tick_component.tick(60) event_iterator.check_events() self.game_screen.fill(PacManPortalGame.game_color) if not play_menu.high_score_menu: intro_ghost_chase.update() intro_ghost_chase.blit() play_menu.blit() else: high_score_menu.blit() high_score_menu.check_done() if play_menu.ready_to_play: pygame.mixer.music.stop() self.play_game() for g in self.pacman_ghosts: g.reset_speed() play_menu.ready_to_play = False self.hold_score.save_high_scores() high_score_menu.prep_images() high_score_menu.position() elif not pygame.mixer.music.get_busy(): pygame.mixer.music.play(-1) pygame.display.flip() def init_ghosts(self): if not self.pacman_ghosts_blinky.state['enabled']: self.pacman_ghosts_blinky.enable() self.pacman_ghosts_begin_chase = self.pacman_ghosts_others.copy() pygame.time.set_timer(PacManPortalGame.event_start, 0) pygame.time.set_timer(PacManPortalGame.event_start, self.pacman_ghosts_interval_active) else: try: g = self.pacman_ghosts_begin_chase.pop() g.enable() except IndexError: pygame.time.set_timer(PacManPortalGame.event_start, 0) def blit_ghosts(self): ghost_images_file = [ 'ghost-pink.png', 'ghost-lblue.png', 'ghost-orange.png', 'ghost-red.png' ] location = 0 while len(self.map_layout.ghost_spawn) > 0: spawn_info = self.map_layout.ghost_spawn.pop() g = Ghost(screen=self.game_screen, maze=self.map_layout, target=self.pacman_player, spawn_info=spawn_info, ghost_file=ghost_images_file[location], sound_manager=self.pacman_ghosts_sound) if ghost_images_file[location] == 'ghost-red.png': self.pacman_ghosts_blinky = g else: self.pacman_ghosts_others.append(g) self.pacman_ghosts.add(g) location = (location + 1) % len(ghost_images_file) def next_level(self): pygame.time.set_timer(PacManPortalGame.event_next_level, 0) self.pacman_player.clear_portals() self.hold_score.increment_level() self.rebuild_maze() def rebuild_maze(self): if self.lives_left.lives > 0: for g in self.pacman_ghosts: if g.state['enabled']: g.disable() self.map_layout.build_maze() self.pacman_player.reset_position() for g in self.pacman_ghosts: g.reset_position() if self.pacman_player.dead: self.pacman_player.revive() if self.pause_game: self.pause_game = False self.next_level.set_show_transition() else: self.lost_game = True pygame.time.set_timer(PacManPortalGame.event_remake, 0) def check_player(self): pacman_player_score, pacman_player_fruits, pacman_player_ability = self.pacman_player.eat( ) self.hold_score.add_score( score=pacman_player_score, items=pacman_player_fruits if pacman_player_fruits > 0 else None) if pacman_player_ability: for g in self.pacman_ghosts: g.begin_blue_state() touch_ghost = pygame.sprite.spritecollideany(self.pacman_player, self.pacman_ghosts) if touch_ghost and touch_ghost.state['blue']: touch_ghost.set_eaten() self.hold_score.add_score(200) elif touch_ghost and not (self.pacman_player.dead or touch_ghost.state['return']): self.lives_left.decrement() self.pacman_player.clear_portals() self.pacman_player.set_death() for g in self.pacman_ghosts: if g.state['enabled']: g.disable() pygame.time.set_timer(PacManPortalGame.event_start, 0) pygame.time.set_timer(PacManPortalGame.event_remake, 4000) elif not self.map_layout.pellets_left() and not self.pause_game: pygame.mixer.stop() self.pause_game = True pygame.time.set_timer(PacManPortalGame.event_next_level, 1000) def update_screen(self): if not self.next_level.transition_show: self.game_screen.fill(PacManPortalGame.game_color) self.check_player() self.map_layout.blit() if not self.pause_game: self.pacman_ghosts.update() self.pacman_player.update() self.map_layout.teleport.check_teleport( self.pacman_player.rect) for g in self.pacman_ghosts: if self.hold_score.level > 3: if not g.state['speed_boost']: g.increase_speed() self.map_layout.teleport.check_teleport(g.rect) g.blit() self.pacman_player.blit() self.hold_score.blit() self.lives_left.blit() elif self.pacman_player.dead: self.pacman_player.update() self.pacman_player.blit() else: self.next_level.draw() if not self.next_level.transition_show: self.init_ghosts() pygame.display.flip()
class PacManPortalGame: """Contains the main logic and methods for the running and updating of the PacMan portal game""" BLACK_BG = (0, 0, 0) START_EVENT = pygame.USEREVENT + 1 REBUILD_EVENT = pygame.USEREVENT + 2 LEVEL_TRANSITION_EVENT = pygame.USEREVENT + 3 def __init__(self): pygame.init() pygame.mixer.music.load('sounds/bg-music.wav') self.screen = pygame.display.set_mode((800, 600)) pygame.display.set_caption('PacMan Portal') self.clock = pygame.time.Clock() self.score_keeper = ScoreController( screen=self.screen, sb_pos=((self.screen.get_width() // 5), (self.screen.get_height() * 0.965)), items_image='cherry.png', itc_pos=(int(self.screen.get_width() * 0.6), self.screen.get_height() * 0.965)) self.maze = Maze(screen=self.screen, maze_map_file='maze_map.txt') self.life_counter = PacManCounter( screen=self.screen, ct_pos=((self.screen.get_width() // 3), (self.screen.get_height() * 0.965)), images_size=(self.maze.block_size, self.maze.block_size)) self.level_transition = LevelTransition( screen=self.screen, score_controller=self.score_keeper) self.game_over = True self.pause = False self.player = PacMan(screen=self.screen, maze=self.maze) self.ghosts = pygame.sprite.Group() self.ghost_sound_manager = SoundManager( sound_files=['ghost-blue.wav', 'ghost-eaten.wav', 'ghost-std.wav'], keys=['blue', 'eaten', 'std'], channel=Ghost.GHOST_AUDIO_CHANNEL) self.ghost_active_interval = 2500 self.ghosts_to_activate = None self.first_ghost = None self.other_ghosts = [] self.spawn_ghosts() self.actions = { PacManPortalGame.START_EVENT: self.init_ghosts, PacManPortalGame.REBUILD_EVENT: self.rebuild_maze, PacManPortalGame.LEVEL_TRANSITION_EVENT: self.next_level } def init_ghosts(self): """kick start the ghost AI over a period of time""" if not self.first_ghost.state['enabled']: self.first_ghost.enable() self.ghosts_to_activate = self.other_ghosts.copy() pygame.time.set_timer(PacManPortalGame.START_EVENT, 0) # disable timer repeat pygame.time.set_timer(PacManPortalGame.START_EVENT, self.ghost_active_interval) else: try: g = self.ghosts_to_activate.pop() g.enable() except IndexError: pygame.time.set_timer(PacManPortalGame.START_EVENT, 0) # disable timer repeat def spawn_ghosts(self): """Create all ghosts at their starting positions""" files = [ 'ghost-pink.png', 'ghost-lblue.png', 'ghost-orange.png', 'ghost-red.png' ] idx = 0 while len(self.maze.ghost_spawn) > 0: spawn_info = self.maze.ghost_spawn.pop() g = Ghost(screen=self.screen, maze=self.maze, target=self.player, spawn_info=spawn_info, ghost_file=files[idx], sound_manager=self.ghost_sound_manager) if files[idx] == 'ghost-red.png': self.first_ghost = g # red ghost should be first else: self.other_ghosts.append(g) self.ghosts.add(g) idx = (idx + 1) % len(files) def next_level(self): """Increment the game level and then continue the game""" pygame.time.set_timer(PacManPortalGame.LEVEL_TRANSITION_EVENT, 0) # reset timer self.player.clear_portals() self.score_keeper.increment_level() self.rebuild_maze() def rebuild_maze(self): """Resets the maze to its initial state if the game is still active""" if self.life_counter.lives > 0: for g in self.ghosts: if g.state['enabled']: g.disable() self.maze.build_maze() self.player.reset_position() for g in self.ghosts: g.reset_position() if self.player.dead: self.player.revive() if self.pause: self.pause = False self.level_transition.set_show_transition() else: self.game_over = True pygame.time.set_timer(PacManPortalGame.REBUILD_EVENT, 0) # disable timer repeat def check_player(self): """Check the player to see if they have been hit by an enemy, or if they have consumed pellets/fruit""" n_score, n_fruits, power = self.player.eat() self.score_keeper.add_score(score=n_score, items=n_fruits if n_fruits > 0 else None) if power: for g in self.ghosts: g.begin_blue_state() ghost_collide = pygame.sprite.spritecollideany(self.player, self.ghosts) if ghost_collide and ghost_collide.state['blue']: ghost_collide.set_eaten() self.score_keeper.add_score(200) elif ghost_collide and not (self.player.dead or ghost_collide.state['return']): self.life_counter.decrement() self.player.clear_portals() self.player.set_death() for g in self.ghosts: if g.state['enabled']: # disable any ghosts g.disable() pygame.time.set_timer(PacManPortalGame.START_EVENT, 0) # cancel start event pygame.time.set_timer(PacManPortalGame.REBUILD_EVENT, 4000) elif not self.maze.pellets_left() and not self.pause: pygame.mixer.stop() self.pause = True pygame.time.set_timer(PacManPortalGame.LEVEL_TRANSITION_EVENT, 1000) def update_screen(self): """Update the game screen""" if not self.level_transition.transition_show: self.screen.fill(PacManPortalGame.BLACK_BG) self.check_player() self.maze.blit() if not self.pause: self.ghosts.update() self.player.update() self.maze.teleport.check_teleport( self.player.rect) # teleport player/projectiles for g in self.ghosts: if self.score_keeper.level > 3: if not g.state['speed_boost']: g.increase_speed() self.maze.teleport.check_teleport( g.rect) # teleport ghosts g.blit() self.player.blit() self.score_keeper.blit() self.life_counter.blit() elif self.player.dead: self.player.update() self.player.blit() else: self.level_transition.draw() # if transition just finished, init ghosts if not self.level_transition.transition_show: self.init_ghosts() pygame.display.flip() def run(self): """Run the game application, starting from the menu""" menu = Menu(self.screen) hs_screen = HighScoreScreen(self.screen, self.score_keeper) intro_seq = Intro(self.screen) e_loop = EventLoop( loop_running=True, actions={pygame.MOUSEBUTTONDOWN: menu.check_buttons}) while e_loop.loop_running: self.clock.tick(60) # 60 fps limit e_loop.check_events() self.screen.fill(PacManPortalGame.BLACK_BG) if not menu.hs_screen: intro_seq.update() # display intro/menu intro_seq.blit() menu.update() menu.blit() else: hs_screen.blit() # display highs score screen hs_screen.check_done() if menu.ready_to_play: pygame.mixer.music.stop() # stop menu music self.play_game() # player selected play, so run game for g in self.ghosts: g.reset_speed() menu.ready_to_play = False self.score_keeper.save_high_scores( ) # save high scores only on complete play hs_screen.prep_images() # update high scores page hs_screen.position() elif not pygame.mixer.music.get_busy(): pygame.mixer.music.play(-1) # music loop pygame.display.flip() def play_game(self): """Run the game's event loop, using an EventLoop object""" e_loop = EventLoop(loop_running=True, actions={ **self.player.event_map, **self.actions }) # game init signal # pygame.time.set_timer(PacManPortalGame.START_EVENT, self.level_transition.transition_time) self.level_transition.set_show_transition() self.game_over = False if self.player.dead: self.player.revive() self.score_keeper.reset_level() self.life_counter.reset_counter() self.rebuild_maze() while e_loop.loop_running: self.clock.tick(60) # 60 fps limit e_loop.check_events() self.update_screen() if self.game_over: pygame.mixer.stop() self.score_keeper.reset_level() e_loop.loop_running = False