diff --git a/background.png b/background.png new file mode 100644 index 0000000..53f4212 Binary files /dev/null and b/background.png differ diff --git a/button.png b/button.png new file mode 100644 index 0000000..0675cb6 Binary files /dev/null and b/button.png differ diff --git a/button_hover.png b/button_hover.png new file mode 100644 index 0000000..45187d8 Binary files /dev/null and b/button_hover.png differ diff --git a/main.py b/main.py index 6324704..6b9c0d3 100644 --- a/main.py +++ b/main.py @@ -11,6 +11,8 @@ font = pygame.font.SysFont(None, 36) big_font = pygame.font.SysFont(None, 64) small_font = pygame.font.SysFont(None, 26) +tiny_font = pygame.font.SysFont(None, 20) + MENU_OPTIONS = ["Play", "Quit"] MODE_NAMES = ["easy", "medium", "hard"] @@ -33,7 +35,7 @@ class ChaserEnemy: self.rect.y = random.randint(0, HEIGHT - 36) if abs(self.rect.x - avoid_rect.x) > 150 or abs(self.rect.y - avoid_rect.y) > 150: break - self.speed = 2 + self.speed = 1 self.spawn_timer = 60 def update(self, target): @@ -46,6 +48,10 @@ class ChaserEnemy: elif self.rect.centery > target.centery: self.rect.y -= self.speed class DasherEnemy: + WAIT_BEFORE_FIRST_DASH = 90 + DASH_FRAMES = 30 + WAIT_AFTER_DASH = 120 + DASH_SPEED = 6 def __init__(self, avoid_rect): self.rect = pygame.Rect(0, 0, 36, 36) while True: @@ -58,29 +64,36 @@ class DasherEnemy: self.vx = 0 self.vy = 0 self.state = "wait" - self.timer = 90 + self.timer = self.WAIT_BEFORE_FIRST_DASH self.spawn_timer = 60 def update(self, target): self.timer -= 1 + if self.state == "wait": + if self.timer > 0: + return dx = target.centerx - self.rect.centerx dy = target.centery - self.rect.centery dist = max(1, (dx ** 2 + dy ** 2) ** 0.5) - self.vx = dx / dist * 9 - self.vy = dy / dist * 9 + self.vx = dx / dist * self.DASH_SPEED + self.vy = dy / dist * self.DASH_SPEED self.state = "dash" - self.timer = 30 - else: - self.x += self.vx - self.y += self.vy - self.rect.x = int(self.x) - self.rect.y = int(self.y) - self.rect.clamp_ip(screen.get_rect()) - self.x, self.y = float(self.rect.x), float(self.rect.y) - if self.timer <= 0: - self.state = "wait" - self.timer = 90 + self.timer = self.DASH_FRAMES + return + + self.x += self.vx + self.y += self.vy + self.rect.x = int(self.x) + self.rect.y = int(self.y) + self.rect.clamp_ip(screen.get_rect()) + self.x, self.y = float(self.rect.x), float(self.rect.y) + + if self.timer <= 0: + self.state = "wait" + self.vx = 0 + self.vy = 0 + self.timer = self.WAIT_AFTER_DASH class BouncerEnemy: def __init__(self, avoid_rect): self.rect = pygame.Rect(0, 0, 36, 36) @@ -119,6 +132,12 @@ Chaser_img = pygame.transform.scale(Chaser_img, (30, 45)) Dasher_img = pygame.image.load("Dasher.png").convert_alpha() Dasher_img = pygame.transform.scale(Dasher_img, (30, 45)) +ENEMY_IMAGES = { + "Bouncer": Bouncer_img, + "Dasher": Dasher_img, + "Chaser": Chaser_img +} + sugar_img = pygame.image.load("honey.png").convert_alpha() sugar_img = pygame.transform.scale(sugar_img, (50, 50)) @@ -127,6 +146,19 @@ sugar_img = pygame.transform.scale(sugar_img, (50, 50)) heart_img = pygame.image.load("Heart.png").convert_alpha() heart_img = pygame.transform.scale(heart_img,(40, 40)) +background_img = pygame.image.load("background.png").convert_alpha() +background_img = pygame.transform.scale(background_img, (WIDTH, HEIGHT)) + +title_img = pygame.image.load("titles.png").convert_alpha() + +button_img = pygame.image.load("button.png").convert_alpha() +button_hover_img = pygame.image.load("button_hover.png").convert_alpha() + +quit_img = pygame.image.load("quit.png").convert_alpha() +quit_hover_img = pygame.image.load("quit_hover.png").convert_alpha() + +panel_img = pygame.image.load("panel.png").convert_alpha() + def new_sugar(): return pygame.Rect(random.randint(0, WIDTH - 20), @@ -152,69 +184,225 @@ def game_screen(mode): if event.type == pygame.QUIT: return "quit", score + if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE: + return "menu", score + + #movement - keys = pygame.key.get_pressed() - if keys[pygame.K_LEFT] or keys[pygame.K_a]: - player.x -= speed - if keys[pygame.K_RIGHT] or keys[pygame.K_d]: - player.x += speed - if keys[pygame.K_UP] or keys[pygame.K_w]: - player.y -= speed - if keys[pygame.K_DOWN] or keys[pygame.K_s]: - player.y += speed + keys = pygame.key.get_pressed() + if keys[pygame.K_LEFT] or keys[pygame.K_a]: + player.x -= speed + if keys[pygame.K_RIGHT] or keys[pygame.K_d]: + player.x += speed + if keys[pygame.K_UP] or keys[pygame.K_w]: + player.y -= speed + if keys[pygame.K_DOWN] or keys[pygame.K_s]: + player.y += speed - for enemy in Enemies: - if enemy.spawn_timer > 0: - enemy.spawn_timer -= 1 - else: - enemy.update(player) - if hit_timer > 0: - hit_timer -= 1 + for enemy in Enemies: + if enemy.spawn_timer > 0: + enemy.spawn_timer -= 1 else: - for enemy in Enemies: - if enemy.spawn_timer <= 0 and player.colliderect(enemy.rect): - lives -= 1 - hit_timer = 90 - break - if lives <= 0: - return "gameover", score + enemy.update(player) + if hit_timer > 0: + hit_timer -= 1 + else: + for enemy in Enemies: + if enemy.spawn_timer <= 0 and player.colliderect(enemy.rect): + lives -= 1 + hit_timer = 90 + break + if lives <= 0: + return "gameover", score #keep player in - player.clamp_ip(screen.get_rect()) + player.clamp_ip(screen.get_rect()) # score - if player.colliderect(sugar): - score += 1 - sugar = new_sugar() - if score % cfg["spawn_every"] == 0 and cfg["enemy_pool"]: - enemy_name = random.choice(cfg["enemy_pool"]) - Enemies.append(ENEMY_CLASSES[enemy_name](player)) + if player.colliderect(sugar): + score += 1 + sugar = new_sugar() + if score % cfg["spawn_every"] == 0 and cfg["enemy_pool"]: + enemy_name = random.choice(cfg["enemy_pool"]) + enemy = ENEMY_CLASSES[enemy_name](player) + enemy.image = ENEMY_IMAGES[enemy_name] + Enemies.append(enemy) - screen.fill((255, 228, 240)) # screen - screen.blit(sugar_img, sugar) #sugar - screen.blit(player_img, player) #player - if hit_timer % 10 < 5: - screen.blit(player_img, player) - text = font.render(f"score: {score}", True, (90, 30, 60)) - screen.blit(text, (10, 10)) - for enemy in Enemies: - if enemy.spawn_timer % 10 < 5: - screen.blit(Bouncer_img, enemy.rect) - screen.blit(player_img, player) - if hit_timer > 0 and hit_timer % 10 < 5: - red = player_img.copy() - red.fill((255, 40, 40, 0), special_flags=pygame.BLEND_RGBA_ADD) - screen.blit(red, player) - for i in range(lives): - screen.blit(heart_img, (10 + i * 35, 40)) - pygame.display.flip() - clock.tick(60) -pygame.quit() + screen.fill((255, 228, 240)) # screen + screen.blit(sugar_img, sugar) #sugar + text = font.render(f"score: {score}", True, (90, 30, 60)) + screen.blit(text, (10, 10)) + for enemy in Enemies: + if enemy.spawn_timer % 10 < 5: + screen.blit(enemy.image, enemy.image.get_rect(center=enemy.rect.center)) + screen.blit(player_img, player) + if hit_timer > 0 and hit_timer % 10 < 5: + red = player_img.copy() + red.fill((255, 40, 40, 0), special_flags=pygame.BLEND_RGBA_ADD) + screen.blit(red, player) + for i in range(lives): + screen.blit(heart_img, (10 + i * 35, 40)) + pygame.display.flip() + clock.tick(60) + + + +class MenuButton: + def __init__(self, center, action, image, hover_image): + self.action = action + self.image = image + self.hover_image = hover_image + self.rect = self.image.get_rect(center = center) + + def hovered(self, mouse_pos): + return self.rect.collidepoint(mouse_pos) + + def draw(self, surface, highlighted): + picture = self.hover_image if highlighted else self.image + surface.blit(picture, self.rect) def menu_screen(): selected = 0 + + buttons = [ + MenuButton((WIDTH // 2, 250), "mode", button_img, button_hover_img), + MenuButton((WIDTH // 2, 340), "quit", quit_img, quit_hover_img), + ] + + while True: - for + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return "quit" + + if event.type == pygame.KEYDOWN: + if event.key in (pygame.K_UP, pygame.K_w): + selected = (selected - 1) % len(buttons) + elif event.key in (pygame.K_DOWN, pygame.K_s): + selected = (selected + 1) % len(buttons) + elif event.key == pygame.K_RETURN: + if selected == -1: + selected = 0 + return buttons [selected].action + elif event.key == pygame.K_ESCAPE: + return "quit" + + if event.type == pygame.MOUSEMOTION: + selected = -1 + for i, button in enumerate(buttons): + if button.hovered(event.pos): + selected = i + + if event.type == pygame.WINDOWLEAVE: + selected = -1 + + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + for button in buttons: + if button.hovered(event.pos): + return button.action + + screen.blit(background_img, (0, 0)) + screen.blit(title_img, title_img.get_rect(center=(WIDTH // 2, 80))) + + for i, button in enumerate(buttons): + button.draw(screen, i == selected) + pygame.display.flip() + clock.tick(60) + +def mode_screen(): + selected = 0 + hovered = -1 + + play_button = MenuButton((210, 405), "game", button_img, button_hover_img) + back_button = MenuButton((430, 405), "menu", quit_img, quit_hover_img) + buttons = [play_button, back_button] + + panel_rect = panel_img.get_rect(center=(WIDTH // 2, 225)) + slot_rects = [ + pygame.Rect(panel_rect.x + 22, panel_rect.y + 22, 78, 68), + pygame.Rect(panel_rect.x + 133, panel_rect.y + 22, 77, 68), + pygame.Rect(panel_rect.x + 234, panel_rect.y + 28, 73, 66), + ] + + while True: + for event in pygame.event.get(): + if event.type == pygame.QUIT: + return "quit", None + + if event.type == pygame.KEYDOWN: + if event.key in (pygame.K_LEFT, pygame.K_a): + selected = (selected - 1) % len(MODE_NAMES) + elif event.key in (pygame.K_RIGHT, pygame.K_d): + selected = (selected + 1) % len(MODE_NAMES) + elif event.key == pygame.K_RETURN: + return "game", MODE_NAMES[selected] + elif event.key == pygame.K_ESCAPE: + return "menu", None + + if event.type == pygame.MOUSEMOTION: + hovered = -1 + for i, button in enumerate(buttons): + if button.hovered(event.pos): + hovered = i + + if event.type == pygame.WINDOWLEAVE: + hovered = -1 + + if event.type == pygame.MOUSEBUTTONDOWN and event.button == 1: + for i, slot in enumerate(slot_rects): + if slot.collidepoint(event.pos): + selected = i + for button in buttons: + if button.hovered(event.pos): + if button.action == "game": + return "game", MODE_NAMES[selected] + return "menu", None + + screen.blit(background_img, (0, 0)) + screen.blit(title_img, title_img.get_rect(center=(WIDTH // 2, 70))) + + pygame.draw.rect(screen, (255, 129, 195), panel_rect) + screen.blit(panel_img, panel_rect) + + for i, slot in enumerate(slot_rects): + fill = (255, 200, 225) if i == selected else (227, 89, 161) + pygame.draw.rect(screen, fill, slot.inflate (-4, -4), border_radius=6) + + for i, slot in enumerate(slot_rects): + name = small_font.render(MODE_NAMES[i].upper(), True, (255, 240, 8)) + screen.blit(name, name.get_rect(center=slot.center)) + + cfg = MODES[MODE_NAMES[selected]] + first_sentence, second_sentence = cfg["desc"].split(". ", 1) + line1 = tiny_font.render(first_sentence + ".", True, (255, 240, 8)) + line2 = tiny_font.render(second_sentence, True, (255, 240, 8)) + screen.blit(line1, line1.get_rect(center=(panel_rect.centerx, panel_rect.top + 108))) + screen.blit(line2, line2.get_rect(center=(panel_rect.centerx, panel_rect.top + 132))) + + for i, button in enumerate(buttons): + button.draw(screen, i == hovered) + + pygame.display.flip() + clock.tick(60) + + +if __name__ == "__main__": + state = "menu" + mode = None + score = 0 + + while state != "quit": + if state == "menu": + state = menu_screen() + elif state == "mode": + state, mode = mode_screen() + elif state == "game": + state, score = game_screen(mode) + elif state == "gameover": + print(f"Game Over - score: {score}") + state = "menu" + + pygame.quit() diff --git a/panel.png b/panel.png new file mode 100644 index 0000000..d4c4cbb Binary files /dev/null and b/panel.png differ diff --git a/quit.png b/quit.png new file mode 100644 index 0000000..57287e2 Binary files /dev/null and b/quit.png differ diff --git a/quit_hover.png b/quit_hover.png new file mode 100644 index 0000000..56a6307 Binary files /dev/null and b/quit_hover.png differ diff --git a/titles.png b/titles.png new file mode 100644 index 0000000..a68534a Binary files /dev/null and b/titles.png differ