Game/main.py

316 lines
13 KiB
Python
Raw 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 — это Y-координаты центров трёх дорожек
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
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"
# Ускоряем движение в зависимости от HP при выходе из фазы роста
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":
self.image = pygame.transform.scale(self.base_image, (450, 450))
self.rect.x += 12; self.rect.y -= 12
if self.rect.bottom < 0: self.kill()
# --- Инициализация объектов ---
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
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
score, speed, game_over, win = 0, 10, 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 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 win:
boss_group.add(Boss())
enemies.empty(); kotleta_group.empty()
if boss_group:
b = boss_group.sprite
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 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()
if b.lives <= 0:
b.state = "DEAD"; projectiles.empty()
# Проверка победы
if boss_group and boss_group.sprite.state == "DEAD":
pass # Ждем пока улетит
elif score >= 50 and not boss_group and not win:
win = True
vp = pygame.sprite.Sprite()
vp.image = load_s('pizza.png', (450, 450))
vp.rect = vp.image.get_rect(center=(SCREEN_WIDTH//2, SCREEN_HEIGHT//2))
victory_pizza.add(vp)
# Обновление всех объектов
player.update()
enemies.update()
projectiles.update()
kotleta_group.update()
boss_group.update()
victory_pizza.update()
# Коллизии
if pygame.sprite.spritecollide(player, enemies, True): player.lives -= 1
if pygame.sprite.spritecollide(player, kotleta_group, True): player.lives += 1
for p in 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
screen.blit(font.render(f"BOSS HP: {b.lives}", 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("ИГРА ОКОНЧЕНА", True, (255, 50, 50))
screen.blit(msg, msg.get_rect(center=(SCREEN_WIDTH//2, 400)))
if win:
msg = pygame.font.Font(None, 100).render("ПОБЕДА! СУПЕР ПИЦЦА ТВОЯ!", True, (255, 200, 0))
screen.blit(msg, msg.get_rect(center=(SCREEN_WIDTH//2, 100)))
pygame.display.flip()
clock.tick(60)
pygame.quit()