Загрузка данных


import math
import random
import sys
import pygame

# =============================================================================
# НАСТРОЙКИ И ЦВЕТА КИБЕРПАНКА
# =============================================================================
SCREEN_WIDTH = 1000
SCREEN_HEIGHT = 700
FPS = 60

COLOR_BG = (10, 10, 26)
COLOR_GRID = (0, 220, 255)
COLOR_NEON_PINK = (255, 20, 147)
COLOR_NEON_YELLOW = (255, 230, 0)
COLOR_WHITE = (245, 245, 255)

# =============================================================================
# ЧАСТИЦЫ И ЭФФЕКТЫ
# =============================================================================
class Particle:
    def __init__(self, x, y, vx, vy, color, size, life):
        self.x = float(x)
        self.y = float(y)
        self.vx = float(vx)
        self.vy = float(vy)
        self.color = color
        self.size = float(size)
        self.life = float(life)
        self.max_life = float(life)

    def update(self, dt):
        self.x += self.vx * (dt * 60.0)
        self.y += self.vy * (dt * 60.0)
        self.life -= 1.0 * (dt * 60.0)

    def draw(self, surface):
        if self.life <= 0:
            return
        alpha_scale = self.life / self.max_life
        cur_size = max(1, int(self.size * alpha_scale))
        pygame.draw.circle(surface, self.color, (int(self.x), int(self.y)), cur_size)

# =============================================================================
# ТРАФИК И ПРЕПЯТСТВИЯ
# =============================================================================
class TrafficCar:
    def __init__(self, lane, speed, color):
        self.lane = lane              # Индекс полосы: 0, 1, 2, 3
        self.lane_x = 0.0
        self.y = -120.0
        self.speed = speed
        self.width = 54
        self.height = 96
        self.color = color
        self.passed = False

    def update(self, dt, player_speed, lane_positions):
        self.lane_x = lane_positions[self.lane]
        # Движение относительно игрока
        rel_speed = player_speed - self.speed
        self.y += rel_speed * dt

    def get_rect(self):
        return pygame.Rect(int(self.lane_x - self.width // 2), int(self.y), self.width, self.height)

    def draw(self, surface):
        rx = int(self.lane_x - self.width // 2)
        ry = int(self.y)

        # Неоновое свечение (тень)
        glow_rect = pygame.Rect(rx - 4, ry - 4, self.width + 8, self.height + 8)
        pygame.draw.rect(surface, (self.color[0] // 3, self.color[1] // 3, self.color[2] // 3), glow_rect, border_radius=8)

        # Корпус
        pygame.draw.rect(surface, self.color, (rx, ry, self.width, self.height), border_radius=6)

        # Стекла
        pygame.draw.rect(surface, (20, 25, 40), (rx + 8, ry + 24, self.width - 16, 44), border_radius=4)

        # Фары спереди (светят вперед) и габариты сзади
        pygame.draw.rect(surface, (255, 255, 200), (rx + 6, ry + 2, 10, 6))
        pygame.draw.rect(surface, (255, 255, 200), (rx + self.width - 16, ry + 2, 10, 6))
        pygame.draw.rect(surface, (255, 0, 50), (rx + 6, ry + self.height - 8, 12, 6))
        pygame.draw.rect(surface, (255, 0, 50), (rx + self.width - 18, ry + self.height - 8, 12, 6))

# =============================================================================
# ГОРОДСКИЕ НЕОНОВЫЕ БАШНИ ПО БОКАМ
# =============================================================================
class Building:
    def __init__(self, x, y, w, h, is_left):
        self.x = x
        self.y = y
        self.w = w
        self.h = h
        self.is_left = is_left
        self.color = random.choice([(25, 20, 45), (15, 30, 50), (35, 15, 45)])
        self.edge_color = random.choice([COLOR_GRID, COLOR_NEON_PINK])

    def update(self, dt, speed):
        self.y += speed * dt
        if self.y > SCREEN_HEIGHT + 100:
            self.y = -self.h - random.randint(20, 80)

    def draw(self, surface):
        pygame.draw.rect(surface, self.color, (self.x, int(self.y), self.w, self.h))
        # Неоновые грани
        line_x = self.x + self.w if self.is_left else self.x
        pygame.draw.line(surface, self.edge_color, (line_x, int(self.y)), (line_x, int(self.y + self.h)), 2)

# =============================================================================
# ОСНОВНОЙ КЛАСС ИГРЫ
# =============================================================================
class CyberHighwayRacer:
    def __init__(self):
        pygame.init()
        pygame.font.init()
        self.screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
        pygame.display.set_caption("Cyber Highway: Neon Drift")
        self.clock = pygame.time.Clock()

        self.font_big = pygame.font.SysFont("impact", 54)
        self.font_mid = pygame.font.SysFont("impact", 32)
        self.font_ui = pygame.font.SysFont("monospace", 18, bold=True)

        # Конфигурация шоссе
        self.road_left = 260
        self.road_right = 740
        self.road_width = self.road_right - self.road_left
        self.lanes_count = 4
        self.lane_step = self.road_width // self.lanes_count
        self.lane_x = [self.road_left + self.lane_step // 2 + i * self.lane_step for i in range(self.lanes_count)]

        # Игрок
        self.player_x = float(self.lane_x[1])
        self.player_y = float(SCREEN_HEIGHT - 150)
        self.player_w = 50
        self.player_h = 92
        self.player_vx = 0.0
        self.speed = 400.0
        self.base_speed = 550.0
        self.max_speed = 1050.0
        self.nitro = 100.0
        self.nitro_active = False

        # Игровые показатели
        self.score = 0
        self.near_miss_streak = 0
        self.distance = 0.0
        self.game_over = False
        self.road_offset = 0.0

        # Мир
        self.traffic = []
        self.particles = []
        self.buildings = self._spawn_city()
        self.spawn_timer = 0.0

    def _spawn_city(self):
        b_list = []
        for i in range(7):
            # Левая сторона
            b_list.append(Building(random.randint(10, 40), i * 160, random.randint(140, 200), random.randint(100, 150), True))
            # Правая сторона
            b_list.append(Building(SCREEN_WIDTH - random.randint(180, 240), i * 160, random.randint(140, 200), random.randint(100, 150), False))
        return b_list

    def spawn_traffic_car(self):
        occupied_lanes = [c.lane for c in self.traffic if c.y < 150]
        free_lanes = [idx for idx in range(self.lanes_count) if idx not in occupied_lanes]
        if not free_lanes:
            return

        lane = random.choice(free_lanes)
        spd = random.uniform(180.0, 320.0)
        cols = [
            (255, 60, 0), (0, 255, 200), (255, 200, 0),
            (180, 50, 255), (0, 150, 255)
        ]
        self.traffic.append(TrafficCar(lane, spd, random.choice(cols)))

    def handle_input(self, dt):
        if self.game_over:
            return

        keys = pygame.key.get_pressed()

        # Руление
        target_vx = 0.0
        if keys[pygame.K_LEFT] or keys[pygame.K_a]:
            target_vx -= 650.0
        if keys[pygame.K_RIGHT] or keys[pygame.K_d]:
            target_vx += 650.0

        # Плавная смена полосы (инерция)
        self.player_vx += (target_vx - self.player_vx) * 12.0 * dt
        self.player_x += self.player_vx * dt

        # Ограничения дороги
        self.player_x = max(self.road_left + 25, min(self.road_right - 25, self.player_x))

        # Нитро и скорость
        self.nitro_active = (keys[pygame.K_UP] or keys[pygame.K_w] or keys[pygame.K_SPACE]) and self.nitro > 0
        if self.nitro_active:
            self.speed += 600.0 * dt
            self.nitro = max(0.0, self.nitro - 28.0 * dt)
            # Огонь из выхлопа
            for _ in range(2):
                self.particles.append(Particle(
                    self.player_x + random.uniform(-14, 14),
                    self.player_y + self.player_h,
                    random.uniform(-15, 15), random.uniform(80, 160),
                    COLOR_GRID if random.random() < 0.5 else COLOR_NEON_PINK,
                    random.randint(4, 7), 18
                ))
        elif keys[pygame.K_DOWN] or keys[pygame.K_s]:
            self.speed -= 800.0 * dt
            # Регенерация нитро при торможении
            self.nitro = min(100.0, self.nitro + 15.0 * dt)
        else:
            self.speed += (self.base_speed - self.speed) * 3.0 * dt
            self.nitro = min(100.0, self.nitro + 8.0 * dt)

        self.speed = max(200.0, min(self.max_speed, self.speed))

    def update(self, dt):
        if self.game_over:
            return

        self.handle_input(dt)
        self.distance += (self.speed * dt) * 0.05
        self.score += int(self.speed * dt * 0.2)
        self.road_offset = (self.road_offset + self.speed * dt) % 80

        # Фон города
        for b in self.buildings:
            b.update(dt, self.speed * 0.6)

        # Генерация соперников
        self.spawn_timer += dt
        spawn_rate = max(0.45, 1.2 - (self.speed / self.max_speed) * 0.5)
        if self.spawn_timer >= spawn_rate:
            self.spawn_traffic_car()
            self.spawn_timer = 0.0

        p_rect = pygame.Rect(int(self.player_x - self.player_w // 2), int(self.player_y), self.player_w, self.player_h)

        # Обновление трафика
        for car in self.traffic[:]:
            car.update(dt, self.speed, self.lane_x)

            c_rect = car.get_rect()

            # Столкновение
            if p_rect.colliderect(c_rect):
                self.game_over = True
                for _ in range(45):
                    self.particles.append(Particle(
                        self.player_x, self.player_y + 40,
                        random.uniform(-200, 200), random.uniform(-200, 200),
                        COLOR_NEON_YELLOW if random.random() < 0.5 else COLOR_NEON_PINK,
                        random.randint(5, 12), 40
                    ))

            # Опасный обгон (Near Miss Bonus)
            if not car.passed and car.y > self.player_y:
                dist = abs(car.lane_x - self.player_x)
                if dist < 85:
                    self.near_miss_streak += 1
                    bonus = 150 * self.near_miss_streak
                    self.score += bonus
                    self.nitro = min(100.0, self.nitro + 12.0)
                car.passed = True

            # Удаление уехавших
            if car.y > SCREEN_HEIGHT + 150:
                self.traffic.remove(car)

        # Частицы
        for p in self.particles[:]:
            p.update(dt)
            if p.life <= 0:
                self.particles.remove(p)

    def draw_road(self):
        # Асфальт
        pygame.draw.rect(self.screen, (16, 16, 28), (self.road_left, 0, self.road_width, SCREEN_HEIGHT))

        # Неоновые отбойники
        glow_color = COLOR_NEON_PINK if self.nitro_active else COLOR_GRID
        pygame.draw.line(self.screen, glow_color, (self.road_left, 0), (self.road_left, SCREEN_HEIGHT), 5)
        pygame.draw.line(self.screen, glow_color, (self.road_right, 0), (self.road_right, SCREEN_HEIGHT), 5)

        # Бегущие неоновые разделительные полосы
        for lane_idx in range(1, self.lanes_count):
            lx = self.road_left + lane_idx * self.lane_step
            y = -80 + self.road_offset
            while y < SCREEN_HEIGHT:
                pygame.draw.line(self.screen, (0, 180, 220), (lx, int(y)), (lx, int(y + 40)), 3)
                y += 80

    def draw_player(self):
        px = int(self.player_x - self.player_w // 2)
        py = int(self.player_y)

        # Наклон при маневре
        tilt = int(self.player_vx * 0.015)

        # Неоновое подсвечивание днища
        underglow = COLOR_GRID if not self.nitro_active else (255, 0, 120)
        pygame.draw.ellipse(self.screen, underglow, (px - 10, py + 10, self.player_w + 20, self.player_h))

        # Корпус киберкара
        car_points = [
            (px + 8 + tilt, py),
            (px + self.player_w - 8 + tilt, py),
            (px + self.player_w, py + self.player_h),
            (px, py + self.player_h)
        ]
        pygame.draw.polygon(self.screen, (230, 235, 245), car_points)

        # Кабина
        windshield = [
            (px + 12 + tilt, py + 22),
            (px + self.player_w - 12 + tilt, py + 22),
            (px + self.player_w - 10, py + 55),
            (px + 10, py + 55)
        ]
        pygame.draw.polygon(self.screen, (20, 20, 30), windshield)

        # Неоновые полосы на кузове
        pygame.draw.line(self.screen, COLOR_NEON_PINK, (px + self.player_w // 2 + tilt, py + 4), (px + self.player_w // 2, py + self.player_h - 4), 3)

        # Задние фонари
        pygame.draw.rect(self.screen, (255, 20, 40), (px + 4, py + self.player_h - 6, 14, 5))
        pygame.draw.rect(self.screen, (255, 20, 40), (px + self.player_w - 18, py + self.player_h - 6, 14, 5))

    def render(self):
        self.screen.fill(COLOR_BG)

        # Башни города
        for b in self.buildings:
            b.draw(self.screen)

        self.draw_road()

        # Трафик
        for car in self.traffic:
            car.draw(self.screen)

        # Частицы
        for p in self.particles:
            p.draw(self.screen)

        if not self.game_over:
            self.draw_player()

        # HUD / Интерфейс
        kmh = int(self.speed * 0.38)
        spd_text = self.font_big.render(f"{kmh}", True, COLOR_NEON_YELLOW if self.nitro_active else COLOR_WHITE)
        unit_text = self.font_ui.render("KM/H", True, (160, 160, 180))
        score_text = self.font_mid.render(f"SCORE: {self.score}", True, COLOR_WHITE)
        dist_text = self.font_ui.render(f"DIST: {self.distance:.1f} KM", True, (140, 220, 255))

        self.screen.blit(spd_text, (30, SCREEN_HEIGHT - 90))
        self.screen.blit(unit_text, (135, SCREEN_HEIGHT - 65))
        self.screen.blit(score_text, (30, 30))
        self.screen.blit(dist_text, (30, 75))

        # Шкала Nitro (N2O)
        pygame.draw.rect(self.screen, (30, 30, 50), (SCREEN_WIDTH - 220, SCREEN_HEIGHT - 50, 180, 20), border_radius=4)
        nitro_w = int(1.76 * self.nitro)
        if nitro_w > 0:
            pygame.draw.rect(self.screen, COLOR_GRID, (SCREEN_WIDTH - 218, SCREEN_HEIGHT - 48, nitro_w, 16), border_radius=3)
        nitro_lbl = self.font_ui.render("NITRO [W / UP]", True, (200, 200, 220))
        self.screen.blit(nitro_lbl, (SCREEN_WIDTH - 220, SCREEN_HEIGHT - 75))

        if self.game_over:
            overlay = pygame.Surface((SCREEN_WIDTH, SCREEN_HEIGHT), pygame.SRCALPHA)
            overlay.fill((0, 0, 0, 190))
            self.screen.blit(overlay, (0, 0))

            over_surf = self.font_big.render("CRASHED!", True, (255, 30, 60))
            final_surf = self.font_mid.render(f"FINAL SCORE: {self.score}", True, COLOR_WHITE)
            restart_surf = self.font_ui.render("PRESS 'R' TO RESTART", True, COLOR_GRID)

            self.screen.blit(over_surf, (SCREEN_WIDTH // 2 - over_surf.get_width() // 2, SCREEN_HEIGHT // 2 - 80))
            self.screen.blit(final_surf, (SCREEN_WIDTH // 2 - final_surf.get_width() // 2, SCREEN_HEIGHT // 2))
            self.screen.blit(restart_surf, (SCREEN_WIDTH // 2 - restart_surf.get_width() // 2, SCREEN_HEIGHT // 2 + 60))

        pygame.display.flip()

    def reset_game(self):
        self.player_x = float(self.lane_x[1])
        self.player_vx = 0.0
        self.speed = 400.0
        self.nitro = 100.0
        self.score = 0
        self.near_miss_streak = 0
        self.distance = 0.0
        self.game_over = False
        self.traffic.clear()
        self.particles.clear()

    def run(self):
        while True:
            dt = self.clock.tick(FPS) / 1000.0
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    pygame.quit()
                    sys.exit()
                if event.type == pygame.KEYDOWN and event.key == pygame.K_r and self.game_over:
                    self.reset_game()

            self.update(dt)
            self.render()

if __name__ == "__main__":
    game = CyberHighwayRacer()
    game.run()