import pygame import pygameControls as PC from actors.enums import Colors, PlayerDirection from actors.pacman import ActorPacman from actors.ghost import Blinky, Pinky, Inky, Clyde # adjust import path as needed from actors.ghost_mode_controller import GhostModeController from hud import HUD from maze import Maze __version__ = "0.4.1" # Constants HAT_REPEAT_DELAY = 300 # milliseconds before first repeat HAT_REPEAT_INTERVAL = 100 # milliseconds between repeats class Game: def __init__(self): #states self.hat_direction = (0, 0) self.hat_timer = 0 self.hat_first_press = True self.setup() self.running = True while self.running: self.main_loop() if self.joysticks: self.joysticks[self.joy.get_instance_id()].controllers[0].close() pygame.quit() def setup(self): pygame.init() pygame.key.set_repeat(200) self.joystick_count = pygame.joystick.get_count() self.joysticks = {} self.screen_width, self.screen_height = (32*29), (32*30)+32 self.screen = pygame.display.set_mode((self.screen_width, self.screen_height)) pygame.display.set_caption("Pac-Man " + __version__) self.cherry_img = pygame.image.load("assets/cherry.png").convert_alpha() self.hud = HUD(self.screen_width, self.screen_height, cherry_image=self.cherry_img) self.maze = Maze("maze/pacman_maze.txt") self.player = ActorPacman(self.screen, center=self.maze.pacman_start, start_location=self.maze.pacman_location, maze=self.maze) self.ghost_mode_controller = GhostModeController() self.ghost_home_center = (self.screen_width // 2, (self.screen_height) // 2) self.ghosts = self.spawn_ghosts(self.ghost_home_center) self.clock = pygame.time.Clock() def spawn_ghosts(self, center_position): """ Spawns up to 4 ghosts at the center of the maze ("ghost house"). Args: center_position (tuple): (x, y) coordinates for spawn point. Returns: pygame.sprite.Group: A group containing all ghost instances. """ offset = 20 # spread out a little bit inside ghost home self.blinky = Blinky((center_position[0] - offset, center_position[1] - offset)) self.pinky = Pinky((center_position[0] + offset, center_position[1] - offset)) self.inky = Inky((center_position[0] - offset, center_position[1] + offset)) self.clyde = Clyde((center_position[0] + offset, center_position[1] + offset)) return pygame.sprite.Group(self.blinky, self.pinky, self.inky, self.clyde) def handle_hat_repeat(self): now = pygame.time.get_ticks() if self.hat_direction != (0, 0): if self.hat_first_press: if now - self.hat_timer >= HAT_REPEAT_DELAY: self.hat_timer = now self.hat_first_press = False self.post_hat_repeat_event() else: if now - self.hat_timer >= HAT_REPEAT_INTERVAL: self.hat_timer = now self.post_hat_repeat_event() def post_hat_repeat_event(self): pygame.event.post(pygame.event.Event(pygame.USEREVENT, { "type_name": "JOYHATREPEAT", "value": self.hat_direction })) def main_loop(self): self.screen.fill(Colors.Black.value) self.handle_events() # In your main game loop: self.handle_hat_repeat() self.hud.draw(self.screen) self.maze.draw(self.screen) self.player.update() self.player.draw() self.ghost_mode_controller.update() self.current_mode = self.ghost_mode_controller.mode for ghost in self.ghosts: ghost.set_mode(self.current_mode) ghost.update(self.maze, self.player) self.ghosts.draw(self.screen) pygame.display.flip() self.clock.tick(60) def handle_events(self): for event in pygame.event.get(): match event.type: case pygame.QUIT: self.running = False case pygame.KEYDOWN: match event.key: case pygame.K_RIGHT: if self.maze.is_wall(self.player.location[0] + 1, self.player.location[1]): break self.player.direction = PlayerDirection.DirectionRight self.player.move_right() case pygame.K_LEFT: if self.maze.is_wall(self.player.location[0] - 1, self.player.location[1]): break self.player.direction = PlayerDirection.DirectionLeft self.player.move_left() case pygame.K_UP: if self.maze.is_wall(self.player.location[0], self.player.location[1] - 1): break self.player.direction = PlayerDirection.DirectionUp self.player.move_up() case pygame.K_DOWN: if self.maze.is_wall(self.player.location[0], self.player.location[1] + 1): break self.player.direction = PlayerDirection.DirectionDown self.player.move_down() case pygame.JOYHATMOTION: self.hat_direction = event.value self.hat_x, self.hat_y = self.hat_direction self.hat_timer = pygame.time.get_ticks() self.hat_first_press = True if self.hat_x == 1: if event.type == pygame.USEREVENT and event.dict.get("type_name") == "JOYHATREPEAT": if self.maze.is_wall(self.player.location[0] + 1, self.player.location[1]): break self.player.direction = PlayerDirection.DirectionRight self.player.move_right() elif self.hat_x == -1: if event.type == pygame.USEREVENT and event.dict.get("type_name") == "JOYHATREPEAT": if self.maze.is_wall(self.player.location[0] - 1, self.player.location[1]): break self.player.direction = PlayerDirection.DirectionLeft self.player.move_left() elif self.hat_y == 1: if event.type == pygame.USEREVENT and event.dict.get("type_name") == "JOYHATREPEAT": if self.maze.is_wall(self.player.location[0], self.player.location[1] - 1): break self.player.direction = PlayerDirection.DirectionUp self.player.move_up() elif self.hat_y == -1: if event.type == pygame.USEREVENT and event.dict.get("type_name") == "JOYHATREPEAT": if self.maze.is_wall(self.player.location[0], self.player.location[1] + 1): break self.player.direction = PlayerDirection.DirectionDown self.player.move_down() # Handle hotplugging case pygame.JOYDEVICEADDED: # This event will be generated when the program starts for every # joystick, filling up the list without needing to create them manually. self.joy = pygame.joystick.Joystick(event.device_index) self.joysticks[self.joy.get_instance_id()] = PC.controller.Controllers(self.joy) case pygame.JOYDEVICEREMOVED: del self.joysticks[event.instance_id] print(f"Joystick {event.instance_id} disconnected") match self.maze.collect_dot(self.player.location[0], self.player.location[1]): case "dot": self.hud.add_points(10) case "power": self.hud.add_points(50) if __name__ == "__main__": pman = Game()