Files
PyGame-Pacman/pman.py
2025-04-21 13:56:21 +02:00

247 lines
12 KiB
Python

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.5.2"
# Constants
HAT_REPEAT_DELAY = 0 # milliseconds before first repeat
HAT_REPEAT_INTERVAL = 200 # milliseconds between repeats
RUMBLE_TIMEOUT = 200
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()
self.rumble_timer = pygame.time.get_ticks()
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] - 64, center_position[1] - 32), start_location=(13, 14))
self.pinky = Pinky((center_position[0] + 64, center_position[1] - 32), start_location=(17, 14))
self.inky = Inky((center_position[0] - 64, center_position[1] + 32), start_location=(13, 16))
self.clyde = Clyde((center_position[0] + 64, center_position[1] + 32), start_location=(17, 16))
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):
if self.joysticks:
if pygame.time.get_ticks() - self.rumble_timer > RUMBLE_TIMEOUT:
self.joysticks[self.joy.get_instance_id()].controllers[0].stop_rumble()
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
elif self.maze.is_ghost_home(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
elif self.maze.is_ghost_home(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
elif self.maze.is_ghost_home(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
elif self.maze.is_ghost_home(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 self.maze.is_wall(self.player.location[0] + 1, self.player.location[1]):
break
elif self.maze.is_ghost_home(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 self.maze.is_wall(self.player.location[0] - 1, self.player.location[1]):
break
elif self.maze.is_ghost_home(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 self.maze.is_wall(self.player.location[0], self.player.location[1] - 1):
break
elif self.maze.is_ghost_home(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 self.maze.is_wall(self.player.location[0], self.player.location[1] + 1):
break
elif self.maze.is_ghost_home(self.player.location[0], self.player.location[1] + 1):
break
self.player.direction = PlayerDirection.DirectionDown
self.player.move_down()
case pygame.USEREVENT:
if event.dict.get("type_name") == "JOYHATREPEAT":
match event.dict['value']:
case (1, 0):
if self.maze.is_wall(self.player.location[0] + 1, self.player.location[1]):
break
elif self.maze.is_ghost_home(self.player.location[0] + 1, self.player.location[1]):
break
self.player.direction = PlayerDirection.DirectionRight
self.player.move_right()
case (-1, 0):
if self.maze.is_wall(self.player.location[0] - 1, self.player.location[1]):
break
elif self.maze.is_ghost_home(self.player.location[0] - 1, self.player.location[1]):
break
self.player.direction = PlayerDirection.DirectionLeft
self.player.move_left()
case (0, 1):
if self.maze.is_wall(self.player.location[0], self.player.location[1] - 1):
break
elif self.maze.is_ghost_home(self.player.location[0], self.player.location[1] - 1):
break
self.player.direction = PlayerDirection.DirectionUp
self.player.move_up()
case (0, -1):
if self.maze.is_wall(self.player.location[0], self.player.location[1] + 1):
break
elif self.maze.is_ghost_home(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)
if self.joysticks:
self.joysticks[self.joy.get_instance_id()].controllers[0].rumble(25, 50)
self.rumble_timer = pygame.time.get_ticks()
case "power":
self.hud.add_points(50)
if self.joysticks:
self.joysticks[self.joy.get_instance_id()].controllers[0].rumble(255, 255)
self.rumble_timer = pygame.time.get_ticks()
self.ghost_mode_controller.trigger_frightened()
if __name__ == "__main__":
pman = Game()