Added HUD with lives and level indicators. /JL
This commit is contained in:
BIN
assets/cherry.png
Normal file
BIN
assets/cherry.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
85
hud.py
Normal file
85
hud.py
Normal file
@@ -0,0 +1,85 @@
|
||||
import pygame
|
||||
import os
|
||||
import math
|
||||
from actors.enums import Colors
|
||||
from actors.pacman import ActorPacman
|
||||
|
||||
class HUD:
|
||||
def __init__(self, screen_width, screen_height, font_size=24, highscore_file="highscore.txt", initial_lives=3, cherry_image=None):
|
||||
self.score = 0
|
||||
self.lives = initial_lives
|
||||
self.p_man = []
|
||||
self.highscore_file = highscore_file
|
||||
self.highscore = self.load_highscore()
|
||||
|
||||
self.font = pygame.font.Font(None, font_size)
|
||||
self.color = Colors.WHITE.value
|
||||
self.screen_width = screen_width
|
||||
self.screen_height = screen_height
|
||||
|
||||
self.padding = 10
|
||||
self.life_radius = 10
|
||||
self.life_spacing = 30
|
||||
|
||||
self.cherry_image = pygame.transform.scale(cherry_image, (24, 24)) if cherry_image else None
|
||||
|
||||
def load_highscore(self):
|
||||
if os.path.exists(self.highscore_file):
|
||||
with open(self.highscore_file, 'r') as file:
|
||||
try:
|
||||
return int(file.read())
|
||||
except ValueError:
|
||||
return 0
|
||||
return 0
|
||||
|
||||
def save_highscore(self):
|
||||
with open(self.highscore_file, 'w') as file:
|
||||
file.write(str(self.highscore))
|
||||
|
||||
def add_points(self, points):
|
||||
self.score += points
|
||||
if self.score > self.highscore:
|
||||
self.highscore = self.score
|
||||
self.save_highscore()
|
||||
|
||||
def lose_life(self):
|
||||
self.lives = max(0, self.lives - 1)
|
||||
|
||||
def reset(self, reset_score=True, reset_lives=True):
|
||||
if reset_score:
|
||||
self.score = 0
|
||||
if reset_lives:
|
||||
self.lives = 3
|
||||
|
||||
def draw_pacman_icon(self, screen, center):
|
||||
start_angle = math.radians(30)
|
||||
end_angle = math.radians(330)
|
||||
rect = pygame.Rect(0, 0, self.life_radius * 2, self.life_radius * 2)
|
||||
rect.center = center
|
||||
# Mouth: draw arc first, then overlay circle to mask
|
||||
pygame.draw.arc(screen, (255, 255, 0), rect, start_angle, end_angle, self.life_radius)
|
||||
pygame.draw.circle(screen, (255, 255, 0), center, self.life_radius)
|
||||
|
||||
def draw(self, screen):
|
||||
# Score (top-left)
|
||||
score_text = self.font.render(f"Score: {self.score}", True, self.color)
|
||||
screen.blit(score_text, (self.padding, self.padding))
|
||||
|
||||
# Highscore (top-center)
|
||||
highscore_text = self.font.render(f"High Score: {self.highscore}", True, self.color)
|
||||
highscore_rect = highscore_text.get_rect(midtop=(self.screen_width // 2, self.padding))
|
||||
screen.blit(highscore_text, highscore_rect)
|
||||
|
||||
# Lives (bottom-left)
|
||||
for i in range(self.lives):
|
||||
x = self.padding + i * self.life_spacing
|
||||
y = self.screen_height - self.padding - self.life_radius
|
||||
self.p_man.append(ActorPacman(screen, (x + self.life_radius, y)))
|
||||
self.p_man[i].draw()
|
||||
#self.draw_pacman_icon(screen, (x + self.life_radius, y))
|
||||
|
||||
# Level indicator (bottom-right)
|
||||
if self.cherry_image:
|
||||
cherry_x = self.screen_width - self.padding - self.cherry_image.get_width()
|
||||
cherry_y = self.screen_height - self.padding - self.cherry_image.get_height()
|
||||
screen.blit(self.cherry_image, (cherry_x, cherry_y))
|
92
labyrinth.py
92
labyrinth.py
@@ -1,92 +0,0 @@
|
||||
from actors.enums import Colors
|
||||
import pygame
|
||||
import math
|
||||
|
||||
class PivotWall:
|
||||
def __init__(self, screen, center, length=40, thickness=8, angle=0):
|
||||
self.screen = screen
|
||||
self.center = center
|
||||
self.length = length
|
||||
self.thickness = thickness
|
||||
self.angle = angle
|
||||
|
||||
def draw(self):
|
||||
x, y = self.center
|
||||
angle_rad = math.radians(self.angle)
|
||||
dx = math.cos(angle_rad) * self.length / 2
|
||||
dy = math.sin(angle_rad) * self.length / 2
|
||||
|
||||
points = [
|
||||
(x - dx - dy * self.thickness / self.length, y - dy + dx * self.thickness / self.length),
|
||||
(x - dx + dy * self.thickness / self.length, y - dy - dx * self.thickness / self.length),
|
||||
(x + dx + dy * self.thickness / self.length, y + dy - dx * self.thickness / self.length),
|
||||
(x + dx - dy * self.thickness / self.length, y + dy + dx * self.thickness / self.length),
|
||||
]
|
||||
|
||||
pygame.draw.polygon(self.screen, Colors.Cyan.value, points)
|
||||
|
||||
def rotate(self, delta_angle):
|
||||
self.angle = (self.angle + delta_angle) % 360
|
||||
|
||||
class GhostHome:
|
||||
def __init__(self, screen, center, width=100, height=60, wall_thickness=8):
|
||||
self.screen = screen
|
||||
self.center = center
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.wall_thickness = wall_thickness
|
||||
cx, cy = self.center
|
||||
self.rect = pygame.Rect(cx - width // 2, cy - height // 2, width, height)
|
||||
|
||||
def draw(self):
|
||||
r = self.rect
|
||||
t = self.wall_thickness
|
||||
|
||||
pygame.draw.rect(self.screen, Colors.Magenta.value, (r.left, r.top + t, t, r.height -t))
|
||||
pygame.draw.rect(self.screen, Colors.Magenta.value, (r.right - t, r.top + t, t, r.height -t))
|
||||
pygame.draw.rect(self.screen, Colors.Magenta.value, (r.left, r.bottom - t, r.width, t))
|
||||
|
||||
gap = 40
|
||||
gap_x1 = r.centerx - gap // 2
|
||||
gap_x2 = r.centerx + gap // 2
|
||||
pygame.draw.rect(self.screen, Colors.Magenta.value, (r.left, r.top, gap_x1 - r.left, t))
|
||||
pygame.draw.rect(self.screen, Colors.Magenta.value, (gap_x2, r.top, r.right - gap_x2, t))
|
||||
|
||||
class Labyrinth:
|
||||
def __init__(self, screen, width, height, wall_thickness=20):
|
||||
self.screen = screen
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.wall_thickness = wall_thickness
|
||||
offset = 50
|
||||
self.pivot_walls = [
|
||||
PivotWall(self.screen, (offset, offset)),
|
||||
PivotWall(self.screen, (width - offset, offset)),
|
||||
PivotWall(self.screen, (offset - height, offset)),
|
||||
PivotWall(self.screen, (width - offset, height - offset))
|
||||
]
|
||||
self.ghost_home = GhostHome(self.screen, center=(width // 2, height // 2))
|
||||
|
||||
def draw(self):
|
||||
w = self.wall_thickness
|
||||
mid_x = self.width // 2
|
||||
mid_y = self.height // 2
|
||||
|
||||
pygame.draw.rect(self.screen, Colors.Blue.value, (0, 0, mid_x - 50, w))
|
||||
pygame.draw.rect(self.screen, Colors.Blue.value, (mid_x + 50, 0, self.width - (mid_x + 50), w))
|
||||
pygame.draw.rect(self.screen, Colors.Blue.value, (0, self.height - w, mid_x - 50, w))
|
||||
pygame.draw.rect(self.screen, Colors.Blue.value, (mid_x + 50, self.height - w, self.width - (mid_x + 50), w))
|
||||
|
||||
pygame.draw.rect(self.screen, Colors.Blue.value, (0, 0, w, mid_y -50))
|
||||
pygame.draw.rect(self.screen, Colors.Blue.value, (0, mid_y + 50, w, self.height - (mid_y + 50)))
|
||||
pygame.draw.rect(self.screen, Colors.Blue.value, (self.width - w, 0, w, mid_y - 50))
|
||||
pygame.draw.rect(self.screen, Colors.Blue.value, (self.width - w, mid_y + 50, w, self.height - (mid_y + 50)))
|
||||
|
||||
for pivot in self.pivot_walls:
|
||||
pivot.draw()
|
||||
|
||||
self.ghost_home.draw()
|
||||
|
||||
def rotate_all_pivots(self, angle):
|
||||
for pivot in self.pivot_walls:
|
||||
pivot.rotate(angle)
|
8
maze.py
8
maze.py
@@ -35,21 +35,21 @@ class Maze:
|
||||
elif char == "*":
|
||||
self.power_pellets.add((x, y))
|
||||
elif char == "P":
|
||||
self.pacman_start = (x * TILE_SIZE + TILE_SIZE // 2, y * TILE_SIZE + TILE_SIZE // 2)
|
||||
self.pacman_start = (x * TILE_SIZE + TILE_SIZE // 2, y * TILE_SIZE + TILE_SIZE // 2 + 32)
|
||||
|
||||
def draw(self, screen):
|
||||
for y, row in enumerate(self.layout):
|
||||
for x, char in enumerate(row):
|
||||
rect = pygame.Rect(x * TILE_SIZE, y * TILE_SIZE, TILE_SIZE, TILE_SIZE)
|
||||
rect = pygame.Rect(x * TILE_SIZE, y * TILE_SIZE + 32, TILE_SIZE, TILE_SIZE)
|
||||
if char == "W":
|
||||
pygame.draw.rect(screen, COLOR_WALL, rect)
|
||||
# Draw dots
|
||||
for (x, y) in self.dots:
|
||||
center = (x * TILE_SIZE + TILE_SIZE // 2, y * TILE_SIZE + TILE_SIZE // 2)
|
||||
center = (x * TILE_SIZE + TILE_SIZE // 2, y * TILE_SIZE + TILE_SIZE // 2 + 32)
|
||||
pygame.draw.circle(screen, COLOR_DOT, center, 2)
|
||||
# Draw power pellets
|
||||
for (x, y) in self.power_pellets:
|
||||
center = (x * TILE_SIZE + TILE_SIZE // 2, y * TILE_SIZE + TILE_SIZE // 2)
|
||||
center = (x * TILE_SIZE + TILE_SIZE // 2, y * TILE_SIZE + TILE_SIZE // 2 + 32)
|
||||
pygame.draw.circle(screen, COLOR_POWER, center, 5)
|
||||
|
||||
def tile_at(self, x, y):
|
||||
|
40
pman.py
40
pman.py
@@ -1,13 +1,12 @@
|
||||
import pygame
|
||||
from actors.enums import Colors, PlayerDirection, PillType
|
||||
from actors.pacman import ActorPacman
|
||||
from labyrinth import Labyrinth
|
||||
from actors.ghost import Blinky, Pinky, Inky, Clyde # adjust import path as needed
|
||||
from actors.ghost_mode_controller import GhostModeController
|
||||
from scoreboard import Scoreboard
|
||||
from hud import HUD
|
||||
from maze import Maze
|
||||
|
||||
__version__ = "0.2.3"
|
||||
__version__ = "0.2.4"
|
||||
|
||||
|
||||
def spawn_ghosts(center_position):
|
||||
@@ -29,43 +28,19 @@ def spawn_ghosts(center_position):
|
||||
|
||||
return pygame.sprite.Group(blinky, pinky, inky, clyde)
|
||||
|
||||
def place_pills(maze, spacing=16):
|
||||
pills = pygame.sprite.Group()
|
||||
height = len(maze)
|
||||
width = len(maze[0])
|
||||
|
||||
# Normal pills
|
||||
for y in range(height):
|
||||
for x in range(width):
|
||||
if maze[y][x] == 0: # assuming 0 is path
|
||||
if x % spacing == 0 and y % spacing == 0:
|
||||
pills.add(PillType.PillTypeRegular(x * spacing + spacing // 2, y * spacing + spacing // 2))
|
||||
|
||||
# Power pills in 4 corners
|
||||
corners = [
|
||||
(1, 1),
|
||||
(1, width - 2),
|
||||
(height - 2, 1),
|
||||
(height - 2, width - 2)
|
||||
]
|
||||
for cy, cx in corners:
|
||||
pills.add(PillType.PillTypePower(cx * spacing + spacing // 2, cy * spacing + spacing // 2))
|
||||
|
||||
return pills
|
||||
|
||||
def main() -> None:
|
||||
pygame.init()
|
||||
screen_width, screen_height = (32*29), (32*30)
|
||||
screen_width, screen_height = (32*29), (32*30)+32
|
||||
screen = pygame.display.set_mode((screen_width, screen_height))
|
||||
pygame.display.set_caption("Pac-Man " + __version__)
|
||||
|
||||
scoreboard = Scoreboard()
|
||||
#labyrinth = Labyrinth(screen, width=screen_width, height=screen_height)
|
||||
cherry_img = pygame.image.load("assets/cherry.png").convert_alpha()
|
||||
hud = HUD(screen_width, screen_height, cherry_image=cherry_img)
|
||||
maze = Maze("maze/pacman_maze.txt")
|
||||
|
||||
player = ActorPacman(screen, center=maze.pacman_start)
|
||||
ghost_mode_controller = GhostModeController()
|
||||
ghost_home_center = (screen_width // 2, screen_height // 2)
|
||||
ghost_home_center = (screen_width // 2, (screen_height) // 2)
|
||||
ghosts = spawn_ghosts(ghost_home_center)
|
||||
clock = pygame.time.Clock()
|
||||
|
||||
@@ -99,8 +74,8 @@ def main() -> None:
|
||||
elif hat_y == -1:
|
||||
player.direction = PlayerDirection.DirectionDown
|
||||
|
||||
#labyrinth.draw()
|
||||
# In your main game loop:
|
||||
hud.draw(screen)
|
||||
maze.draw(screen)
|
||||
player.animate()
|
||||
player.draw()
|
||||
@@ -112,7 +87,6 @@ def main() -> None:
|
||||
#ghost.update(maze, pacman)
|
||||
|
||||
ghosts.draw(screen)
|
||||
scoreboard.draw(screen)
|
||||
pygame.display.flip()
|
||||
clock.tick(60)
|
||||
|
||||
|
@@ -1,42 +0,0 @@
|
||||
import pygame
|
||||
import os
|
||||
from actors.enums import Colors
|
||||
|
||||
class Scoreboard:
|
||||
def __init__(self, font_size=24, highscore_file="highscore.txt"):
|
||||
self.score = 0
|
||||
self.highscore_file = highscore_file
|
||||
self.highscore = self.load_highscore()
|
||||
|
||||
self.font = pygame.font.Font(None, font_size)
|
||||
self.color = Colors.WHITE.value
|
||||
self.score_pos = (10, 10)
|
||||
self.highscore_pos = (10, 40)
|
||||
|
||||
def load_highscore(self):
|
||||
if os.path.exists(self.highscore_file):
|
||||
with open(self.highscore_file, 'r') as file:
|
||||
try:
|
||||
return int(file.read())
|
||||
except ValueError:
|
||||
return 0
|
||||
return 0
|
||||
|
||||
def save_highscore(self):
|
||||
with open(self.highscore_file, 'w') as file:
|
||||
file.write(str(self.highscore))
|
||||
|
||||
def add_points(self, points):
|
||||
self.score += points
|
||||
if self.score > self.highscore:
|
||||
self.highscore = self.score
|
||||
self.save_highscore()
|
||||
|
||||
def reset(self):
|
||||
self.score = 0
|
||||
|
||||
def draw(self, screen):
|
||||
score_text = self.font.render(f"Score: {self.score}", True, self.color)
|
||||
highscore_text = self.font.render(f"High Score: {self.highscore}", True, self.color)
|
||||
screen.blit(score_text, self.score_pos)
|
||||
screen.blit(highscore_text, self.highscore_pos)
|
Reference in New Issue
Block a user