Added HUD with lives and level indicators. /JL

This commit is contained in:
2025-04-17 22:29:00 +02:00
parent 21870930ef
commit ffa7ba0db3
6 changed files with 96 additions and 171 deletions

BIN
assets/cherry.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

85
hud.py Normal file
View 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))

View File

@@ -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)

View File

@@ -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
View File

@@ -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)

View File

@@ -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)