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


import pygame
import random
import math
import sys

pygame.init()

# ----------------- НАСТРОЙКИ ЭКРАНА И МИРА -----------------
SCREEN_W, SCREEN_H = 1100, 720
WORLD_W, WORLD_H = 3200, 2400
FPS = 60

screen = pygame.display.set_mode((SCREEN_W, SCREEN_H))
pygame.display.set_caption("The Witcher 2D: Hunt for the Leshen")
clock = pygame.time.Clock()

font_s = pygame.font.SysFont("georgia", 13, bold=True)
font_m = pygame.font.SysFont("georgia", 16, bold=True)
font_l = pygame.font.SysFont("georgia", 24, bold=True)
font_boss = pygame.font.SysFont("georgia", 20, bold=True)

# ----------------- КАМЕРА С ПЛАВНЫМ СЛЕДОВАНИЕМ -----------------
class Camera:
    def __init__(self):
        self.x = 0
        self.y = 0
        self.shake = 0

    def follow(self, target_pos):
        target_x = target_pos[0] - SCREEN_W // 2
        target_y = target_pos[1] - SCREEN_H // 2
        # Плавная интерполяция
        self.x += (target_x - self.x) * 0.1
        self.y += (target_y - self.y) * 0.1
        self.x = max(0, min(self.x, WORLD_W - SCREEN_W))
        self.y = max(0, min(self.y, WORLD_H - SCREEN_H))

        if self.shake > 0:
            self.x += random.randint(-self.shake, self.shake)
            self.y += random.randint(-self.shake, self.shake)
            self.shake -= 1

    def apply(self, rect):
        return rect.move(-int(self.x), -int(self.y))

    def apply_pos(self, pos):
        return (int(pos[0] - self.x), int(pos[1] - self.y))

camera = Camera()

# ----------------- ВИЗУАЛЬНЫЕ ЭФФЕКТЫ И ЧАСТИЦЫ -----------------
class FloatingText:
    def __init__(self, x, y, text, color, size=18):
        self.x, self.y = x, y
        self.text = text
        self.color = color
        self.life = 45
        self.f = pygame.font.SysFont("georgia", size, bold=True)

    def update(self):
        self.y -= 0.8
        self.life -= 1

    def draw(self, surf):
        r = self.f.render(self.text, True, self.color)
        surf.blit(r, camera.apply_pos((self.x - r.get_width()//2, self.y)))

class BloodDecal:
    def __init__(self, x, y):
        self.x, self.y = x, y
        self.size = random.randint(10, 20)
        self.color = (random.randint(100, 140), 10, 15)

    def draw(self, surf):
        pos = camera.apply_pos((self.x, self.y))
        pygame.draw.circle(surf, self.color, pos, self.size)

class Particle:
    def __init__(self, x, y, vx, vy, color, radius, decay, p_type="circle"):
        self.x, self.y = x, y
        self.vx, self.vy = vx, vy
        self.color = color
        self.radius = radius
        self.decay = decay
        self.p_type = p_type

    def update(self):
        self.x += self.vx
        self.y += self.vy
        self.radius -= self.decay

    def draw(self, surf):
        if self.radius > 0:
            p = camera.apply_pos((self.x, self.y))
            if self.p_type == "circle":
                pygame.draw.circle(surf, self.color, p, int(self.radius))
            elif self.p_type == "glow":
                s = pygame.Surface((int(self.radius*4), int(self.radius*4)), pygame.SRCALPHA)
                pygame.draw.circle(s, (*self.color, 90), (int(self.radius*2), int(self.radius*2)), int(self.radius*2))
                surf.blit(s, (p[0] - int(self.radius*2), p[1] - int(self.radius*2)))

# ----------------- ВЕДЬМАК (ГЕРАЛЬТ) -----------------
class Witcher:
    def __init__(self, x, y):
        self.rect = pygame.Rect(x, y, 36, 48)
        self.max_hp = 150
        self.hp = 150
        self.stamina = 100
        self.max_stamina = 100
        self.toxicity = 0
        self.speed = 4.2
        self.facing = pygame.Vector2(0, 1)

        # Бой
        self.sword_type = "SILVER"  # "SILVER" или "STEEL"
        self.combo = 0
        self.combo_timer = 0
        self.is_attacking = False
        self.attack_hitbox = pygame.Rect(0, 0, 0, 0)
        self.dodge_timer = 0
        self.quen_shield = False
        self.sense_active = False

        # Зелья
        self.potions = 3

    def handle_input(self, keys, mouse_pressed):
        # Движение
        move = pygame.Vector2(0, 0)
        if keys[pygame.K_w]: move.y -= 1
        if keys[pygame.K_s]: move.y += 1
        if keys[pygame.K_a]: move.x -= 1
        if keys[pygame.K_d]: move.x += 1

        if move.length() > 0:
            self.facing = move.normalize()

        # Ведьмачье чутье (ПКМ)
        self.sense_active = mouse_pressed[2]

        # Скорость
        spd = self.speed * 2.2 if self.dodge_timer > 0 else (self.speed * 0.7 if self.sense_active else self.speed)
        return move.normalize() * spd if move.length() > 0 else pygame.Vector2(0, 0)

    def update(self, move_vec, obstacles):
        # Реген выносливости
        if self.stamina < self.max_stamina:
            self.stamina = min(self.max_stamina, self.stamina + 0.4)

        # Токсичность спадает или травит
        if self.toxicity > 0:
            self.toxicity = max(0, self.toxicity - 0.04)
            if self.toxicity > 80:
                self.hp -= 0.08  # Интоксикация наносит урон

        if self.dodge_timer > 0:
            self.dodge_timer -= 1

        if self.combo_timer > 0:
            self.combo_timer -= 1
            if self.combo_timer == 0:
                self.is_attacking = False
                self.attack_hitbox = pygame.Rect(0, 0, 0, 0)

        # Коллизии
        self.rect.x += int(move_vec.x)
        for obs in obstacles:
            if self.rect.colliderect(obs):
                if move_vec.x > 0: self.rect.right = obs.left
                if move_vec.x < 0: self.rect.left = obs.right

        self.rect.y += int(move_vec.y)
        for obs in obstacles:
            if self.rect.colliderect(obs):
                if move_vec.y > 0: self.rect.bottom = obs.top
                if move_vec.y < 0: self.rect.top = obs.bottom

        self.rect.clamp_ip(pygame.Rect(0, 0, WORLD_W, WORLD_H))

    def attack(self):
        if not self.is_attacking and self.stamina >= 12:
            self.is_attacking = True
            self.stamina -= 12
            self.combo = (self.combo % 3) + 1
            self.combo_timer = 16
            
            reach = 45
            w, h = (55, 55)
            ax = self.rect.centerx + int(self.facing.x * reach) - w // 2
            ay = self.rect.centery + int(self.facing.y * reach) - h // 2
            self.attack_hitbox = pygame.Rect(ax, ay, w, h)

    def drink_potion(self, texts):
        if self.potions > 0 and self.toxicity <= 85:
            self.potions -= 1
            self.hp = min(self.max_hp, self.hp + 50)
            self.toxicity = min(100, self.toxicity + 35)
            texts.append(FloatingText(self.rect.centerx, self.rect.top, "+50 HP (Ласточка)", (50, 255, 120), 16))
        elif self.toxicity > 85:
            texts.append(FloatingText(self.rect.centerx, self.rect.top, "Слишком высокая токсичность!", (255, 60, 60), 14))

    def draw(self, surf):
        r = camera.apply(self.rect)
        # Тень под ногами
        shadow = pygame.Surface((34, 14), pygame.SRCALPHA)
        pygame.draw.ellipse(shadow, (0, 0, 0, 100), (0, 0, 34, 14))
        surf.blit(shadow, (r.centerx - 17, r.bottom - 8))

        # Тело (Куртка Школы Волка)
        pygame.draw.rect(surf, (45, 40, 35), r, border_radius=4)
        pygame.draw.rect(surf, (90, 45, 30), (r.x + 4, r.y + 10, r.w - 8, 20)) # Кожаные вставки

        # Мечи за спиной (Стальной и Серебряный крестом)
        pygame.draw.line(surf, (160, 160, 170), (r.left + 2, r.top + 2), (r.right - 2, r.bottom - 10), 3)
        pygame.draw.line(surf, (215, 230, 245), (r.right - 2, r.top + 2), (r.left + 2, r.bottom - 10), 3)

        # Белые волосы Ведьмака
        pygame.draw.circle(surf, (230, 230, 240), (r.centerx, r.top + 5), 10)
        # Глаза ведьмака (янтарные кошачьи)
        look_pos = (r.centerx + int(self.facing.x * 4), r.top + 5 + int(self.facing.y * 3))
        pygame.draw.circle(surf, (255, 200, 0), look_pos, 2)

        # Щит Квен
        if self.quen_shield:
            shield_surf = pygame.Surface((80, 80), pygame.SRCALPHA)
            pygame.draw.circle(shield_surf, (255, 215, 0, 110), (40, 40), 36, 4)
            surf.blit(shield_surf, (r.centerx - 40, r.centery - 40))

        # Эффект удара мечом
        if self.is_attacking:
            atk_r = camera.apply(self.attack_hitbox)
            col = (200, 230, 255) if self.sword_type == "SILVER" else (220, 200, 160)
            pygame.draw.arc(surf, col, atk_r, 0, 3.1415, 5)

# ----------------- МОНСТРЫ: УТОПЕЦ И ЛЕШИЙ (БОСС) -----------------
class Drowner:
    def __init__(self, x, y):
        self.rect = pygame.Rect(x, y, 32, 40)
        self.hp = 70
        self.max_hp = 70
        self.speed = 2.8
        self.hit_cd = 0
        self.slow_timer = 0

    def update(self, player, texts, particles, decals):
        if self.slow_timer > 0:
            self.slow_timer -= 1
        spd = self.speed * 0.4 if self.slow_timer > 0 else self.speed

        self.hit_cd = max(0, self.hit_cd - 1)
        v = pygame.Vector2(player.rect.center) - pygame.Vector2(self.rect.center)
        dist = v.length()

        if 20 < dist < 500:
            d = v.normalize()
            self.rect.x += int(d.x * spd)
            self.rect.y += int(d.y * spd)

        # Удар
        if self.rect.colliderect(player.rect) and self.hit_cd == 0 and player.dodge_timer == 0:
            self.hit_cd = 45
            if player.quen_shield:
                player.quen_shield = False
                camera.shake = 5
                texts.append(FloatingText(player.rect.centerx, player.rect.top, "КВЕН ПОГЛОТИЛ УДАР", (255, 220, 50), 16))
            else:
                dmg = random.randint(18, 25)
                player.hp -= dmg
                camera.shake = 8
                texts.append(FloatingText(player.rect.centerx, player.rect.top, f"-{dmg}", (255, 50, 50), 20))
                decals.append(BloodDecal(player.rect.centerx, player.rect.centery))

    def draw(self, surf, sense_active):
        r = camera.apply(self.rect)
        color = (40, 110, 120)

        # Тень
        pygame.draw.ellipse(surf, (0, 0, 0, 90), (r.centerx - 14, r.bottom - 6, 28, 12))
        pygame.draw.rect(surf, color, r, border_radius=5)
        # Глаза монстра
        pygame.draw.circle(surf, (255, 60, 60), (r.centerx - 4, r.y + 8), 2)
        pygame.draw.circle(surf, (255, 60, 60), (r.centerx + 4, r.y + 8), 2)

        # Подсветка Ведьмачьим чутьем
        if sense_active:
            glow = pygame.Surface((r.w + 14, r.h + 14), pygame.SRCALPHA)
            pygame.draw.rect(glow, (255, 120, 0, 130), glow.get_rect(), width=3, border_radius=6)
            surf.blit(glow, (r.x - 7, r.y - 7))

        # Полоса HP
        pct = max(0, self.hp / self.max_hp)
        pygame.draw.rect(surf, (20, 20, 20), (r.x, r.y - 8, self.rect.w, 4))
        pygame.draw.rect(surf, (220, 40, 40), (r.x, r.y - 8, int(self.rect.w * pct), 4))


class LeshenBoss:
    def __init__(self, x, y):
        self.rect = pygame.Rect(x, y, 64, 90)
        self.max_hp = 550
        self.hp = 550
        self.state = "CHASE"
        self.teleport_cd = 180
        self.root_spell_cd = 120
        self.slow_timer = 0
        self.alive = True

    def update(self, player, roots, crows, texts, particles):
        if self.hp <= 0:
            self.alive = False
            return

        self.teleport_cd -= 1
        self.root_spell_cd -= 1
        if self.slow_timer > 0: self.slow_timer -= 1

        spd = 1.6 if self.slow_timer == 0 else 0.7

        v = pygame.Vector2(player.rect.center) - pygame.Vector2(self.rect.center)
        dist = v.length()

        # Телепортация в тумане
        if self.teleport_cd <= 0 and dist < 700:
            self.teleport_cd = 240
            for _ in range(40):
                particles.append(Particle(self.rect.centerx, self.rect.centery, random.uniform(-4, 4), random.uniform(-4, 4), (40, 40, 40), random.randint(6, 14), 0.2, "glow"))
            # Перемещается за спину игрока
            angle = random.uniform(0, 6.28)
            self.rect.x = int(player.rect.centerx + math.cos(angle) * 220)
            self.rect.y = int(player.rect.centery + math.sin(angle) * 220)
            texts.append(FloatingText(self.rect.centerx, self.rect.top, "*Шепот чащи*", (180, 200, 180), 16))

        # Атака: Вызов корней из-под земли
        if self.root_spell_cd <= 0 and dist < 600:
            self.root_spell_cd = 150
            roots.append(RootTrap(player.rect.centerx, player.rect.centery))
            texts.append(FloatingText(self.rect.centerx, self.rect.top, "ГНЕВ ПРИРОДЫ!", (130, 200, 80), 18))

        # Сближение
        if dist > 40:
            d = v.normalize()
            self.rect.x += int(d.x * spd)
            self.rect.y += int(d.y * spd)

    def draw(self, surf, sense_active):
        if not self.alive: return
        r = camera.apply(self.rect)

        # Тень
        pygame.draw.ellipse(surf, (0, 0, 0, 140), (r.centerx - 28, r.bottom - 10, 56, 20))

        # Деревянное тело Лешего
        pygame.draw.rect(surf, (40, 28, 20), r, border_radius=6)
        # Череп оленя (голова)
        pygame.draw.polygon(surf, (220, 215, 195), [(r.centerx, r.top + 28), (r.left + 10, r.top + 6), (r.right - 10, r.top + 6)])
        # Ветвистые рога
        pygame.draw.line(surf, (30, 20, 15), (r.left + 12, r.top + 6), (r.left - 16, r.top - 20), 4)
        pygame.draw.line(surf, (30, 20, 15), (r.right - 12, r.top + 6), (r.right + 16, r.top - 20), 4)
        pygame.draw.line(surf, (30, 20, 15), (r.left - 8, r.top - 6), (r.left - 2, r.top - 24), 3)
        pygame.draw.line(surf, (30, 20, 15), (r.right + 8, r.top - 6), (r.right + 2, r.top - 24), 3)

        # Зловещие белые огоньки в глазницах
        pygame.draw.circle(surf, (180, 255, 220), (r.centerx - 6, r.top + 14), 3)
        pygame.draw.circle(surf, (180, 255, 220), (r.centerx + 6, r.top + 14), 3)

        # Ведьмачье чутье
        if sense_active:
            glow = pygame.Surface((r.w + 24, r.h + 24), pygame.SRCALPHA)
            pygame.draw.rect(glow, (255, 40, 0, 160), glow.get_rect(), width=4, border_radius=8)
            surf.blit(glow, (r.x - 12, r.y - 12))


class RootTrap:
    def __init__(self, x, y):
        self.rect = pygame.Rect(x - 30, y - 30, 60, 60)
        self.timer = 45 # Время до срабатывания
        self.active_time = 35
        self.hit = False

    def update(self, player, texts, decals):
        self.timer -= 1
        if self.timer <= 0:
            self.active_time -= 1
            if not self.hit and self.rect.colliderect(player.rect):
                self.hit = True
                if player.quen_shield:
                    player.quen_shield = False
                    texts.append(FloatingText(player.rect.centerx, player.rect.top, "КВЕН РАЗБИТ КОРНЯМИ", (255, 215, 0), 16))
                else:
                    player.hp -= 35
                    camera.shake = 12
                    texts.append(FloatingText(player.rect.centerx, player.rect.top, "-35 (Шипы леса)", (220, 30, 30), 20))
                    decals.append(BloodDecal(player.rect.centerx, player.rect.centery))

    def draw(self, surf):
        r = camera.apply(self.rect)
        if self.timer > 0:
            # Предупреждающий круг под ногами
            pygame.draw.circle(surf, (140, 60, 40), r.center, int(26 * (1 - self.timer / 45)), 2)
        else:
            # Вырывающиеся корни
            pygame.draw.circle(surf, (35, 60, 25), r.center, 28)
            for angle in [0, 1.2, 2.4, 3.6, 4.8]:
                ex = r.centerx + int(math.cos(angle) * 25)
                ey = r.centery + int(math.sin(angle) * 25)
                pygame.draw.line(surf, (60, 35, 20), r.center, (ex, ey), 4)

class YrdenTrap:
    def __init__(self, x, y):
        self.x, self.y = x, y
        self.radius = 120
        self.life = 360 # 6 секунд

    def update(self, enemies, boss):
        self.life -= 1
        for e in enemies:
            if math.hypot(self.x - e.rect.centerx, self.y - e.rect.centery) < self.radius:
                e.slow_timer = 20
        if boss.alive and math.hypot(self.x - boss.rect.centerx, self.y - boss.rect.centery) < self.radius:
            boss.slow_timer = 20

    def draw(self, surf):
        pos = camera.apply_pos((self.x, self.y))
        s = pygame.Surface((self.radius * 2, self.radius * 2), pygame.SRCALPHA)
        pygame.draw.circle(s, (180, 50, 240, 45), (self.radius, self.radius), self.radius)
        pygame.draw.circle(s, (210, 100, 255, 120), (self.radius, self.radius), self.radius, 2)
        surf.blit(s, (pos[0] - self.radius, pos[1] - self.radius))

# ----------------- ГЕНЕРАЦИЯ МИРА -----------------
obstacles = []
# Деревья
for _ in range(55):
    obstacles.append(pygame.Rect(random.randint(100, WORLD_W - 200), random.randint(100, WORLD_H - 200), 54, 54))
# Древние камни/руины
for _ in range(12):
    obstacles.append(pygame.Rect(random.randint(300, WORLD_W - 400), random.randint(300, WORLD_H - 400), 100, 70))

witcher = Witcher(WORLD_W // 2, WORLD_H // 2)
drowners = [Drowner(random.randint(400, WORLD_W - 400), random.randint(400, WORLD_H - 400)) for _ in range(12)]
leshen = LeshenBoss(WORLD_W // 2 + 350, WORLD_H // 2 - 400)

roots = []
yrdens = []
particles = []
floating_texts = []
blood_decals = []

# ----------------- ИГРОВОЙ ЦИКЛ -----------------
running = True
while running:
    mouse_buttons = pygame.mouse.get_pressed()
    keys = pygame.key.get_pressed()

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

        if event.type == pygame.KEYDOWN:
            # Атака (SPACE)
            if event.key == pygame.K_SPACE:
                witcher.attack()

            # Кувырок (LSHIFT)
            if event.key == pygame.K_LSHIFT and witcher.dodge_timer == 0 and witcher.stamina >= 25:
                witcher.dodge_timer = 15
                witcher.stamina -= 25

            # Переключение меча (R)
            if event.key == pygame.K_r:
                witcher.sword_type = "STEEL" if witcher.sword_type == "SILVER" else "SILVER"
                col = (200, 230, 255) if witcher.sword_type == "SILVER" else (240, 200, 150)
                name = "СЕРЕБРЯНЫЙ МЕЧ" if witcher.sword_type == "SILVER" else "СТАЛЬНОЙ МЕЧ"
                floating_texts.append(FloatingText(witcher.rect.centerx, witcher.rect.top, name, col, 15))

            # Эликсир Ласточка (F)
            if event.key == pygame.K_f:
                witcher.drink_potion(floating_texts)

            # --- ЗНАКИ ВЕДЬМАКА ---
            # 1: ИГНИ (Огонь веером)
            if event.key == pygame.K_1 and witcher.stamina >= 35:
                witcher.stamina -= 35
                camera.shake = 4
                for _ in range(40):
                    spr = (random.random() - 0.5) * 0.9
                    vec = witcher.facing.rotate_rad(spr) * random.uniform(6, 12)
                    particles.append(Particle(witcher.rect.centerx, witcher.rect.centery, vec.x, vec.y, (255, random.randint(70, 160), 20), random.randint(5, 10), 0.35, "glow"))

            # 2: КВЕН (Щит)
            if event.key == pygame.K_2 and witcher.stamina >= 40:
                witcher.stamina -= 40
                witcher.quen_shield = True
                floating_texts.append(FloatingText(witcher.rect.centerx, witcher.rect.top, "КВЕН", (255, 215, 0), 16))

            # 3: ААРД (Телекинетическая волна - стан и отталкивание)
            if event.key == pygame.K_3 and witcher.stamina >= 35:
                witcher.stamina -= 35
                camera.shake = 6
                for _ in range(35):
                    spr = (random.random() - 0.5) * 0.8
                    vec = witcher.facing.rotate_rad(spr) * random.uniform(8, 15)
                    particles.append(Particle(witcher.rect.centerx, witcher.rect.centery, vec.x, vec.y, (180, 220, 255), random.randint(4, 8), 0.3, "glow"))
                # Отталкивание утопцев
                for d in drowners:
                    v = pygame.Vector2(d.rect.center) - pygame.Vector2(witcher.rect.center)
                    if v.length() < 180 and v.dot(witcher.facing) > 0:
                        d.rect.x += int(v.normalize().x * 70)
                        d.rect.y += int(v.normalize().y * 70)
                        d.hp -= 15
                        floating_texts.append(FloatingText(d.rect.centerx, d.rect.top, "СТАН!", (180, 220, 255), 16))

            # 4: ИРДЕН (Магический круг)
            if event.key == pygame.K_4 and witcher.stamina >= 35:
                witcher.stamina -= 35
                yrdens.append(YrdenTrap(witcher.rect.centerx, witcher.rect.centery))
                floating_texts.append(FloatingText(witcher.rect.centerx, witcher.rect.top, "ИРДЕН", (200, 100, 255), 16))

    # Обновление сущностей
    move_vec = witcher.handle_input(keys, mouse_buttons)
    witcher.update(move_vec, obstacles)
    camera.follow(witcher.rect.center)

    # Проверка попаданий меча
    if witcher.is_attacking and witcher.attack_hitbox.width > 0:
        # Урон по Утопцам
        for d in drowners:
            if witcher.attack_hitbox.colliderect(d.rect):
                base = random.randint(28, 38)
                dmg = base if witcher.sword_type == "SILVER" else int(base * 0.25) # Не серебряный меч слаб против монстров
                d.hp -= dmg
                blood_decals.append(BloodDecal(d.rect.centerx, d.rect.centery))
                txt_color = (240, 240, 255) if witcher.sword_type == "SILVER" else (180, 180, 180)
                msg = f"-{dmg}" if witcher.sword_type == "SILVER" else f"-{dmg} (Нужно Серебро!)"
                floating_texts.append(FloatingText(d.rect.centerx, d.rect.top, msg, txt_color, 16))
                witcher.attack_hitbox = pygame.Rect(0, 0, 0, 0)
                camera.shake = 4
                break

        # Урон по Боссу (Лешему)
        if leshen.alive and witcher.attack_hitbox.colliderect(leshen.rect):
            base = random.randint(25, 34)
            dmg = base if witcher.sword_type == "SILVER" else int(base * 0.2)
            leshen.hp -= dmg
            camera.shake = 6
            blood_decals.append(BloodDecal(leshen.rect.centerx, leshen.rect.centery))
            floating_texts.append(FloatingText(leshen.rect.centerx, leshen.rect.top, f"-{dmg}", (255, 230, 200), 20))
            witcher.attack_hitbox = pygame.Rect(0, 0, 0, 0)

    # Урон огнем от Игни
    for p in particles:
        if p.decay > 0.3: # Огненные частицы
            for d in drowners:
                if math.hypot(p.x - d.rect.centerx, p.y - d.rect.centery) < 28:
                    d.hp -= 1.8
                    if random.random() < 0.1:
                        floating_texts.append(FloatingText(d.rect.centerx, d.rect.top, "ГОРИТ", (255, 120, 20), 12))
            if leshen.alive and math.hypot(p.x - leshen.rect.centerx, p.y - leshen.rect.centery) < 45:
                leshen.hp -= 2.2 # Леший горит сильнее

    # Обновление ловушек и магии
    for yr in yrdens[:]:
        yr.update(drowners, leshen)
        if yr.life <= 0: yrdens.remove(yr)

    for rt in roots[:]:
        rt.update(witcher, floating_texts, blood_decals)
        if rt.active_time <= 0: roots.remove(rt)

    for d in drowners[:]:
        d.update(witcher, floating_texts, particles, blood_decals)
        if d.hp <= 0:
            floating_texts.append(FloatingText(d.rect.centerx, d.rect.top, "+ВЕДЬМАЧИЙ ТРОФЕЙ", (255, 215, 0), 15))
            drowners.remove(d)

    if leshen.alive:
        leshen.update(witcher, roots, None, floating_texts, particles)

    for p in particles[:]:
        p.update()
        if p.radius <= 0: particles.remove(p)

    for ft in floating_texts[:]:
        ft.update()
        if ft.life <= 0: floating_texts.remove(ft)

    # ----------------- РЕНДЕРИНГ -----------------
    screen.fill((30, 36, 32)) # Темная земля Велена

    # Следы крови на земле
    for b in blood_decals:
        b.draw(screen)

    # Круги Ирден
    for yr in yrdens:
        yr.draw(screen)

    # Препятствия (Деревья и Камни)
    for obs in obstacles:
        r = camera.apply(obs)
        if obs.w > 60: # Камень
            pygame.draw.rect(screen, (70, 75, 75), r, border_radius=6)
            pygame.draw.rect(screen, (50, 55, 55), (r.x + 6, r.y + 6, r.w - 12, r.h - 12), border_radius=4)
        else: # Дерево
            pygame.draw.circle(screen, (20, 32, 20), (r.centerx, r.centery), 38)
            pygame.draw.circle(screen, (15, 25, 15), (r.centerx, r.centery), 26)
            pygame.draw.rect(screen, (50, 35, 25), (r.centerx - 7, r.centery - 7, 14, 18))

    # Корни Лешего
    for rt in roots:
        rt.draw(screen)

    # Монстры
    for d in drowners:
        d.draw(screen, witcher.sense_active)

    leshen.draw(screen, witcher.sense_active)

    # Геральт
    witcher.draw(screen)

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

    # Плавающий текст урона
    for ft in floating_texts:
        ft.draw(screen)

    # Эффект Ведьмачьего Чутья (Затемнение краев экрана)
    if witcher.sense_active:
        sense_overlay = pygame.Surface((SCREEN_W, SCREEN_H), pygame.SRCALPHA)
        sense_overlay.fill((10, 5, 0, 75))
        pygame.draw.circle(sense_overlay, (0, 0, 0, 0), (SCREEN_W // 2, SCREEN_H // 2), 340)
        screen.blit(sense_overlay, (0, 0))
        sense_hint = font_m.render("[ ВЕДЬМАЧЬЕ ЧУТЬЕ АКТИВНО ]", True, (255, 140, 0))
        screen.blit(sense_hint, (SCREEN_W // 2 - sense_hint.get_width() // 2, 70))

    # ----------------- HUD (ИНТЕРФЕЙС ИЗ ВЕДЬМАКА 3) -----------------
    # Плашка Волка
    pygame.draw.circle(screen, (40, 40, 40), (60, 55), 32)
    pygame.draw.polygon(screen, (200, 200, 210), [(60, 35), (42, 60), (52, 68), (60, 58), (68, 68), (78, 60)])

    # Полоска HP (Красная)
    pygame.draw.rect(screen, (30, 20, 20), (105, 34, 220, 16), border_radius=3)
    hp_pct = max(0, witcher.hp / witcher.max_hp)
    pygame.draw.rect(screen, (210, 35, 35), (105, 34, int(220 * hp_pct), 16), border_radius=3)

    # Полоска Выносливости (Желтая)
    pygame.draw.rect(screen, (30, 30, 20), (105, 54, 180, 10), border_radius=3)
    stamina_pct = max(0, witcher.stamina / witcher.max_stamina)
    pygame.draw.rect(screen, (240, 190, 30), (105, 54, int(180 * stamina_pct), 10), border_radius=3)

    # Полоска Токсичности (Зеленая под выносливостью)
    pygame.draw.rect(screen, (20, 30, 20), (105, 68, 120, 6), border_radius=2)
    tox_pct = min(1.0, witcher.toxicity / 100)
    pygame.draw.rect(screen, (40, 210, 80), (105, 68, int(120 * tox_pct), 6), border_radius=2)

    # Активный меч и зелья
    sword_col = (190, 220, 255) if witcher.sword_type == "SILVER" else (220, 200, 160)
    sword_txt = font_s.render(f"Меч [R]: {witcher.sword_type}", True, sword_col)
    pot_txt = font_s.render(f"Ласточка [F]: {witcher.potions} шт.", True, (100, 255, 140))
    screen.blit(sword_txt, (105, 80))
    screen.blit(pot_txt, (240, 80))

    # Подсказка по Знакам
    signs_txt = font_s.render("[1] Игни   [2] Квен   [3] Аард   [4] Ирден   |   [ПКМ] Чутье   |   [SHIFT] Рывок", True, (180, 180, 180))
    screen.blit(signs_txt, (SCREEN_W // 2 - signs_txt.get_width() // 2, SCREEN_H - 30))

    # ХП БОССА (Если жив)
    if leshen.alive:
        boss_bar_w = 460
        bx = SCREEN_W // 2 - boss_bar_w // 2
        pygame.draw.rect(screen, (20, 20, 20), (bx, 20, boss_bar_w, 14), border_radius=2)
        boss_hp_pct = max(0, leshen.hp / leshen.max_hp)
        pygame.draw.rect(screen, (160, 25, 25), (bx, 20, int(boss_bar_w * boss_hp_pct), 14), border_radius=2)
        b_name = font_boss.render("ДРЕВНИЙ ЛЕШИЙ ИЗ ВЕЛЕНА", True, (240, 230, 210))
        screen.blit(b_name, (SCREEN_W // 2 - b_name.get_width() // 2, 38))
    else:
        win_txt = font_l.render("ЗАКАЗ ВЫПОЛНЕН: ЛЕШИЙ ПОВЕРЖЕН!", True, (255, 215, 0))
        screen.blit(win_txt, (SCREEN_W // 2 - win_txt.get_width() // 2, 40))

    # Смерть Геральта
    if witcher.hp <= 0:
        overlay = pygame.Surface((SCREEN_W, SCREEN_H), pygame.SRCALPHA)
        overlay.fill((120, 0, 0, 170))
        screen.blit(overlay, (0, 0))
        d_text = font_l.render("ВЫ ПОГИБЛИ. ВЕДЬМАКИ НЕ УМИРАЮТ В СВОЕЙ ПОСТЕЛИ...", True, (255, 255, 255))
        screen.blit(d_text, (SCREEN_W // 2 - d_text.get_width() // 2, SCREEN_H // 2 - 20))

    pygame.display.flip()
    clock.tick(FPS)

pygame.quit()
sys.exit()