import pygame from pygame.locals import K_UP, K_DOWN, QUIT, KEYDOWN, K_r, K_q, K_1, K_2, K_3 import random # Инициализация pygame.init() # --- Настройки экрана --- SCREEN_WIDTH, SCREEN_HEIGHT = 1600, 600 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT)) pygame.display.set_caption("Canteen Rush: The Final Battle") clock = pygame.time.Clock() def load_s(path, size, alpha=True): try: img = pygame.image.load(path).convert_alpha() if alpha else pygame.image.load(path).convert() return pygame.transform.scale(img, size) except: s = pygame.Surface(size); s.fill((100, 100, 100)); return s # --- Загрузка ресурсов --- background_img = load_s('background1.png', (SCREEN_WIDTH, SCREEN_HEIGHT), False) enemy_img = load_s('Sprite-0001.png', (90, 90)) player_img = load_s('player1.jpg', (90, 90)) end_img = load_s('Sprite-0002.png', (300, 300)) povar_img = load_s('povar.png', (150, 150)) kotleta_img = load_s('kotleta.png', (60, 60)) demon_img = load_s('demon.png', (100, 100)) pizza_img = load_s('pizza.png', (65, 65)) pizza_kick_img = load_s('pizza_kick.png', (70, 70)) # --- Логика дорожек --- lane_height = 100 line_thickness = 4 total_height = 4 * line_thickness + 3 * lane_height start_y = (SCREEN_HEIGHT - total_height) // 2 lane_centers = [start_y + line_thickness + i * (lane_height + line_thickness) + lane_height // 2 for i in range(3)] class Player(pygame.sprite.Sprite): def __init__(self): super().__init__() self.image = player_img self.image.set_colorkey((255, 255, 255)) self.lane = 1 self.lives = 1 self.rect = self.image.get_rect(center=(200, lane_centers[self.lane])) def update(self): self.rect.centery = lane_centers[self.lane] class Enemy(pygame.sprite.Sprite): def __init__(self, speed, lane, x_offset=0): super().__init__() self.image = enemy_img self.image.set_colorkey((0, 0, 0)) self.speed = speed self.rect = self.image.get_rect(center=(SCREEN_WIDTH + 100 + x_offset, lane_centers[lane])) self.passed = False def update(self): self.rect.x -= self.speed if self.rect.right < 0: self.kill() class Kotleta(pygame.sprite.Sprite): def __init__(self): super().__init__() self.image = kotleta_img self.lane = random.randint(0, 2) self.rect = self.image.get_rect(center=(SCREEN_WIDTH - 50, lane_centers[self.lane])) self.timer = pygame.time.get_ticks() def update(self): self.rect.x -= 5 if pygame.time.get_ticks() - self.timer > 1500: self.lane = random.randint(0, 2); self.timer = pygame.time.get_ticks() self.rect.centery = lane_centers[self.lane] if self.rect.right < 0: self.kill() class Projectile(pygame.sprite.Sprite): def __init__(self, img, lane, speed, kickable=False): super().__init__() self.image = img self.target_y = lane_centers[lane] self.rect = self.image.get_rect(center=(SCREEN_WIDTH - 250, self.target_y - 40)) self.speed = speed self.kickable = kickable self.returned = False self.held = False self.v_speed = -4 def update(self): if self.held: return if self.returned: self.rect.x += 25 else: self.rect.x -= self.speed if self.rect.centery < self.target_y: self.rect.centery += self.v_speed; self.v_speed += 0.25 else: self.rect.centery = self.target_y if self.rect.right < 0 or self.rect.left > SCREEN_WIDTH: self.kill() class Boss(pygame.sprite.Sprite): def __init__(self): super().__init__() self.base_image = demon_img self.image = self.base_image self.lives = 15 self.lane = 1 self.rect = self.image.get_rect(center=(SCREEN_WIDTH - 200, lane_centers[self.lane])) self.dir = 1 self.move_timer = pygame.time.get_ticks() self.state = "NORMAL" self.move_delay = 2000 self.fire_rate_mod = 1.0 self.grow_start = 0 # Параметры анимации смерти self.death_size = 100 self.death_center_x = 0 self.death_center_y = 0 def update(self): now = pygame.time.get_ticks() if self.state == "NORMAL": if now - self.move_timer > self.move_delay: self.lane += self.dir if self.lane < 0 or self.lane > 2: self.dir *= -1 self.lane += self.dir * 2 self.move_timer = now self.image = self.base_image self.rect = self.image.get_rect(center=(SCREEN_WIDTH - 200, lane_centers[self.lane])) elif self.state == "GROWING": self.image = pygame.transform.scale(self.base_image, (250, 250)) self.rect = self.image.get_rect(center=(SCREEN_WIDTH - 200, lane_centers[self.lane])) if now - self.grow_start > 2000: self.state = "NORMAL" if self.lives <= 5: self.move_delay = 600 elif self.lives <= 10: self.move_delay = 1100 self.fire_rate_mod = max(0.5, self.fire_rate_mod - 0.15) self.rect = self.base_image.get_rect(center=(SCREEN_WIDTH - 200, lane_centers[self.lane])) elif self.state == "DEAD": # Увеличиваемся до 500px if self.death_size < 500: self.death_size = min(500, self.death_size + 8) else: # После достижения максимального размера — летим вправо self.death_center_x += 18 # Перерисовываем с текущим размером self.image = pygame.transform.scale(self.base_image, (int(self.death_size), int(self.death_size))) self.rect = self.image.get_rect(center=(int(self.death_center_x), int(self.death_center_y))) # Когда улетел за экран — уничтожаем спрайт if self.rect.left > SCREEN_WIDTH + 50: self.kill() def start_death(self): # Запускает анимацию смерти из текущей позиции босса. self.state = "DEAD" self.death_size = 100 self.death_center_x = float(self.rect.centerx) self.death_center_y = float(self.rect.centery) # --- Инициализация объектов --- player = Player() enemies = pygame.sprite.Group() projectiles = pygame.sprite.Group() kotleta_group = pygame.sprite.GroupSingle() boss_group = pygame.sprite.GroupSingle() victory_pizza = pygame.sprite.GroupSingle() score, speed, game_over, win = 0, 10, False, False boss_dying = False # Флаг: босс сейчас проигрывает анимацию смерти spawn_timer = pygame.time.get_ticks() p_timer = 0 k_timer = 0 font = pygame.font.Font(None, 40) def reset(): global score, speed, game_over, win, spawn_timer, boss_dying score, speed, game_over, win, boss_dying = 0, 10, False, False, False spawn_timer = pygame.time.get_ticks() player.lives, player.lane = 1, 1 enemies.empty(); projectiles.empty(); kotleta_group.empty(); boss_group.empty(); victory_pizza.empty() # --- Игровой цикл --- running = True while running: now = pygame.time.get_ticks() for event in pygame.event.get(): if event.type == QUIT: running = False if event.type == KEYDOWN: if not game_over and not win: if event.key == K_UP: player.lane = max(0, player.lane - 1) if event.key == K_DOWN: player.lane = min(2, player.lane + 1) t_lane = -1 if event.key == K_1: t_lane = 0 if event.key == K_2: t_lane = 1 if event.key == K_3: t_lane = 2 if t_lane != -1: for p in projectiles: if p.held: p.held = False; p.returned = True p.target_y = lane_centers[t_lane]; p.rect.centery = p.target_y else: if event.key == K_r: reset() if event.key == K_q: running = False if not game_over and not win: # Спавн обычных врагов if not boss_group and not boss_dying and score < 50: delay = random.randint(1100, 1700) if score > 15 else 1500 if now - spawn_timer > delay: lanes = random.sample(range(3), 2) if score > 15 else [random.randint(0, 2)] for i, l in enumerate(lanes): enemies.add(Enemy(speed, l, x_offset=i*random.randint(300, 500))) spawn_timer = now # Повариха с котлетой if (25 <= score <= 29 or score == 42) and not kotleta_group: kotleta_group.add(Kotleta()) # Появление босса if score >= 50 and not boss_group and not boss_dying and not win: boss_group.add(Boss()) enemies.empty(); kotleta_group.empty() if boss_group: b = boss_group.sprite # Стрельба босса только в состоянии NORMAL if b.state == "NORMAL": if now - p_timer > 1400 * b.fire_rate_mod: projectiles.add(Projectile(pizza_img, random.randint(0, 2), 12)) p_timer = now if now - k_timer > 2800 * b.fire_rate_mod: projectiles.add(Projectile(pizza_kick_img, random.randint(0, 2), 9, True)) k_timer = now # Урон боссу от брошенных снарядов for p in list(projectiles): if p.returned and b.rect.colliderect(p.rect): b.lives -= 1 p.kill() if b.lives in [10, 5]: b.state = "GROWING" b.grow_start = now projectiles.empty() elif b.lives <= 0: # Запускаем анимацию смерти b.start_death() boss_dying = True projectiles.empty() break # Прекращаем проверку остальных снарядов # Обновление всех объектов player.update() enemies.update() projectiles.update() kotleta_group.update() boss_group.update() # Если босс умирал и спрайт уже уничтожен — победа if boss_dying and not boss_group: win = True boss_dying = False # Коллизии с игроком (только если босс НЕ в режиме смерти) if pygame.sprite.spritecollide(player, enemies, True): player.lives -= 1 if pygame.sprite.spritecollide(player, kotleta_group, True): player.lives += 1 if boss_group and boss_group.sprite.state != "DEAD": for p in list(projectiles): if player.rect.colliderect(p.rect) and not p.returned: if p.kickable: p.held = True; p.rect.center = player.rect.center else: player.lives -= 1; p.kill() elif p.held and not player.rect.colliderect(p.rect): p.kill() elif not boss_group: for p in list(projectiles): if player.rect.colliderect(p.rect) and not p.returned: if p.kickable: p.held = True; p.rect.center = player.rect.center else: player.lives -= 1; p.kill() elif p.held and not player.rect.colliderect(p.rect): p.kill() # Начисление очков for e in enemies: if not e.passed and e.rect.right < player.rect.left: e.passed = True; score += 1 if score % 5 == 0 and speed < 35: speed += 1 if player.lives <= 0: game_over = True # --- Отрисовка --- screen.blit(background_img, (0, 0)) for i in range(4): pygame.draw.rect(screen, (0, 0, 0), (0, start_y + i*(lane_height+line_thickness), SCREEN_WIDTH, line_thickness)) if kotleta_group: screen.blit(povar_img, (SCREEN_WIDTH // 2 - 75, start_y - 145)) enemies.draw(screen) projectiles.draw(screen) kotleta_group.draw(screen) boss_group.draw(screen) victory_pizza.draw(screen) screen.blit(player.image, player.rect) # Интерфейс screen.blit(font.render(f"Score: {score}", True, (0,0,0)), (130, 20)) screen.blit(font.render(f"Speed: {speed}", True, (0,0,0)), (450, 20)) screen.blit(font.render(f"Lives: {player.lives}", True, (0,0,0)), (850, 20)) if boss_group: b = boss_group.sprite hp_text = f"BOSS HP: {max(0, b.lives)}" screen.blit(font.render(hp_text, True, (0,0,0)), (SCREEN_WIDTH - 300, 20)) # Экраны окончания if game_over: overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA) overlay.fill((0, 0, 0, 200)); screen.blit(overlay, (0,0)) screen.blit(end_img, (SCREEN_WIDTH//2 - 150, 50)) msg = pygame.font.Font(None, 100).render("SPĒLE BEIGUSIES", True, (255, 50, 50)) screen.blit(msg, msg.get_rect(center=(SCREEN_WIDTH//2, 400))) hint = font.render("R — vēlreiz | Q — padoties", True, (200, 200, 200)) screen.blit(hint, hint.get_rect(center=(SCREEN_WIDTH//2, 500))) if win: msg = pygame.font.Font(None, 100).render("UZVARA! PICA IR TAVA!", True, (255, 200, 0)) screen.blit(msg, msg.get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2 - 40))) hint = font.render("R — vēlreiz | Q — iziet", True, (200, 200, 200)) screen.blit(hint, hint.get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2 + 60))) pygame.display.flip() clock.tick(60) pygame.quit()