Загрузка данных
import pygame, math, random
pygame.init()
W, H = 1000, 650
GROUND_Y = 590
screen = pygame.display.set_mode((W, H))
pygame.display.set_caption("Angry Birds - 10 Levels")
clock = pygame.time.Clock()
font = pygame.font.SysFont("arial", 28, bold=True)
font_big = pygame.font.SysFont("arial", 64, bold=True)
font_small = pygame.font.SysFont("arial", 20)
GRAVITY = 0.45
SLING_X, SLING_Y = 150, GROUND_Y - 80
MAX_DRAG = 110
BIRD_RADIUS = 15
SKY_TOP = (135, 206, 250)
SKY_BOTTOM = (200, 230, 255)
def gradient_sky():
surf = pygame.Surface((W, H))
for y in range(H):
t = y / H
r = SKY_TOP[0] + (SKY_BOTTOM[0]-SKY_TOP[0]) * t
g = SKY_TOP[1] + (SKY_BOTTOM[1]-SKY_TOP[1]) * t
b = SKY_TOP[2] + (SKY_BOTTOM[2]-SKY_TOP[2]) * t
pygame.draw.line(surf, (r, g, b), (0, y), (W, y))
return surf
sky = gradient_sky()
clouds = [(random.randint(0, W), random.randint(30, 200), random.randint(40, 70)) for _ in range(6)]
# ---------- ГЕНЕРАЦИЯ УРОВНЕЙ ----------
def generate_level(n):
random.seed(n * 17 + 5)
blocks = []
pigs = []
num_towers = 2 + n // 3
start_x = 550
gap = (W - 80 - start_x) / max(1, num_towers - 1) if num_towers > 1 else 0
for t in range(num_towers):
tower_x = start_x + gap * t if num_towers > 1 else start_x + 150
tower_h = random.randint(2, 2 + n // 2)
bw, bh = 42, 40
for level in range(tower_h):
by = GROUND_Y - bh - level * bh
blocks.append({"x": tower_x - bw/2, "y": by, "w": bw, "h": bh,
"hp": 30 + n * 4, "max_hp": 30 + n * 4, "vy": 0})
# свинка на верху башни (не всегда)
if random.random() < 0.85:
pigs.append({"x": tower_x, "y": GROUND_Y - tower_h * bh - 18, "r": 18, "alive": True})
# немного свинок прямо на земле между башнями
extra_pigs = n // 3
for _ in range(extra_pigs):
px = random.randint(int(start_x - 60), W - 100)
pigs.append({"x": px, "y": GROUND_Y - 18, "r": 18, "alive": True})
birds_count = max(3, 3 + len(pigs) - n // 2)
return {"blocks": blocks, "pigs": pigs, "birds": birds_count}
LEVELS = [generate_level(i) for i in range(10)]
def reset_level_state(level_index):
lvl_data = generate_level(level_index)
return {
"blocks": lvl_data["blocks"],
"pigs": lvl_data["pigs"],
"birds_left": lvl_data["birds"],
"birds_total": lvl_data["birds"],
}
class Bird:
def __init__(self):
self.x, self.y = SLING_X, SLING_Y
self.vx, self.vy = 0, 0
self.launched = False
self.trail = []
self.stopped_timer = 0
def launch(self, vx, vy):
self.vx, self.vy = vx, vy
self.launched = True
def update(self):
if not self.launched:
return
self.vy += GRAVITY
self.x += self.vx
self.y += self.vy
self.trail.append((self.x, self.y))
if len(self.trail) > 12:
self.trail.pop(0)
if self.y + BIRD_RADIUS > GROUND_Y:
self.y = GROUND_Y - BIRD_RADIUS
self.vy *= -0.35
self.vx *= 0.8
if abs(self.vy) < 1.5:
self.vy = 0
speed = math.hypot(self.vx, self.vy)
if speed < 0.6 and self.y >= GROUND_Y - BIRD_RADIUS - 2:
self.stopped_timer += 1
else:
self.stopped_timer = 0
def draw(self, surf):
for i, (tx, ty) in enumerate(self.trail):
a = int(120 * (i / len(self.trail)))
s = pygame.Surface((10, 10), pygame.SRCALPHA)
pygame.draw.circle(s, (255, 255, 255, a), (5, 5), 4)
surf.blit(s, (tx-5, ty-5))
pygame.draw.circle(surf, (200, 30, 30), (int(self.x), int(self.y)), BIRD_RADIUS)
pygame.draw.circle(surf, (150, 20, 20), (int(self.x), int(self.y)), BIRD_RADIUS, 2)
# клюв
pygame.draw.polygon(surf, (240, 180, 40), [
(self.x + BIRD_RADIUS, self.y),
(self.x + BIRD_RADIUS + 8, self.y - 4),
(self.x + BIRD_RADIUS + 8, self.y + 4)])
# глаз
pygame.draw.circle(surf, (255, 255, 255), (int(self.x + 4), int(self.y - 5)), 5)
pygame.draw.circle(surf, (0, 0, 0), (int(self.x + 5), int(self.y - 5)), 2)
def draw_block(surf, b):
if b["hp"] <= 0:
return
rect = pygame.Rect(b["x"], b["y"], b["w"], b["h"])
dmg_ratio = b["hp"] / b["max_hp"]
base_color = (150, 100, 60)
color = tuple(int(c * (0.5 + 0.5*dmg_ratio)) for c in base_color)
pygame.draw.rect(surf, color, rect)
pygame.draw.rect(surf, (90, 55, 30), rect, 2)
for i in range(1, 3):
yy = b["y"] + b["h"] * i / 3
pygame.draw.line(surf, (90, 55, 30), (b["x"], yy), (b["x"]+b["w"], yy), 1)
if dmg_ratio < 0.6:
pygame.draw.line(surf, (60, 35, 20), (b["x"]+4, b["y"]+4), (b["x"]+b["w"]-4, b["y"]+b["h"]-4), 1)
def draw_pig(surf, p):
if not p["alive"]:
return
x, y, r = p["x"], p["y"], p["r"]
pygame.draw.circle(surf, (110, 180, 70), (int(x), int(y)), r)
pygame.draw.circle(surf, (80, 140, 50), (int(x), int(y)), r, 2)
pygame.draw.circle(surf, (255, 255, 255), (int(x-6), int(y-4)), 5)
pygame.draw.circle(surf, (255, 255, 255), (int(x+6), int(y-4)), 5)
pygame.draw.circle(surf, (0, 0, 0), (int(x-6), int(y-3)), 2)
pygame.draw.circle(surf, (0, 0, 0), (int(x+6), int(y-3)), 2)
pygame.draw.ellipse(surf, (90, 160, 60), (x-6, y+3, 12, 8))
def circle_rect_collision(cx, cy, cr, rect):
closest_x = max(rect.x, min(cx, rect.x + rect.w))
closest_y = max(rect.y, min(cy, rect.y + rect.h))
dx, dy = cx - closest_x, cy - closest_y
dist = math.hypot(dx, dy)
if dist < cr:
if dist == 0:
dist = 0.01
dx, dy = 1, 0
return True, dx/dist, dy/dist, dist
return False, 0, 0, 0
current_level = 0
state = reset_level_state(current_level)
bird = Bird()
dragging = False
level_complete_timer = 0
level_failed = False
game_won = False
def spawn_new_bird():
global bird
bird = Bird()
running = True
while running:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.MOUSEBUTTONDOWN and not bird.launched and not level_failed and not game_won:
mx, my = event.pos
if math.hypot(mx - SLING_X, my - SLING_Y) < 60:
dragging = True
if event.type == pygame.MOUSEBUTTONUP and dragging:
dragging = False
mx, my = event.pos
dx, dy = SLING_X - mx, SLING_Y - my
dist = min(math.hypot(dx, dy), MAX_DRAG)
if dist > 8:
angle = math.atan2(dy, dx)
power = dist / MAX_DRAG * 24
bird.x, bird.y = SLING_X, SLING_Y
bird.launch(math.cos(angle) * power, math.sin(angle) * power)
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_r and level_failed:
state = reset_level_state(current_level)
spawn_new_bird()
level_failed = False
if event.key == pygame.K_r and game_won:
current_level = 0
state = reset_level_state(current_level)
spawn_new_bird()
game_won = False
mx, my = pygame.mouse.get_pos()
if not level_failed and not game_won:
if dragging:
dx, dy = mx - SLING_X, my - SLING_Y
dist = min(math.hypot(dx, dy), MAX_DRAG)
angle = math.atan2(dy, dx)
bird.x = SLING_X + math.cos(angle) * dist
bird.y = SLING_Y + math.sin(angle) * dist
else:
bird.update()
for b in state["blocks"]:
if b["hp"] <= 0:
continue
rect = pygame.Rect(b["x"], b["y"], b["w"], b["h"])
hit, nx, ny, dist = circle_rect_collision(bird.x, bird.y, BIRD_RADIUS, rect)
if hit and bird.launched:
speed = math.hypot(bird.vx, bird.vy)
b["hp"] -= speed * 4
bird.vx = nx * speed * 0.4
bird.vy = ny * speed * 0.4
overlap = BIRD_RADIUS - dist
bird.x += nx * overlap
bird.y += ny * overlap
for p in state["pigs"]:
if not p["alive"]:
continue
if math.hypot(bird.x - p["x"], bird.y - p["y"]) < BIRD_RADIUS + p["r"]:
p["alive"] = False
bird.vx *= 0.6
bird.vy *= 0.6
if bird.stopped_timer > 40 or bird.x > W + 50 or bird.x < -50:
state["birds_left"] -= 1
if all(not p["alive"] for p in state["pigs"]):
pass
elif state["birds_left"] <= 0:
level_failed = True
if not level_failed:
spawn_new_bird()
if all(not p["alive"] for p in state["pigs"]) and level_complete_timer == 0:
level_complete_timer = 1
if level_complete_timer > 0:
level_complete_timer += 1
if level_complete_timer > 90:
level_complete_timer = 0
current_level += 1
if current_level >= 10:
game_won = True
else:
state = reset_level_state(current_level)
spawn_new_bird()
# ---------- ОТРИСОВКА ----------
screen.blit(sky, (0, 0))
for (cx, cy, cr) in clouds:
pygame.draw.ellipse(screen, (255, 255, 255), (cx, cy, cr*2, cr))
pygame.draw.ellipse(screen, (255, 255, 255), (cx+cr*0.6, cy-8, cr*1.4, cr))
pygame.draw.rect(screen, (90, 160, 60), (0, GROUND_Y, W, H - GROUND_Y))
pygame.draw.rect(screen, (70, 130, 45), (0, GROUND_Y, W, 8))
# рогатка
pygame.draw.line(screen, (90, 60, 30), (SLING_X - 15, GROUND_Y), (SLING_X - 15, SLING_Y - 40), 8)
pygame.draw.line(screen, (90, 60, 30), (SLING_X + 15, GROUND_Y), (SLING_X + 15, SLING_Y - 30), 8)
if not bird.launched:
pygame.draw.line(screen, (60, 40, 20), (SLING_X - 15, SLING_Y - 40), (bird.x, bird.y), 4)
pygame.draw.line(screen, (60, 40, 20), (SLING_X + 15, SLING_Y - 30), (bird.x, bird.y), 4)
for b in state["blocks"]:
draw_block(screen, b)
for p in state["pigs"]:
draw_pig(screen, p)
bird.draw(screen)
if not bird.launched and not dragging:
pygame.draw.line(screen, (60, 40, 20), (SLING_X - 15, SLING_Y - 40), (bird.x, bird.y), 4)
pygame.draw.line(screen, (60, 40, 20), (SLING_X + 15, SLING_Y - 30), (bird.x, bird.y), 4)
# траектория прицеливания
if dragging:
dx, dy = SLING_X - bird.x, SLING_Y - bird.y
power = min(math.hypot(dx, dy), MAX_DRAG) / MAX_DRAG * 24
angle = math.atan2(dy, dx)
sx, sy = bird.x, bird.y
svx, svy = math.cos(angle)*power, math.sin(angle)*power
for i in range(18):
svy += GRAVITY
sx += svx
sy += svy
if i % 2 == 0:
pygame.draw.circle(screen, (255, 255, 255), (int(sx), int(sy)), 3)
# HUD
pigs_alive = sum(1 for p in state["pigs"] if p["alive"])
hud1 = font.render(f"Уровень {current_level + 1}/10", True, (40, 40, 40))
hud2 = font.render(f"Свиней осталось: {pigs_alive}", True, (40, 100, 40))
hud3 = font.render(f"Птиц: {state['birds_left']}", True, (150, 30, 30))
screen.blit(hud1, (20, 15))
screen.blit(hud2, (20, 50))
screen.blit(hud3, (20, 85))
for i in range(state["birds_left"]):
pygame.draw.circle(screen, (200, 30, 30), (W - 30 - i*35, 35), 13)
if level_complete_timer > 0:
overlay = pygame.Surface((W, H), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 120))
screen.blit(overlay, (0, 0))
text = font_big.render("УРОВЕНЬ ПРОЙДЕН!", True, (255, 220, 60))
screen.blit(text, (W//2 - text.get_width()//2, H//2 - 40))
if level_failed:
overlay = pygame.Surface((W, H), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 160))
screen.blit(overlay, (0, 0))
text = font_big.render("ПРОВАЛ", True, (255, 70, 70))
screen.blit(text, (W//2 - text.get_width()//2, H//2 - 60))
sub = font.render("Нажми R чтобы повторить уровень", True, (255, 255, 255))
screen.blit(sub, (W//2 - sub.get_width()//2, H//2 + 10))
if game_won:
overlay = pygame.Surface((W, H), pygame.SRCALPHA)
overlay.fill((0, 0, 0, 170))
screen.blit(overlay, (0, 0))
text = font_big.render("ВСЕ 10 УРОВНЕЙ ПРОЙДЕНЫ!", True, (100, 255, 130))
screen.blit(text, (W//2 - text.get_width()//2, H//2 - 60))
sub = font.render("Нажми R чтобы начать заново", True, (255, 255, 255))
screen.blit(sub, (W//2 - sub.get_width()//2, H//2 + 10))
pygame.display.flip()
pygame.quit()