|
| 1 | +import pygame |
| 2 | +import random |
| 3 | +import sys |
| 4 | + |
| 5 | +# Initialize |
| 6 | +pygame.init() |
| 7 | +WIDTH, HEIGHT = 600, 800 |
| 8 | +screen = pygame.display.set_mode((WIDTH, HEIGHT)) |
| 9 | +pygame.display.set_caption("Brick") |
| 10 | +clock = pygame.time.Clock() |
| 11 | + |
| 12 | +font = pygame.font.SysFont("Arial", 48) |
| 13 | +small_font = pygame.font.SysFont("Arial", 24) |
| 14 | + |
| 15 | +WHITE = (255, 255, 255) |
| 16 | +RED = (255, 0, 0) |
| 17 | +BLACK = (0, 0, 0) |
| 18 | +GRAY = (100, 100, 100) |
| 19 | +BRICK_COLORS = [(255, 99, 71), (173, 216, 230), (144, 238, 144), (255, 215, 0), (221, 160, 221)] |
| 20 | + |
| 21 | +def draw_vertical_gradient(surface, top_color, bottom_color): |
| 22 | + height = surface.get_height() |
| 23 | + for y in range(height): |
| 24 | + ratio = y / height |
| 25 | + r = int(top_color[0] * (1 - ratio) + bottom_color[0] * ratio) |
| 26 | + g = int(top_color[1] * (1 - ratio) + bottom_color[1] * ratio) |
| 27 | + b = int(top_color[2] * (1 - ratio) + bottom_color[2] * ratio) |
| 28 | + pygame.draw.line(surface, (r, g, b), (0, y), (surface.get_width(), y)) |
| 29 | + |
| 30 | +# Ball skins |
| 31 | +ball_skins = { |
| 32 | + "default": None, |
| 33 | + "tennis": pygame.image.load("tennis.png"), |
| 34 | + "golf": pygame.image.load("golf-ball.png"), |
| 35 | + "mirror": pygame.image.load("mirror-ball.png") |
| 36 | +} |
| 37 | +for key in ball_skins: |
| 38 | + if ball_skins[key]: |
| 39 | + ball_skins[key] = pygame.transform.scale(ball_skins[key], (20, 20)) |
| 40 | +selected_skin = "default" |
| 41 | + |
| 42 | +# Powerup icons |
| 43 | +powerup_icons = { |
| 44 | + 'W': pygame.image.load("widen.png"), |
| 45 | + 'N': pygame.image.load("narrow.png") |
| 46 | +} |
| 47 | +for key in powerup_icons: |
| 48 | + powerup_icons[key] = pygame.transform.scale(powerup_icons[key], (20, 20)) |
| 49 | + |
| 50 | +class Particle: |
| 51 | + def __init__(self, x, y, color): |
| 52 | + self.x = x |
| 53 | + self.y = y |
| 54 | + self.vx = random.uniform(-2, 2) |
| 55 | + self.vy = random.uniform(-2, 2) |
| 56 | + self.color = color |
| 57 | + self.lifetime = 30 |
| 58 | + self.size = 4 |
| 59 | + |
| 60 | + def update(self): |
| 61 | + self.x += self.vx |
| 62 | + self.y += self.vy |
| 63 | + self.lifetime -= 1 |
| 64 | + |
| 65 | + def draw(self, surface): |
| 66 | + if self.lifetime > 0: |
| 67 | + alpha = max(0, int(255 * (self.lifetime / 30))) |
| 68 | + surf = pygame.Surface((self.size, self.size), pygame.SRCALPHA) |
| 69 | + surf.fill((*self.color, alpha)) |
| 70 | + surface.blit(surf, (self.x, self.y)) |
| 71 | + |
| 72 | +def draw_button(rect, text): |
| 73 | + pygame.draw.rect(screen, GRAY, rect) |
| 74 | + pygame.draw.rect(screen, WHITE, rect, 2) |
| 75 | + text_surface = small_font.render(text, True, WHITE) |
| 76 | + screen.blit(text_surface, (rect.x + rect.width // 2 - text_surface.get_width() // 2, rect.y + rect.height // 2 - text_surface.get_height() // 2)) |
| 77 | + |
| 78 | +def show_main_menu(): |
| 79 | + global selected_skin |
| 80 | + while True: |
| 81 | + screen.fill(BLACK) |
| 82 | + title = font.render("Brick", True, WHITE) |
| 83 | + screen.blit(title, (WIDTH // 2 - title.get_width() // 2, HEIGHT // 2 - 150)) |
| 84 | + start_button = pygame.Rect(WIDTH // 2 - 75, HEIGHT // 2 - 30, 150, 50) |
| 85 | + exit_button = pygame.Rect(WIDTH // 2 - 75, HEIGHT // 2 + 40, 150, 50) |
| 86 | + skin_button = pygame.Rect(WIDTH // 2 - 250, HEIGHT // 2 - 30, 150, 50) |
| 87 | + draw_button(skin_button, "Ball Skins") |
| 88 | + draw_button(start_button, "Start") |
| 89 | + draw_button(exit_button, "Exit") |
| 90 | + pygame.display.flip() |
| 91 | + for event in pygame.event.get(): |
| 92 | + if event.type == pygame.QUIT: |
| 93 | + pygame.quit() |
| 94 | + sys.exit() |
| 95 | + elif event.type == pygame.MOUSEBUTTONDOWN: |
| 96 | + if skin_button.collidepoint(event.pos): |
| 97 | + show_skin_menu() |
| 98 | + elif start_button.collidepoint(event.pos): |
| 99 | + return |
| 100 | + elif exit_button.collidepoint(event.pos): |
| 101 | + pygame.quit() |
| 102 | + sys.exit() |
| 103 | + |
| 104 | +def show_skin_menu(): |
| 105 | + global selected_skin |
| 106 | + while True: |
| 107 | + screen.fill(BLACK) |
| 108 | + title = font.render("Choose Skin", True, WHITE) |
| 109 | + screen.blit(title, (WIDTH // 2 - title.get_width() // 2, 100)) |
| 110 | + skins = [("default", "Default"), ("tennis", "Tennis"), ("golf", "Golf Ball"), ("mirror", "Mirror Ball")] |
| 111 | + buttons = [] |
| 112 | + for i, (key, label) in enumerate(skins): |
| 113 | + btn_rect = pygame.Rect(WIDTH // 2 - 100, 200 + i * 70, 200, 50) |
| 114 | + buttons.append((btn_rect, key)) |
| 115 | + draw_button(btn_rect, label) |
| 116 | + back_button = pygame.Rect(WIDTH // 2 - 75, HEIGHT - 100, 150, 50) |
| 117 | + draw_button(back_button, "Back") |
| 118 | + pygame.display.flip() |
| 119 | + for event in pygame.event.get(): |
| 120 | + if event.type == pygame.QUIT: |
| 121 | + pygame.quit() |
| 122 | + sys.exit() |
| 123 | + elif event.type == pygame.MOUSEBUTTONDOWN: |
| 124 | + for btn_rect, key in buttons: |
| 125 | + if btn_rect.collidepoint(event.pos): |
| 126 | + selected_skin = key |
| 127 | + return |
| 128 | + if back_button.collidepoint(event.pos): |
| 129 | + return |
| 130 | + |
| 131 | +def show_pause_menu(): |
| 132 | + while True: |
| 133 | + screen.fill(BLACK) |
| 134 | + title = font.render("Paused", True, WHITE) |
| 135 | + screen.blit(title, (WIDTH // 2 - title.get_width() // 2, HEIGHT // 2 - 150)) |
| 136 | + resume_button = pygame.Rect(WIDTH // 2 - 75, HEIGHT // 2 - 30, 150, 50) |
| 137 | + menu_button = pygame.Rect(WIDTH // 2 - 75, HEIGHT // 2 + 40, 150, 50) |
| 138 | + exit_button = pygame.Rect(WIDTH // 2 - 75, HEIGHT // 2 + 110, 150, 50) |
| 139 | + draw_button(resume_button, "Resume") |
| 140 | + draw_button(menu_button, "Quit to Menu") |
| 141 | + draw_button(exit_button, "Exit") |
| 142 | + pygame.display.flip() |
| 143 | + for event in pygame.event.get(): |
| 144 | + if event.type == pygame.QUIT: |
| 145 | + pygame.quit() |
| 146 | + sys.exit() |
| 147 | + elif event.type == pygame.MOUSEBUTTONDOWN: |
| 148 | + if resume_button.collidepoint(event.pos): |
| 149 | + return None |
| 150 | + elif menu_button.collidepoint(event.pos): |
| 151 | + return "menu" |
| 152 | + elif exit_button.collidepoint(event.pos): |
| 153 | + pygame.quit() |
| 154 | + sys.exit() |
| 155 | + |
| 156 | +def reset_game(level=1): |
| 157 | + paddle = pygame.Rect(WIDTH // 2 - 80, HEIGHT - 20, 120, 10) |
| 158 | + ball = pygame.Rect(WIDTH // 2 - 10, HEIGHT // 2 - 10, 20, 20) |
| 159 | + speed = 5 + (level - 1) * 0.5 |
| 160 | + ball_speed = [speed, -speed] |
| 161 | + rows = min(5 + level, 10) |
| 162 | + bricks, brick_colors, explosive_indices = [], [], set() |
| 163 | + for i in range(10): |
| 164 | + for j in range(rows): |
| 165 | + idx = len(bricks) |
| 166 | + rect = pygame.Rect(5 + i * 60, 40 + j * 30, 50, 20) |
| 167 | + bricks.append(rect) |
| 168 | + if random.random() < 0.1: |
| 169 | + explosive_indices.add(idx) |
| 170 | + brick_colors.append(RED) # Red for explosive bricks |
| 171 | + else: |
| 172 | + brick_colors.append(random.choice(BRICK_COLORS)) |
| 173 | + |
| 174 | + return paddle, ball, ball_speed, bricks, brick_colors, explosive_indices, [], None, 0, [] |
| 175 | + |
| 176 | +def spawn_powerup(brick_rect): |
| 177 | + if random.random() < 0.3: |
| 178 | + kind = random.choice(['W', 'N']) |
| 179 | + return {'rect': pygame.Rect(brick_rect.centerx, brick_rect.centery, 20, 20), 'type': kind} |
| 180 | + return None |
| 181 | + |
| 182 | +def show_game_over_screen(): |
| 183 | + while True: |
| 184 | + screen.fill(BLACK) |
| 185 | + title = font.render("Game Over", True, WHITE) |
| 186 | + screen.blit(title, (WIDTH // 2 - title.get_width() // 2, HEIGHT // 2 - 120)) |
| 187 | + restart_button = pygame.Rect(WIDTH // 2 - 75, HEIGHT // 2 - 20, 150, 50) |
| 188 | + exit_button = pygame.Rect(WIDTH // 2 - 75, HEIGHT // 2 + 60, 150, 50) |
| 189 | + draw_button(restart_button, "Restart") |
| 190 | + draw_button(exit_button, "Exit") |
| 191 | + pygame.display.flip() |
| 192 | + for event in pygame.event.get(): |
| 193 | + if event.type == pygame.QUIT: |
| 194 | + pygame.quit() |
| 195 | + sys.exit() |
| 196 | + elif event.type == pygame.MOUSEBUTTONDOWN: |
| 197 | + if restart_button.collidepoint(event.pos): |
| 198 | + return "restart" |
| 199 | + elif exit_button.collidepoint(event.pos): |
| 200 | + pygame.quit() |
| 201 | + sys.exit() |
| 202 | + |
| 203 | +def game_loop(): |
| 204 | + level = 1 |
| 205 | + while True: |
| 206 | + show_main_menu() |
| 207 | + level = 1 |
| 208 | + paddle, ball, ball_speed, bricks, brick_colors, explosive_indices, powerups, active_powerup, powerup_timer, particles = reset_game(level) |
| 209 | + running = True |
| 210 | + while running: |
| 211 | + if level == 1: |
| 212 | + draw_vertical_gradient(screen, (0, 0, 50), (0, 0, 0)) # Level 1 gradient |
| 213 | + elif level == 2: |
| 214 | + draw_vertical_gradient(screen, (50, 0, 0), (0, 0, 50)) # Level 2 gradient |
| 215 | + elif level == 3: |
| 216 | + draw_vertical_gradient(screen, (0, 50, 0), (0, 0, 50)) # Level 3 gradient |
| 217 | + # Add more levels with their respective backgrounds as needed |
| 218 | + for event in pygame.event.get(): |
| 219 | + if event.type == pygame.QUIT: |
| 220 | + pygame.quit() |
| 221 | + sys.exit() |
| 222 | + elif event.type == pygame.KEYDOWN: |
| 223 | + if event.key == pygame.K_ESCAPE: |
| 224 | + result = show_pause_menu() |
| 225 | + if result == "menu": |
| 226 | + running = False |
| 227 | + keys = pygame.key.get_pressed() |
| 228 | + if keys[pygame.K_a] and paddle.left > 0: |
| 229 | + paddle.move_ip(-8, 0) |
| 230 | + if keys[pygame.K_d] and paddle.right < WIDTH: |
| 231 | + paddle.move_ip(8, 0) |
| 232 | + ball.move_ip(*ball_speed) |
| 233 | + if ball.left <= 0 or ball.right >= WIDTH: |
| 234 | + ball_speed[0] *= -1 |
| 235 | + if ball.top <= 0: |
| 236 | + ball_speed[1] *= -1 |
| 237 | + if ball.bottom >= HEIGHT: |
| 238 | + if show_game_over_screen() == "restart": |
| 239 | + paddle, ball, ball_speed, bricks, brick_colors, explosive_indices, powerups, active_powerup, powerup_timer, particles = reset_game(level) |
| 240 | + continue |
| 241 | + else: |
| 242 | + pygame.quit() |
| 243 | + sys.exit() |
| 244 | + if ball.colliderect(paddle) and ball_speed[1] > 0: |
| 245 | + ball.bottom = paddle.top |
| 246 | + offset = (ball.centerx - paddle.centerx) / (paddle.width / 2) |
| 247 | + ball_speed[0] = 5 * offset |
| 248 | + ball_speed[1] = -abs(ball_speed[1]) |
| 249 | + hit_index = ball.collidelist(bricks) |
| 250 | + if hit_index != -1: |
| 251 | + brick_rect = bricks.pop(hit_index) |
| 252 | + brick_color = brick_colors.pop(hit_index) |
| 253 | + if hit_index in explosive_indices: |
| 254 | + explosive_indices.remove(hit_index) |
| 255 | + center_x, center_y = brick_rect.center |
| 256 | + explosion_radius = 70 |
| 257 | + for i in reversed(range(len(bricks))): |
| 258 | + if bricks[i].collidepoint(center_x, center_y) or bricks[i].colliderect(pygame.Rect(center_x - explosion_radius, center_y - explosion_radius, explosion_radius * 2, explosion_radius * 2)): |
| 259 | + for _ in range(10): |
| 260 | + particles.append(Particle(bricks[i].centerx, bricks[i].centery, brick_colors[i])) |
| 261 | + bricks.pop(i) |
| 262 | + brick_colors.pop(i) |
| 263 | + explosive_indices.discard(i) |
| 264 | + else: |
| 265 | + ball_speed[1] *= -1 |
| 266 | + for _ in range(10): |
| 267 | + particles.append(Particle(brick_rect.centerx, brick_rect.centery, brick_color)) |
| 268 | + p = spawn_powerup(brick_rect) |
| 269 | + if p: |
| 270 | + powerups.append(p) |
| 271 | + for p in powerups[:]: |
| 272 | + p['rect'].y += 4 |
| 273 | + if p['rect'].colliderect(paddle): |
| 274 | + if p['type'] == 'W': |
| 275 | + paddle.width = 180 |
| 276 | + elif p['type'] == 'N': |
| 277 | + paddle.width = 60 |
| 278 | + active_powerup = p['type'] |
| 279 | + powerup_timer = pygame.time.get_ticks() |
| 280 | + powerups.remove(p) |
| 281 | + elif p['rect'].top > HEIGHT: |
| 282 | + powerups.remove(p) |
| 283 | + if active_powerup and pygame.time.get_ticks() - powerup_timer > 10000: |
| 284 | + paddle.width = 120 |
| 285 | + active_powerup = None |
| 286 | + if not bricks: |
| 287 | + level += 1 |
| 288 | + pygame.time.delay(1000) |
| 289 | + paddle, ball, ball_speed, bricks, brick_colors, explosive_indices, powerups, active_powerup, powerup_timer, particles = reset_game(level) |
| 290 | + for particle in particles[:]: |
| 291 | + particle.update() |
| 292 | + if particle.lifetime <= 0: |
| 293 | + particles.remove(particle) |
| 294 | + for particle in particles: |
| 295 | + particle.draw(screen) |
| 296 | + pygame.draw.rect(screen, WHITE, paddle) |
| 297 | + if selected_skin == "default" or ball_skins[selected_skin] is None: |
| 298 | + pygame.draw.ellipse(screen, WHITE, ball) |
| 299 | + else: |
| 300 | + screen.blit(ball_skins[selected_skin], ball.topleft) |
| 301 | + for i, brick in enumerate(bricks): |
| 302 | + pygame.draw.rect(screen, brick_colors[i], brick) |
| 303 | + for p in powerups: |
| 304 | + screen.blit(powerup_icons[p['type']], p['rect'].topleft) |
| 305 | + level_text = small_font.render(f"Level: {level}", True, WHITE) |
| 306 | + screen.blit(level_text, (10, 10)) |
| 307 | + pygame.display.flip() |
| 308 | + clock.tick(60) |
| 309 | + |
| 310 | +game_loop() |
0 commit comments