Game/main.py

348 lines
14 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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