# Ur18U — Ultimate Roguelike Version Ultra (Full Version 4500+ lines)
# Bullet Hell режим с процедурной генерацией подземелий

import pygame
import random
import math
import time
import sys
import json
import os
from enum import Enum, auto
from collections import defaultdict
from array import array
ACHIEVEMENTS_DISABLED = os.path.exists("UrC.txt")

# Import new room system
try:
    from bullet_hell_dungeon import Floor, RoomType, Door, BOSS_PATTERNS
    from new_room_system import (
        init_floor, spawn_room_enemies, create_dungeon_enemy,
        create_cursed_enemy, create_mini_boss, create_boss_for_room,
        check_room_clear, update_boss_phase, get_door_at_position,
        draw_room, get_room_floor_color, draw_minimap, get_room_minimap_color
    )
    NEW_ROOM_SYSTEM_AVAILABLE = True
except ImportError:
    NEW_ROOM_SYSTEM_AVAILABLE = False

pygame.init()
pygame.mixer.init()
W, H = 1200, 800
screen = pygame.display.set_mode((W, H), pygame.RESIZABLE)
pygame.display.set_caption("Ur20 — Ultimate Roguelike U")  # Изменено на Ur20
clock = pygame.time.Clock()

FONT_PATH = None


def load_font():
    font_file = "PressStart2P.ttf"
    if os.path.exists(font_file):
        try:
            font = pygame.font.Font(font_file, 16)
            return font
        except:
            return pygame.font.SysFont("dejavusansmono", 16, bold=True)
    else:
        return pygame.font.SysFont("dejavusansmono", 16, bold=True)


base_font = load_font()
try:
    FONT_XS = pygame.font.Font("PressStart2P.ttf", 12)
    FONT_S = pygame.font.Font("PressStart2P.ttf", 16)
    FONT = pygame.font.Font("PressStart2P.ttf", 20)
    FONT_L = pygame.font.Font("PressStart2P.ttf", 28)
    FONT_XL = pygame.font.Font("PressStart2P.ttf", 36)
except:
    FONT_XS = pygame.font.SysFont("dejavusansmono", 12, bold=True)
    FONT_S = pygame.font.SysFont("dejavusansmono", 16, bold=True)
    FONT = pygame.font.SysFont("dejavusansmono", 20, bold=True)
    FONT_L = pygame.font.SysFont("dejavusansmono", 28, bold=True)
    FONT_XL = pygame.font.SysFont("dejavusansmono", 36, bold=True)


def draw(t, x, y, c=(255, 255, 255), f=FONT):
    screen.blit(f.render(str(t), True, c), (int(x), int(y)))


# -------------------- Colors --------------------
WHITE = (255, 255, 255)
RED = (255, 70, 70)
GREEN = (60, 230, 140)
YELLOW = (255, 220, 60)
CYAN = (90, 210, 255)
GRAY = (150, 150, 150)
BLUE = (80, 120, 255)
PURPLE = (180, 70, 220)
ORANGE = (255, 150, 50)
PINK = (255, 100, 180)
BROWN = (180, 120, 80)
GOLD = (255, 215, 0)
DARK_PURPLE = (80, 0, 80)
LIME = (0, 255, 128)
MAGENTA = (255, 0, 255)
DARK_RED = (139, 0, 0)
BLOOD_RED = (80, 0, 0)
HELL_RED = (60, 0, 0)
BLACK = (0, 0, 0)
DARK_GRAY = (20, 20, 20)
LIGHT_GRAY = (200, 200, 200)


# -------------------- Game States --------------------
class GS(Enum):
    MENU = auto()
    BESTIARY = auto()
    GAME = auto()
    PAUSE = auto()
    OVER = auto()
    WIN = auto()
    SHOP = auto()
    ACHIEVEMENTS = auto()
    SETTINGS = auto()
    INVENTORY = auto()
    SECRET_SHOP = auto()
    BOSS_ROOM = auto()
    DUNGEON = auto()


state = GS.MENU
menu_items = ["Start", "Bestiary", "Achievements", "Settings", "Quit"]
menu_idx = 0
tab_names = ["Enemies", "Bosses", "Items", "Actives"]
tab_idx = 0
tab_scroll = 0
ach_scroll = 0
shop_scroll = 0
settings_idx = 0
control_mode = "Классический"

# -------------------- Arena System --------------------
class Arena:
    def __init__(self, id, name, color, bg_color, pattern=None):
        self.id = id
        self.name = name
        self.color = color
        self.bg_color = bg_color
        self.pattern = pattern
        self.pattern_time = 0

    def update(self, dt):
        if self.pattern == "mother3":
            self.pattern_time += dt

    def draw_background(self, screen):
        if self.pattern == "mother3":
            t = self.pattern_time * 2
            for y in range(0, H, 20):
                for x in range(0, W, 20):
                    shade = int(128 + 64 * math.sin(x * 0.01 + t) * math.cos(y * 0.01 + t))
                    r = min(255, self.color[0] + shade // 4)
                    g = min(255, self.color[1] + shade // 8)
                    b = min(255, self.color[2] + shade // 8)
                    pygame.draw.rect(screen, (r, g, b), (x, y, 20, 20))
        else:
            screen.fill(self.bg_color)


ARENAS = {
    1: Arena(1, "Город", (50, 50, 80), (0, 0, 0)),
    2: Arena(2, "Пригород", (60, 70, 60), (0, 0, 0)),
    3: Arena(3, "Разлом", (80, 40, 40), (0, 0, 0)),
    4: Arena(4, "Пустырь", (100, 80, 40), (0, 0, 0)),
    5: Arena(5, "Пустырь", (100, 80, 40), (0, 0, 0)),
    6: Arena(6, "Полигон", (70, 70, 100), (0, 0, 0)),
    7: Arena(7, "Полигон", (70, 70, 100), (0, 0, 0)),
    8: Arena(8, "Полигон", (70, 70, 100), (0, 0, 0)),
    9: Arena(9, "Ад", (150, 30, 30), (0, 0, 0)),
    10: Arena(10, "Ад II", (180, 20, 20), (0, 0, 0)),
    11: Arena(11, "Ад III", (200, 0, 0), (0, 0, 0)),
    12: Arena(12, "Ад IV", (220, 20, 0), (0, 0, 0)),
    13: Arena(13, "Ад V", (240, 40, 0), (0, 0, 0)),
    14: Arena(14, "Ад VI", (255, 60, 0), (0, 0, 0)),
    15: Arena(15, "Порох", (150, 150, 100), (0, 0, 0), "mother3"),
    16: Arena(16, "Бесконечность", (100, 200, 255), (0, 0, 0), "mother3"),
    17: Arena(17, "Проклят", (80, 0, 80), (0, 0, 0), "mother3"),
    18: Arena(18, "Проклят", (80, 0, 80), (0, 0, 0), "mother3"),
    19: Arena(19, "Вознесение", (255, 255, 200), (0, 0, 0), "mother3"),
    20: Arena(20, "ФИНАЛ", (255, 215, 0), (0, 0, 0), "mother3"),
}

current_arena = ARENAS[1]

# -------------------- Binding of Isaac Style Room System --------------------

class RoomType(Enum):
    START = auto()      # Starting room
    NORMAL = auto()     # Regular enemy room
    SHOP = auto()       # Shop room
    TREASURE = auto()   # Treasure room
    CURSE = auto()      # Cursed room
    SECRET = auto()     # Secret room
    BOSS = auto()       # Boss room
    MINI_BOSS = auto()  # Mini-boss room

class Door:
    """Door connecting rooms"""
    def __init__(self, direction, locked=False, secret=False):
        self.direction = direction
        self.locked = locked
        self.secret = secret
        self.open = False

class Room:
    """Room in Binding of Isaac style"""
    def __init__(self, x, y, room_type=RoomType.NORMAL, width=1000, height=700):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.room_type = room_type
        self.doors = {}
        self.enemies = []
        self.boss = None
        self.cleared = False
        self.visited = False
        self.items = []
        self.obstacles = []
        self.wave = 1
        self.max_waves = 1
        
    def add_door(self, direction, locked=False, secret=False):
        self.doors[direction] = Door(direction, locked, secret)
        
    def get_center(self):
        return (self.width // 2, self.height // 2)
        
    def get_door_position(self, direction):
        if direction == 'up':
            return (self.width // 2, 50)
        elif direction == 'down':
            return (self.width // 2, self.height - 50)
        elif direction == 'left':
            return (50, self.height // 2)
        elif direction == 'right':
            return (self.width - 50, self.height // 2)
        return (self.width // 2, self.height // 2)

class Floor:
    """Floor of dungeon (collection of rooms)"""
    def __init__(self, floor_number=1):
        self.floor_number = floor_number
        self.rooms = {}
        self.current_room = None
        self.start_room = None
        self.boss_room = None
        self.shop_room = None
        self.treasure_room = None
        self.secret_rooms = []
        self.curse_rooms = []
        self.size = random.randint(8, 12) + floor_number // 3
        
    def generate(self):
        start_x, start_y = 0, 0
        start_room = Room(start_x, start_y, RoomType.START)
        self.rooms[(start_x, start_y)] = start_room
        self.start_room = start_room
        self.current_room = start_room
        
        self._generate_boss_path(start_x, start_y)
        self._add_extra_rooms()
        self._place_special_rooms()
        self._connect_rooms()
        self._populate_rooms()
        
    def _generate_boss_path(self, start_x, start_y):
        current_x, current_y = start_x, start_y
        min_boss_distance = 5 + self.floor_number // 2
        
        attempts = 0
        while attempts < 100:
            directions = [(0, -1), (0, 1), (-1, 0), (1, 0)]
            random.shuffle(directions)
            
            placed = False
            for dx, dy in directions:
                new_x, new_y = current_x + dx, current_y + dy
                
                if (new_x, new_y) not in self.rooms and self._can_place_room(new_x, new_y):
                    distance = abs(new_x) + abs(new_y)
                    if distance >= min_boss_distance and random.random() < 0.4:
                        room = Room(new_x, new_y, RoomType.BOSS)
                        self.boss_room = room
                    else:
                        room = Room(new_x, new_y, RoomType.NORMAL)
                    
                    self.rooms[(new_x, new_y)] = room
                    current_x, current_y = new_x, new_y
                    placed = True
                    
                    if room.room_type == RoomType.BOSS:
                        return
                    break
            
            if not placed:
                attempts += 1
                if attempts % 10 == 0:
                    current_x, current_y = random.choice(list(self.rooms.keys()))
                    
        if not self.boss_room:
            self._force_create_boss_room()
            
    def _force_create_boss_room(self):
        max_dist = 0
        farthest = None
        for (x, y), room in self.rooms.items():
            dist = abs(x) + abs(y)
            if dist > max_dist and room.room_type == RoomType.NORMAL:
                max_dist = dist
                farthest = (x, y)
        
        if farthest:
            room = self.rooms[farthest]
            room.room_type = RoomType.BOSS
            self.boss_room = room
            
    def _can_place_room(self, x, y):
        neighbors = 0
        for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
            if (x + dx, y + dy) in self.rooms:
                neighbors += 1
        return neighbors <= 1 and len(self.rooms) < self.size
        
    def _add_extra_rooms(self):
        existing = list(self.rooms.keys())
        for (x, y) in existing:
            if random.random() < 0.4:
                for dx, dy in [(0, 1), (0, -1), (1, 0), (-1, 0)]:
                    new_x, new_y = x + dx, y + dy
                    if (new_x, new_y) not in self.rooms and self._can_place_room(new_x, new_y):
                        room_type = RoomType.NORMAL
                        if random.random() < 0.1:
                            room_type = RoomType.MINI_BOSS
                        room = Room(new_x, new_y, room_type)
                        self.rooms[(new_x, new_y)] = room
                        break
                        
    def _place_special_rooms(self):
        normal_rooms = [r for r in self.rooms.values()
                       if r.room_type == RoomType.NORMAL and r != self.start_room]
        
        if normal_rooms:
            shop = random.choice(normal_rooms)
            shop.room_type = RoomType.SHOP
            self.shop_room = shop
            normal_rooms.remove(shop)
            
        if normal_rooms:
            treasure = random.choice(normal_rooms)
            treasure.room_type = RoomType.TREASURE
            self.treasure_room = treasure
            normal_rooms.remove(treasure)
            
        for _ in range(random.randint(1, 2)):
            if normal_rooms:
                curse = random.choice(normal_rooms)
                curse.room_type = RoomType.CURSE
                self.curse_rooms.append(curse)
                normal_rooms.remove(curse)
                
        for _ in range(random.randint(1, 3)):
            if normal_rooms:
                secret = random.choice(normal_rooms)
                secret.room_type = RoomType.SECRET
                self.secret_rooms.append(secret)
                normal_rooms.remove(secret)
                
    def _connect_rooms(self):
        for (x, y), room in self.rooms.items():
            for direction, (dx, dy) in [('up', (0, -1)), ('down', (0, 1)),
                                         ('left', (-1, 0)), ('right', (1, 0))]:
                neighbor_pos = (x + dx, y + dy)
                if neighbor_pos in self.rooms:
                    neighbor = self.rooms[neighbor_pos]
                    secret = (room.room_type == RoomType.SECRET or
                             neighbor.room_type == RoomType.SECRET)
                    locked = (room.room_type in [RoomType.BOSS, RoomType.CURSE] or
                             neighbor.room_type in [RoomType.BOSS, RoomType.CURSE])
                    room.add_door(direction, locked=locked, secret=secret)
                    
    def _populate_rooms(self):
        for room in self.rooms.values():
            if room.room_type == RoomType.NORMAL:
                room.max_waves = random.randint(1, 3)
            elif room.room_type == RoomType.MINI_BOSS:
                room.max_waves = 1
                
    def change_room(self, direction):
        if direction not in self.current_room.doors:
            return False
            
        door = self.current_room.doors[direction]
        if door.locked and not self.current_room.cleared:
            return False
            
        dx, dy = {'up': (0, -1), 'down': (0, 1),
                  'left': (-1, 0), 'right': (1, 0)}[direction]
        new_x = self.current_room.x + dx
        new_y = self.current_room.y + dy
        
        new_room = self.rooms.get((new_x, new_y))
        if new_room:
            self.current_room = new_room
            new_room.visited = True
            
            if direction == 'up':
                player["y"] = new_room.height - 100
                player["x"] = new_room.width // 2
            elif direction == 'down':
                player["y"] = 100
                player["x"] = new_room.width // 2
            elif direction == 'left':
                player["x"] = new_room.width - 100
                player["y"] = new_room.height // 2
            elif direction == 'right':
                player["x"] = 100
                player["y"] = new_room.height // 2
                
            return True
        return False

# Keep old Dungeon class temporarily for compatibility
class Dungeon:
    def __init__(self, width=80, height=60, dungeon_level=1):  # Увеличил размер
        self.width = width
        self.height = height
        self.dungeon_level = dungeon_level
        self.grid = [['#' for _ in range(width)] for _ in range(height)]
        self.rooms = []
        self.corridors = []
        self.room_types = {}
        self.player_x = 0
        self.player_y = 0
        self.current_room_idx = 0
        self.enemies = []
        self.special_rooms = []
        self.exit_x = 0
        self.exit_y = 0
        self.discovered_rooms = set()
        self.doors = []  # Список дверей [(x,y)]
        self.room_connections = []  # Список соединений между комнатами
        self.arena_transition = False  # Флаг перехода между аренами

    def generate(self):
        """Генерация подземелья с огромными комнатами"""
        
        # Создаем первую комнату (стартовая) - ОГРОМНАЯ
        first_room = self._create_room(10, 10, random.randint(25, 40), random.randint(20, 32))
        self.rooms.append(first_room)
        self._carve_room(first_room)
        self.room_types[first_room] = RoomType.NORMAL
        
        # Создаем последнюю комнату (выход) - ОГРОМНАЯ
        last_room = self._create_room(self.width - 45, self.height - 40,
                                     random.randint(25, 40), random.randint(20, 32))
        self.rooms.append(last_room)
        self._carve_room(last_room)
        self.room_types[last_room] = RoomType.BOSS if self.dungeon_level % 5 == 0 else RoomType.NORMAL
        self.exit_x = last_room[0] + last_room[2] // 2
        self.exit_y = last_room[1] + last_room[3] // 2
        
        # Генерация дополнительных комнат - все огромные
        num_rooms = random.randint(5, 8) + self.dungeon_level // 3  # Меньше комнат, но огромные
        
        for _ in range(num_rooms):
            attempts = 0
            while attempts < 100:
                w = random.randint(20, 35)  # Огромные комнаты
                h = random.randint(16, 28)
                x = random.randint(5, self.width - w - 5)
                y = random.randint(5, self.height - h - 5)
                new_room = (x, y, w, h)
                
                # Проверка на пересечение с существующими комнатами
                overlap = False
                margin = 12  # Больше отступ для огромных комнат
                for rx, ry, rw, rh in self.rooms:
                    if not (x + w + margin < rx or rx + rw + margin < x or
                            y + h + margin < ry or ry + rh + margin < y):
                        overlap = True
                        break
                
                if not overlap:
                    self.rooms.append(new_room)
                    self._carve_room(new_room)
                    
                    # Определяем тип комнаты
                    room_type = self._determine_room_type()
                    self.room_types[new_room] = room_type
                    if room_type != RoomType.NORMAL:
                        self.special_rooms.append(new_room)
                    break
                attempts += 1
        
        # Соединяем все комнаты широкими коридорами
        self._connect_all_rooms()
        
        # Добавляем дополнительные соединения
        self._add_extra_connections()
        
        # Добавляем тупики и ответвления
        self._add_dead_ends()
        
        # Устанавливаем игрока в стартовой комнате
        self.player_x = first_room[0] + first_room[2] // 2
        self.player_y = first_room[1] + first_room[3] // 2

    def _determine_room_type(self):
        """Определяет тип комнаты на основе вероятности"""
        roll = random.random()
        if roll < 0.05:  # 5% - магазин
            return RoomType.SHOP
        elif roll < 0.1:  # 5% - сокровищница
            return RoomType.TREASURE
        elif roll < 0.15:  # 5% - проклятая комната
            return RoomType.CURSE
        elif roll < 0.18:  # 3% - секретная комната
            return RoomType.SECRET
        else:
            return RoomType.NORMAL

    def _create_room(self, x, y, w, h):
        return (x, y, w, h)

    def _carve_room(self, room):
        x, y, w, h = room
        for i in range(y, y + h):
            for j in range(x, x + w):
                if 0 <= i < self.height and 0 <= j < self.width:
                    self.grid[i][j] = '.'

    def _connect_all_rooms(self):
        """Соединяет все комнаты широкими коридорами"""
        if len(self.rooms) < 2:
            return
        
        # Создаем минимальное остовное дерево
        unconnected = set(range(1, len(self.rooms)))
        connected = {0}
        connections = []
        
        while unconnected:
            best_dist = float('inf')
            best_pair = None
            
            for i in connected:
                for j in unconnected:
                    dist = self._room_distance(self.rooms[i], self.rooms[j])
                    if dist < best_dist:
                        best_dist = dist
                        best_pair = (i, j)
            
            if best_pair:
                i, j = best_pair
                connections.append((i, j))
                connected.add(j)
                unconnected.remove(j)
        
        # Создаем широкие коридоры
        for i, j in connections:
            self._connect_rooms_wide(self.rooms[i], self.rooms[j])
            self.room_connections.append((i, j))

    def _add_extra_connections(self):
        """Добавляет дополнительные соединения"""
        num_extra = len(self.rooms) // 4
        
        for _ in range(num_extra):
            i = random.randint(0, len(self.rooms) - 1)
            j = random.randint(0, len(self.rooms) - 1)
            
            if i != j and (i, j) not in self.room_connections and (j, i) not in self.room_connections:
                if random.random() < 0.4:
                    self._connect_rooms_wide(self.rooms[i], self.rooms[j])
                    self.room_connections.append((i, j))

    def _room_distance(self, room1, room2):
        x1, y1, w1, h1 = room1
        x2, y2, w2, h2 = room2
        cx1 = x1 + w1 // 2
        cy1 = y1 + h1 // 2
        cx2 = x2 + w2 // 2
        cy2 = y2 + h2 // 2
        return abs(cx1 - cx2) + abs(cy1 - cy2)

    def _connect_rooms_wide(self, room1, room2):
        """Создает широкий коридор между комнатами (3 клетки шириной)"""
        x1, y1, w1, h1 = room1
        x2, y2, w2, h2 = room2
        
        # Выбираем точки соединения
        start_x = x1 + random.randint(2, w1 - 3)
        start_y = y1 + random.randint(2, h1 - 3)
        end_x = x2 + random.randint(2, w2 - 3)
        end_y = y2 + random.randint(2, h2 - 3)
        
        # Создаем широкий коридор (3 клетки шириной)
        self._create_wide_corridor(start_x, start_y, end_x, end_y)
        
        # Добавляем большие двери
        for dx in [-1, 0, 1]:
            for dy in [-1, 0, 1]:
                if 0 <= start_y + dy < self.height and 0 <= start_x + dx < self.width:
                    self.grid[start_y + dy][start_x + dx] = ' '
                    self.doors.append((start_x + dx, start_y + dy))
                if 0 <= end_y + dy < self.height and 0 <= end_x + dx < self.width:
                    self.grid[end_y + dy][end_x + dx] = ' '
                    self.doors.append((end_x + dx, end_y + dy))

    def _create_wide_corridor(self, x1, y1, x2, y2):
        """Создает широкий коридор (3 клетки)"""
        # Горизонтальная часть
        for x in range(min(x1, x2) - 1, max(x1, x2) + 2):
            for dy in [-1, 0, 1]:
                if 0 <= y1 + dy < self.height and 0 <= x < self.width:
                    if self.grid[y1 + dy][x] == '#':
                        self.grid[y1 + dy][x] = '.'
        
        # Вертикальная часть
        for y in range(min(y1, y2) - 1, max(y1, y2) + 2):
            for dx in [-1, 0, 1]:
                if 0 <= y < self.height and 0 <= x2 + dx < self.width:
                    if self.grid[y][x2 + dx] == '#':
                        self.grid[y][x2 + dx] = '.'

    def _add_dead_ends(self):
        """Добавляет тупики с маленькими комнатами"""
        num_dead_ends = random.randint(3, 6)
        
        for _ in range(num_dead_ends):
            attempts = 0
            while attempts < 30:
                x = random.randint(10, self.width - 10)
                y = random.randint(10, self.height - 10)
                
                if self.grid[y][x] == '.':
                    # Создаем тупик с комнатой
                    direction = random.choice(['up', 'down', 'left', 'right'])
                    length = random.randint(5, 8)
                    
                    # Создаем коридор
                    for i in range(1, length + 1):
                        if direction == 'up' and y - i >= 0:
                            for dx in [-1, 0, 1]:
                                if 0 <= x + dx < self.width:
                                    self.grid[y - i][x + dx] = '.'
                        elif direction == 'down' and y + i < self.height:
                            for dx in [-1, 0, 1]:
                                if 0 <= x + dx < self.width:
                                    self.grid[y + i][x + dx] = '.'
                        elif direction == 'left' and x - i >= 0:
                            for dy in [-1, 0, 1]:
                                if 0 <= y + dy < self.height:
                                    self.grid[y + dy][x - i] = '.'
                        elif direction == 'right' and x + i < self.width:
                            for dy in [-1, 0, 1]:
                                if 0 <= y + dy < self.height:
                                    self.grid[y + dy][x + i] = '.'
                    
                    # Добавляем маленькую комнату в конце
                    if direction == 'up' and y - length - 3 >= 0:
                        self._create_small_room(x - 2, y - length - 3, 5, 4)
                    elif direction == 'down' and y + length + 3 < self.height:
                        self._create_small_room(x - 2, y + length, 5, 4)
                    elif direction == 'left' and x - length - 3 >= 0:
                        self._create_small_room(x - length - 3, y - 2, 4, 5)
                    elif direction == 'right' and x + length + 3 < self.width:
                        self._create_small_room(x + length, y - 2, 4, 5)
                    break
                attempts += 1

    def _create_small_room(self, x, y, w, h):
        """Создает маленькую комнату"""
        for i in range(y, y + h):
            for j in range(x, x + w):
                if 0 <= i < self.height and 0 <= j < self.width:
                    self.grid[i][j] = '.'

    def spawn_enemies(self):
        """Спавн врагов в текущей комнате"""
        self.enemies.clear()
        rx, ry, rw, rh = self.rooms[self.current_room_idx]
        room_type = self.room_types.get((rx, ry, rw, rh), RoomType.NORMAL)
        
        # Определяем количество врагов (как в основной игре)
        if room_type == RoomType.BOSS:
            # Спавним мини-босса в комнате
            self._spawn_mini_boss(rx, ry, rw, rh)
            num_enemies = random.randint(3, 5)  # Дополнительные враги
        elif room_type == RoomType.SHOP:
            num_enemies = random.randint(0, 2)  # Почти нет врагов
        elif room_type == RoomType.TREASURE:
            num_enemies = random.randint(5, 8)  # Охраняется
        elif room_type == RoomType.CURSE:
            num_enemies = random.randint(8, 12)  # Много врагов
        elif room_type == RoomType.SECRET:
            num_enemies = random.randint(3, 5)  # Немного врагов
        else:
            # Обычная комната: как в основной игре
            num_enemies = spawn_count() // 2 + random.randint(-2, 2)
            num_enemies = max(3, min(15, num_enemies))
        
        self._spawn_enemies_in_area(rx, ry, rw, rh, num_enemies, room_type)
        
        # Спавним врагов в коридорах
        self._spawn_enemies_in_corridors(rx, ry, rw, rh)

    def _spawn_mini_boss(self, rx, ry, rw, rh):
        """Спавнит мини-босса в комнате"""
        x = rx + rw // 2
        y = ry + rh // 2
        screen_x = x * 20 + 10
        screen_y = y * 20 + 10
        
        # Выбираем случайного босса для этого уровня
        boss_waves = [5, 10, 15, 20, 25, 30, 35, 40, 45, 50]
        boss_wave = random.choice([w for w in boss_waves if w <= self.dungeon_level * 5])
        
        if boss_wave in BOSSES:
            b = BOSSES[boss_wave]
            enemy = {
                "x": screen_x,
                "y": screen_y,
                "type": "MINI_BOSS",
                "room_x": x,
                "room_y": y,
                "hp": b["hp"] // 3,
                "max_hp": b["hp"] // 3,
                "shoot_timer": 0,
                "dmg": 3 + self.dungeon_level,
                "name": f"Младший {b['name']}",
                "color": b["color"],
                "char": b["char"],
                "pat": b["ph"][0]["pat"],
                "delay": b["ph"][0]["delay"] * 1.2,
                "spd": b["ph"][0]["spd"] * 0.8,
                "boss": True,
                "phase": 0,
                "phases": b["ph"]
            }
            self.enemies.append(enemy)

    def _spawn_enemies_in_area(self, rx, ry, rw, rh, count, room_type):
        """Спавнит врагов в заданной области"""
        for _ in range(count):
            x = rx + random.randint(2, rw - 3)
            y = ry + random.randint(2, rh - 3)
            
            screen_x = x * 20 + 10
            screen_y = y * 20 + 10
            
            # Определяем тип врага как в основной игре
            if room_type == RoomType.CURSE:
                ekey = random.choice(["E+", "F+", "S+", "W+"])
            elif room_type == RoomType.BOSS:
                ekey = random.choice(["E", "F", "S", "W", "H", "L", "V", "G"])
            else:
                ekey = random.choice(["E", "F", "S", "W", "H"])
            
            # Используем те же характеристики, что и в основной игре
            d = ENEMIES.get(ekey) or ELITES.get(ekey) or ULTRAS.get(ekey)
            if d is None:
                d = ENEMIES["E"]
            
            hp = hp_scale(d["hp"]) + self.dungeon_level
            
            enemy = {
                "x": screen_x,
                "y": screen_y,
                "type": ekey,
                "name": d["name"],
                "char": d["char"],
                "color": d["color"],
                "hp": hp,
                "max_hp": hp,
                "pat": d["pat"],
                "delay": max(0.4, d["delay"] * 0.9),
                "timer": random.random() * d["delay"],
                "spd": d["spd"] * (1.0 + 0.1 * self.dungeon_level),
                "ai": d["ai"],
                "room_x": x,
                "room_y": y,
                "shoot_timer": random.random() * 2,
                "dmg": 2 + self.dungeon_level // 3,
                "cursed": room_type == RoomType.CURSE,
                "bleeding": 0,
                "burning": 0,
                "poisoned": room_type == RoomType.CURSE,
                "stoned": 0
            }
            
            self.enemies.append(enemy)

    def _spawn_enemies_in_corridors(self, rx, ry, rw, rh):
        """Спавнит врагов в коридорах"""
        for dx in range(-8, rw + 8):
            for dy in range(-8, rh + 8):
                x = rx + dx
                y = ry + dy
                
                if 0 <= x < self.width and 0 <= y < self.height:
                    if (self.grid[y][x] == '.' and 
                        not (rx <= x < rx + rw and ry <= y < ry + rh) and
                        random.random() < 0.03):
                        
                        screen_x = x * 20 + 10
                        screen_y = y * 20 + 10
                        
                        enemy = {
                            "x": screen_x,
                            "y": screen_y,
                            "type": random.choice(["E", "F"]),
                            "room_x": x,
                            "room_y": y,
                            "hp": 4 + self.dungeon_level,
                            "max_hp": 4 + self.dungeon_level,
                            "shoot_timer": random.random() * 2,
                            "dmg": 1 + self.dungeon_level // 3,
                            "cursed": False,
                            "bleeding": 0,
                            "burning": 0,
                            "poisoned": 0,
                            "stoned": 0
                        }
                        
                        self.enemies.append(enemy)

    def move_player(self, dx, dy):
        """Движение игрока по подземелью"""
        new_x = self.player_x + dx
        new_y = self.player_y + dy
        
        if 0 <= new_x < self.width and 0 <= new_y < self.height:
            cell = self.grid[new_y][new_x]
            
            if cell != '#':
                old_room = self.current_room_idx
                self.player_x = new_x
                self.player_y = new_y
                
                # Проверка смены комнаты
                for i, (rx, ry, rw, rh) in enumerate(self.rooms):
                    if (rx <= self.player_x < rx + rw and
                        ry <= self.player_y < ry + rh):
                        if i != self.current_room_idx:
                            self.current_room_idx = i
                            self.discovered_rooms.add(i)
                            self.spawn_enemies()
                            
                            # Применяем эффект комнаты
                            room_type = self.room_types.get((rx, ry, rw, rh), RoomType.NORMAL)
                            self._apply_room_effect(room_type, rx, ry, rw, rh)
                            
                            # Эффект перехода между комнатами
                            self.arena_transition = True
                        break
                
                return True
        return False

    def _apply_room_effect(self, room_type, rx, ry, rw, rh):
        """Применяет эффект специальной комнаты"""
        global souls, money
        
        if room_type == RoomType.CURSE:
            # Проклятье гнили
            if random.random() < 0.3:
                damage = max(1, player["max_hp"] // 6)
                player["hp"] -= damage
                console.add_output(f"Проклятие комнаты: -{damage} HP!")
        
        elif room_type == RoomType.SHOP:
            console.add_output("Магазин! Здесь можно найти предметы.")
            self._spawn_shop_items(rx, ry, rw, rh)
        
        elif room_type == RoomType.TREASURE:
            console.add_output("Сокровищница! Здесь много ценного!")
            self._spawn_treasure(rx, ry, rw, rh)
        
        elif room_type == RoomType.SECRET:
            console.add_output("Секретная комната! Очень редкие предметы!")
            self._spawn_secret_treasure(rx, ry, rw, rh)

    def _spawn_shop_items(self, rx, ry, rw, rh):
        """Спавнит предметы в магазине"""
        for i in range(4):
            x = rx + random.randint(2, rw - 3)
            y = ry + random.randint(2, rh - 3)
            screen_x = x * 20 + 10
            screen_y = y * 20 + 10
            
            if random.random() < 0.6:
                drop_passive(screen_x, screen_y)
            else:
                drop_active(screen_x, screen_y)

    def _spawn_treasure(self, rx, ry, rw, rh):
        """Спавнит сокровища"""
        center_x = rx + rw // 2
        center_y = ry + rh // 2
        
        for _ in range(random.randint(3, 5)):
            x = center_x + random.randint(-3, 3)
            y = center_y + random.randint(-3, 3)
            screen_x = x * 20 + 10
            screen_y = y * 20 + 10
            
            if random.random() < 0.4:
                drop_passive(screen_x, screen_y, random.choice(list(powerups.keys())))
            else:
                if random.random() < 0.7:
                    drop_passive(screen_x, screen_y)
                else:
                    drop_active(screen_x, screen_y)
            
            for _ in range(random.randint(4, 8)):
                drop_coin(screen_x + random.randint(-15, 15), 
                         screen_y + random.randint(-15, 15), 
                         random.randint(1, 4))

    def _spawn_secret_treasure(self, rx, ry, rw, rh):
        """Спавнит секретные сокровища"""
        center_x = rx + rw // 2
        center_y = ry + rh // 2
        
        rare_items = ["INFINITY", "D6_II", "GLASS_CANNON", "SOUL_DEVOURER", "GREED", "MADE_OF_GLASS",
                     "BLOOD_MAGIC_III", "TIME_STOP", "METEOR"]
        
        for i in range(random.randint(2, 4)):
            x = center_x + random.randint(-2, 2)
            y = center_y + random.randint(-2, 2)
            screen_x = x * 20 + 10
            screen_y = y * 20 + 10
            
            item_key = random.choice(rare_items)
            if item_key in powerups:
                drop_passive(screen_x, screen_y, item_key)
            elif item_key in actives:
                drop_active(screen_x, screen_y, item_key)

    def is_exit(self):
        """Проверка, находится ли игрок на выходе"""
        return (self.player_x == self.exit_x and 
                self.player_y == self.exit_y and
                len(self.enemies) == 0)

    def draw(self, screen):
        """Отрисовка подземелья с большими клетками"""
        cell_size = 20
        
        for y in range(self.height):
            for x in range(self.width):
                rect = (x * cell_size, y * cell_size, cell_size - 1, cell_size - 1)
                
                if self.grid[y][x] == '#':
                    color = (60, 60, 80)
                elif self.grid[y][x] == ' ':
                    color = (180, 180, 100)
                else:
                    if (x, y) in self.doors:
                        color = (150, 120, 80)
                    else:
                        room_type = self._get_room_type_at(x, y)
                        if room_type == RoomType.SHOP:
                            color = (100, 100, 150)
                        elif room_type == RoomType.TREASURE:
                            color = (150, 150, 50)
                        elif room_type == RoomType.CURSE:
                            color = (80, 40, 80)
                        elif room_type == RoomType.SECRET:
                            color = (100, 80, 100)
                        else:
                            color = (40, 40, 50)
                
                pygame.draw.rect(screen, color, rect)
                
                # Эффект перехода между комнатами
                if self.arena_transition and (x + y) % 8 == 0:
                    pygame.draw.rect(screen, (255, 255, 255, 50), rect, 2)

    def _get_room_type_at(self, x, y):
        """Определяет тип комнаты по координатам"""
        for rx, ry, rw, rh in self.rooms:
            if rx <= x < rx + rw and ry <= y < ry + rh:
                return self.room_types.get((rx, ry, rw, rh), RoomType.NORMAL)
        return RoomType.NORMAL

    def draw_minimap(self, screen, x, y, size=200):
        """Отрисовка мини-карты"""
        cell_size = size // max(self.width, self.height)
        
        for i in range(self.height):
            for j in range(self.width):
                map_x = x + j * cell_size
                map_y = y + i * cell_size
                
                if self.grid[i][j] == '#':
                    color = (60, 60, 60)
                elif self.grid[i][j] == ' ':
                    color = (180, 180, 80)
                else:
                    room_type = self._get_room_type_at(j, i)
                    if room_type == RoomType.SHOP:
                        color = (80, 80, 150)
                    elif room_type == RoomType.TREASURE:
                        color = (150, 150, 50)
                    elif room_type == RoomType.CURSE:
                        color = (100, 40, 100)
                    elif room_type == RoomType.SECRET:
                        color = (120, 80, 120)
                    else:
                        color = (40, 40, 40)
                
                pygame.draw.rect(screen, color,
                               (map_x, map_y, cell_size - 1, cell_size - 1))
        
        # Текущая комната
        rx, ry, rw, rh = self.rooms[self.current_room_idx]
        pygame.draw.rect(screen, (0, 255, 0),
                        (x + rx * cell_size, y + ry * cell_size,
                         rw * cell_size, rh * cell_size), 2)
        
        # Игрок
        pygame.draw.circle(screen, (255, 255, 0),
                          (x + self.player_x * cell_size + cell_size // 2,
                           y + self.player_y * cell_size + cell_size // 2),
                          cell_size // 2)
        
        # Выход
        pygame.draw.circle(screen, (0, 255, 255),
                          (x + self.exit_x * cell_size + cell_size // 2,
                           y + self.exit_y * cell_size + cell_size // 2),
                          cell_size // 3)
        
        # Специальные комнаты
        for rx, ry, rw, rh in self.special_rooms:
            pygame.draw.rect(screen, (255, 215, 0),
                            (x + rx * cell_size, y + ry * cell_size,
                             rw * cell_size, rh * cell_size), 1)


# -------------------- Обновленная функция after_boss_victory --------------------

def after_boss_victory():
    global state, dungeon, wave, boss_defeated, souls, player, walls_active, can_advance

    # Поедатель душ - +1 душа за босса
    if player.get("soul_devourer", False):
        souls += 1
        player["soul_devourer_kills"] += 1
        console.add_output(f"Поедатель душ поглотил душу босса! Души: {souls}")

    # Обычный поедатель душ
    if player.get("soul_eater", False):
        souls += 1

    # Переходим к следующей волне без подземелья
    wave += 1
    boss_defeated = False
    walls_active = True
    can_advance = False
    dungeon = None
    state = GS.GAME
    
    console.add_output(f"ВОЛНА {wave}!")
    
    # Спавним врагов
    if wave % 5 == 0:
        spawn_boss(wave)
        console.add_output(f"ВОЛНА {wave} - БОСС!")
    else:
        for _ in range(spawn_count()):
            spawn_enemy()
        console.add_output(f"Заспавнено {len(enemies)} врагов")


# -------------------- Обновленная функция check_exit_boundaries --------------------

def check_exit_boundaries():
    global can_advance, boss_defeated, state, secret_room, current_arena, wave, walls_active, dungeon

    if walls_active:
        player["x"] = max(15, min(screen.get_width() - 15, player["x"]))
        player["y"] = max(15, min(screen.get_height() - 15, player["y"]))
        return

    if player["x"] > screen.get_width() + 20:
        if wave >= 100:
            state = GS.WIN
            return

        # Переходим к следующей волне без подземелья
        after_boss_victory()


# -------------------- Обновленная функция handle_dungeon_input --------------------

def handle_dungeon_input():
    global parrying, parry_cooldown, parry_success, parry_window_start, state, wave, dungeon

    keys = pygame.key.get_pressed()
    sp = PLAYER_SPEED * player["speed_mult"]  # Та же скорость, что и в основной игре

    dx = dy = 0
    if keys[pygame.K_a] or keys[pygame.K_LEFT]:
        dx = -1
    if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
        dx = 1
    if keys[pygame.K_w] or keys[pygame.K_UP]:
        dy = -1
    if keys[pygame.K_s] or keys[pygame.K_DOWN]:
        dy = 1

    if dx != 0 or dy != 0:
        if dx != 0 and dy != 0:
            sp *= 0.707
        moved = dungeon.move_player(dx, dy)
        if moved:
            player["x"] = dungeon.player_x * 20 + 10
            player["y"] = dungeon.player_y * 20 + 10

    # Стрельба в новом режиме при зажатой ЛКМ в 4 направлениях
    # Автоматическая стрельба только в классическом режиме
    player["timer"] += 1
    if player["timer"] >= player.get("fire_cd", player["base_fire_cd"]):
        shoot_dx = shoot_dy = 0
        if control_mode == "Новый":
            if mouse_pressed:
                mx, my = pygame.mouse.get_pos()
                angle = math.atan2(my - player["y"], mx - player["x"])
                # Нормализуем угол в диапазон [0, 2π)
                if angle < 0:
                    angle += 2 * math.pi
                # Ограничиваем только 4 направлениями: вправо, вверх, влево, вниз
                if angle < math.pi / 4:
                    shoot_dx = 1
                    shoot_dy = 0
                elif angle < 3 * math.pi / 4:
                    shoot_dx = 0
                    shoot_dy = -1
                elif angle < 5 * math.pi / 4:
                    shoot_dx = -1
                    shoot_dy = 0
                elif angle < 7 * math.pi / 4:
                    shoot_dx = 0
                    shoot_dy = 1
                else:
                    shoot_dx = 1
                    shoot_dy = 0
        else:
            if keys[pygame.K_UP]:
                shoot_dy = -1
            elif keys[pygame.K_DOWN]:
                shoot_dy = 1
            elif keys[pygame.K_LEFT]:
                shoot_dx = -1
            elif keys[pygame.K_RIGHT]:
                shoot_dx = 1

        if shoot_dx or shoot_dy:
            d = math.hypot(shoot_dx, shoot_dy)
            shoot_dx /= d
            shoot_dy /= d

            if "wave" in player["effects"]:
                offset = math.sin(time.time() * 5.0) * 0.3
                dx2 = math.cos(offset) * shoot_dx - math.sin(offset) * shoot_dy
                dy2 = math.sin(offset) * shoot_dx + math.cos(offset) * shoot_dy
                make_bullet(dx2, dy2)
            else:
                make_bullet(shoot_dx, shoot_dy)

            player["timer"] = 0

    # Парирование - ТАКОЕ ЖЕ
    if keys[pygame.K_SPACE] and parry_cooldown <= 0 and not parrying:
        parrying = True
        parry_window_start = time.time()
        if parry_bullet():
            parry_success = True
        else:
            parry_success = False
        parry_cooldown = PARRY_COOLDOWN

    if parrying:
        parry_cooldown -= 1 / 60
        if parry_cooldown <= 0:
            parrying = False

    # Активные предметы
    if keys[pygame.K_LCTRL] or keys[pygame.K_RCTRL]:
        now = time.time()
        for key in list(player["actives"].keys()):
            meta = player["actives"].get(key)
            if not meta:
                continue
            a = actives[key]
            cd = a["cd"]
            cdt = a["cd_type"]
            ready = (now >= meta["last_t"] + cd) if cdt == "time" else (wave >= meta["last_w"] + cd)
            if ready:
                activate_active(key)

    # Проверка выхода из подземелья
    if dungeon.is_exit() and len(dungeon.enemies) == 0:
        state = GS.GAME
        walls_active = True
        bullets.clear()
        ebullets.clear()
        player["x"] = screen.get_width() - 50
        player["y"] = screen.get_height() // 2
        dungeon = None

        # Спавним следующую волну
        if wave % 5 == 0:
            spawn_boss(wave)
            console.add_output(f"ВОЛНА {wave} - БОСС!")
        else:
            for _ in range(spawn_count()):
                spawn_enemy()
            console.add_output(f"Волна {wave}")
# -------------------- Исправленные параметры баланса --------------------
PLAYER_SPEED = 3.0
PLAYER_HIT_R = 8
BULLET_SPEED = 8.0  # Было 12.0 - теперь быстрее
ENEMY_BULLET_SPEED = 10.0  # Было 4.5 - ЗНАЧИТЕЛЬНО медленнее вражеские пули
INVUL_AFTER_HIT = 0.6
DROP_RATE = 0.08
DISPLAY_LIMIT = 8
PARRY_TIME = 0.001
PARRY_COOLDOWN = 2.0
PARRY_WINDOW = 0.12
ITEM_DESPAWN_TIME = 120
IMPACT_FORCE = 2.0
PARRY_CIRCLE_DURATION = 0.3
COIN_DROP_RATE = 0.25

# Хитбоксы для пуль и боссов (радиус в пикселях)
BULLET_HITBOX_R = 18   # Игровые пули против врагов
EBULLET_HITBOX_R = PLAYER_HIT_R + 3  # Вражеские пули против игрока
BOSS_HITBOX_R = 28     # Игровые пули против боссов
ENEMY_HITBOX_SIZE = 30   # Квадратный хитбокс врага
BOSS_HITBOX_SIZE = 45    # Квадратный хитбокс босса

# Новые параметры сложности
ENEMY_SPEED_MULT = 0.25  # Сильно медленнее игрока
BOSS_SPEED_MULT = 0.25  # Боссы ещё медленнее
BULLET_DENSITY_MULT = 3.0  # Было 2.0 - БОЛЬШЕ пуль для компенсации
MIN_BULLET_SIZE = 5  # Было 4 - чуть больше минимальный размер
MAX_BULLET_SIZE = 14  # Было 12 - больше максимальный размер

show_full = False
parrying = False
parry_cooldown = 0
parry_success = False
parry_window_start = 0
mouse_pressed = False
current_music = 0
music_play_time = 0

# Sound settings
master_volume = 1.0
music_volume = 1.0
sfx_volume = 1.0

# sound_effects = []


def play_random_sound():
    """Play a test sound effect (generated)"""
    try:
        freq = random.randint(200, 800)
        duration = 0.2
        sample_rate = 22050
        frames = int(duration * sample_rate)
        arr = array('h', [0] * frames)
        for i in range(frames):
            t = i / sample_rate
            arr[i] = int(32767 * sfx_volume * master_volume * math.sin(2 * math.pi * freq * t))
        sound = pygame.mixer.Sound(buffer=arr.tobytes())
        sound.play()
    except:
        pass


def load_music_files():
    """Load all numbered .wav files from assets/audio/"""
    music_files = []
    audio_dir = "assets/audio"
    if os.path.exists(audio_dir):
        i = 1
        while True:
            path = os.path.join(audio_dir, f"{i}.wav")
            if os.path.exists(path):
                music_files.append(path)
                i += 1
            else:
                break
    return music_files

def play_next_music():
    """Play next music track"""
    global current_music, music_play_time
    music_files = load_music_files()
    if music_files:
        current_music = (current_music + 1) % len(music_files)
        try:
            pygame.mixer.music.load(music_files[current_music])
            pygame.mixer.music.set_volume(music_volume * master_volume)
            pygame.mixer.music.play()
            music_play_time = time.time()
        except:
            pass


def bscale():
    base = 1.0 + 0.01 * min(wave, 50)
    return min(1.5, base)


def hp_scale(base):
    if wave <= 10:
        inc = base + max(0, wave // 8)
    else:
        inc = base + max(0, wave // 5)

    if wave >= 30:
        inc = int(inc * 1.3)
    if wave >= 60:
        inc = int(inc * 1.4)
    if wave >= 90:
        inc = int(inc * 1.6)
    return max(1, inc)


def spawn_count():
    return max(1, wave)


# -------------------- Enums --------------------
class AI(Enum):
    CHASE = auto()
    FLEE = auto()
    CIRCLE = auto()
    PATTERN = auto()
    SPECIAL = auto()
    HEALER = auto()


class PAT(Enum):
    SINGLE = auto()
    FOUR = auto()
    EIGHT = auto()
    SPIRAL = auto()
    SPREAD = auto()
    HOMING = auto()
    WAVE = auto()
    RING = auto()
    SPLIT = auto()
    LASER = auto()
    CROSS = auto()
    VORTEX = auto()
    ARC = auto()
    BOUNCE = auto()
    RAIN = auto()
    PENTAGON = auto()


class BULLET_SHAPE(Enum):
    CIRCLE = auto()
    DIAMOND = auto()
    STICK = auto()
    SPINNING_STICK = auto()
    STAR = auto()
    TRIANGLE = auto()
    GEAR = auto()
    ORBIT = auto()
    CRESCENT = auto()
    HALO = auto()
    NOVA = auto()


# Типы пуль (BULLET_TYPE) - определяют ФОРМУ
class BULLET_TYPE(Enum):
    # ----- КРУГЛЫЕ (основные) -----
    NORMAL  = auto()      # Простая круглая
    FAST    = auto()      # Маленькая круглая
    HEAVY   = auto()      # Большая круглая
    HOMING  = auto()      # Круглая, самонаводящаяся
    EXPLOSIVE = auto()    # Круглая, взрывная

    # ----- ЭЛЕМЕНТАЛЬНЫЕ (сложные формы) -----
    HOLY    = auto()      # Золотая (сфера)
    GHOST   = auto()      # Полупрозрачная (сфера)
    DEMON   = auto()      # Красная (полумесяц)
    FIRE    = auto()      # Огненная (вспышка/звезда-6)
    ICE     = auto()      # Ледяная (ореол)
    POISON  = auto()      # Ядовитая (шестерёнка)

    # ----- ГРАВИТАЦИОННЫЕ (звезды) -----
    GRAVITY = auto()      # Звёзда, под действием гравитации
    ELECTRIC = auto()     # Звёзда, электрическая
    VOID    = auto()      # Звёзда, чёрная дыра

    # ----- ТРЕУГОЛЬНИКИ / РОМБЫ -----
    PLASMA  = auto()      # Треугольник, плазма
    SHARD   = auto()      # Ромб, осколок стекла

    # ----- ПАЛОЧКИ (лазеры) -----
    LASER   = auto()      # Статичная палочка
    SPIRAL  = auto()      # Вращающаяся палочка


# Цвета для разных ТИПОВ пуль (запасные/дефолтные, используются если нет паттерна)
BULLET_COLORS = {
    BULLET_TYPE.NORMAL:    (255, 220, 50),   # Жёлтый
    BULLET_TYPE.FAST:      (100, 220, 255),  # Голубой
    BULLET_TYPE.HEAVY:     (255, 100, 100),  # Красный
    BULLET_TYPE.HOMING:    (180, 50, 220),   # Пурпурный
    BULLET_TYPE.EXPLOSIVE: (255, 140, 50),   # Оранжевый
    BULLET_TYPE.HOLY:      (255, 255, 200),  # Святой
    BULLET_TYPE.GHOST:     (200, 200, 220),  # Призрачный
    BULLET_TYPE.DEMON:     (255, 50, 50),    # Демонический
    BULLET_TYPE.FIRE:      (255, 80, 50),    # Огненный
    BULLET_TYPE.ICE:       (80, 200, 255),   # Ледяной
    BULLET_TYPE.POISON:    (80, 255, 80),    # Зелёный
    BULLET_TYPE.GRAVITY:   (255, 215, 0),    # Золотой (звезда)
    BULLET_TYPE.ELECTRIC:  (100, 255, 255),  # Электрический (звезда)
    BULLET_TYPE.VOID:      (80, 80, 100),    # Тёмный (звезда)
    BULLET_TYPE.PLASMA:    (160, 100, 255),  # Фиолетовый (треугольник)
    BULLET_TYPE.SHARD:     (255, 255, 80),   # Желтоватый (ромб)
    BULLET_TYPE.LASER:     (255, 80, 80),    # Красный (палочка)
    BULLET_TYPE.SPIRAL:    (180, 80, 255),   # Сиреневый (вращающаяся палочка)
}


# Сопоставление ТИПА ПУЛИ → ФОРМЫ
BULLET_TYPE_TO_SHAPE = {
    # КРУГЛЫЕ
    BULLET_TYPE.NORMAL:    BULLET_SHAPE.CIRCLE,
    BULLET_TYPE.FAST:      BULLET_SHAPE.CIRCLE,
    BULLET_TYPE.HEAVY:     BULLET_SHAPE.CIRCLE,
    BULLET_TYPE.HOLY:      BULLET_SHAPE.CIRCLE,
    BULLET_TYPE.GHOST:     BULLET_SHAPE.CIRCLE,

    # СЛОЖНЫЕ УСЛОЖНЁННЫЕ
    BULLET_TYPE.DEMON:     BULLET_SHAPE.CRESCENT,
    BULLET_TYPE.FIRE:      BULLET_SHAPE.NOVA,
    BULLET_TYPE.ICE:       BULLET_SHAPE.HALO,
    BULLET_TYPE.POISON:    BULLET_SHAPE.GEAR,
    BULLET_TYPE.HOMING:    BULLET_SHAPE.ORBIT,

    # ЗВЁЗДЫ (гравитационные)
    BULLET_TYPE.GRAVITY:   BULLET_SHAPE.STAR,
    BULLET_TYPE.ELECTRIC:  BULLET_SHAPE.STAR,
    BULLET_TYPE.VOID:      BULLET_SHAPE.STAR,

    # ВЗРЫВНЫЕ
    BULLET_TYPE.EXPLOSIVE: BULLET_SHAPE.NOVA,

    # ТРЕУГОЛЬНИКИ / РОМБЫ
    BULLET_TYPE.PLASMA:    BULLET_SHAPE.TRIANGLE,
    BULLET_TYPE.SHARD:     BULLET_SHAPE.DIAMOND,

    # ПАЛОЧКИ
    BULLET_TYPE.LASER:     BULLET_SHAPE.STICK,
    BULLET_TYPE.SPIRAL:    BULLET_SHAPE.SPINNING_STICK,
}

# Маппинг паттернов к типам пуль (форма зависит от типа)
PATTERN_TO_BULLET_TYPE = {
    PAT.SINGLE:  BULLET_TYPE.NORMAL,
    PAT.FOUR:    BULLET_TYPE.SHARD,
    PAT.EIGHT:   BULLET_TYPE.NORMAL,
    PAT.SPIRAL:  BULLET_TYPE.SPIRAL,
    PAT.SPREAD:  BULLET_TYPE.PLASMA,
    PAT.HOMING:  BULLET_TYPE.HOMING,
    PAT.WAVE:    BULLET_TYPE.PLASMA,
    PAT.RING:    BULLET_TYPE.SHARD,
    PAT.SPLIT:   BULLET_TYPE.SHARD,
    PAT.LASER:   BULLET_TYPE.LASER,
    PAT.CROSS:   BULLET_TYPE.NORMAL,
    PAT.VORTEX:  BULLET_TYPE.SPIRAL,
    PAT.ARC:     BULLET_TYPE.PLASMA,
    PAT.BOUNCE:  BULLET_TYPE.NORMAL,
    PAT.RAIN:    BULLET_TYPE.GRAVITY,
    PAT.PENTAGON:BULLET_TYPE.HOLY,
}


# Цвета ПАТТЕРНОВ — определяют цвет пули для данного паттерна врага
# Форма пули зависит от bullet_type, цвет — от паттерна
PATTERN_COLORS = {
    PAT.SINGLE:   (255, 220, 50),   # Жёлтый
    PAT.FOUR:     (100, 220, 255),  # Голубой
    PAT.EIGHT:    (255, 255,  50),  # Ярко-жёлтый
    PAT.SPIRAL:   (160, 100, 255),  # Фиолетовый
    PAT.SPREAD:   (255, 100, 100),  # Алый
    PAT.HOMING:   (180,  50, 220),  # Пурпурный
    PAT.WAVE:     (  0, 200, 255),  # Бирюзовый
    PAT.RING:     (220, 200,   0),  # Золотистый
    PAT.SPLIT:    (120, 255, 120),  # Светло-зелёный
    PAT.LASER:    (255,  80,  80),  # Красный
    PAT.CROSS:    (255, 160,  50),  # Оранжевый
    PAT.VORTEX:   (180,  80, 255),  # Сиреневый
    PAT.ARC:      (100, 255, 180),  # Мятный
    PAT.BOUNCE:   (  0, 180, 200),  # Циановый
    PAT.RAIN:     (255, 100,  50),  # Оранжево-красный
    PAT.PENTAGON: (200, 100, 255),  # Фиолетовый
}

# Обратный индекс: bullet_type → PAT (для определения цвета, если у пули нет паттерна)
BULLET_TYPE_TO_PATTERN = {
    BULLET_TYPE.NORMAL:  PAT.SINGLE,
    BULLET_TYPE.SHARD:   PAT.FOUR,
    BULLET_TYPE.SPIRAL:  PAT.SPIRAL,
    BULLET_TYPE.PLASMA:  PAT.SPREAD,
    BULLET_TYPE.HOMING:  PAT.HOMING,
    BULLET_TYPE.LASER:   PAT.LASER,
    BULLET_TYPE.GRAVITY: PAT.RAIN,
    BULLET_TYPE.HOLY:    PAT.PENTAGON,
    # Остальные типы без паттерна — будут использовать BULLET_COLORS напрямую
    BULLET_TYPE.FAST:       None,
    BULLET_TYPE.HEAVY:      None,
    BULLET_TYPE.EXPLOSIVE:  None,
    BULLET_TYPE.GHOST:      None,
    BULLET_TYPE.DEMON:      None,
    BULLET_TYPE.FIRE:       None,
    BULLET_TYPE.ICE:        None,
    BULLET_TYPE.POISON:     None,
    BULLET_TYPE.ELECTRIC:   None,
    BULLET_TYPE.VOID:       None,
}


# Маппинг паттерн → форма (для игровых пуль, ч/з PAT_SHAPE_COLOR)
# Форме соответствуют фигура BULLET_SHAPE
PAT_SHAPE_MAP = {
    PAT.SINGLE:  BULLET_SHAPE.CIRCLE,
    PAT.FOUR:    BULLET_SHAPE.DIAMOND,
    PAT.EIGHT:   BULLET_SHAPE.STAR,
    PAT.SPIRAL:  BULLET_SHAPE.SPINNING_STICK,
    PAT.SPREAD:  BULLET_SHAPE.TRIANGLE,
    PAT.HOMING:  BULLET_SHAPE.STICK,
    PAT.WAVE:    BULLET_SHAPE.DIAMOND,
    PAT.RING:    BULLET_SHAPE.CIRCLE,
    PAT.SPLIT:   BULLET_SHAPE.STAR,
    PAT.LASER:   BULLET_SHAPE.STICK,
    PAT.CROSS:   BULLET_SHAPE.STAR,
    PAT.VORTEX:  BULLET_SHAPE.SPINNING_STICK,
    PAT.ARC:     BULLET_SHAPE.DIAMOND,
    PAT.BOUNCE:  BULLET_SHAPE.TRIANGLE,
    PAT.RAIN:    BULLET_SHAPE.CIRCLE,
    PAT.PENTAGON:BULLET_SHAPE.STAR,
}


def draw_shape(surf, shape, color, cx, cy, size, angle=0.0):
    """Нарисовать фигуру формы shape на поверхности surf. БЕЗ обводки."""
    if not isinstance(color, (tuple, list)) or len(color) < 3:
        return

    if shape == BULLET_SHAPE.CIRCLE:
        pygame.draw.circle(surf, color, (int(cx), int(cy)), max(1, size))

    elif shape == BULLET_SHAPE.DIAMOND:
        pts = [
            (int(cx),           int(cy) - max(1, size)),
            (int(cx) + max(1, size), int(cy)),
            (int(cx),           int(cy) + max(1, size)),
            (int(cx) - max(1, size), int(cy)),
        ]
        pygame.draw.polygon(surf, color, pts)

    elif shape == BULLET_SHAPE.STICK:
        ex = int(cx + math.cos(angle) * max(1, size))
        ey = int(cy + math.sin(angle) * max(1, size))
        bx = int(cx - math.cos(angle) * max(1, size))
        by = int(cy - math.sin(angle) * max(1, size))
        pygame.draw.line(surf, color, (bx, by), (ex, ey), 2)

    elif shape == BULLET_SHAPE.SPINNING_STICK:
        sp = abs(math.sin(time.time() * 6))
        lx = cx + math.cos(angle) * max(1, size) * sp
        ly = cy + math.sin(angle) * max(1, size) * sp
        bx2 = cx - math.cos(angle) * max(1, size) * sp
        by2 = cy - math.sin(angle) * max(1, size) * sp
        pygame.draw.line(surf, color, (int(bx2), int(by2)), (int(lx), int(ly)), 2)

    elif shape == BULLET_SHAPE.STAR:
        for arm_i in range(5):
            a0 = angle + arm_i * math.tau / 5
            a1 = a0 + math.tau / 10
            a2 = a0 - math.tau / 10
            r_outer = max(1, size)
            r_inner = max(1, int(size * 0.38))
            pts = [
                (int(cx + math.cos(a0) * r_outer), int(cy + math.sin(a0) * r_outer)),
                (int(cx + math.cos(a1) * r_inner), int(cy + math.sin(a1) * r_inner)),
                (int(cx + math.cos(a2) * r_inner), int(cy + math.sin(a2) * r_inner)),
            ]
            pygame.draw.polygon(surf, color, pts)

    elif shape == BULLET_SHAPE.TRIANGLE:
        pts = [
            (int(cx + math.cos(angle) * max(1, size)), int(cy + math.sin(angle) * max(1, size))),
            (int(cx - math.sin(angle) * max(1, size)), int(cy + math.cos(angle) * max(1, size))),
            (int(cx + math.sin(angle) * max(1, size)), int(cy - math.cos(angle) * max(1, size))),
        ]
        pygame.draw.polygon(surf, color, pts)

    elif shape in (BULLET_SHAPE.GEAR, BULLET_SHAPE.ORBIT,
                   BULLET_SHAPE.CRESCENT, BULLET_SHAPE.HALO,
                   BULLET_SHAPE.NOVA):

        if shape == BULLET_SHAPE.GEAR:
            r1 = max(1, size)
            r2 = max(1, int(size * 0.72))
            teeth = 8
            seg = teeth * 2
            gs = abs(math.sin(time.time() * 4)) * 0.4 + 0.8
            os = int(size * 0.18 * gs)
            pts = []
            for i in range(seg):
                a = angle + i * math.tau / seg
                r = (r1 + os) if (i % 2 == 0) else r2
                pts.append((int(cx + math.cos(a) * r), int(cy + math.sin(a) * r)))
            pygame.draw.polygon(surf, color, pts)
            r_hole = max(1, int(size * 0.35))
            pygame.draw.circle(surf, (0, 0, 0), (int(cx), int(cy)), r_hole)
            pygame.draw.circle(surf, color,  (int(cx), int(cy)), r_hole, 1)

        elif shape == BULLET_SHAPE.ORBIT:
            orbit_r = size * 0.6
            ring_surf = pygame.Surface((int(orbit_r * 2 + 6), int(orbit_r * 2 + 6)), pygame.SRCALPHA)
            rc = int(orbit_r) + 3
            pygame.draw.circle(ring_surf, (*color, 80), (rc, rc), int(orbit_r), 1)
            off = 0.38 + 0.38 * math.sin(time.time() * 2.4)
            n_dots = 3
            for oi in range(n_dots):
                ring_ang = oi * math.tau / n_dots + time.time() * 1.5
                ox = rc + math.cos(ring_ang) * orbit_r
                oy = rc + math.sin(ring_ang) * orbit_r
                pygame.draw.circle(ring_surf, color, (int(ox), int(oy)), max(1, int(size * 0.28)))
            dx = int(cx) - rc
            dy = int(cy) - rc
            surf.blit(ring_surf, (dx, dy))
            pygame.draw.circle(surf, color, (int(cx), int(cy)), max(1, int(size * 0.44)))

        elif shape == BULLET_SHAPE.CRESCENT:
            off = size * 0.52
            cx_main = int(cx)
            cy_main = int(cy)
            # base circle
            pygame.draw.circle(surf, color, (cx_main, cy_main), max(1, size))
            # subtract right part with two stacked circles
            cut_r = int(size * 0.9)
            gap_cy = cy_main + int(off * 0.25)
            pygame.draw.circle(surf, (0, 0, 0), (cx_main + cut_r, gap_cy), max(1, int(size * 0.75)))
            pygame.draw.circle(surf, (0, 0, 0), (cx_main + cut_r, cy_main - cut_r // 2), cut_r)

        elif shape == BULLET_SHAPE.HALO:
            r1 = max(1, int(size * 1.2))
            halo_surf = pygame.Surface((r1 * 2 + 4, r1 * 2 + 4), pygame.SRCALPHA)
            hc = r1 + 2
            pygame.draw.circle(halo_surf, (*color, 50),  (hc, hc), r1)
            pygame.draw.circle(halo_surf, (*color, 25),  (hc, hc), max(1, int(r1 * 0.72)))
            surf.blit(halo_surf, (int(cx) - hc, int(cy) - hc))
            r2 = max(1, int(size * 0.42))
            pygame.draw.circle(surf, color, (int(cx), int(cy)), r2)

        elif shape == BULLET_SHAPE.NOVA:
            spikes = 6
            for si in range(spikes * 2):
                a0 = angle + si * math.tau / (spikes * 2)
                a1 = a0 + math.tau / (spikes * 4)
                r_o = max(1, size)
                r_i = max(1, int(size * 0.46))
                r = r_o if (si % 2 == 0) else r_i
                pts = [(int(cx), int(cy)),
                       (int(cx + math.cos(a0) * r), int(cy + math.sin(a0) * r)),
                       (int(cx + math.cos(a1) * r), int(cy + math.sin(a1) * r))]
                pygame.draw.polygon(surf, color, pts)


def draw_bullet(surf, bullet_type, color, cx, cy, size, angle=0.0):
    """Нарисовать пулю с белой обводкой (форма зависит от ТИПА)"""

    if not isinstance(color, (tuple, list)) or len(color) < 3:
        color = BULLET_COLORS.get(bullet_type, (255, 80, 80))

    shape = BULLET_TYPE_TO_SHAPE.get(bullet_type, BULLET_SHAPE.CIRCLE)
    import sys
    _dbg_draw = getattr(sys, '_dbg_draw', 0) + 1
    setattr(sys, '_dbg_draw', _dbg_draw)
    if _dbg_draw <= 12:
        print(f"draw_bullet #{_dbg_draw}: bt={bullet_type.name}, color={color}, shape={shape.name}, size={size}")

    lw = max(2, size // 2)
    if shape in (BULLET_SHAPE.STICK, BULLET_SHAPE.SPINNING_STICK):
        outline_w = lw + 2
        ex = int(cx + math.cos(angle) * (size + 2))
        ey = int(cy + math.sin(angle) * (size + 2))
        bx_v = int(cx - math.cos(angle) * (size + 2))
        by_v = int(cy - math.sin(angle) * (size + 2))
        if shape == BULLET_SHAPE.SPINNING_STICK:
            sp = abs(math.sin(time.time() * 6))
            lx = cx + math.cos(angle) * (size + 2) * sp
            ly = cy + math.sin(angle) * (size + 2) * sp
            bx2 = cx - math.cos(angle) * (size + 2) * sp
            by2 = cy - math.sin(angle) * (size + 2) * sp
            pygame.draw.line(surf, (255, 255, 255), (int(bx2), int(by2)), (int(lx), int(ly)), outline_w)
            sp2 = abs(math.sin(time.time() * 6))
            lx2 = cx + math.cos(angle) * size * sp2
            ly2 = cy + math.sin(angle) * size * sp2
            bx3 = cx - math.cos(angle) * size * sp2
            by3 = cy - math.sin(angle) * size * sp2
            pygame.draw.line(surf, color, (int(bx3), int(by3)), (int(lx2), int(ly2)), lw)
        else:
            pygame.draw.line(surf, (255, 255, 255), (bx_v, by_v), (ex, ey), outline_w)
            pygame.draw.line(surf, color, (bx_v, by_v), (ex, ey), lw)
    else:
        draw_shape(surf, shape, (255, 255, 255), cx, cy, size + 2, angle)
        draw_shape(surf, shape, color,           cx, cy, size,     angle)


# Семь смертных грехов
SIN_BOSSES = {
    "pride": {"name": "Гордыня", "char": "P", "color": (255, 215, 0), "hp": 800,
              "pattern": PAT.SPIRAL, "delay": 0.7, "spd": 2.2, "special": "cross_spiral"},
    "greed": {"name": "Алчность", "char": "G", "color": (255, 215, 0), "hp": 700,
              "pattern": PAT.RING, "delay": 0.8, "spd": 2.0, "special": "homing_ring"},
    "lust": {"name": "Похоть", "char": "L", "color": (255, 100, 180), "hp": 650,
             "pattern": PAT.HOMING, "delay": 0.6, "spd": 2.4, "special": "god_pattern"},
    "envy": {"name": "Зависть", "char": "E", "color": (0, 255, 0), "hp": 600,
             "pattern": PAT.SPLIT, "delay": 0.7, "spd": 2.2, "special": "vortex"},
    "gluttony": {"name": "Чревоугодие", "char": "G", "color": (150, 100, 50), "hp": 750,
                 "pattern": PAT.WAVE, "delay": 0.8, "spd": 1.9, "special": "ring"},
    "wrath": {"name": "Гнев", "char": "W", "color": (255, 0, 0), "hp": 850,
              "pattern": PAT.LASER, "delay": 0.6, "spd": 2.5, "special": "cross_spiral"},
    "sloth": {"name": "Лень", "char": "S", "color": (100, 100, 100), "hp": 550,
              "pattern": PAT.SINGLE, "delay": 1.2, "spd": 1.3, "special": "homing"},
}


# -------------------- Console System --------------------
class Console:
    def __init__(self):
        self.active = False
        self.text = ""
        self.history = []
        self.history_index = 0
        self.max_history = 20
        self.output = []
        self.max_output = 10
        self.paused = False
        # Check for UrC.txt file
        self.enabled = False
        try:
            if os.path.exists("UrC.txt"):
                with open("UrC.txt", "r") as f:
                    content = f.read().strip().lower()
                    if content == "true":
                        self.enabled = True
        except:
            pass

    def toggle(self):
        if not self.enabled:
            return
        self.active = not self.active
        if self.active:
            self.text = ""
            self.paused = True
            self.add_output("Консоль активирована. Введите 'help' для списка команд.")
        else:
            self.paused = False

    def add_char(self, char):
        self.text += char

    def backspace(self):
        self.text = self.text[:-1]

    def execute(self):
        if not self.text.strip():
            return

        command = self.text.strip().lower()
        self.history.insert(0, command)
        if len(self.history) > self.max_history:
            self.history.pop()

        self.add_output(f"> {self.text}")
        result = self.process_command(command)
        if result:
            self.add_output(result)

        self.text = ""
        self.history_index = 0

    def process_command(self, cmd):
        global wave, souls, money, player, enemies, bosses, ppick, apick, hearts, game_over, victory, current_arena, walls_active, boss_defeated, can_advance, dungeon

        parts = cmd.split()
        if not parts:
            return "Пустая команда"

        command = parts[0]
        args = parts[1:]

        if command == "help":
            help_text = [
                "Доступные команды:",
                "  help - показать эту справку",
                "  god - включить/выключить режим бога",
                "  wave [num] - установить волну",
                "  souls [num] - установить количество душ",
                "  money [num] - установить количество денег",
                "  hp [num] - установить HP",
                "  maxhp [num] - установить максимальное HP",
                "  dmg [num] - установить урон",
                "  speed [num] - установить скорость",
                "  item [name] - получить предмет",
                "  spawn [type] - заспавнить врага",
                "  killall - убить всех врагов",
                "  clear - очистить консоль",
                "  giveall - получить все предметы",
                "  achievements - получить все ачивки",
                "  win - победить текущую волну",
                "  next - принудительный переход на следующую волну",
                "  dungeon - перейти в подземелье",
                "  status - показать статус игрока"
            ]
            return "\n".join(help_text)

        elif command == "god":
            player.setdefault("god_mode", False)
            player["god_mode"] = not player["god_mode"]
            return f"Режим бога: {'ВКЛЮЧЕН' if player['god_mode'] else 'ВЫКЛЮЧЕН'}"

        elif command == "wave":
            if args:
                try:
                    wave = int(args[0])
                    enemies.clear()
                    bosses.clear()
                    ebullets.clear()
                    bullets.clear()
                    return f"Волна установлена: {wave}"
                except:
                    return "Неверный аргумент"
            else:
                return f"Текущая волна: {wave}"

        elif command == "next":
            wave += 1
            enemies.clear()
            bosses.clear()
            ebullets.clear()
            bullets.clear()
            walls_active = True
            boss_defeated = False
            can_advance = False

            if wave % 5 == 0:
                spawn_boss(wave)
                return f"Переход на волну {wave} (БОСС!)"
            else:
                for _ in range(spawn_count()):
                    spawn_enemy()
                return f"Переход на волну {wave}"

        elif command == "dungeon":
            dungeon = Dungeon()
            dungeon.generate()
            if NEW_ROOM_SYSTEM_AVAILABLE:
                init_floor(1)
            dungeon.player_x = dungeon.rooms[0][0] + dungeon.rooms[0][2] // 2
            dungeon.player_y = dungeon.rooms[0][1] + dungeon.rooms[0][3] // 2
            player["x"] = dungeon.player_x * 20 + 10
            player["y"] = dungeon.player_y * 20 + 10
            dungeon.spawn_enemies()
            return "Подземелье создано!"

        elif command == "souls":
            if args:
                try:
                    souls = int(args[0])
                    return f"Души установлены: {souls}"
                except:
                    return "Неверный аргумент"
            else:
                return f"Текущие души: {souls}"

        elif command == "money":
            if args:
                try:
                    money = int(args[0])
                    return f"Деньги установлены: {money}"
                except:
                    return "Неверный аргумент"
            else:
                return f"Текущие деньги: {money}"

        elif command == "hp":
            if args:
                try:
                    player["hp"] = int(args[0])
                    return f"HP установлено: {player['hp']}"
                except:
                    return "Неверный аргумент"
            else:
                return f"Текущее HP: {player['hp']}/{player['max_hp']}"

        elif command == "maxhp":
            if args:
                try:
                    player["max_hp"] = int(args[0])
                    player["hp"] = min(player["hp"], player["max_hp"])
                    return f"Максимальное HP установлено: {player['max_hp']}"
                except:
                    return "Неверный аргумент"
            else:
                return f"Максимальное HP: {player['max_hp']}"

        elif command == "dmg":
            if args:
                try:
                    player["dmg"] = int(args[0])
                    return f"Урон установлен: {player['dmg']}"
                except:
                    return "Неверный аргумент"
            else:
                return f"Текущий урон: {player['dmg']}"

        elif command == "speed":
            if args:
                try:
                    player["speed_mult"] = float(args[0])
                    return f"Скорость установлена: {player['speed_mult']}"
                except:
                    return "Неверный аргумент"
            else:
                return f"Текущая скорость: {player['speed_mult']}"

        elif command == "item":
            if args:
                item_name = args[0].upper()
                if item_name in powerups:
                    apply_passive(item_name)
                    return f"Получен предмет: {powerups[item_name]['name']}"
                elif item_name in actives:
                    pick_active(item_name)
                    return f"Получен активный предмет: {actives[item_name]['name']}"
                else:
                    return f"Предмет '{item_name}' не найден"
            else:
                return "Использование: item [название_предмета]"

        elif command == "spawn":
            if args:
                enemy_type = args[0].upper()
                if enemy_type == "BOSS":
                    spawn_boss(wave)
                    return f"Заспавнен босс волны {wave}"
                elif enemy_type in ENEMIES or enemy_type in ELITES or enemy_type in ULTRAS:
                    spawn_enemy(enemy_type)
                    return f"Заспавнен враг: {enemy_type}"
                else:
                    return f"Тип врага '{enemy_type}' не найден"
            else:
                return "Использование: spawn [тип_врага] или spawn BOSS"

        elif command == "killall":
            count_enemies = len(enemies)
            count_bosses = len(bosses)
            enemies.clear()
            bosses.clear()
            ebullets.clear()
            bullets.clear()
            return f"Убито {count_enemies} врагов и {count_bosses} боссов"

        elif command == "clear":
            self.output.clear()
            return "Консоль очищена"

        elif command == "giveall":
            count = 0
            for item_key in powerups:
                apply_passive(item_key)
                count += 1
            for item_key in actives:
                pick_active(item_key)
                count += 1
            return f"Получены все {count} предметов"

        elif command == "achievements":
            for ach_key in ACHIEVEMENTS_LIST:
                achievements[ach_key] = True
            return "Все ачивки получены!"

        elif command == "win":
            enemies.clear()
            bosses.clear()
            ebullets.clear()
            bullets.clear()
            return "Волна очищена!"

        elif command == "status":
            status_text = [
                f"Волна: {wave}",
                f"HP: {player['hp']}/{player['max_hp']}",
                f"Души: {souls}",
                f"Деньги: {money}",
                f"Урон: {player['dmg']}",
                f"Скорость: {player['speed_mult']}",
                f"Счёт: {player['score']}",
                f"Врагов: {len(enemies)}",
                f"Боссов: {len(bosses)}",
                f"Предметов: {len(player['effects'])}",
                f"Активных: {len(player['actives'])}",
                f"Проклятых: {player.get('cursed_items', 0)}"
            ]
            return "\n".join(status_text)

        elif command == "rooms":
            if NEW_ROOM_SYSTEM_AVAILABLE:
                from new_room_system import toggle_room_system, is_room_system_enabled
                if args:
                    if args[0] in ("on", "1", "true", "yes"):
                        enabled = True
                    elif args[0] in ("off", "0", "false", "no"):
                        enabled = False
                    else:
                        enabled = toggle_room_system()
                else:
                    enabled = toggle_room_system()
                status = "ВКЛЮЧЕНА" if is_room_system_enabled() else "ВЫКЛЮЧЕНА"
                return f"Новая система комнат: {status}"
            else:
                return "Новая система комнат недоступна"

        else:
            return f"Неизвестная команда: {command}. Введите 'help' для справки."

    def add_output(self, text):
        self.output.insert(0, text)
        if len(self.output) > self.max_output:
            self.output.pop()

    def history_up(self):
        if self.history_index < len(self.history):
            self.history_index += 1
            if self.history_index <= len(self.history):
                self.text = self.history[self.history_index - 1]

    def history_down(self):
        if self.history_index > 0:
            self.history_index -= 1
            if self.history_index > 0:
                self.text = self.history[self.history_index - 1]
            else:
                self.text = ""

    def draw(self, screen):
        if not self.active:
            return

        console_height = 300
        s = pygame.Surface((screen.get_width(), console_height), pygame.SRCALPHA)
        s.fill((0, 0, 0, 220))
        screen.blit(s, (0, 0))

        draw("КОНСОЛЬ ОТЛАДКИ (~ для закрытия)", 10, 10, CYAN, FONT_S)

        y = 40
        for i, line in enumerate(self.output):
            draw(line, 10, y, WHITE, FONT_XS)
            y += 20
            if y > console_height - 60:
                break

        input_y = console_height - 40
        pygame.draw.rect(screen, (50, 50, 50), (5, input_y - 5, screen.get_width() - 10, 30))
        draw(f"> {self.text}_", 10, input_y, GREEN, FONT_S)


console = Console()

# -------------------- Achievements --------------------
ACHIEVEMENTS_LIST = {
    "first_blood": ("First Blood", "Убить первого врага"),
    "boss_slayer": ("Boss Slayer", "Победить первого босса"),
    "wave_10": ("Survivor", "Достичь 10 волны"),
    "wave_25": ("Veteran", "Достичь 25 волны"),
    "wave_50": ("Legend", "Достичь 50 волны"),
    "wave_100": ("God Killer", "Победить Бога"),
    "no_hit_boss": ("Flawless", "Победить босса без получения урона"),
    "parry_master": ("Parry Master", "Парировать 100 снарядов"),
    "collector": ("Collector", "Собрать 50 предметов"),
    "rich": ("Rich", "Накопить 1000 очков"),
    "soul_saver": ("Soul Saver", "Пройти волну не потеряв души"),
    "speedrunner": ("Speedrunner", "Пройти 20 волн за 5 минут"),
    "combo_king": ("Combo King", "Набрать комбо из 50 убийств подряд"),
    "elementalist": ("Elementalist", "Применить все 4 статусных эффекта за одну волну"),
    "lucky_devil": ("Lucky Devil", "Выиграть в азартном предмете 3 раза подряд"),
    "immortal": ("Immortal", "Пройти 5 волн подряд без получения урона"),
    "hoarder": ("Hoarder", "Иметь 10+ пассивных предметов одновременно"),
    "specialist": ("Specialist", "Максимально прокачать одну характеристику"),
    "glass_cannon": ("Glass Cannon", "Победить босса с 1 HP"),
    "pacifist": ("Pacifist", "Пройти волну без убийств врагов"),
    "blessed": ("Blessed", "Получить 3 легендарных предмета за игру"),
    "cursed": ("Cursed", "Получить 5 проклятых предметов за игру"),
    "multitasker": ("Multitasker", "Активировать 3 активных предмета одновременно"),
    "sharpshooter": ("Sharpshooter", "Попасть 1000 снарядов без промаха"),
    "unstoppable": ("Unstoppable", "Пройти 50 волн без смерти"),
    "treasure_hunter": ("Treasure Hunter", "Собрать 20 уникальных предметов"),
    "time_lord": ("Time Lord", "Замедлить время на 30 секунд суммарно"),
    "invincible": ("Invincible", "Остаться с неуязвимостью на 10 секунд подряд"),
    "blood_bank": ("Blood Bank", "Восстановить 50 HP через вампиризм"),
    "ninja": ("Ninja", "Увернуться от 100 снарядов подряд"),
    "demigod": ("Demigod", "Достичь 100 урона за один выстрел"),
    "perfect_game": ("Perfect Game", "Пройти игру без единой смерти"),
    "bullet_hell": ("Bullet Hell", "Одновременно уворачиваться от 50+ пуль"),
    "paragon": ("Paragon", "Получить все положительные характеристики"),
    "cursed_one": ("Cursed One", "Собрать все проклятые предметы"),
    "ghost": ("Ghost", "Пройти волну не получив урона"),
    "berserk": ("Berserk", "Убить 100 врагов за 1 волну"),
    "economist": ("Economist", "Накопить 5000 очков"),
    "kamikaze": ("Kamikaze", "Умереть с 0 душами и полным HP"),
    "explorer": ("Explorer", "Увидеть всех врагов"),
    "completionist": ("Completionist", "Получить 50% ачивок"),
    "master": ("Master", "Получить 75% ачивок"),
    "legendary": ("Legendary", "Получить все ачивки"),
    "curse_collector": ("Curse Collector", "Собрать все проклятые предметы за одну игру"),
    "moneybags": ("Moneybags", "Накопить 1000 монет"),
    "parry_god": ("Parry God", "Парировать 10 раз подряд без промаха"),
    "d6_luck": ("D6 Luck", "Воскреснуть благодаря D6 3 раза"),
    "infinity": ("Infinity", "Взять предмет Бесконечность и пройти 10 волн с ним"),
    "glass_demon": ("Glass Demon", "Взять Стеклянную пушку и не потерять ни одной души"),
    "dungeon_master": ("Dungeon Master", "Пройти 10 подземелий"),
    "explorer_II": ("Explorer II", "Посетить все специальные комнаты"),
    "creditor": ("Creditor", "Выжить с Кредитом при отрицательном HP"),
    "soul_eater_II": ("Soul Eater", "Съесть 10 душ боссов"),
    "traitor": ("Traitor", "Убить 50 врагов выстрелами сзади"),
    "curse_ bearer": ("Curse Bearer", "Иметь 3 проклятых предмета одновременно"),
    "glass_God": ("Glass God", "Пройти волну с 1 HP не получив урона"),
}


def get_achievement_progress():
    total = len(ACHIEVEMENTS_LIST)
    completed = sum(1 for a in ACHIEVEMENTS_LIST if achievements[a])
    return completed, total, completed / total if total > 0 else 0


# -------------------- Items registry --------------------
powerups = {}
actives = {}
rand = random.Random()


def reg_items():
    powerups.clear()
    actives.clear()

    def P(key, name, char, color, desc, **p):
        powerups[key] = {"name": name, "char": char, "color": color, "desc": desc, "p": p}

    def A(key, name, char, color, desc, cd_type, cd, action, **p):
        actives[key] = {"name": name, "char": char, "color": color, "desc": desc,
                        "cd_type": cd_type, "cd": cd, "action": action, "p": p}

    # Базовые предметы
    P("HP_UP", "HP UP", "H", (255, 80, 80), "+1 контейнер (можно уйти в минус)", maxhp=1, negative_hp=True)
    P("DMG_UP", "DMG UP", "D", (255, 120, 120), "+1 урон", dmg=1)
    P("LUCK_UP", "LUCK UP", "L", (255, 215, 0), "+1 удача", luck=1)
    P("SPEED_UP", "SPEED UP", "V", (120, 200, 255), "+10% скорости", spd=0.1)
    P("ALL_UP", "ALL UP", "A", (255, 255, 180), "+1 всех характеристик", dmg=1, maxhp=1, spd=0.05, luck=1)
    P("SOUL", "Душа", "Q", (180, 80, 255), "+1 душа", soul=1)
    P("FIRERATE", "Скорострельность", "R", (255, 200, 100), "+1 скорострельность", firerate=1)
    P("RANGE", "Дальность", "Z", (100, 220, 255), "+1 дальность", range=1)

    # Новые предметы
    P("CREDIT", "Кредит", "$", (255, 215, 0), "Можно уйти в минус, 60 сек на восстановление",
      credit=True, cursed=True)
    P("D6_II", "D6 II", "6", (255, 100, 255), "1/6 шанс воскреснуть (работает с душами)",
      d6_II=True, legendary=True)
    P("TRAITOR", "Предатель", "T", (100, 0, 0), "Выстрелы сзади: 100% кровотечение",
      traitor=True)
    P("SOUL_EATER", "Поедатель душ", "S", (80, 0, 80), "Души = 0, каждый босс даёт +1 душу",
      soul_eater=True, cursed=True, soul=-3)

    # Новые предметы из задания
    P("ROT_CURSE", "Проклятье гнили", "R", (0, 100, 0), 
      "Враги отравляются с шансом 100%, каждую волну 50% шанс потери HP",
      rot_curse=True, cursed=True)
    
    P("SOUL_DEVOURER", "Поедатель душ", "S", (80, 0, 80),
      "Души обнуляются, за каждого босса +1 душа",
      soul_devourer=True, cursed=True, soul=-999)
    
    P("GREED", "Алчнее", "G", (255, 215, 0),
      "HP заменяется на деньги, души не могут быть больше 0, деньги исчезают за 2 секунды",
      greed=True, cursed=True)
    
    P("MADE_OF_GLASS", "Словно сделан из стекла", "G", (200, 200, 255),
      "Души обнуляются, 1 HP, +50 урона",
      made_of_glass=True, cursed=True, dmg=50, maxhp=-999, soul=-999)

    # Остальные предметы
    P("CRAB", "Рак", "C", (255, 100, 100), "Регенерация", regen=0.08)
    P("PIERCE", "Пробитие", "P", (180, 0, 255), "Пули проходят сквозь врагов", pierce=True)
    P("BURNER", "Выжигатель", "B", (255, 60, 0), "Луч, поджигающий врагов", laser=True, burn=True)
    P("GRENADIER", "Гренадёр", "G", (150, 100, 50), "Навесные взрывающиеся снаряды", grenade=True)
    P("GLASS", "Стекло", "S", (200, 200, 255), "Снаряды разлетаются при попадании", split=True, bleed=True)
    P("CHAOS", "Смута", "~", (180, 60, 220), "Волнообразные выстрелы", wave=True)
    P("WWW", "WWW", "W", (100, 200, 255), "Враги разлетаются на 8 пуль при смерти", explode=True)
    P("PYRO", "Пироманьяк", "F", (255, 100, 0), "Пули заменяются на огонь", fire=True)
    P("BOMBER", "Бомбер мен", "M", (100, 100, 100), "Бомбы, взрывающиеся крестом", bomb=True)
    P("PACMAN", "Pac-Man", "O", (255, 255, 0), "Телепортация через края", pacman=True)
    P("BLEED", "Кровотечение", "&", (200, 0, 0), "8% шанс на кровотечение", bleed_chance=0.08)
    P("IGNITE", "Поджигатель", "I", (255, 140, 0), "8% шанс на горение", burn_chance=0.08)
    P("POISON", "Новичок", "N", (80, 255, 80), "8% шанс на отравление", poison_chance=0.08)
    P("MEDUSA", "Медуза", "J", (100, 255, 255), "5% шанс на окаменение", stone_chance=0.05)
    P("SOUL_CONTRACT", "Контракт души", "X", (220, 50, 220), "-1 душа, +5 урона", soul=-1, dmg=5, cursed=True)
    P("BODY", "Тело", "T", (180, 120, 80), "+1 душа, -1 контейнер", soul=1, maxhp=-1, cursed=True)
    P("TELEPATHY", "Телепатия", "Y", (180, 255, 180), "Базовая самонаводка", homing=True)
    P("TELEPATHY_II", "Телепатия II", "Y", (100, 255, 100), "Улучшенная самонаводка", homing_plus=True)
    P("BLOOD_RAY", "Кровавый луч", "K", (220, 0, 0), "Луч, проходящий сквозь врагов", blood_ray=True)
    P("INFALLIBILITY", "Непогрешимость", "U", (255, 255, 100), "+1 урон за волну без урона", infallibility=True)
    P("CRUELTY", "Жестокость", "C", (150, 0, 0), "+1 урон за убийство, сбрасывается при получении урона", cruelty=True)
    P("LUCK_?", "Удача ?", "?", (255, 150, 255), "50% шанс +10 или -10 удачи", gamble=True)

    P("DOUBLE", "2X", "2", (200, 200, 255), "Двойной выстрел (-скорострельность)", double=True, firerate=-1)
    P("TRIPLE", "3X", "3", (180, 180, 255), "Тройной выстрел (-скорострельность)", triple=True, firerate=-2)
    P("QUAD", "4X", "4", (150, 150, 255), "Четверной выстрел (-скорострельность)", quad=True, firerate=-3)

    P("MAGNET", "Магнит", "M", (100, 200, 100), "Притягивание предметов", magnet=100)
    P("TIME_DILATION", "Замедление времени", "T", (180, 100, 255), "Замедление вражеских пуль", time_slow=0.7)
    P("SHIELD_GEN", "Генератор щита", "S", (80, 160, 255), "Автоматический щит каждые 30 сек", shield_gen=True)
    P("VAMPIRE", "Вампир", "V", (200, 0, 0), "+15% вампиризм", vamp=0.15)
    P("TITAN", "Титан", "T", (150, 100, 50), "+5 HP, -20% скорости", maxhp=5, spd=-0.2)
    P("SPRINT", "Спринт", "S", (100, 255, 100), "+30% скорости", spd=0.3)
    P("BERSERKER", "Берсерк", "B", (255, 50, 50), "+5 урона, -2 HP", dmg=5, maxhp=-2, cursed=True)
    P("GHOST", "Призрак", "G", (200, 200, 200), "10% шанс стать невидимым для пуль", ghost=0.1)
    P("CRYSTAL", "Кристалл", "K", (100, 255, 255), "Пули отскакивают от врагов", ricochet=True)
    P("MINES", "Мины", "M", (255, 150, 50), "Оставляют мины при движении", mines=True)
    P("CLONE", "Клон", "C", (180, 100, 180), "Шанс создать клона при выстреле", clone_chance=0.1)
    P("BLACK_HOLE", "Черная дыра", "H", (40, 40, 60), "Враги притягиваются к пулям", black_hole=True)
    P("CHAIN", "Цепь", "C", (255, 200, 100), "Пули перескакивают между врагами", chain=True)
    P("FROST", "Мороз", "F", (100, 150, 255), "Замедляет пораженных врагов", frost=True)
    P("LIGHTNING", "Молния", "L", (255, 255, 100), "Шанс ударить молнией рядом", lightning_chance=0.05)
    P("VENOM", "Яд", "V", (80, 200, 80), "Яд наносит урон с течением времени", venom=True)
    P("ECHO", "Эхо", "E", (200, 150, 255), "Повторный выстрел через 0.5 сек", echo=True)

    P("INFINITY", "Бесконечность", "X", (255, 255, 255), "+2 сложности, +10 урона",
      difficulty=2, dmg=10, legendary=True)
    P("HARDER", "Сложнее", "H", (200, 50, 50), "+1 сложность, +2 урона",
      difficulty=1, dmg=2)
    P("CASUAL", "Казуал", "C", (100, 255, 100), "-1 сложность, +0.5 сек парирования, -2 урона",
      difficulty=-1, parry_time=0.5, dmg=-2)
    P("PARRY_MASTER", "Мастер парирования", "P", (0, 200, 255), "+0.5 сек парирования",
      parry_time=0.5)
    P("FOREVER_CURSED", "На веки проклят", "C", (100, 0, 100), "Только проклятые предметы, они дают больше статов",
      only_cursed=True, cursed_bonus=2.0, cursed=True)
    P("GLASS_CANNON", "Стеклянная пушка", "G", (200, 200, 255), "+8 урона, -4 HP, +100% шанс на кровотечение",
      dmg=8, maxhp=-4, bleed_chance=1.0, achievement=True)
    P("DEMENTIA", "Деменция", "D", (150, 150, 150), "Счётчик этажей исчезает, +2 скорострельности",
      hide_wave=True, firerate=2)
    P("D6", "D6", "6", (255, 215, 0), "1/6 шанс на воскрешение при смерти (0 душ)",
      d6_revive=True)
    P("MULTIPLIER", "Множетель", "M", (255, 100, 255), "x2 рандомный стат, x0.5 другие",
      multiplier=True)
    P("MOUNTAIN", "Гора", "M", (139, 69, 19), "+2 дальность", range=2)
    P("WANKER", "Рукоблуд", "W", (255, 150, 150), "+2 скорострельность, -1 урон",
      firerate=2, dmg=-1)
    P("MACHINE_GUN", "Пулемёт", "M", (100, 100, 100), "+2 сложности, +10 скорострельности",
      difficulty=2, firerate=10)
    P("DLC", "DLC", "D", (0, 255, 0), "+2 к рандомному стату",
      dlc=True)
    P("PARRY_GHOST", "Призрак парирования", "G", (200, 200, 255), "После парирования 1 сек неуязвимости",
      parry_ghost=True)

    # Активные предметы
    A("BLOOD_MAGIC", "Магия крови", "1", (200, 0, 0), "-1 HP, мощный луч крови",
      "time", 10.0, "blood_magic", dmg=20)
    A("BLOOD_MAGIC_II", "Магия крови II", "2", (220, 0, 50), "-1 HP, выстрел пятиугольником",
      "time", 14.0, "blood_magic_ii", dmg=5)
    A("WORMHOLE", "Червоточина", "3", (40, 40, 60), "Выстрел черной дырой",
      "time", 22.0, "wormhole")
    A("PARRY_PLUS", "Парирование+", "4", (255, 255, 100), "Парирование любого снаряда",
      "time", 18.0, "parry_plus")
    A("SHIELD", "Щит", "5", (80, 160, 255), "Кратковременная неуязвимость",
      "time", 22.0, "shield", duration=2.0)
    A("TIME_STOP", "Остановка времени", "T", (100, 200, 255), "Останавливает время на 3 сек",
      "time", 25.0, "time_stop", duration=3.0)
    A("METEOR", "Метеор", "M", (255, 100, 0), "Призывает метеоритный дождь",
      "time", 35.0, "meteor", count=20)
    A("HEAL", "Исцеление", "H", (100, 255, 100), "Восстанавливает 5 HP",
      "time", 18.0, "heal", amount=5)
    A("NOVA", "Нова", "N", (255, 255, 200), "Взрывная волна вокруг игрока",
      "time", 13.0, "nova", radius=150)
    A("DECOY", "Приманка", "D", (200, 200, 100), "Создает приманку на 5 сек",
      "time", 22.0, "decoy", duration=5)
    A("TELEPORT", "Телепорт", "T", (180, 100, 255), "Мгновенная телепортация",
      "time", 8.0, "teleport")
    A("RAGE", "Ярость", "R", (255, 50, 50), "+100% урона на 10 сек",
      "time", 40.0, "rage", duration=10)
    A("FREEZE", "Заморозка", "F", (100, 150, 255), "Замораживает всех врагов на 3 сек",
      "time", 30.0, "freeze", duration=3)
    A("INVISIBILITY", "Невидимость", "I", (150, 150, 150), "Становится невидимым на 5 сек",
      "time", 35.0, "invisibility", duration=5)
    A("SUMMON", "Призыв", "S", (200, 150, 100), "Призывает помощника на 20 сек",
      "time", 50.0, "summon", duration=20)
    A("BERSERK", "Берсерк", "B", (255, 0, 0), "Бессмертие на 3 сек",
      "time", 45.0, "berserk", duration=3)
    A("GRAVITY", "Гравитация", "G", (100, 100, 150), "Притягивает всех врагов",
      "time", 18.0, "gravity", duration=4)
    A("MIRROR", "Зеркало", "Z", (255, 255, 100), "Отражает все снаряды на 5 сек",
      "time", 27.0, "mirror", duration=5)
    A("TRAP", "Ловушка", "T", (150, 100, 50), "Устанавливает ловушку",
      "time", 8.0, "trap", duration=10)

    A("BLOOD_MAGIC_III", "Магия крови III", "3", (200, 0, 100), "-1 HP, кольцо с большим уроном, пробитие",
      "time", 16.0, "blood_magic_iii", dmg=15, pierce=True)
    A("BLOOD_MAGIC_IV", "Магия крови IV", "4", (255, 0, 255), "-1 HP, 4 самонаводящихся снаряда",
      "time", 20.0, "blood_magic_iv", dmg=12, homing=True)
    A("AMNESIA", "Амнезия", "A", (150, 150, 150), "Волна + или - 1",
      "time", 27.0, "amnesia")
    A("ALL_IN", "ALL IN", "$", (255, 215, 0), "50% шанс: x2 деньги или потерять всё",
      "time", 50.0, "all_in")


reg_items()

# -------------------- Enemies --------------------
def E(name, char, color, hp, pat, delay, spd, ai):
    return {"name": name, "char": char, "color": color, "hp": hp,
            "pat": pat, "delay": delay, "spd": spd, "ai": ai}


ENEMIES = {
    "E": E("Ectoplasm", "E", (0, 255, 0), 4, PAT.SINGLE, 1.8, 2.0, AI.CHASE),
    "F": E("Phantom", "F", (150, 150, 255), 3, PAT.HOMING, 2.5, 0.0, AI.SPECIAL),
    "S": E("Skeleton", "S", (255, 255, 0), 6, PAT.EIGHT, 2.8, 0.0, AI.PATTERN),
    "W": E("Whirl", "W", (100, 200, 255), 3, PAT.SPIRAL, 2.0, 1.5, AI.CIRCLE),
    "H": E("Harpy", "H", (200, 100, 200), 5, PAT.SPREAD, 1.5, 2.5, AI.CHASE),
    "L": E("Lich", "L", (150, 0, 150), 8, PAT.SPLIT, 3.2, 0.0, AI.SPECIAL),
    "V": E("Vampire", "V", (200, 0, 0), 6, PAT.SINGLE, 2.2, 1.8, AI.CHASE),
    "G": E("Golem", "G", (110, 110, 110), 12, PAT.WAVE, 3.6, 1.2, AI.PATTERN),
    "N": E("Necromancer", "N", (50, 50, 50), 6, PAT.RING, 2.7, 0.0, AI.SPECIAL),
    "C": E("Cyclops", "C", (255, 150, 0), 9, PAT.LASER, 3.5, 0.9, AI.PATTERN),
    "R": E("Raider", "R", (255, 100, 100), 7, PAT.CROSS, 2.3, 2.2, AI.CHASE),
    "X": E("Xenoarch", "X", (180, 50, 220), 15, PAT.VORTEX, 2.2, 1.8, AI.SPECIAL),
    "Q": E("Quantum Weaver", "Q", (0, 200, 255), 10, PAT.SPLIT, 1.8, 2.2, AI.CIRCLE),
    "Y": E("Yokai", "Y", (255, 100, 180), 12, PAT.WAVE, 2.5, 1.5, AI.PATTERN),
    "Z": E("Zero Point", "Z", (0, 255, 200), 18, PAT.LASER, 3.0, 0.8, AI.SPECIAL),
    "P": E("Psion", "P", (255, 50, 100), 14, PAT.HOMING, 2.0, 2.0, AI.HEALER),
    "B": E("Floor Breaker", "B", (255, 100, 50), 22, PAT.RAIN, 3.0, 0.9, AI.PATTERN),
    "T": E("Celestial", "T", (100, 200, 255), 25, PAT.SPIRAL, 2.8, 1.0, AI.CIRCLE),
    "W": E("Wormhole Turret", "W", (40, 120, 255), 8, PAT.SPREAD, 1.2, 0.0, AI.CIRCLE),
    "M": E("Vortex Spinner", "M", (255, 180, 50), 12, PAT.SPIRAL, 2.0, 0.5, AI.CIRCLE),
    "F": E("Starburst", "F", (180, 0, 120), 10, PAT.EIGHT, 1.5, 2.0, AI.CHASE),
    "O": E("Oscillator", "O", (0, 220, 150), 15, PAT.VORTEX, 1.8, 1.5, AI.CIRCLE),
}

ELITES = {}
ULTRAS = {}
for k, d in ENEMIES.items():
    ELITES[k + "+"] = E(d["name"] + "+", d["char"] + "+", tuple(min(255, c + 40) for c in d["color"]),
                        d["hp"] + 4, d["pat"], max(0.7, d["delay"] * 0.8), d["spd"] * 1.3, d["ai"])
    ULTRAS[k + "++"] = E(d["name"] + "++", d["char"] + "+", tuple(min(255, c + 70) for c in d["color"]),
                          int(d["hp"] * 2.0) + 4, random.choice([d["pat"], PAT.SPREAD, PAT.RING, PAT.HOMING, PAT.VORTEX]),
                          max(0.5, d["delay"] * 0.6), d["spd"] * 1.6 + 0.3, d["ai"])

# -------------------- Bosses --------------------
BOSSES = {
    5: {"name": "Демон", "char": "D", "color": (200, 50, 50), "hp": 180,
        "ph": [{"pat": PAT.RING, "delay": 1.2, "spd": 1.3, "ai": AI.CIRCLE},
               {"pat": PAT.CROSS, "delay": 1.0, "spd": 1.5, "ai": AI.CHASE},
               {"pat": PAT.VORTEX, "delay": 0.8, "spd": 1.8, "ai": AI.SPECIAL}],
        "special_attack": "cross_spiral", "special_chance": 0.2},
    10: {"name": "Сверхчеловек", "char": "S", "color": (100, 200, 255), "hp": 250,
         "ph": [{"pat": PAT.WAVE, "delay": 1.1, "spd": 1.4, "ai": AI.PATTERN},
                {"pat": PAT.SPREAD, "delay": 0.9, "spd": 1.7, "ai": AI.CHASE},
                {"pat": PAT.VORTEX, "delay": 0.6, "spd": 2.0, "ai": AI.SPECIAL}],
         "special_attack": "homing_ring", "special_chance": 0.25},
    15: {"name": "Барон ада", "char": "B", "color": (180, 60, 60), "hp": 320,
         "ph": [{"pat": PAT.SPIRAL, "delay": 1.0, "spd": 1.3, "ai": AI.CIRCLE},
                {"pat": PAT.WAVE, "delay": 0.8, "spd": 1.6, "ai": AI.CHASE},
                {"pat": PAT.VORTEX, "delay": 0.5, "spd": 1.9, "ai": AI.SPECIAL}],
         "special_attack": "cross_spiral", "special_chance": 0.25},
    20: {"name": "Еврей", "char": "J", "color": (255, 215, 0), "hp": 400,
         "ph": [{"pat": PAT.LASER, "delay": 0.9, "spd": 1.5, "ai": AI.CHASE},
                {"pat": PAT.EIGHT, "delay": 0.7, "spd": 1.4, "ai": AI.PATTERN},
                {"pat": PAT.VORTEX, "delay": 0.5, "spd": 1.8, "ai": AI.SPECIAL}],
         "special_attack": "homing_ring", "special_chance": 0.3},
    25: {"name": "Инжинер", "char": "I", "color": (150, 150, 150), "hp": 480,
         "ph": [{"pat": PAT.SPREAD, "delay": 0.9, "spd": 1.5, "ai": AI.PATTERN},
                {"pat": PAT.RING, "delay": 0.7, "spd": 1.8, "ai": AI.CIRCLE},
                {"pat": PAT.VORTEX, "delay": 0.4, "spd": 2.1, "ai": AI.SPECIAL}],
         "special_attack": "cross_spiral", "special_chance": 0.3, "quake": True},
    30: {"name": "Машина войны", "char": "M", "color": (100, 100, 120), "hp": 550,
         "ph": [{"pat": PAT.HOMING, "delay": 0.8, "spd": 1.5, "ai": AI.SPECIAL},
                {"pat": PAT.SPLIT, "delay": 0.6, "spd": 1.7, "ai": AI.CHASE},
                {"pat": PAT.VORTEX, "delay": 0.4, "spd": 2.1, "ai": AI.SPECIAL}],
         "special_attack": "god_pattern", "special_chance": 0.2},
     35: {"name": "Он", "char": "O", "color": (40, 40, 40), "hp": 650,
         "ph": [{"pat": PAT.RING, "delay": 0.8, "spd": 1.4, "ai": AI.CIRCLE},
                {"pat": PAT.SPREAD, "delay": 0.6, "spd": 1.7, "ai": AI.CHASE},
                {"pat": PAT.VORTEX, "delay": 0.4, "spd": 2.0, "ai": AI.SPECIAL}],
         "special_attack": "ring_burst", "special_chance": 0.3, "summons": True},
     40: {"name": "Мехо-гитлер", "char": "H", "color": (180, 180, 200), "hp": 720,
         "ph": [{"pat": PAT.RING, "delay": 0.8, "spd": 1.6, "ai": AI.CIRCLE},
                {"pat": PAT.SPREAD, "delay": 0.6, "spd": 1.8, "ai": AI.CHASE},
                {"pat": PAT.VORTEX, "delay": 0.4, "spd": 2.2, "ai": AI.SPECIAL}],
         "special_attack": "void_burst", "special_chance": 0.3, "summons": True},
     45: {"name": "Падший ангел", "char": "A", "color": (200, 100, 200), "hp": 800,
         "ph": [{"pat": PAT.LASER, "delay": 0.7, "spd": 1.3, "ai": AI.PATTERN},
                {"pat": PAT.CROSS, "delay": 0.5, "spd": 1.6, "ai": AI.SPECIAL},
                {"pat": PAT.VORTEX, "delay": 0.3, "spd": 2.0, "ai": AI.SPECIAL}],
         "special_attack": "homing_ring", "special_chance": 0.25, "summons": True},
     50: {"name": "Голод", "char": "G", "color": (100, 200, 100), "hp": 950,
         "ph": [{"pat": PAT.WAVE, "delay": 0.7, "spd": 1.3, "ai": AI.PATTERN},
                {"pat": PAT.EIGHT, "delay": 0.5, "spd": 1.4, "ai": AI.SPECIAL},
                {"pat": PAT.VORTEX, "delay": 0.3, "spd": 1.8, "ai": AI.CHASE}],
         "special_attack": "ring_burst", "special_chance": 0.3, "quake": True, "summons": True},
     55: {"name": "Смерть", "char": "T", "color": (0, 0, 0), "hp": 1050,
         "ph": [{"pat": PAT.SPIRAL, "delay": 0.6, "spd": 1.5, "ai": AI.CIRCLE},
                {"pat": PAT.SPLIT, "delay": 0.4, "spd": 1.8, "ai": AI.CHASE},
                {"pat": PAT.VORTEX, "delay": 0.2, "spd": 2.2, "ai": AI.SPECIAL}],
         "special_attack": "void_burst", "special_chance": 0.25, "summons": True},
     60: {"name": "Чума", "char": "P", "color": (100, 200, 100), "hp": 1150,
         "ph": [{"pat": PAT.SPIRAL, "delay": 0.6, "spd": 1.8, "ai": AI.CIRCLE},
                {"pat": PAT.WAVE, "delay": 0.4, "spd": 2.0, "ai": AI.SPECIAL},
                {"pat": PAT.VORTEX, "delay": 0.2, "spd": 2.5, "ai": AI.SPECIAL}],
         "special_attack": "pinwheel", "special_chance": 0.2, "summons": True},
     65: {"name": "Война", "char": "W", "color": (200, 50, 50), "hp": 1250,
         "ph": [{"pat": PAT.WAVE, "delay": 0.6, "spd": 1.4, "ai": AI.PATTERN},
                {"pat": PAT.RING, "delay": 0.4, "spd": 1.7, "ai": AI.SPECIAL},
                {"pat": PAT.VORTEX, "delay": 0.2, "spd": 2.3, "ai": AI.SPECIAL}],
         "special_attack": "homing_vortex", "special_chance": 0.2, "summons": True},
     70: {"name": "Люцифер", "char": "L", "color": (255, 100, 100), "hp": 1350,
         "ph": [{"pat": PAT.RING, "delay": 0.5, "spd": 1.8, "ai": AI.CIRCLE},
                {"pat": PAT.SPREAD, "delay": 0.3, "spd": 2.0, "ai": AI.SPECIAL},
                {"pat": PAT.VORTEX, "delay": 0.1, "spd": 2.5, "ai": AI.SPECIAL}],
         "special_attack": "ring_burst", "special_chance": 0.2, "summons": True},
     75: {"name": "Сенатор", "char": "S", "color": (100, 100, 200), "hp": 1450,
         "ph": [{"pat": PAT.SPREAD, "delay": 0.5, "spd": 1.6, "ai": AI.PATTERN},
                {"pat": PAT.WAVE, "delay": 0.3, "spd": 2.0, "ai": AI.CHASE},
                {"pat": PAT.VORTEX, "delay": 0.1, "spd": 2.7, "ai": AI.SPECIAL}],
         "special_attack": "void_burst", "special_chance": 0.2, "summons": True},
     80: {"name": "Император человечества", "char": "E", "color": (255, 215, 0), "hp": 1550,
         "ph": [{"pat": PAT.HOMING, "delay": 0.5, "spd": 1.8, "ai": AI.SPECIAL},
                {"pat": PAT.LASER, "delay": 0.3, "spd": 2.0, "ai": AI.CHASE},
                {"pat": PAT.VORTEX, "delay": 0.1, "spd": 2.8, "ai": AI.SPECIAL}],
         "special_attack": "ring_burst", "special_chance": 0.2, "summons": True},
     85: {"name": "Мастер", "char": "M", "color": (180, 100, 180), "hp": 1650,
         "ph": [{"pat": PAT.EIGHT, "delay": 0.5, "spd": 1.7, "ai": AI.PATTERN},
                {"pat": PAT.SPIRAL, "delay": 0.3, "spd": 1.9, "ai": AI.CIRCLE},
                {"pat": PAT.VORTEX, "delay": 0.1, "spd": 2.8, "ai": AI.SPECIAL}],
         "special_attack": "homing_vortex", "special_chance": 0.2, "summons": True},
     90: {"name": "Кто-то", "char": "?", "color": (200, 200, 200), "hp": 1750,
         "ph": [{"pat": PAT.EIGHT, "delay": 0.4, "spd": 1.8, "ai": AI.PATTERN},
                {"pat": PAT.LASER, "delay": 0.2, "spd": 2.0, "ai": AI.SPECIAL},
                {"pat": PAT.VORTEX, "delay": 0.1, "spd": 3.0, "ai": AI.SPECIAL}],
         "special_attack": "pinwheel", "special_chance": 0.2, "summons": True},
     95: {"name": "Император человечества II", "char": "E", "color": (255, 255, 100), "hp": 1850,
         "ph": [{"pat": PAT.VORTEX, "delay": 0.4, "spd": 1.8, "ai": AI.SPECIAL},
                {"pat": PAT.SPREAD, "delay": 0.2, "spd": 2.2, "ai": AI.CHASE},
                {"pat": PAT.VORTEX, "delay": 0.1, "spd": 3.2, "ai": AI.SPECIAL}],
         "special_attack": "ring_burst", "special_chance": 0.2, "summons": True},
     100: {"name": "БОГ", "char": "G", "color": (255, 255, 255), "hp": 2500,
           "ph": [{"pat": PAT.SPREAD, "delay": 0.3, "spd": 1.8, "ai": AI.CIRCLE},
                  {"pat": PAT.SPIRAL, "delay": 0.2, "spd": 2.0, "ai": AI.PATTERN},
                  {"pat": PAT.VORTEX, "delay": 0.15, "spd": 2.5, "ai": AI.SPECIAL},
                  {"pat": PAT.VORTEX, "delay": 0.1, "spd": 3.0, "ai": AI.CHASE}],
           "special_attack": "pinwheel", "special_chance": 0.3, "quake": True, "summons": True},
}


# -------------------- Collision Helper Functions --------------------
def bullet_hitbox_radius(bullet):
    """Get the hitbox radius based on the bullet's SHAPE (from bullet_type)"""
    size = bullet.get("size", 5)
    shape = BULLET_TYPE_TO_SHAPE.get(bullet.get("bullet_type", BULLET_TYPE.NORMAL), BULLET_SHAPE.CIRCLE)

    if shape == BULLET_SHAPE.CIRCLE:
        return size
    elif shape == BULLET_SHAPE.DIAMOND:
        return size * 0.707
    elif shape == BULLET_SHAPE.STICK:
        return size * 0.5
    elif shape == BULLET_SHAPE.SPINNING_STICK:
        return size * 0.5
    elif shape == BULLET_SHAPE.STAR:
        return size
    elif shape == BULLET_SHAPE.TRIANGLE:
        return size * 0.866
    elif shape in (BULLET_SHAPE.GEAR, BULLET_SHAPE.ORBIT, BULLET_SHAPE.HALO, BULLET_SHAPE.NOVA):
        return size
    elif shape == BULLET_SHAPE.CRESCENT:
        return size * 0.85
    return size

def point_in_triangle(px, py, cx, cy, angle, size):
    """Check if point is inside a triangle pointing in direction angle"""
    import math
    # Triangle vertices
    tip_x = cx + math.cos(angle) * size
    tip_y = cy + math.sin(angle) * size
    bl_x = cx - math.sin(angle) * size
    bl_y = cy + math.cos(angle) * size
    br_x = cx + math.sin(angle) * size
    br_y = cy - math.cos(angle) * size
    
    # Barycentric technique
    def sign(p1x, p1y, p2x, p2y, p3x, p3y):
        return (p1x - p3x) * (p2y - p3y) - (p2x - p3x) * (p1y - p3y)
    
    d1 = sign(px, py, tip_x, tip_y, bl_x, bl_y)
    d2 = sign(px, py, bl_x, bl_y, br_x, br_y)
    d3 = sign(px, py, br_x, br_y, tip_x, tip_y)
    
    has_neg = (d1 < 0) or (d2 < 0) or (d3 < 0)
    has_pos = (d1 > 0) or (d2 > 0) or (d3 > 0)
    
    return not (has_neg and has_pos)


def point_in_diamond(px, py, cx, cy, size):
    """Check if point is inside a diamond (rotated square)"""
    dx = abs(px - cx)
    dy = abs(py - cy)
    return (dx + dy) <= size


def point_in_stick(px, py, cx, cy, angle, size):
    """Check if point is near a line segment (stick)"""
    ex = cx + math.cos(angle) * size
    ey = cy + math.sin(angle) * size
    bx = cx - math.cos(angle) * size
    by = cy - math.sin(angle) * size
    
    line_len_sq = (ex - bx) ** 2 + (ey - by) ** 2
    if line_len_sq == 0:
        return (px - bx) ** 2 + (py - by) ** 2 <= 9
    
    t = ((px - bx) * (ex - bx) + (py - by) * (ey - by)) / line_len_sq
    t = max(0, min(1, t))
    
    proj_x = bx + t * (ex - bx)
    proj_y = by + t * (ey - by)
    
    return (px - proj_x) ** 2 + (py - proj_y) ** 2 <= (max(2, size // 2) ** 2)


def point_in_star(px, py, cx, cy, angle, size):
    """Check if point is inside a star shape (simplified as circle for performance)"""
    return (px - cx) ** 2 + (py - cy) ** 2 <= (size * 0.9) ** 2


# -------------------- Улучшенная функция создания пуль --------------------
def create_bullet(src, angle, speed, color, size=8, homing=False, homing_strength=0.05, max_dist=None, pattern_type=None, pat=None, bullet_type=BULLET_TYPE.NORMAL):
    """Создание пули с настройкой дальности и уникальными свойствами"""
    bullet = {
        "x": src["x"], "y": src["y"],
        "dx": math.cos(angle) * speed,
        "dy": math.sin(angle) * speed,
        "color": color,
        "size": max(MIN_BULLET_SIZE, min(MAX_BULLET_SIZE, size)),
        "boss": True,
        "created": time.time(),
        "pattern": pat,
        "pattern_type": pattern_type,
        "outline": True,
        "bullet_type": bullet_type,
    }
    
    if pattern_type:
        if pattern_type == "ghost":
            bullet["ghost"] = True
            bullet["alpha"] = random.randint(100, 255)
        elif pattern_type == "explosive":
            bullet["explosive"] = True
            bullet["explosion_radius"] = 50
        elif pattern_type == "split":
            bullet["split"] = True
            bullet["split_count"] = 4
        elif pattern_type == "accelerate":
            bullet["accelerate"] = True
            bullet["accel"] = 0.1
        elif pattern_type == "decelerate":
            bullet["decelerate"] = True
            bullet["decel"] = 0.05
        elif pattern_type == "wave":
            bullet["wave"] = True
            bullet["wave_amp"] = 20
            bullet["wave_freq"] = 5
        elif pattern_type == "spiral":
            bullet["spiral"] = True
            bullet["spiral_speed"] = 0.1
        elif pattern_type == "homing_strong":
            homing = True
            homing_strength = 0.1
        elif pattern_type == "laser":
            bullet["laser"] = True
            bullet["length"] = 30
        elif pattern_type == "gravity":
            bullet["gravity"] = True
            bullet["gravity_strength"] = 0.05
    
    if homing:
        bullet["homing"] = True
        bullet["homing_strength"] = homing_strength
        bullet["max_dist"] = max_dist if max_dist else 800
    else:
        bullet["max_dist"] = float('inf')
    
    return bullet

# -------------------- УНИКАЛЬНЫЕ БУЛЛЕТ ХЕЛЛ ПАТТЕРНЫ ДЛЯ КАЖДОГО БОССА --------------------

def boss_pattern_demon(boss, phase):
    """Демон - огненные КРУГЛЫЕ пули"""
    bx, by = boss["x"], boss["y"]
    px, py = player["x"], player["y"]
    base_angle = math.atan2(py - by, px - bx)
    
    if phase == 0:
        for i in range(12):
            spread = random.uniform(-0.3, 0.3)
            angle = base_angle + spread
            speed = ENEMY_BULLET_SPEED * random.uniform(0.9, 1.3) * BOSS_SPEED_MULT
            ebullets.append(create_bullet(boss, angle, speed, BULLET_COLORS[BULLET_TYPE.FIRE], 10, bullet_type=BULLET_TYPE.FIRE))
    elif phase == 1:
        for i in range(24):
            angle = i * (2 * math.pi / 24) + time.time() * 2
            for j in range(3):
                offset = (j - 1) * 0.2
                speed = ENEMY_BULLET_SPEED * (0.6 + j * 0.2) * BOSS_SPEED_MULT
                ebullets.append(create_bullet(boss, angle + offset, speed, BULLET_COLORS[BULLET_TYPE.DEMON], 8 + j*2, bullet_type=BULLET_TYPE.DEMON))

def boss_pattern_superhuman(boss, phase):
    """Сверхчеловек - ЛАЗЕРЫ (палочки)"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(8):
            angle = i * math.pi/4
            for j in range(4):
                speed = ENEMY_BULLET_SPEED * (0.5 + j * 0.3) * BOSS_SPEED_MULT
                ebullets.append(create_bullet(boss, angle, speed, BULLET_COLORS[BULLET_TYPE.LASER], 6, bullet_type=BULLET_TYPE.LASER))

def boss_pattern_baron(boss, phase):
    """Барон ада (волна 15) - Взрывные волны и шрапнель"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(16):
            angle = i * math.pi/8
            for j in range(2):
                delay = j * 0.3
                bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.7 * BOSS_SPEED_MULT, (180, 60, 60), 12, pattern_type="explosive")
                bullet["delay"] = delay
                bullet["explosion_particles"] = 8
                ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(10):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.5 * BOSS_SPEED_MULT, (220, 80, 80), 15)
            bullet["chain"] = True
            bullet["chain_count"] = 3
            ebullets.append(bullet)

def boss_pattern_jew(boss, phase):
    """Еврей (волна 20) - Золотой дождь и самонаводящиеся монеты"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(20):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.6 * BOSS_SPEED_MULT, (255, 215, 0), 8)
            bullet["gravity"] = True
            bullet["gravity_strength"] = 0.2
            ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(15):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.4 * BOSS_SPEED_MULT, (255, 200, 0), 12,
                                  homing=True, homing_strength=0.08, pattern_type="homing_strong")
            bullet["homing_delay"] = 0.5
            ebullets.append(bullet)

def boss_pattern_engineer(boss, phase):
    """Инжинер (волна 25) - Механический рай: шестерни и ловушки"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(8):
            base_angle = i * math.pi/4
            for j in range(5):
                angle = base_angle + j * 0.2 + time.time()
                speed = ENEMY_BULLET_SPEED * (0.5 + j * 0.1) * BOSS_SPEED_MULT
                bullet = create_bullet(boss, angle, speed, (150, 150, 150), 5 + j, pattern_type="spiral")
                bullet["rotate"] = True
                ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(12):
            angle = i * math.pi/6
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.3 * BOSS_SPEED_MULT, (200, 200, 200), 20)
            bullet["invisible"] = True
            bullet["visible_distance"] = 100
            ebullets.append(bullet)

def boss_pattern_war_machine(boss, phase):
    """Машина войны (волна 30) - Пулеметная очередь и хаос"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(15):
            spread = random.uniform(-0.4, 0.4)
            angle = math.atan2(player["y"] - by, player["x"] - bx) + spread
            speed = ENEMY_BULLET_SPEED * random.uniform(1.0, 1.8) * BOSS_SPEED_MULT
            bullet = create_bullet(boss, angle, speed, (100, 100, 120), random.randint(4, 10))
            bullet["trail"] = True
            ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(25):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * random.uniform(0.5, 1.5) * BOSS_SPEED_MULT, 
                                  (150, 150, 170), random.randint(3, 8))
            bullet["split_on_death"] = True
            ebullets.append(bullet)

def boss_pattern_him(boss, phase):
    """Он (волна 35) - Кошмар: пули меняют размер и направление"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(20):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.7 * BOSS_SPEED_MULT, (40, 40, 40), 12, pattern_type="ghost")
            bullet["size_pulse"] = True
            bullet["color_pulse"] = True
            ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(10):
            angle = math.atan2(player["y"] - by, player["x"] - bx) + random.uniform(-0.3, 0.3)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 1.0 * BOSS_SPEED_MULT, (60, 60, 60), 10)
            bullet["copy_player"] = True
            bullet["prediction_time"] = 0.5
            ebullets.append(bullet)

def boss_pattern_mech_hitler(boss, phase):
    """Мехо-гитлер (волна 40) - Железный крест и свастики из пуль"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(4):
            base = i * math.pi/2
            for j in range(8):
                angle = base + (j - 4) * 0.15
                speed = ENEMY_BULLET_SPEED * (0.6 + j * 0.1) * BOSS_SPEED_MULT
                bullet = create_bullet(boss, angle, speed, (180, 180, 200), 7)
                bullet["formation"] = "cross"
                ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(16):
            angle = i * math.pi/8
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.8 * BOSS_SPEED_MULT, (200, 200, 220), 9)
            bullet["music"] = True
            bullet["beat"] = random.uniform(0.5, 2.0)
            ebullets.append(bullet)

def boss_pattern_fallen_angel(boss, phase):
    """Падший ангел (волна 45) - Свет и тьма: противоположности"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(14):
            angle = i * (2 * math.pi / 14)
            bullet_light = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.9 * BOSS_SPEED_MULT, (255, 255, 200), 6)
            ebullets.append(bullet_light)
            
            bullet_dark = create_bullet(boss, angle + math.pi/14, ENEMY_BULLET_SPEED * 1.1 * BOSS_SPEED_MULT, (100, 100, 150), 8)
            ebullets.append(bullet_dark)
    
    elif phase == 1:
        for i in range(20):
            angle = math.sin(i * 0.5 + time.time()) * math.pi/2
            speed = ENEMY_BULLET_SPEED * (0.5 + math.sin(time.time() + i) * 0.3) * BOSS_SPEED_MULT
            bullet = create_bullet(boss, angle, speed, (200, 150, 200), 7, pattern_type="wave")
            bullet["wave_vertical"] = True
            ebullets.append(bullet)

def boss_pattern_hunger(boss, phase):
    """Голод (волна 50) - Пожирающие пули и черные дыры"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(12):
            angle = i * math.pi/6
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.5 * BOSS_SPEED_MULT, BULLET_COLORS[BULLET_TYPE.GRAVITY], 14,
                                   bullet_type=BULLET_TYPE.GRAVITY)
            bullet["vacuum"] = True
            bullet["vacuum_strength"] = 0.1
            ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(18):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.6 * BOSS_SPEED_MULT, BULLET_COLORS[BULLET_TYPE.GRAVITY], 10,
                                  bullet_type=BULLET_TYPE.GRAVITY, homing=True, homing_strength=0.12)
            bullet["soul_eater"] = True
            ebullets.append(bullet)

def boss_pattern_death(boss, phase):
    """Смерть (волна 55) - Косы, призраки и души"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(6):
            base_angle = i * math.pi/3
            for j in range(5):
                angle = base_angle + (j - 2) * 0.2 + math.sin(time.time() * 2) * 0.2
                bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.8 * BOSS_SPEED_MULT, BULLET_COLORS[BULLET_TYPE.SPIRAL], 10,
                                       bullet_type=BULLET_TYPE.SPIRAL)
                bullet["scythe"] = True
                bullet["arc"] = math.pi/2
                ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(24):
            angle = i * math.pi/12 + time.time()
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.4 * BOSS_SPEED_MULT, BULLET_COLORS[BULLET_TYPE.GHOST], 12,
                                   bullet_type=BULLET_TYPE.GHOST, homing=True, homing_strength=0.03, pattern_type="ghost")
            bullet["phase_shift"] = True
            ebullets.append(bullet)

def boss_pattern_plague(boss, phase):
    """Чума (волна 60) - Болезнь: отравляющие облака и вирусы"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(30):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.3 * BOSS_SPEED_MULT, BULLET_COLORS[BULLET_TYPE.POISON], 8, bullet_type=BULLET_TYPE.POISON)
            bullet["poison_cloud"] = True
            bullet["cloud_duration"] = 2.0
            bullet["cloud_radius"] = 40
            ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(20):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.7 * BOSS_SPEED_MULT, BULLET_COLORS[BULLET_TYPE.ELECTRIC], 6, bullet_type=BULLET_TYPE.ELECTRIC)
            bullet["infect"] = True
            bullet["infection_range"] = 100
            ebullets.append(bullet)

def boss_pattern_war(boss, phase):
    """Война (волна 65) - Поле битвы: шрапнель и взрывы"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(20):
            target_x = random.randint(100, screen.get_width() - 100)
            target_y = random.randint(100, screen.get_height() - 100)
            angle = math.atan2(target_y - by, target_x - bx)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 1.2 * BOSS_SPEED_MULT, (200, 50, 50), 12, pattern_type="explosive")
            bullet["target"] = (target_x, target_y)
            bullet["impact_radius"] = 80
            ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(15):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.8 * BOSS_SPEED_MULT, (220, 70, 70), 14)
            bullet["cluster"] = True
            bullet["cluster_count"] = 6
            ebullets.append(bullet)

def boss_pattern_lucifer(boss, phase):
    """Люцифер (волна 70) - Адское пламя и демонические символы"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(10):
            base_angle = i * math.pi/5
            for j in range(5):
                angle = base_angle + j * 2*math.pi/5
                speed = ENEMY_BULLET_SPEED * (0.6 + j * 0.1) * BOSS_SPEED_MULT
                bullet = create_bullet(boss, angle, speed, BULLET_COLORS[BULLET_TYPE.DEMON], 8, bullet_type=BULLET_TYPE.DEMON)
                bullet["pentagram"] = True
                ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(24):
            angle = i * math.pi/12 + time.time()
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.9 * BOSS_SPEED_MULT, BULLET_COLORS[BULLET_TYPE.DEMON], 9, bullet_type=BULLET_TYPE.DEMON)
            bullet["hellfire"] = True
            bullet["burn_duration"] = 5.0
            ebullets.append(bullet)

def boss_pattern_senator(boss, phase):
    """Сенатор (волна 75) - Закон и порядок: геометрическая точность"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(8):
            for j in range(8):
                x = bx - 200 + i * 50
                y = by - 200 + j * 50
                dx = player["x"] - x
                dy = player["y"] - y
                angle = math.atan2(dy, dx)
                fake_src = {"x": x, "y": y}
                bullet = create_bullet(fake_src, angle, ENEMY_BULLET_SPEED * 0.8 * BOSS_SPEED_MULT, (100, 100, 200), 6)
                bullet["grid"] = True
                ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(12):
            angle = i * math.pi/6
            for j in range(3):
                offset = (j - 1) * 20
                fake_src = {"x": bx + math.cos(angle) * offset, "y": by + math.sin(angle) * offset}
                bullet = create_bullet(fake_src, angle, ENEMY_BULLET_SPEED * 0.7 * BOSS_SPEED_MULT, (150, 150, 220), 8)
                bullet["formation"] = "line"
                ebullets.append(bullet)

def boss_pattern_emperor(boss, phase):
    """Император человечества (волна 80) - Золотой трон: имперская мощь"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(16):
            angle = i * math.pi/8
            for j in range(2):
                bullet = create_bullet(boss, angle + j * 0.3, ENEMY_BULLET_SPEED * 0.9 * BOSS_SPEED_MULT, (255, 215, 0), 10)
                bullet["march"] = True
                bullet["beat"] = i / 4
                ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(20):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.7 * BOSS_SPEED_MULT, (255, 200, 0), 12,
                                  homing=True, homing_strength=0.06)
            bullet["fleet"] = True
            bullet["formation_angle"] = angle
            ebullets.append(bullet)

def boss_pattern_master(boss, phase):
    """Мастер (волна 85) - Мастерство всех стилей: микс паттернов"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        patterns = ["wave", "spiral", "accelerate", "decelerate", "ghost"]
        for i in range(20):
            pattern = random.choice(patterns)
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * random.uniform(0.6, 1.2) * BOSS_SPEED_MULT,
                                  (180, 100, 180), random.randint(5, 10), pattern_type=pattern)
            ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(30):
            angle = i * math.pi/15
            for j in range(2):
                speed = ENEMY_BULLET_SPEED * (0.5 + j * 0.3) * BOSS_SPEED_MULT
                bullet1 = create_bullet(boss, angle, speed, (200, 150, 200), 7, pattern_type="spiral")
                bullet2 = create_bullet(boss, angle + math.pi, speed, (150, 100, 150), 7, pattern_type="wave")
                ebullets.extend([bullet1, bullet2])

def boss_pattern_someone(boss, phase):
    """Кто-то (волна 90) - Загадка: непредсказуемые пули"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(25):
            if random.random() < 0.5:
                angle = random.uniform(0, 2*math.pi)
                bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.8 * BOSS_SPEED_MULT, (200, 200, 200), 8)
                ebullets.append(bullet)
            else:
                angle = random.uniform(0, 2*math.pi)
                bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.8 * BOSS_SPEED_MULT, (100, 100, 100), 8)
                bullet["phantom"] = True
                bullet["alpha"] = 128
                ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(20):
            angle = i * math.pi/10
            for j in range(3):
                bullet = create_bullet(boss, angle + (j-1)*0.2, ENEMY_BULLET_SPEED * 0.6 * BOSS_SPEED_MULT, (180, 180, 180), 7)
                bullet["phase_shift"] = random.uniform(0, 2*math.pi)
                ebullets.append(bullet)

def boss_pattern_emperor2(boss, phase):
    """Император человечества II (волна 95) - Усиленная версия"""
    bx, by = boss["x"], boss["y"]
    
    if phase == 0:
        for i in range(24):
            angle = i * math.pi/12 + time.time()
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 1.2 * BOSS_SPEED_MULT, (255, 255, 100), 12)
            bullet["imperial_rage"] = True
            bullet["growth_rate"] = 0.5
            ebullets.append(bullet)
    
    elif phase == 1:
        for i in range(30):
            angle = i * math.pi/15
            for j in range(2):
                speed = ENEMY_BULLET_SPEED * (0.7 + j * 0.4) * BOSS_SPEED_MULT
                bullet = create_bullet(boss, angle + j * 0.2, speed, (255, 220, 50), 10,
                                      homing=(j==1), homing_strength=0.08)
                bullet["golden_fleet"] = True
                ebullets.append(bullet)

def boss_pattern_god(boss, phase):
    """БОГ - ВСЕ ТИПЫ ПУЛЬ (круги, звёзды, треугольники, палочки)"""
    bx, by = boss["x"], boss["y"]
    
    all_types = [BULLET_TYPE.NORMAL, BULLET_TYPE.FAST, BULLET_TYPE.HEAVY,
                 BULLET_TYPE.GRAVITY, BULLET_TYPE.LASER, BULLET_TYPE.PLASMA,
                 BULLET_TYPE.SHARD, BULLET_TYPE.SPIRAL, BULLET_TYPE.HOMING,
                 BULLET_TYPE.EXPLOSIVE, BULLET_TYPE.POISON, BULLET_TYPE.FIRE,
                 BULLET_TYPE.ICE, BULLET_TYPE.ELECTRIC, BULLET_TYPE.VOID,
                 BULLET_TYPE.HOLY, BULLET_TYPE.DEMON]
    
    if phase == 0:
        for i in range(30):
            angle = i * math.pi/15 + time.time()
            bullet_type = all_types[i % len(all_types)]
            ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.7 * BOSS_SPEED_MULT, BULLET_COLORS[bullet_type], 8, bullet_type=bullet_type))
    elif phase == 1:
        for i in range(40):
            angle = random.uniform(0, 2*math.pi)
            bullet_type = random.choice(all_types)
            ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * random.uniform(0.5, 1.5) * BOSS_SPEED_MULT, BULLET_COLORS[bullet_type], random.randint(4, 12), bullet_type=bullet_type))

# -------------------- Паттерны для грехов (1 фаза) --------------------

def sin_pattern_pride(boss):
    """Гордыня - золотые спирали"""
    bx, by = boss["x"], boss["y"]
    for i in range(16):
        angle = i * math.pi/8 + time.time() * 1.5
        ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.8, (255, 215, 0), 10))

def sin_pattern_greed(boss):
    """Алчность - кольца монет"""
    bx, by = boss["x"], boss["y"]
    for i in range(12):
        angle = i * math.pi/6
        for j in range(2):
            bullet = create_bullet(boss, angle + j*0.2, ENEMY_BULLET_SPEED * 0.7, (255, 215, 0), 8,
                                  homing=(j==1), homing_strength=0.02)
            ebullets.append(bullet)

def sin_pattern_lust(boss):
    """Похоть - розовые самонаводящиеся"""
    bx, by = boss["x"], boss["y"]
    px, py = player["x"], player["y"]
    base_angle = math.atan2(py - by, px - bx)
    
    for i in range(8):
        angle = base_angle + (i - 4) * 0.15
        ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.9, (255, 100, 180), 9,
                                    homing=True, homing_strength=0.04))

def sin_pattern_envy(boss):
    """Зависть - зеленые разделяющиеся"""
    bx, by = boss["x"], boss["y"]
    
    for i in range(8):
        angle = i * math.pi/4
        bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.6, (0, 255, 0), 10)
        bullet["split"] = True
        ebullets.append(bullet)

def sin_pattern_gluttony(boss):
    """Чревоугодие - волны еды"""
    bx, by = boss["x"], boss["y"]
    
    for i in range(10):
        angle = i * math.pi/5 + math.sin(time.time() * 2) * 0.3
        ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.7, (150, 100, 50), 12))

def sin_pattern_wrath(boss):
    """Гнев - красные лазеры"""
    bx, by = boss["x"], boss["y"]
    px, py = player["x"], player["y"]
    base_angle = math.atan2(py - by, px - bx)
    
    for i in range(5):
        angle = base_angle + (i - 2) * 0.1
        ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * 1.2, (255, 0, 0), 14))

def sin_pattern_sloth(boss):
    """Лень - медленные, но большие пули"""
    bx, by = boss["x"], boss["y"]
    
    for i in range(6):
        angle = i * math.pi/3
        ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.4, (100, 100, 100), 18))


def boss_pattern_ring_burst(boss, phase):
    """Кольцевая очередь: мульти-кольцо с летящими обрывками"""
    bx, by = boss["x"], boss["y"]
    base_ring = 14 + phase * 4
    for ring_i in range(3):
        ring_size = base_ring + ring_i
        for i in range(ring_size):
            angle = i * (2 * math.pi / ring_size) + time.time() * (0.4 + ring_i * 0.15)
            ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * (0.5 + ring_i * 0.15) * BOSS_SPEED_MULT,
                                          boss["color"], 7, pattern_type="wave"))


def boss_pattern_void_burst(boss, phase):
    """Разлетающиеся по воздуху разрывные выстрелы"""
    bx, by = boss["x"], boss["y"]
    px, py = player["x"], player["y"]
    base_angle = math.atan2(py - by, px - bx)
    bursts = 4 + phase
    shots_per_burst = 8

    for b_i in range(bursts):
        delay = b_i * 0.12
        for s_i in range(shots_per_burst):
            spread = (s_i - shots_per_burst // 2) * (0.18 + phase * 0.04)
            angle = base_angle + spread
            bullet = create_bullet(boss, angle, ENEMY_BULLET_SPEED * (0.6 + b_i * 0.08) * BOSS_SPEED_MULT,
                                   (60 + b_i * 30, 60, 120), 8, pattern_type="explosive")
            ebullets.append(bullet)


def boss_pattern_homing_vortex(boss, phase):
    """Вихрь самонаводящихся снарядов: кольцо с домом на игрока"""
    bx, by = boss["x"], boss["y"]
    px, py = player["x"], player["y"]
    base_angle = math.atan2(py - by, px - bx)
    for ring_i in range(3):
        ring_size = 10 + ring_i * 4
        offset_angle = ring_i * 0.3
        for i in range(ring_size):
            angle = base_angle + i * (2 * math.pi / ring_size) + offset_angle
            ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.55 * BOSS_SPEED_MULT,
                                          (180, 50, 220), 9,
                                          homing=True, homing_strength=0.025 + ring_i * 0.01))
    # Дополнительная волна SPIRAL снарядов
    spiral_speed = 0.08 + phase * 0.02
    for i in range(12):
        angle = time.time() * spiral_speed + i * (2 * math.pi / 12)
        ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * 0.8 * BOSS_SPEED_MULT,
                                      (100, 50, 180), 9, pattern_type="spiral"))


def boss_pattern_pinwheel(boss, phase):
    """Вертушка: 8 лучей по 8 снарядов каждый"""
    bx, by = boss["x"], boss["y"]
    spokes = 8
    rays = 6 + phase
    for spoke in range(spokes):
        base_angle = spoke * (2 * math.pi / spokes)
        for ray_i in range(rays):
            angle = base_angle + ray_i * 0.1
            speed_mult = 0.55 + ray_i * 0.1
            ebullets.append(create_bullet(boss, angle, ENEMY_BULLET_SPEED * speed_mult * BOSS_SPEED_MULT,
                                          (220, 120, 50), 6 + ray_i))


# Словари для маппинга паттернов
boss_pattern_map = {
    "Демон": boss_pattern_demon,
    "Сверхчеловек": boss_pattern_superhuman,
    "Барон ада": boss_pattern_baron,
    "Еврей": boss_pattern_jew,
    "Инжинер": boss_pattern_engineer,
    "Машина войны": boss_pattern_war_machine,
    "Он": boss_pattern_him,
    "Мехо-гитлер": boss_pattern_mech_hitler,
    "Падший ангел": boss_pattern_fallen_angel,
    "Голод": boss_pattern_hunger,
    "Смерть": boss_pattern_death,
    "Чума": boss_pattern_plague,
    "Война": boss_pattern_war,
    "Люцифер": boss_pattern_lucifer,
    "Сенатор": boss_pattern_senator,
    "Император человечества": boss_pattern_emperor,
    "Мастер": boss_pattern_master,
    "Кто-то": boss_pattern_someone,
    "Император человечества II": boss_pattern_emperor2,
    "БОГ": boss_pattern_god,
    "Кольцевая очередь": boss_pattern_ring_burst,
    "Вихрь пустоты": boss_pattern_void_burst,
    "Гоминг вихрь": boss_pattern_homing_vortex,
    "Вертушка": boss_pattern_pinwheel,
}

sin_pattern_map = {
    "pride": sin_pattern_pride,
    "greed": sin_pattern_greed,
    "lust": sin_pattern_lust,
    "envy": sin_pattern_envy,
    "gluttony": sin_pattern_gluttony,
    "wrath": sin_pattern_wrath,
    "sloth": sin_pattern_sloth,
}

# -------------------- Global variables for game objects --------------------
bullets = []
ebullets = []
enemies = []
bosses = []
ppick = []
apick = []
hearts = []
particles = []
coins = []
wave = 1
dungeon = None
current_floor = None
last_time = time.time()
game_over = False
victory = False
souls = 3
money = 0
achievements = defaultdict(bool)
inventory = []
wave_cleared = False
shop_door_open = False
secret_room = None
boss_defeated = False
can_advance = False
walls_active = True

# Player dictionary
player = {}

# -------------------- Damage Player with Credit and D6 II --------------------
def damage_player(dmg):
    global souls, money, game_over, wave_cleared, wave, state, current_arena, dungeon

    if player.get("god_mode", False):
        return

    if player["shield_active"] or player["inv"] > 0.0:
        return

    if "ghost" in player["effects"] and random.random() < player.get("ghost_chance", 0.1):
        return

    if player["armor"] > 0.0:
        dmg = max(1, int(round(dmg * (1 - player["armor"]))))

    player["hp"] -= dmg
    player["inv"] = INVUL_AFTER_HIT

    if player.get("parry_ghost", False) and parry_success:
        player["inv"] = 1.0

    if "cruelty" in player["effects"]:
        player["cruelty_bonus"] = 0
        player["last_hit_wave"] = wave

    player["waves_without_hit"] = 0
    player["combo_kills"] = 0
    player["dodged_bullets"] = 0

    # Проверка смерти
    if player["hp"] <= 0:
        # D6 II
        d6_II_revive = False
        if player.get("d6_II_revive", False) and random.randint(1, 6) == 1:
            d6_II_revive = True
            player.setdefault("d6_II_revive_count", 0)
            player["d6_II_revive_count"] += 1

        # D6
        d6_revive = False
        if "d6_revive" in player["effects"] and souls <= 0 and random.randint(1, 6) == 1:
            d6_revive = True
            player.setdefault("d6_revive_count", 0)
            player["d6_revive_count"] += 1

        if d6_II_revive or d6_revive:
            player["hp"] = player["max_hp"] // 2
            player["inv"] = 1.8
            console.add_output("Воскрешение!")
        elif player.get("phoenix"):
            player["phoenix"] = False
            player["hp"] = player["max_hp"]
            player["inv"] = 2.0
        elif souls > 0:
            souls -= 1
            player["lives"] -= 1
            player["hp"] = player["max_hp"]
            player["inv"] = 1.8
            wave = max(1, wave - 1)
            enemies.clear()
            bosses.clear()
            ebullets.clear()
            bullets.clear()
            wave_cleared = True
        else:
            # Проверка кредита
            if player.get("credit_timer", 0) > 0 and player["hp"] < 0:
                pass
            else:
                if player["hp"] >= player["max_hp"] and not achievements["kamikaze"]:
                    achievements["kamikaze"] = True
                player["deaths"] += 1
                game_over = True

# -------------------- Apply Passive with New Items --------------------
def apply_passive(key):
    global souls, money, parry_window_start, PARRY_WINDOW
    d = powerups[key]["p"]

    if "soul" in d:
        souls += d["soul"]
        souls = max(0, souls)

    # Поедатель душ
    if d.get("soul_eater", False):
        player["soul_eater"] = True
        souls = 0

    # Предатель
    if d.get("traitor", False):
        player["traitor"] = True

    # D6 II
    if d.get("d6_II", False):
        player["d6_II_revive"] = True

    # Кредит
    if d.get("credit", False):
        player["credit_timer"] = 60
        player["credit_type"] = "hp"

    # Проклятье гнили
    if d.get("rot_curse", False):
        player["rot_curse"] = True
        player["rot_curse_timer"] = 0

    # Поедатель душ (новый)
    if d.get("soul_devourer", False):
        player["soul_devourer"] = True
        souls = 0
        player["soul_devourer_kills"] = 0

    # Алчнее
    if d.get("greed", False):
        player["greed"] = True
        player["max_hp"] = 1
        player["hp"] = 1
        souls = 0
        player["money_timer"] = 120

    # Словно сделан из стекла
    if d.get("made_of_glass", False):
        player["made_of_glass"] = True
        player["max_hp"] = 1
        player["hp"] = 1
        souls = 0
        player["dmg"] += 50

    # Бесконечность - запоминаем волну взятия
    if key == "INFINITY":
        player["infinity_taken_wave"] = wave

    player["unique_items"].add(key)
    player["items_collected"] += 1

    if d.get("legendary", False) or key in ["HP_UP", "DMG_UP", "LUCK_UP", "SPEED_UP", "ALL_UP", "FIRERATE", "RANGE"]:
        player.setdefault("legendary_items", 0)
        player["legendary_items"] += 1

    if d.get("cursed", False):
        player.setdefault("cursed_items", 0)
        player["cursed_items"] += 1

    for tgl in ("pierce", "laser", "burn", "grenade", "split", "wave",
                "explode", "fire", "bomb", "pacman", "blood_ray",
                "infallibility", "cruelty", "gamble", "double", "triple", "quad",
                "magnet", "time_slow", "shield_gen", "ricochet", "mines", "clone",
                "black_hole", "chain", "frost", "venom", "echo",
                "only_cursed", "hide_wave", "d6_revive", "parry_ghost"):
        if d.get(tgl):
            player["effects"].add(tgl)
            if tgl == "only_cursed":
                player["only_cursed"] = True
            if tgl == "hide_wave":
                player["hide_wave"] = True
            if tgl == "d6_revive":
                player["d6_revive"] = True
            if tgl == "parry_ghost":
                player["parry_ghost"] = True

    if "bleed_chance" in d:
        player["bleed_chance"] = player.get("bleed_chance", 0) + d["bleed_chance"]
    if "burn_chance" in d:
        player["burn_chance"] = player.get("burn_chance", 0) + d["burn_chance"]
    if "poison_chance" in d:
        player["poison_chance"] = player.get("poison_chance", 0) + d["poison_chance"]
    if "stone_chance" in d:
        player["stone_chance"] = player.get("stone_chance", 0) + d["stone_chance"]
    if "homing" in d:
        player["effects"].add("homing")
    if "homing_plus" in d:
        player["effects"].add("homing_plus")
    if "ghost" in d:
        player["ghost_chance"] = player.get("ghost_chance", 0) + d["ghost"]
    if "lightning_chance" in d:
        player["lightning_chance"] = player.get("lightning_chance", 0) + d["lightning_chance"]

    player["dmg"] += d.get("dmg", 0)
    player["speed_mult"] += d.get("spd", 0.0)
    if d.get("maxhp"):
        player["max_hp"] += d["maxhp"]
        if d.get("negative_hp", False):
            player["hp"] += d["maxhp"]
        else:
            player["hp"] = min(player["hp"] + d["maxhp"], player["max_hp"])

    player["armor"] = min(0.8, player["armor"] + d.get("armor", 0.0))
    player["vamp"] = min(0.8, player["vamp"] + d.get("vamp", 0.0))
    player["luck"] = min(0.8, player["luck"] + d.get("luck", 0.0))
    player["regen"] = max(player["regen"], d.get("regen", 0.0))

    if "firerate" in d:
        player["base_fire_cd"] = max(5, player["base_fire_cd"] - d["firerate"])
        player["fire_cd"] = player["base_fire_cd"]

    if "range" in d:
        player["bullet_range"] = player.get("bullet_range", 0) + d["range"]

    if "magnet" in d:
        player["magnet"] = d["magnet"]

    if "time_slow" in d:
        player["time_slow_power"] = d["time_slow"]

    if "difficulty" in d:
        player["difficulty"] = player.get("difficulty", 0) + d["difficulty"]

    if "parry_time" in d:
        player["parry_time_bonus"] = player.get("parry_time_bonus", 0) + d["parry_time"]
        global PARRY_WINDOW
        PARRY_WINDOW = 0.12 + player["parry_time_bonus"]

    if "cursed_bonus" in d:
        player["cursed_bonus"] = d["cursed_bonus"]

    if "dlc" in d:
        stat = random.choice(["dmg", "maxhp", "spd", "luck", "firerate", "range"])
        if stat == "dmg":
            player["dmg"] += 2
            player["dlc_stats"]["dmg"] += 2
        elif stat == "maxhp":
            player["max_hp"] += 2
            player["hp"] += 2
            player["dlc_stats"]["maxhp"] += 2
        elif stat == "spd":
            player["speed_mult"] += 0.2
            player["dlc_stats"]["spd"] += 2
        elif stat == "luck":
            player["luck"] += 0.2
            player["dlc_stats"]["luck"] += 2
        elif stat == "firerate":
            player["base_fire_cd"] = max(5, player["base_fire_cd"] - 2)
            player["dlc_stats"]["firerate"] += 2
        elif stat == "range":
            player["bullet_range"] = player.get("bullet_range", 0) + 2
            player["dlc_stats"]["range"] += 2


def pick_active(key):
    player["actives"].setdefault(key, {"last_t": 0.0, "last_w": -999})

# -------------------- Функция для проверки и исправления размера пуль --------------------
def update_bullet_visuals():
    """Обеспечивает постоянный видимый размер пуль"""
    for b in ebullets:
        if not b.get("boss", False):
            current_size = b.get("size", 8)
            if current_size < MIN_BULLET_SIZE:
                b["size"] = MIN_BULLET_SIZE
            elif current_size > MAX_BULLET_SIZE:
                b["size"] = MAX_BULLET_SIZE
            b["outline"] = True

# -------------------- Функция обновления специальных эффектов --------------------
def update_special_effects(dt):
    """Обновляет специальные эффекты предметов"""
    global souls, money
    
    # Проклятье гнили - каждую волну 50% шанс потери HP
    if player.get("rot_curse", False):
        player["rot_curse_timer"] += dt
        if player["rot_curse_timer"] >= 60:  # Каждую секунду проверяем
            player["rot_curse_timer"] = 0
            if random.random() < 0.5 / 60:  # 50% шанс в секунду
                damage = max(1, player["max_hp"] // 10)
                player["hp"] -= damage
                if player["hp"] <= 0:
                    damage_player(0)
                console.add_output(f"Проклятье гнили: -{damage} HP!")
        
        # Все враги отравлены
        for e in enemies:
            e["poisoned"] = True
        for e in bosses:
            e["poisoned"] = True
    
    # Алчнее - деньги исчезают
    if player.get("greed", False):
        player["money_timer"] -= dt * 60
        if player["money_timer"] <= 0:
            money = 0
            player["money_timer"] = 120
        # Души всегда 0
        souls = 0

# -------------------- Reset/Spawners --------------------
def reset():
    global player, bullets, ebullets, enemies, bosses, ppick, apick, hearts, particles, coins
    global wave, game_over, victory, souls, money, wave_cleared, shop_door_open
    global current_arena, boss_defeated, can_advance, secret_room, walls_active, dungeon

    player = {
        "x": screen.get_width() // 2,
        "y": screen.get_height() - 80,
        "hp": 6,
        "max_hp": 6,
        "lives": 3,
        "souls": 3,
        "fire_cd": 30,
        "timer": 0,
        "base_fire_cd": 30,
        "inv": 0.0,
        "score": 0,
        "waves_without_hit": 0,
        "kill_streak": 0,
        "speed_mult": 1.0,
        "dmg": 2,
        "armor": 0.0,
        "effects": set(),
        "ricochet": 0,
        "magnet": 0,
        "aura_slow": 0.0,
        "aura_r": 0,
        "reflect": 0.0,
        "vamp": 0.0,
        "regen": 0.0,
        "luck": 0.0,
        "phoenix": False,
        "golem": 0,
        "blades": 0,
        "turrets": [],
        "actives": {},
        "decoys": [],
        "bhole": None,
        "time_slow_until": 0.0,
        "shield_active": False,
        "shield_until": 0.0,
        "shield_cd_until": 0.0,
        "shockwaves": [],
        "cruelty_bonus": 0,
        "parry_count": 0,
        "last_hit_wave": 0,
        "items_collected": 0,
        "combo_kills": 0,
        "status_effects_applied": set(),
        "gamble_wins": 0,
        "max_passives": 0,
        "dodged_bullets": 0,
        "max_damage": 0,
        "vampire_heal": 0,
        "time_slowed_total": 0,
        "invincibility_time": 0,
        "unique_items": set(),
        "all_enemies_seen": set(),
        "bullet_hell_count": 0,
        "total_kills": 0,
        "waves_completed": 0,
        "deaths": 0,
        "god_mode": False,
        "difficulty": 0,
        "parry_time_bonus": 0.0,
        "only_cursed": False,
        "cursed_bonus": 1.0,
        "hide_wave": False,
        "d6_revive": False,
        "d6_II_revive": False,
        "parry_ghost": False,
        "infinity_taken_wave": 0,
        "dlc_stats": {"dmg": 0, "maxhp": 0, "spd": 0, "luck": 0, "firerate": 0, "range": 0},
        "multipliers": {},
        "arenas_visited": set([1]),
        "sins_defeated": set(),
        "credit_timer": 0,
        "credit_type": None,
        "soul_eater": False,
        "traitor": False,
        "dungeons_completed": 0,
        "rot_curse": False,
        "rot_curse_timer": 0,
        "soul_devourer": False,
        "soul_devourer_kills": 0,
        "greed": False,
        "money_timer": 0,
        "made_of_glass": False,
    }
    
    bullets.clear()
    ebullets.clear()
    enemies.clear()
    bosses.clear()
    ppick.clear()
    apick.clear()
    hearts.clear()
    particles.clear()
    coins.clear()
    wave = 1
    current_arena = ARENAS[1]
    game_over = False
    victory = False
    souls = 3
    money = 10
    wave_cleared = False
    shop_door_open = False
    boss_defeated = False
    can_advance = False
    secret_room = None
    walls_active = True
    dungeon = None

    print(f"Старт игры, волна {wave}")
    count = spawn_count()
    for i in range(count):
        spawn_enemy()
    print(f"Заспавнено {len(enemies)} врагов на волне {wave}")
    console.add_output(f"Волна {wave}")


def spawn_enemy(ekey=None):
    global wave
    if wave < 15:
        pools = [ENEMIES]
        if wave >= 9:
            pools.append(ELITES)
    else:
        pools = [ELITES, ULTRAS]
        if wave >= 18:
            pools.append(ULTRAS)

    difficulty_bonus = player.get("difficulty", 0)
    actual_wave = max(1, wave + difficulty_bonus)

    if wave >= 15 and random.random() < 0.3 and ekey is None:
        new_enemies = ["X", "Q", "Y", "Z", "P", "B", "T"]
        ekey = random.choice(new_enemies)

    if ekey is None:
        lib = random.choice(pools)
        if lib:
            ekey = random.choice(list(lib.keys()))
        else:
            ekey = "E"

    d = ENEMIES.get(ekey) or ELITES.get(ekey) or ULTRAS.get(ekey)
    if d is None:
        d = ENEMIES["E"]
        ekey = "E"

    x = random.randint(50, max(50, screen.get_width() - 50))
    y = random.randint(40, 200)
    hp = hp_scale(d["hp"]) + difficulty_bonus

    enemy = {
        "x": x, "y": y, "type": ekey, "name": d["name"], "char": d["char"], "color": d["color"],
        "hp": hp, "max_hp": hp, "pat": d["pat"], "delay": max(0.4, d["delay"] * max(0.7, 1 - 0.008 * actual_wave)),
        "timer": random.random() * d["delay"], "spd": d["spd"] * (1.0 + min(0.3, 0.005 * actual_wave)) * ENEMY_SPEED_MULT,
        "ai": d["ai"], "ai_t": 0.0, "tx": x, "ty": y,
        "bleeding": 0, "burning": 0, "poisoned": 0, "stoned": 0,
        "spawn_time": time.time(),
        "impact_x": 0, "impact_y": 0, "impact_time": 0
    }

    player["all_enemies_seen"].add(ekey)
    if len(player["all_enemies_seen"]) >= len(ENEMIES) and not achievements["explorer"]:
        achievements["explorer"] = True

    if random.random() < 0.1 + min(0.15, wave * 0.002):
        enemy["ai"] = AI.HEALER

    # На волнах выше 5 добавляем уникальные свойства врагам
    if wave >= 5:
        trait_roll = random.random()
        if trait_roll < 0.1 and wave >= 10:
            enemy["explosive"] = True
            enemy["color"] = (255, 100, 0)
            enemy["char"] = "*"
        elif trait_roll < 0.2 and wave >= 8:
            enemy["regenerating"] = True
            enemy["color"] = (0, 200, 100)
            enemy["char"] = "+"
            enemy["hp"] = int(enemy["hp"] * 1.5)
            enemy["max_hp"] = enemy["hp"]
        elif trait_roll < 0.3 and wave >= 12:
            enemy["fast_shooter"] = True
            enemy["delay"] = max(0.3, enemy["delay"] * 0.5)
            enemy["color"] = (255, 0, 255)
            enemy["char"] = "!"
        elif trait_roll < 0.35 and wave >= 6:
            enemy["shielder"] = True
            enemy["shield_hp"] = 3
            enemy["color"] = (80, 160, 255)
            enemy["char"] = "#"
        elif trait_roll < 0.40 and wave >= 10:
            enemy["summoner"] = True
            enemy["summon_timer"] = 5.0
            enemy["color"] = (150, 50, 150)
            enemy["char"] = "&"
        elif trait_roll < 0.43 and wave >= 15:
            enemy["teleporter"] = True
            enemy["teleport_timer"] = 3.0
            enemy["color"] = (200, 200, 0)
            enemy["char"] = "?"
        elif trait_roll < 0.46 and wave >= 7:
            enemy["status_immune"] = True
            enemy["color"] = (150, 150, 150)
            enemy["char"] = "@"

    enemies.append(enemy)


def spawn_boss(wv):
    boss_wave = ((wv + 4) // 5) * 5
    if boss_wave < 5:
        boss_wave = 5

    if boss_wave in BOSSES:
        b = BOSSES[boss_wave]
    else:
        available_bosses = [w for w in BOSSES.keys() if w <= wv]
        if available_bosses:
            boss_wave = max(available_bosses)
            b = BOSSES[boss_wave]
        else:
            b = BOSSES[5]

    difficulty_bonus = player.get("difficulty", 0)
    actual_wave = max(1, wv + difficulty_bonus)
    hp = int(b["hp"] * (1.5 + 0.05 * actual_wave))

    boss_x = screen.get_width() // 2
    boss_y = 120

    boss = {
        "x": boss_x, "y": boss_y,
        "start_x": boss_x, "start_y": boss_y,
        "name": b["name"], "char": b["char"], "color": b["color"],
        "hp": hp, "max_hp": hp, "pat": b["ph"][0]["pat"],
        "delay": b["ph"][0]["delay"], "base_delay": b["ph"][0]["delay"],
        "spd": b["ph"][0]["spd"] * 0.5 * BOSS_SPEED_MULT,
        "timer": 0.0, "phase": 0, "phases": b["ph"],
        "ai": AI.SPECIAL,
        "ai_t": 0.0,
        "tx": boss_x, "ty": boss_y,
        "quake": b.get("quake", False),
        "special_attack": b.get("special_attack"),
        "special_chance": b.get("special_chance", 0.25),
        "summons": b.get("summons", False),
        "impact_x": 0, "impact_y": 0, "impact_time": 0
    }

    bosses.append(boss)

    for _ in range(5):
        drop_coin(boss["x"] + random.randint(-30, 30), boss["y"] + random.randint(-30, 30), 1)

    if not achievements["boss_slayer"]:
        achievements["boss_slayer"] = True


def spawn_sin_boss(sin_name):
    b = SIN_BOSSES[sin_name]
    boss = {
        "x": screen.get_width() // 2, "y": 110,
        "start_x": screen.get_width() // 2, "start_y": 110,
        "name": b["name"], "char": b["char"], "color": b["color"],
        "hp": b["hp"], "max_hp": b["hp"], "pat": b["pattern"], "delay": b["delay"],
        "base_delay": b["delay"],
        "spd": b["spd"] * 0.5 * BOSS_SPEED_MULT, "timer": 0.0, "phase": 0,
        "phases": [{"pat": b["pattern"], "delay": b["delay"], "spd": b["spd"], "ai": AI.SPECIAL}],
        "ai": AI.SPECIAL, "ai_t": 0.0, "tx": screen.get_width() // 2, "ty": 110,
        "special_attack": b["special"],
        "special_chance": 0.25,
        "sin": sin_name,
        "impact_x": 0, "impact_y": 0, "impact_time": 0
    }
    bosses.append(boss)


def drop_coin(x, y, amount=1):
    coins.append({
        "x": x, "y": y,
        "char": "$",
        "color": GOLD,
        "amount": amount,
        "spawn_time": time.time()
    })


def drop_passive(x, y, key=None):
    if player.get("only_cursed", False):
        cursed_items = [k for k, d in powerups.items() if d["p"].get("cursed", False)]
        if cursed_items:
            key = random.choice(cursed_items)

    if key is None:
        weights = {}
        for k in powerups:
            weights[k] = 1.0
            if powerups[k]["p"].get("cursed", False):
                weights[k] *= player.get("cursed_bonus", 1.0)
            if powerups[k]["p"].get("achievement", False):
                weights[k] = 0.1

        total = sum(weights.values())
        r = random.random() * total
        current = 0
        for k, w in weights.items():
            current += w
            if r <= current:
                key = k
                break

    d = powerups[key]
    ppick.append({
        "x": x, "y": y, "key": key, "char": d["char"],
        "color": d["color"], "spawn_time": time.time()
    })


def drop_active(x, y, key=None):
    if key is None:
        key = random.choice(list(actives.keys()))
    d = actives[key]
    apick.append({
        "x": x, "y": y, "key": key, "char": d["char"],
        "color": d["color"], "spawn_time": time.time()
    })


def drop_heart(x, y):
    hearts.append({
        "x": x, "y": y, "char": "+", "color": (255, 70, 70),
        "spawn_time": time.time()
    })


# -------------------- Make Bullet with Traitor Effect --------------------
def make_bullet(dx, dy):
    pat = PAT.SINGLE
    if "spread" in player["effects"]:
        pat = PAT.SPREAD
    elif "wave" in player["effects"]:
        pat = PAT.WAVE
    elif "homing" in player["effects"] or "homing_plus" in player["effects"]:
        pat = PAT.HOMING
    elif "laser" in player["effects"]:
        pat = PAT.LASER
    
    b = {
        "x": player["x"], "y": player["y"], "dx": dx * BULLET_SPEED, "dy": dy * BULLET_SPEED,
        "dmg": player["dmg"], "pierce": "pierce" in player["effects"],
        "burn": "burn" in player["effects"] or random.random() < player.get("burn_chance", 0),
        "bleed": "bleed" in player["effects"] or random.random() < player.get("bleed_chance", 0),
        "poison": random.random() < player.get("poison_chance", 0),
        "stone": random.random() < player.get("stone_chance", 0),
        "ttl": 2.5 if player.get("bullet_range") else 2.0,
        "created": time.time(),
        "impact": True,
        "size": 5,
        "direction": (dx, dy),
        "pattern": pat,
    }

    # Эффект Предатель - выстрелы сзади дают 100% кровотечение
    if player.get("traitor", False):
        keys = pygame.key.get_pressed()
        moving = False
        move_dir = (0, 0)

        if keys[pygame.K_w]:
            moving = True
            move_dir = (0, -1)
        elif keys[pygame.K_s]:
            moving = True
            move_dir = (0, 1)
        elif keys[pygame.K_a]:
            moving = True
            move_dir = (-1, 0)
        elif keys[pygame.K_d]:
            moving = True
            move_dir = (1, 0)

        if moving and (move_dir[0] * dx + move_dir[1] * dy) < -0.5:
            b["bleed"] = True

    if "fire" in player["effects"]:
        b["fire"] = True
        b["color"] = (255, 100, 0)
    elif "laser" in player["effects"]:
        b["laser"] = True
        b["dx"] *= 1.5
        b["dy"] *= 1.5
        b["dmg"] += 2
    elif "blood_ray" in player["effects"]:
        b["blood_ray"] = True
        b["pierce"] = True
        b["dmg"] += 3
    elif "grenade" in player["effects"]:
        b["grenade"] = True
        b["arc"] = random.uniform(-0.3, 0.3)
        b["gravity"] = 0.5
    elif "bomb" in player["effects"]:
        b["bomb"] = True
        b["timer"] = 1.5

    if "homing_plus" in player["effects"]:
        b["homing"] = "strong"
    elif "homing" in player["effects"]:
        b["homing"] = "weak"

    bullets.append(b)

    if "quad" in player["effects"]:
        for ang in (-30, -10, 10, 30):
            r = math.radians(ang)
            bx = math.cos(r) * dx - math.sin(r) * dy
            by = math.sin(r) * dx + math.cos(r) * dy
            bullets.append({**b, "dx": bx * BULLET_SPEED, "dy": by * BULLET_SPEED})
    elif "triple" in player["effects"]:
        for ang in (-20, 0, 20):
            r = math.radians(ang)
            bx = math.cos(r) * dx - math.sin(r) * dy
            by = math.sin(r) * dx + math.cos(r) * dy
            bullets.append({**b, "dx": bx * BULLET_SPEED, "dy": by * BULLET_SPEED})
    elif "double" in player["effects"]:
        for ang in (-10, 10):
            r = math.radians(ang)
            bx = math.cos(r) * dx - math.sin(r) * dy
            by = math.sin(r) * dx + math.cos(r) * dy
            bullets.append({**b, "dx": bx * BULLET_SPEED, "dy": by * BULLET_SPEED})

    if "split" in player["effects"] and random.random() < 0.3:
        for ang in (-45, 45):
            r = math.radians(ang)
            bx = math.cos(r) * dx - math.sin(r) * dy
            by = math.sin(r) * dx + math.cos(r) * dy
            bullets.append({**b, "dx": bx * BULLET_SPEED * 0.7, "dy": by * BULLET_SPEED * 0.7,
                            "dmg": max(1, b["dmg"] // 2)})

    if "echo" in player["effects"] and random.random() < 0.3:
        b_echo = b.copy()
        b_echo["x"] = player["x"] + random.randint(-10, 10)
        b_echo["y"] = player["y"] + random.randint(-10, 10)
        b_echo["dmg"] = max(1, b["dmg"] // 2)
        bullets.append(b_echo)


# -------------------- Enemy fire --------------------
def enemy_fire(src, pat, boss=False, phase=0):
    """Стрельба врагов с правильными ТИПАМИ пуль (форма зависит от типа)"""

    # Проверка на босса с уникальным паттерном
    if boss and src.get("name") in boss_pattern_map:
        boss_pattern_map[src["name"]](src, phase)
        if phase >= 1:
            for _ in range(8):  # Было 5 - больше дополнительных пуль
                angle = random.uniform(0, 2*math.pi)
                bullet = create_bullet(src, angle, ENEMY_BULLET_SPEED * random.uniform(0.6, 0.9) * BOSS_SPEED_MULT,
                                       src.get("color", (255, 255, 255)), random.randint(5, 10))
                ebullets.append(bullet)
        return

    # Проверка на грехи
    if boss and src.get("sin") in sin_pattern_map:
        sin_pattern_map[src["sin"]](src)
        return

    # Стандартные паттерны для обычных врагов
    px, py = player["x"], player["y"]
    dx, dy = px - src["x"], py - src["y"]
    base = math.atan2(dy, dx)
    
    spd = ENEMY_BULLET_SPEED * ENEMY_SPEED_MULT * 0.7
    phase_mult = 0.7 + phase * 0.15
    
    # Выбираем ТИП пули в зависимости от паттерна и врага
    if pat == PAT.SINGLE:
        bullet_type = BULLET_TYPE.NORMAL  # КРУГ
        for _ in range(4):
            spread = random.uniform(-0.15, 0.15)
            ebullets.append(create_bullet(src, base + spread, spd, BULLET_COLORS[BULLET_TYPE.NORMAL], 9, pat=PAT.SINGLE))
            
    elif pat == PAT.FOUR:
        bullet_type = BULLET_TYPE.SHARD  # ТРЕУГОЛЬНИК (осколок)
        for a in [base, base + math.pi/2, base + math.pi, base + 3*math.pi/2]:
            for j in range(3):
                offset = (j - 1) * 0.15
                ebullets.append(create_bullet(src, a + offset, spd * phase_mult, BULLET_COLORS[BULLET_TYPE.SHARD], 8, pat=PAT.FOUR))
                
    elif pat == PAT.EIGHT:
        bullet_type = BULLET_TYPE.NORMAL  # КРУГ
        count = 18
        for i in range(count):
            angle = base + i * (2 * math.pi / count)
            ebullets.append(create_bullet(src, angle, spd, BULLET_COLORS[BULLET_TYPE.NORMAL], 8, pat=PAT.EIGHT))
            
    elif pat == PAT.SPIRAL:
        bullet_type = BULLET_TYPE.SPIRAL  # ВРАЩАЮЩАЯСЯ ПАЛОЧКА
        origin = time.time() * 1.5
        count = 22
        for i in range(count):
            angle = base + origin + i * math.pi * 0.15
            ebullets.append(create_bullet(src, angle, spd * 0.7, BULLET_COLORS[BULLET_TYPE.SPIRAL], 8, pat=PAT.SPIRAL))
            
    elif pat == PAT.SPREAD:
        bullet_type = BULLET_TYPE.PLASMA  # ТРЕУГОЛЬНИК
        count = 14
        for i in range(count):
            angle = base + (i - count//2) * 0.12
            ebullets.append(create_bullet(src, angle, spd, BULLET_COLORS[BULLET_TYPE.PLASMA], 8, pat=PAT.SPREAD))
            
    elif pat == PAT.HOMING:
        bullet_type = BULLET_TYPE.HOMING  # КРУГ (самонаводящийся)
        count = 11
        for i in range(count):
            angle = base + (i - count//2) * 0.08
            ebullets.append(create_bullet(src, angle, spd * 0.6, BULLET_COLORS[BULLET_TYPE.HOMING], 8, homing=True, homing_strength=0.03, pat=PAT.HOMING))
            
    elif pat == PAT.RING:
        bullet_type = BULLET_TYPE.SHARD  # ТРЕУГОЛЬНИК
        count = 24
        for i in range(count):
            angle = i * (2 * math.pi / count)
            bullet = create_bullet(src, angle, spd * 0.6, BULLET_COLORS[BULLET_TYPE.SHARD], 8, pat=PAT.RING)
            bullet["split_on_hit"] = True
            ebullets.append(bullet)
            
    elif pat == PAT.WAVE:
        bullet_type = BULLET_TYPE.PLASMA  # ТРЕУГОЛЬНИК
        count = 12
        for i in range(count):
            angle = base + math.sin(i * 0.5 + time.time()) * 0.5
            ebullets.append(create_bullet(src, angle, spd * 0.7, BULLET_COLORS[BULLET_TYPE.PLASMA], 9, pat=PAT.WAVE))
            
    elif pat == PAT.SPLIT:
        bullet_type = BULLET_TYPE.SHARD  # ТРЕУГОЛЬНИК
        for i in range(10):
            angle = random.uniform(0, 2*math.pi)
            bullet = create_bullet(src, angle, spd * 0.5, BULLET_COLORS[BULLET_TYPE.SHARD], 10, pat=PAT.SPLIT)
            bullet["split_count"] = 4
            ebullets.append(bullet)
            
    elif pat == PAT.LASER:
        bullet_type = BULLET_TYPE.LASER  # ПАЛОЧКА
        count = 7
        for i in range(count):
            angle = base + (i - count//2) * 0.08
            ebullets.append(create_bullet(src, angle, spd * 0.8, BULLET_COLORS[BULLET_TYPE.LASER], 6, pat=PAT.LASER))
            
    elif pat == PAT.CROSS:
        bullet_type = BULLET_TYPE.NORMAL  # КРУГ
        for ang in [0, math.pi/2, math.pi, 3*math.pi/2]:
            for j in range(4):
                offset = (j - 1.5) * 0.15
                ebullets.append(create_bullet(src, ang + offset, spd * 0.8, BULLET_COLORS[BULLET_TYPE.NORMAL], 7, pat=PAT.CROSS))
                
    elif pat == PAT.VORTEX:
        bullet_type = BULLET_TYPE.SPIRAL  # ВРАЩАЮЩАЯСЯ ПАЛОЧКА
        count = 20
        for i in range(count):
            angle = i * (2*math.pi/count) + time.time() * 0.5
            ebullets.append(create_bullet(src, angle, spd * 0.6, BULLET_COLORS[BULLET_TYPE.SPIRAL], 9, pat=PAT.VORTEX))
            
    elif pat == PAT.ARC:
        bullet_type = BULLET_TYPE.PLASMA  # ТРЕУГОЛЬНИК
        for i in range(9):
            angle = base + (i - 4) * 0.2
            ebullets.append(create_bullet(src, angle, spd * 0.7, BULLET_COLORS[BULLET_TYPE.PLASMA], 8, pat=PAT.ARC))
            
    elif pat == PAT.RAIN:
        bullet_type = BULLET_TYPE.GRAVITY  # ЗВЕЗДА (гравитация)
        for _ in range(15):
            x = random.randint(50, max(50, screen.get_width() - 50))
            y = random.randint(-50, 0)
            bullet = create_bullet({"x": x, "y": y}, math.pi/2, spd * 0.3, BULLET_COLORS[BULLET_TYPE.GRAVITY], 10, pat=pat)
            bullet["gravity"] = True
            bullet["gravity_strength"] = 0.03
            ebullets.append(bullet)
            
    elif pat == PAT.BOUNCE:
        bullet_type = BULLET_TYPE.NORMAL  # КРУГ
        for i in range(10):
            angle = base + (i - 5) * 0.15
            bullet = create_bullet(src, angle, spd * 0.9, BULLET_COLORS[BULLET_TYPE.NORMAL], 8, pat=PAT.BOUNCE)
            bullet["bounce"] = 4
            bullet["bounce_count"] = 0
            ebullets.append(bullet)
            
    elif pat == PAT.PENTAGON:
        bullet_type = BULLET_TYPE.HOLY  # КРУГ (святой)
        for arm in range(5):
            arm_angle = base + arm * (2 * math.pi / 5)
            for j in range(3):
                spread = (j - 1) * 0.3
                ebullets.append(create_bullet(src, arm_angle + spread, spd * (0.7 + j * 0.15), BULLET_COLORS[BULLET_TYPE.HOLY], 7, pat=PAT.PENTAGON))

    # -------------------- Update Functions --------------------
def update_enemy_ai(e, dt):
    if e.get("stoned", 0) > time.time():
        return

    # Особые свойства врагов
    now = time.time()

    # Регенерация
    if e.get("regenerating", False) and e["hp"] < e["max_hp"]:
        e["hp"] = min(e["max_hp"], e["hp"] + 0.5 * dt)

    # Телепортация
    if e.get("teleporter", False):
        e["teleport_timer"] = e.get("teleport_timer", 3.0) - dt
        if e["teleport_timer"] <= 0:
            e["x"] = player["x"] + random.randint(-200, 200)
            e["y"] = player["y"] + random.randint(-200, 200)
            e["x"] = max(50, min(screen.get_width() - 50, e["x"]))
            e["y"] = max(50, min(screen.get_height() - 50, e["y"]))
            e["teleport_timer"] = 3.0 + random.uniform(0, 2)

    # Призыв
    if e.get("summoner", False):
        e["summon_timer"] = e.get("summon_timer", 5.0) - dt
        if e["summon_timer"] <= 0 and len(enemies) < 25:
            for _ in range(random.randint(1, 2)):
                spawn_enemy()
            e["summon_timer"] = 5.0 + random.uniform(0, 3)

    # Иммунитет к статусам
    if e.get("status_immune", False):
        e["bleeding"] = 0
        e["burning"] = 0
        e["poisoned"] = 0
        e["stoned"] = 0

    if e.get("impact_time", 0) > now:
        e["x"] += e.get("impact_x", 0) * dt * 5
        e["y"] += e.get("impact_y", 0) * dt * 5

    e["ai_t"] = e.get("ai_t", 0.0) + dt
    if e["ai_t"] > random.uniform(2.0, 4.0):
        e["ai_t"] = 0.0
        if e["ai"] == AI.CHASE:
            e["ai"] = random.choice([AI.FLEE, AI.CIRCLE, AI.PATTERN, AI.SPECIAL])
        elif e["ai"] == AI.FLEE:
            e["ai"] = random.choice([AI.CHASE, AI.CIRCLE, AI.SPECIAL])
        elif e["ai"] == AI.CIRCLE:
            e["ai"] = random.choice([AI.CHASE, AI.FLEE, AI.PATTERN])
        elif e["ai"] == AI.PATTERN:
            e["ai"] = random.choice([AI.CHASE, AI.FLEE, AI.SPECIAL])
        elif e["ai"] == AI.SPECIAL:
            e["ai"] = random.choice([AI.CHASE, AI.CIRCLE, AI.PATTERN])
        elif e["ai"] == AI.HEALER:
            e["ai"] = random.choice([AI.CHASE, AI.CIRCLE, AI.SPECIAL])
        e["tx"] = random.randint(50, screen.get_width() - 50)
        e["ty"] = random.randint(50, 300)

    if e["ai"] == AI.HEALER:
        for other in enemies:
            if other is not e and math.hypot(other["x"] - e["x"], other["y"] - e["y"]) < 120:
                other["hp"] = min(other["max_hp"], other["hp"] + 0.6 * dt)

    speed_mult = 0.6 if player["aura_slow"] > 0 and math.hypot(e["x"] - player["x"], e["y"] - player["y"]) < player["aura_r"] else 1.0
    aggression = 1.0 + min(0.1, wave * 0.002)

    if e["ai"] == AI.CHASE:
        dx, dy = player["x"] - e["x"], player["y"] - e["y"]
        d = max(1.0, math.hypot(dx, dy))
        e["x"] += dx / d * e["spd"] * speed_mult * aggression
        e["y"] += dy / d * e["spd"] * speed_mult * aggression
    elif e["ai"] == AI.FLEE:
        dx, dy = e["x"] - player["x"], e["y"] - player["y"]
        d = max(1.0, math.hypot(dx, dy))
        if d < 150:
            e["x"] += dx / d * e["spd"] * 0.8 * speed_mult * aggression
            e["y"] += dy / d * e["spd"] * 0.8 * speed_mult * aggression
        else:
            dx, dy = player["x"] - e["x"], player["y"] - e["y"]
            d = max(1.0, math.hypot(dx, dy))
            e["x"] += dx / d * e["spd"] * 0.5 * speed_mult * aggression
            e["y"] += dy / d * e["spd"] * 0.5 * speed_mult * aggression
    elif e["ai"] == AI.CIRCLE:
        ang = e["ai_t"] * 2.5
        r = 100 + math.sin(e["ai_t"]) * 30
        tx = player["x"] + math.cos(ang) * r
        ty = player["y"] + math.sin(ang) * r
        dx, dy = tx - e["x"], ty - e["y"]
        d = max(1.0, math.hypot(dx, dy))
        e["x"] += dx / d * e["spd"] * 0.9 * speed_mult * aggression
        e["y"] += dy / d * e["spd"] * 0.9 * speed_mult * aggression
    elif e["ai"] == AI.PATTERN:
        dx, dy = e["tx"] - e["x"], e["ty"] - e["y"]
        d = max(1.0, math.hypot(dx, dy))
        if d > 30:
            e["x"] += dx / d * e["spd"] * 0.7 * speed_mult * aggression
            e["y"] += dy / d * e["spd"] * 0.7 * speed_mult * aggression
    elif e["ai"] == AI.SPECIAL:
        ang = e["ai_t"] * 1.8
        tx = player["x"] + math.cos(ang) * 150
        ty = player["y"] + math.sin(ang) * 100
        dx, dy = tx - e["x"], ty - e["y"]
        d = max(1.0, math.hypot(dx, dy))
        e["x"] += dx / d * e["spd"] * 0.6 * speed_mult * aggression
        e["y"] += dy / d * e["spd"] * 0.6 * speed_mult * aggression

    if e.get("bleeding", 0) > time.time():
        e["hp"] -= 0.15 * dt
    if e.get("burning", 0) > time.time():
        e["hp"] -= 0.25 * dt
    if e.get("poisoned", 0) > time.time():
        e["hp"] -= 0.1 * dt
        e["spd"] *= 0.85
    if e.get("stoned", 0) > time.time():
        e["spd"] *= 0.3

    if e["pat"] == PAT.HOMING and e["type"].startswith("F"):
        if e.get("timer", 0) > e["delay"]:
            e["x"] = random.randint(50, screen.get_width() - 50)
            e["y"] = random.randint(50, 300)
            e["timer"] = 0.0


def update_boss_ai(b, dt):
    now = time.time()
    if b.get("impact_time", 0) > now:
        b["x"] += b.get("impact_x", 0) * dt * 3
        b["y"] += b.get("impact_y", 0) * dt * 3

    b["ai_t"] = b.get("ai_t", 0.0) + dt
    if b["ai_t"] > random.uniform(4, 6):
        b["ai_t"] = 0.0
        if b["phase"] >= 2:
            b["tx"] = b.get("start_x", b["x"])
            b["ty"] = b.get("start_y", b["y"])
        else:
            b["tx"] = b.get("start_x", b["x"]) + random.randint(-40, 40)
            b["ty"] = b.get("start_y", b["y"]) + random.randint(-20, 20)

    hp_ratio = b["hp"] / b["max_hp"]

    if hp_ratio < 0.66 and b["phase"] == 0:
        b["phase"] = 1
        b["base_delay"] *= 0.8
        console.add_output(f"Фаза 2 босса {b['name']}!")
    elif hp_ratio < 0.33 and b["phase"] == 1:
        b["phase"] = 2
        b["base_delay"] *= 0.7
        console.add_output(f"ФИНАЛЬНАЯ ФАЗА босса {b['name']}!")

    dx, dy = b["tx"] - b["x"], b["ty"] - b["y"]
    d = max(1.0, math.hypot(dx, dy))
    if d > 5:
        move_speed = b["spd"] * 0.5
        b["x"] += dx / d * move_speed
        b["y"] += dy / d * move_speed


def update_enemies(dt):
    for e in enemies[:]:
        e["timer"] = e.get("timer", 0.0) + dt
        update_enemy_ai(e, dt)
        if e["timer"] > e["delay"]:
            e["timer"] = 0.0
            enemy_fire(e, e["pat"], boss=False)
            if ("+" in e["type"] or "++" in e["type"]) and random.random() < 0.2:
                e["timer"] = -0.2


def update_bosses(dt):
    global boss_defeated, can_advance, walls_active, wave, money
    for b in bosses[:]:
        b["timer"] = b.get("timer", 0.0) + dt
        update_boss_ai(b, dt)

        if b["timer"] > b["delay"]:
            b["timer"] = 0.0
            enemy_fire(b, b["pat"], boss=True, phase=b["phase"])

            if b.get("special_attack") and random.random() < b.get("special_chance", 0.25):
                boss_special_attack(b, b["special_attack"], phase=b["phase"])

            if b.get("quake"):
                player["shockwaves"].append((b["x"], b["y"], time.time(), 2.2))

        # УБИРАЕМ ПРИЗЫВ МОБОВ - этот блок закомментирован
        # if b.get("summons") and random.random() < 0.02:
        #     for _ in range(random.randint(1, 2)):
        #         spawn_enemy()

        if b["hp"] <= 0:
            if b.get("sin"):
                player["sins_defeated"].add(b["sin"])
            bosses.remove(b)
            player["score"] += 100

            if player["hp"] == player["max_hp"] and not achievements["no_hit_boss"]:
                achievements["no_hit_boss"] = True

            if player["hp"] == 1 and not achievements["glass_cannon"]:
                achievements["glass_cannon"] = True

            for _ in range(8):
                drop_coin(b["x"] + random.randint(-30, 30), b["y"] + random.randint(-30, 30), 1)
            drop_passive(b["x"], b["y"])
            drop_active(b["x"], b["y"])
            drop_heart(b["x"], b["y"])

    # Проверка на очистку волны
    if len(bosses) == 0 and len(enemies) == 0:
        if wave % 5 == 0 and not boss_defeated:
            boss_defeated = True
            can_advance = True
            walls_active = False
            console.add_output(f"Волна {wave} пройдена! Выходи за правый край...")
        elif wave % 5 != 0:
            wave += 1
            print(f"Автоматический переход на волну {wave}")
            console.add_output(f"Волна {wave}")
            
            if wave % 5 == 0:
                spawn_boss(wave)
                console.add_output(f"ВОЛНА {wave} - БОСС!")
            else:
                for _ in range(spawn_count()):
                    spawn_enemy()
            
            walls_active = True
            boss_defeated = False
            can_advance = False
            
            # С каждым боссом начисляем немного монет
            money += 2


def update_bullets(dt):
    for b in bullets[:]:
        if b.get("grenade"):
            b["dy"] += b.get("gravity", 0)
            b["dx"] += math.sin(b.get("arc", 0)) * 0.1
        elif b.get("bomb"):
            b["timer"] -= dt
            if b.get("bomb_dir") is None:
                # Store the initial direction when bomb is created
                d = math.hypot(b["dx"], b["dy"])
                if d > 0:
                    b["bomb_dir"] = (b["dx"] / d, b["dy"] / d)
                else:
                    b["bomb_dir"] = (1, 0)
            if b["timer"] <= 0:
                # Get the initial direction
                bomb_dx, bomb_dy = b.get("bomb_dir", (1, 0))
                # Main explosion in forward direction with spread
                for spread in [-0.4, 0, 0.4]:
                    ang = math.atan2(bomb_dy, bomb_dx) + spread
                    dx = math.cos(ang) * BULLET_SPEED * 0.8
                    dy = math.sin(ang) * BULLET_SPEED * 0.8
                    bullets.append({
                        "x": b["x"], "y": b["y"], "dx": dx, "dy": dy,
                        "dmg": b["dmg"] // 2, "ttl": 1.0, "created": time.time()
                    })
                bullets.remove(b)
                continue

        if b.get("homing") and enemies:
            closest = min(enemies, key=lambda e: math.hypot(e["x"] - b["x"], e["y"] - b["y"]))
            dx, dy = closest["x"] - b["x"], closest["y"] - b["y"]
            d = max(1.0, math.hypot(dx, dy))
            strength = 0.15 if b["homing"] == "strong" else 0.08
            b["dx"] += dx / d * strength
            b["dy"] += dy / d * strength
            sp = math.hypot(b["dx"], b["dy"])
            cap = BULLET_SPEED * 1.8
            if sp > cap:
                b["dx"] = b["dx"] / sp * cap
                b["dy"] = b["dy"] / sp * cap

        b["x"] += b["dx"]
        b["y"] += b["dy"]

        if "pacman" in player["effects"]:
            # Add max wraps to prevent infinite bullets
            b.setdefault("wraps", 0)
            b["wraps"] += 1
            if b["wraps"] > 10:  # Max 10 screen wraps then remove
                bullets.remove(b)
                continue
            if b["x"] < 0:
                b["x"] = screen.get_width()
            elif b["x"] > screen.get_width():
                b["x"] = 0
            if b["y"] < 0:
                b["y"] = screen.get_height()
            elif b["y"] > screen.get_height():
                b["y"] = 0
        else:
            if is_bullet_offscreen(b):
                bullets.remove(b)


def is_bullet_offscreen(bullet):
    """Проверка, вышла ли пуля за экран"""
    margin = 100
    return (bullet["x"] < -margin or bullet["x"] > screen.get_width() + margin or
            bullet["y"] < -margin or bullet["y"] > screen.get_height() + margin)


def update_enemy_bullets(dt):
    bullets_to_remove = []

    for i, b in enumerate(ebullets):
        if b.get("accelerate"):
            speed = math.hypot(b["dx"], b["dy"])
            b["dx"] *= 1 + b.get("accel", 0.1) * dt
            b["dy"] *= 1 + b.get("accel", 0.1) * dt
        
        if b.get("decelerate"):
            speed = math.hypot(b["dx"], b["dy"])
            b["dx"] *= 1 - b.get("decel", 0.05) * dt
            b["dy"] *= 1 - b.get("decel", 0.05) * dt
        
        if b.get("wave"):
            t = time.time() * b.get("wave_freq", 5)
            perp_x = -b["dy"]
            perp_y = b["dx"]
            norm = math.hypot(perp_x, perp_y)
            if norm > 0:
                perp_x /= norm
                perp_y /= norm
            b["x"] += math.sin(t) * b.get("wave_amp", 20) * perp_x * dt
            b["y"] += math.sin(t) * b.get("wave_amp", 20) * perp_y * dt
        
        if b.get("spiral"):
            angle = math.atan2(b["dy"], b["dx"])
            angle += b.get("spiral_speed", 0.1) * dt
            speed = math.hypot(b["dx"], b["dy"])
            b["dx"] = math.cos(angle) * speed
            b["dy"] = math.sin(angle) * speed
        
        if b.get("homing", False):
            dist_from_player = math.hypot(b["x"] - player["x"], b["y"] - player["y"])
            max_dist = b.get("max_dist", 800)
            if dist_from_player > max_dist:
                bullets_to_remove.append(i)
                continue
        
        b["x"] += b["dx"]
        b["y"] += b["dy"]
        
        if is_bullet_offscreen(b):
            bullets_to_remove.append(i)
            continue
        
        if b.get("homing"):
            dx, dy = player["x"] - b["x"], player["y"] - b["y"]
            d = max(1.0, math.hypot(dx, dy))
            strength = b.get("homing_strength", 0.04)
            b["dx"] += (dx / d) * strength * dt * 60
            b["dy"] += (dy / d) * strength * dt * 60
            
            sp = math.hypot(b["dx"], b["dy"])
            cap = ENEMY_BULLET_SPEED * 1.5
            if sp > cap:
                b["dx"] = b["dx"] / sp * cap
                b["dy"] = b["dy"] / sp * cap

    for i in sorted(bullets_to_remove, reverse=True):
        if i < len(ebullets):
            ebullets.pop(i)
    
    update_bullet_visuals()


def update_dungeon_enemies(dt):
    """Update dungeon enemies using same AI and firing patterns as main game"""
    for e in dungeon.enemies[:]:
        # Use same AI as main game
        e["timer"] = e.get("timer", 0.0) + dt
        
        # Simple AI movement for dungeon
        dx = player["x"] - e["x"]
        dy = player["y"] - e["y"]
        dist = math.hypot(dx, dy)
        
        # Apply AI behavior based on type
        if e.get("ai") == "chase" and dist < 400 and dist > 40:
            e["x"] += dx / dist * e.get("spd", 2)
            e["y"] += dy / dist * e.get("spd", 2)
        elif e.get("ai") == "keep_dist":
            if dist < 150:
                e["x"] -= dx / dist * e.get("spd", 2) * 0.8
                e["y"] -= dy / dist * e.get("spd", 2) * 0.8
            elif dist > 250:
                e["x"] += dx / dist * e.get("spd", 2) * 0.5
                e["y"] += dy / dist * e.get("spd", 2) * 0.5
        elif e.get("ai") == "orbit":
            angle = math.atan2(dy, dx) + 0.02
            orbit_r = 150
            target_x = player["x"] - math.cos(angle) * orbit_r
            target_y = player["y"] - math.sin(angle) * orbit_r
            e["x"] += (target_x - e["x"]) * 0.02
            e["y"] += (target_y - e["y"]) * 0.02
        
        # Status effects
        now = time.time()
        if e.get("bleeding", 0) > now:
            if random.random() < 0.1:
                e["hp"] -= 1
                particles.append({
                    "x": e["x"], "y": e["y"],
                    "dx": random.uniform(-1, 1), "dy": random.uniform(-1, 1),
                    "life": 0.5, "color": (200, 0, 0)
                })
        if e.get("burning", 0) > now:
            if random.random() < 0.15:
                e["hp"] -= 1
        if e.get("poisoned", 0) > now:
            if random.random() < 0.08:
                e["hp"] -= 1
        if e.get("stoned", 0) > now:
            e["timer"] -= dt * 0.5  # Slower when stunned
        
        # Use same firing patterns as main game
        if e["timer"] > e.get("delay", 1.0):
            e["timer"] = 0.0
            # Fire using the enemy's pattern
            if e.get("pat"):
                enemy_fire(e, e["pat"], boss=False)
            else:
                # Fallback simple shot
                d = max(1.0, dist)
                ebullets.append({
                    "x": e["x"], "y": e["y"],
                    "dx": dx / d * ENEMY_BULLET_SPEED * 0.8,
                    "dy": dy / d * ENEMY_BULLET_SPEED * 0.8,
                    "color": e.get("color", (255, 100, 100)),
                    "size": 7,
                    "max_dist": 500
                })
        
        # Mini-boss special attacks
        if e.get("boss") and random.random() < 0.02:
            # Occasionally fire special pattern
            boss_special_attack(e, e.get("pat", "aim"), phase=e.get("phase", 0))

        if e["hp"] <= 0:
            dungeon.enemies.remove(e)
            player["score"] += 10 if e.get("boss") else 1
            # Drop coins on death
            for _ in range(random.randint(1, 3)):
                drop_coin(e["x"] + random.randint(-20, 20), e["y"] + random.randint(-20, 20), random.randint(1, 3))
            # Chance for items
            if random.random() < 0.15:
                if random.random() < 0.5:
                    drop_passive(e["x"], e["y"])
                else:
                    drop_active(e["x"], e["y"])
            # Chance for heart
            if random.random() < 0.1:
                drop_heart(e["x"], e["y"])


def collisions():
    global money

    for pb in bullets[:]:
        pb_shape = pb.get("pattern")
        if pb_shape is None:
            pb_shape = PAT.SINGLE
        hit = False
        pb_ang = math.atan2(pb.get("dy", 0), pb.get("dx", 1))
        pb_size = pb.get("size", 8)
        for e in enemies[:]:
            # Square hitbox collision for enemies
            enemy_size = ENEMY_HITBOX_SIZE
            enemy_left = e["x"] - enemy_size
            enemy_right = e["x"] + enemy_size
            enemy_top = e["y"] - enemy_size
            enemy_bottom = e["y"] + enemy_size
            
            # Determine shape type for collision (player bullets)
            shape_type = PAT_SHAPE_MAP.get(pb_shape, BULLET_SHAPE.CIRCLE)
            
            bullet_in_enemy = False
            
            if shape_type == BULLET_SHAPE.TRIANGLE:
                # Check if enemy square corners or center are inside triangle
                corners = [(enemy_left, enemy_top), (enemy_right, enemy_top),
                          (enemy_left, enemy_bottom), (enemy_right, enemy_bottom),
                          (e["x"], e["y"])]
                for corner_x, corner_y in corners:
                    if point_in_triangle(corner_x, corner_y, pb["x"], pb["y"], pb_ang, pb_size):
                        bullet_in_enemy = True
                        break
            elif shape_type == BULLET_SHAPE.DIAMOND:
                if point_in_diamond(pb["x"], pb["y"], e["x"], e["y"], pb_size):
                    bullet_in_enemy = True
                else:
                    for corner_x, corner_y in [(enemy_left, enemy_top), (enemy_right, enemy_top),
                                                (enemy_left, enemy_bottom), (enemy_right, enemy_bottom)]:
                        if point_in_diamond(corner_x, corner_y, e["x"], e["y"], pb_size):
                            bullet_in_enemy = True
                            break
            elif shape_type == BULLET_SHAPE.STICK or shape_type == BULLET_SHAPE.SPINNING_STICK:
                if point_in_stick(pb["x"], pb["y"], e["x"], e["y"], pb_ang, pb_size):
                    bullet_in_enemy = True
                else:
                    for corner_x, corner_y in [(enemy_left, enemy_top), (enemy_right, enemy_top),
                                                (enemy_left, enemy_bottom), (enemy_right, enemy_bottom)]:
                        if point_in_stick(corner_x, corner_y, e["x"], e["y"], pb_ang, pb_size):
                            bullet_in_enemy = True
                            break
            elif shape_type == BULLET_SHAPE.STAR:
                closest_x = max(enemy_left, min(pb["x"], enemy_right))
                closest_y = max(enemy_top, min(pb["y"], enemy_bottom))
                dx = pb["x"] - closest_x
                dy = pb["y"] - closest_y
                if dx * dx + dy * dy <= pb_size * pb_size:
                    bullet_in_enemy = True
            else:  # CIRCLE
                closest_x = max(enemy_left, min(pb["x"], enemy_right))
                closest_y = max(enemy_top, min(pb["y"], enemy_bottom))
                dx = pb["x"] - closest_x
                dy = pb["y"] - closest_y
                if dx * dx + dy * dy <= pb_size * pb_size:
                    bullet_in_enemy = True
            
            if not bullet_in_enemy:
                continue
            if "cruelty" in player["effects"] and player["last_hit_wave"] != wave:
                player["cruelty_bonus"] += 1
                player["dmg"] += 1
            hit = True

            dmg = pb["dmg"]

            if dmg > player["max_damage"]:
                player["max_damage"] = dmg
                if player["max_damage"] >= 100 and not achievements["demigod"]:
                    achievements["demigod"] = True

            if pb.get("impact"):
                apply_impact(e, pb["x"], pb["y"], IMPACT_FORCE)

            if pb.get("bleed"):
                e["bleeding"] = time.time() + 3.0
                player["status_effects_applied"].add("bleed")
                if player.get("traitor", False):
                    player.setdefault("traitor_kills", 0)
                    player["traitor_kills"] += 1
                    if player["traitor_kills"] >= 50 and not achievements["traitor"]:
                        achievements["traitor"] = True
            if pb.get("burn"):
                e["burning"] = time.time() + 2.0
                player["status_effects_applied"].add("burn")
            if pb.get("poison"):
                e["poisoned"] = time.time() + 4.0
                player["status_effects_applied"].add("poison")
            if pb.get("stone") and random.random() < 0.1:
                e["stoned"] = time.time() + 1.5
                player["status_effects_applied"].add("stone")

            # Shield-враги поглощают первые удары
            if e.get("shielder", False) and e.get("shield_hp", 0) > 0:
                absorbed = min(e["shield_hp"], dmg)
                e["shield_hp"] -= absorbed
                dmg -= absorbed
                if e["shield_hp"] <= 0:
                    e["shielder"] = False

            e["hp"] -= dmg

            if player["vamp"] > 0 and random.random() < 0.2:
                heal = 1
                player["hp"] = min(player["hp"] + heal, player["max_hp"])
                player["vampire_heal"] += heal
                if player["vampire_heal"] >= 50 and not achievements["blood_bank"]:
                    achievements["blood_bank"] = True

            if e["hp"] <= 0:
                if "explode" in player["effects"]:
                    for i in range(6):
                        ang = i * (2 * math.pi / 6)
                        ebullets.append({
                            "x": e["x"], "y": e["y"],
                            "dx": math.cos(ang) * ENEMY_BULLET_SPEED * 0.4,
                            "dy": math.sin(ang) * ENEMY_BULLET_SPEED * 0.4,
                            "color": e["color"],
                            "size": 6, "beh": "split", "max_dist": 500
                        })
                
                # Взрывные враги взрываются при смерти
                if e.get("explosive", False):
                    explosion_radius = 80
                    for other in enemies[:]:
                        if other is not e:
                            dist = math.hypot(other["x"] - e["x"], other["y"] - e["y"])
                            if dist < explosion_radius:
                                other["hp"] -= 5
                                apply_impact(other, e["x"], e["y"], 4)
                    for _ in range(12):
                        ang = random.uniform(0, 2 * math.pi)
                        ebullets.append({
                            "x": e["x"], "y": e["y"],
                            "dx": math.cos(ang) * ENEMY_BULLET_SPEED * 0.3,
                            "dy": math.sin(ang) * ENEMY_BULLET_SPEED * 0.3,
                            "color": (255, 100, 0),
                            "size": 8, "max_dist": 300
                        })
                    if math.hypot(player["x"] - e["x"], player["y"] - e["y"]) < explosion_radius:
                        damage_player(1)

                enemies.remove(e)
                player["score"] += 1
                player["total_kills"] += 1

                if player["total_kills"] >= 100 and wave > 0 and not achievements["berserk"]:
                    achievements["berserk"] = True

                if not achievements["first_blood"]:
                    achievements["first_blood"] = True

                player["combo_kills"] += 1
                if player["combo_kills"] >= 50 and not achievements["combo_king"]:
                    achievements["combo_king"] = True

                if random.random() < COIN_DROP_RATE:
                    drop_coin(e["x"], e["y"])

                if random.random() < DROP_RATE * (0.8 + player["luck"]):
                    if random.random() < 0.7:
                        drop_passive(e["x"], e["y"])
                    else:
                        drop_active(e["x"], e["y"])
                    if random.random() < 0.2:
                        drop_heart(e["x"], e["y"])

            if not pb.get("pierce", False):
                break

        if hit and not pb.get("pierce", False) and pb in bullets:
            bullets.remove(pb)

    for pb in bullets[:]:
        hit = False
        for b in bosses[:]:
            # Square hitbox for bosses
            boss_size = BOSS_HITBOX_SIZE
            boss_left = b["x"] - boss_size
            boss_right = b["x"] + boss_size
            boss_top = b["y"] - boss_size
            boss_bottom = b["y"] + boss_size
            
            if (boss_left <= pb["x"] <= boss_right and 
                boss_top <= pb["y"] <= boss_bottom):
                b["hp"] -= pb["dmg"]
                hit = True

                if pb.get("impact"):
                    apply_impact(b, pb["x"], pb["y"], IMPACT_FORCE * 0.5)

                if b["hp"] <= 0:
                    if b in bosses:
                        bosses.remove(b)
                        player["score"] += 100

                        if player["hp"] == player["max_hp"] and not achievements["no_hit_boss"]:
                            achievements["no_hit_boss"] = True

                        if player["hp"] == 1 and not achievements["glass_cannon"]:
                            achievements["glass_cannon"] = True

                        for _ in range(5):
                            drop_coin(b["x"] + random.randint(-30, 30), b["y"] + random.randint(-30, 30), 1)

                        drop_passive(b["x"], b["y"])
                        drop_active(b["x"], b["y"])
                        drop_heart(b["x"], b["y"])
                        if random.random() < 0.3:
                            drop_heart(b["x"] + 20, b["y"])
                            drop_heart(b["x"] - 20, b["y"])

                if not pb.get("pierce", False):
                    break

        if hit and not pb.get("pierce", False) and pb in bullets:
            bullets.remove(pb)

    for e in enemies:
        # Square hitbox for enemy collision with player
        enemy_size = ENEMY_HITBOX_SIZE
        enemy_left = e["x"] - enemy_size
        enemy_right = e["x"] + enemy_size
        enemy_top = e["y"] - enemy_size
        enemy_bottom = e["y"] + enemy_size
        
        closest_x = max(enemy_left, min(player["x"], enemy_right))
        closest_y = max(enemy_top, min(player["y"], enemy_bottom))
        dx = player["x"] - closest_x
        dy = player["y"] - closest_y
        if dx * dx + dy * dy < PLAYER_HIT_R ** 2:
            damage_player(1 if not e.get("boss", False) else 2)

    for eb in ebullets[:]:
        if (eb["x"] - player["x"]) ** 2 + (eb["y"] - player["y"]) ** 2 < (PLAYER_HIT_R + 3) ** 2:
            if eb in ebullets:
                ebullets.remove(eb)
                damage_player(2 if eb.get("boss", False) else 1)
        else:
            if math.hypot(eb["x"] - player["x"], eb["y"] - player["y"]) < PLAYER_HIT_R + 12:
                player["dodged_bullets"] += 1
                if player["dodged_bullets"] >= 100 and not achievements["ninja"]:
                    achievements["ninja"] = True


def apply_impact(e, bx, by, force):
    dx = e["x"] - bx
    dy = e["y"] - by
    d = max(1.0, math.hypot(dx, dy))

    e["impact_x"] = dx / d * force
    e["impact_y"] = dy / d * force
    e["impact_time"] = time.time() + 0.2

    for _ in range(5):
        ang = random.uniform(0, 2 * math.pi)
        speed = random.uniform(2, 4)
        particles.append({
            "x": e["x"], "y": e["y"],
            "dx": math.cos(ang) * speed, "dy": math.sin(ang) * speed,
            "color": (255, 200, 100), "life": 0.3
        })


def parry_bullet():
    global parry_success, parry_window_start
    now = time.time()

    if now > parry_window_start + PARRY_WINDOW:
        return False

    parry_range = 28
    parry_count = 0
    for b in ebullets[:]:
        dist = math.hypot(b["x"] - player["x"], b["y"] - player["y"])
        if dist < parry_range:
            dx, dy = b["x"] - player["x"], b["y"] - player["y"]
            d = max(1.0, math.hypot(dx, dy))
            b["dx"] = dx / d * BULLET_SPEED * 1.5
            b["dy"] = dy / d * BULLET_SPEED * 1.5
            b["color"] = (0, 255, 255)

            for _ in range(8):
                ang = random.uniform(0, 2 * math.pi)
                speed = random.uniform(2, 5)
                particles.append({
                    "x": b["x"], "y": b["y"],
                    "dx": math.cos(ang) * speed, "dy": math.sin(ang) * speed,
                    "color": (100, 255, 255), "life": 0.5
                })

            player["parry_count"] += 1
            parry_count += 1

            if player["parry_count"] >= 100 and not achievements["parry_master"]:
                achievements["parry_master"] = True

            parry_success = True

    if parry_count > 0:
        player.setdefault("parry_streak", 0)
        player["parry_streak"] += parry_count
        if player["parry_streak"] >= 10 and not achievements["parry_god"]:
            achievements["parry_god"] = True
    else:
        player["parry_streak"] = 0

    return parry_count > 0


# -------------------- Pickups --------------------
def pick_powerups():
    global money
    now = time.time()
    magnet_range = player.get("magnet", 0)

    for p in ppick[:]:
        if magnet_range > 0:
            dx, dy = player["x"] - p["x"], player["y"] - p["y"]
            d = math.hypot(dx, dy)
            if d < magnet_range:
                p["x"] += dx / d * 5
                p["y"] += dy / d * 5

        if (p["x"] - player["x"]) ** 2 + (p["y"] - player["y"]) ** 2 < 16 ** 2:
            apply_passive(p["key"])
            ppick.remove(p)
        elif now - p["spawn_time"] > ITEM_DESPAWN_TIME:
            ppick.remove(p)

    for a in apick[:]:
        if magnet_range > 0:
            dx, dy = player["x"] - a["x"], player["y"] - a["y"]
            d = math.hypot(dx, dy)
            if d < magnet_range:
                a["x"] += dx / d * 5
                a["y"] += dy / d * 5

        if (a["x"] - player["x"]) ** 2 + (a["y"] - player["y"]) ** 2 < 16 ** 2:
            pick_active(a["key"])
            apick.remove(a)
        elif now - a["spawn_time"] > ITEM_DESPAWN_TIME:
            apick.remove(a)

    for h in hearts[:]:
        if magnet_range > 0:
            dx, dy = player["x"] - h["x"], player["y"] - h["y"]
            d = math.hypot(dx, dy)
            if d < magnet_range:
                h["x"] += dx / d * 5
                h["y"] += dy / d * 5

        if (h["x"] - player["x"]) ** 2 + (h["y"] - player["y"]) ** 2 < 16 ** 2:
            player["hp"] = min(player["hp"] + 1, player["max_hp"])
            hearts.remove(h)
        elif now - h["spawn_time"] > ITEM_DESPAWN_TIME:
            hearts.remove(h)

    for c in coins[:]:
        if magnet_range > 0:
            dx, dy = player["x"] - c["x"], player["y"] - c["y"]
            d = math.hypot(dx, dy)
            if d < magnet_range:
                c["x"] += dx / d * 8
                c["y"] += dy / d * 8

        if (c["x"] - player["x"]) ** 2 + (c["y"] - player["y"]) ** 2 < 18 ** 2:
            money += c["amount"]
            coins.remove(c)

            if money >= 1000 and not achievements["moneybags"]:
                achievements["moneybags"] = True
        elif now - c["spawn_time"] > ITEM_DESPAWN_TIME * 1.5:
            coins.remove(c)


# -------------------- Controls --------------------
def handle_input():
    global parrying, parry_cooldown, parry_success, parry_window_start

    keys = pygame.key.get_pressed()
    sp = PLAYER_SPEED * player["speed_mult"]

    if keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]:
        sp *= 1.3

    if "pacman" in player["effects"]:
        if keys[pygame.K_a]:
            player["x"] -= sp
        if keys[pygame.K_d]:
            player["x"] += sp
        if keys[pygame.K_w]:
            player["y"] -= sp
        if keys[pygame.K_s]:
            player["y"] += sp
    else:
        if keys[pygame.K_a]:
            player["x"] -= sp
        if keys[pygame.K_d]:
            player["x"] += sp
        if keys[pygame.K_w]:
            player["y"] -= sp
        if keys[pygame.K_s]:
            player["y"] += sp

    check_exit_boundaries()

    if keys[pygame.K_SPACE] and parry_cooldown <= 0 and not parrying:
        parrying = True
        parry_window_start = time.time()
        if parry_bullet():
            parry_success = True
        else:
            parry_success = False
        parry_cooldown = PARRY_COOLDOWN

    if parrying:
        parry_cooldown -= 1 / 60
        if parry_cooldown <= 0:
            parrying = False

    player["timer"] += 1
    if player["timer"] >= player.get("fire_cd", player["base_fire_cd"]):
        dx = dy = 0
        if control_mode == "Новый":
            if mouse_pressed:
                mx, my = pygame.mouse.get_pos()
                angle = math.atan2(my - player["y"], mx - player["x"])
                # Нормализуем угол в диапазон [0, 2π)
                if angle < 0:
                    angle += 2 * math.pi
                # Ограничиваем только 4 направлениями: вправо, вверх, влево, вниз
                if angle < math.pi / 4:
                    dx = 1
                    dy = 0
                elif angle < 3 * math.pi / 4:
                    dx = 0
                    dy = -1
                elif angle < 5 * math.pi / 4:
                    dx = -1
                    dy = 0
                elif angle < 7 * math.pi / 4:
                    dx = 0
                    dy = 1
                else:
                    dx = 1
                    dy = 0
        else:
            k = pygame.key.get_pressed()
            if k[pygame.K_UP]:
                dy = -1
            elif k[pygame.K_DOWN]:
                dy = 1
            elif k[pygame.K_LEFT]:
                dx = -1
            elif k[pygame.K_RIGHT]:
                dx = 1

        if dx or dy:
            d = math.hypot(dx, dy)
            dx /= d
            dy /= d

            if "wave" in player["effects"]:
                offset = math.sin(time.time() * 5.0) * 0.3
                dx2 = math.cos(offset) * dx - math.sin(offset) * dy
                dy2 = math.sin(offset) * dx + math.cos(offset) * dy
                make_bullet(dx2, dy2)
            else:
                make_bullet(dx, dy)

            player["timer"] = 0

    if keys[pygame.K_LCTRL] or keys[pygame.K_RCTRL]:
        now = time.time()
        for key in list(player["actives"].keys()):
            meta = player["actives"].get(key)
            if not meta:
                continue

            a = actives[key]
            cd = a["cd"]
            cdt = a["cd_type"]

            ready = (now >= meta["last_t"] + cd) if cdt == "time" else (wave >= meta["last_w"] + cd)
            if ready:
                activate_active(key)


def handle_dungeon_input():
    global parrying, parry_cooldown, parry_success, parry_window_start, state, wave, dungeon

    keys = pygame.key.get_pressed()
    sp = PLAYER_SPEED * player["speed_mult"]
    
    if keys[pygame.K_LSHIFT] or keys[pygame.K_RSHIFT]:
        sp *= 1.3

    dx = dy = 0
    if keys[pygame.K_a] or keys[pygame.K_LEFT]:
        dx = -1
    if keys[pygame.K_d] or keys[pygame.K_RIGHT]:
        dx = 1
    if keys[pygame.K_w] or keys[pygame.K_UP]:
        dy = -1
    if keys[pygame.K_s] or keys[pygame.K_DOWN]:
        dy = 1

    if dx != 0 or dy != 0:
        if dx != 0 and dy != 0:
            sp *= 0.707
        moved = dungeon.move_player(dx, dy)
        if moved:
            player["x"] = dungeon.player_x * 20 + 10
            player["y"] = dungeon.player_y * 20 + 10

    player["timer"] += 1
    if player["timer"] >= player.get("fire_cd", player["base_fire_cd"]):
        shoot_dx = shoot_dy = 0
        if control_mode == "Новый":
            mx, my = pygame.mouse.get_pos()
            angle = math.atan2(my - player["y"], mx - player["x"])
            shoot_dx = math.cos(angle)
            shoot_dy = math.sin(angle)
        else:
            if keys[pygame.K_UP]:
                shoot_dy = -1
            elif keys[pygame.K_DOWN]:
                shoot_dy = 1
            elif keys[pygame.K_LEFT]:
                shoot_dx = -1
            elif keys[pygame.K_RIGHT]:
                shoot_dx = 1

        if shoot_dx or shoot_dy:
            d = math.hypot(shoot_dx, shoot_dy)
            shoot_dx /= d
            shoot_dy /= d

            if "wave" in player["effects"]:
                offset = math.sin(time.time() * 5.0) * 0.3
                dx2 = math.cos(offset) * shoot_dx - math.sin(offset) * shoot_dy
                dy2 = math.sin(offset) * shoot_dx + math.cos(offset) * shoot_dy
                make_bullet(dx2, dy2)
            else:
                make_bullet(shoot_dx, shoot_dy)

            player["timer"] = 0

    if keys[pygame.K_SPACE] and parry_cooldown <= 0 and not parrying:
        parrying = True
        parry_window_start = time.time()
        if parry_bullet():
            parry_success = True
        else:
            parry_success = False
        parry_cooldown = PARRY_COOLDOWN

    if parrying:
        parry_cooldown -= 1 / 60
        if parry_cooldown <= 0:
            parrying = False

    if dungeon.is_exit() and len(dungeon.enemies) == 0:
        state = GS.GAME
        walls_active = True
        bullets.clear()
        ebullets.clear()
        player["x"] = screen.get_width() - 50
        player["y"] = screen.get_height() // 2
        dungeon = None

        # Боссы не призывают мобов, просто спавним волну
        if wave % 5 == 0:
            # Спавним только босса, без дополнительных мобов
            spawn_boss(wave)
            console.add_output(f"ВОЛНА {wave} - БОСС!")
        else:
            # Обычные враги в большем количестве
            for _ in range(spawn_count() + 5):  # +5 дополнительных врагов
                spawn_enemy()
            console.add_output(f"Волна {wave}")


def activate_active(key):
    global souls, money, wave
    now = time.time()
    meta = player["actives"].get(key)

    if not meta:
        return False

    a = actives[key]
    cd = a["cd"]
    cdt = a["cd_type"]

    ready = (now >= meta["last_t"] + cd) if cdt == "time" else (wave >= meta["last_w"] + cd)
    if not ready:
        return False

    act = a["action"]

    if act == "blood_magic":
        if player["hp"] > 1:
            player["hp"] -= 1
            for i in range(5):
                ang = math.radians(i * 15 - 30)
                bx = math.cos(ang)
                by = math.sin(ang)
                bullets.append({
                    "x": player["x"], "y": player["y"], "dx": bx * BULLET_SPEED * 2, "dy": by * BULLET_SPEED * 2,
                    "dmg": a["p"].get("dmg", 20), "color": (200, 0, 0), "ttl": 1.5, "blood": True,
                    "size": 6
                })

    elif act == "shield":
        player["shield_active"] = True
        player["shield_until"] = now + a["p"].get("duration", 2.0)

    elif act == "time_stop":
        player["time_slow_until"] = now + a["p"].get("duration", 3.0)

    elif act == "heal":
        amount = a["p"].get("amount", 5)
        player["hp"] = min(player["hp"] + amount, player["max_hp"])

    elif act == "teleport":
        player["x"] = random.randint(50, screen.get_width() - 50)
        player["y"] = random.randint(50, screen.get_height() - 50)

    meta["last_t"] = now
    meta["last_w"] = wave
    return True


def check_exit_boundaries():
    global can_advance, boss_defeated, state, secret_room, current_arena, wave, walls_active, dungeon

    if walls_active:
        player["x"] = max(15, min(screen.get_width() - 15, player["x"]))
        player["y"] = max(15, min(screen.get_height() - 15, player["y"]))
        return

    if player["x"] > screen.get_width() + 20:
        if wave >= 100:
            state = GS.WIN
            return

        # Переходим к следующей волне без подземелья
        wave += 1
        boss_defeated = False
        walls_active = True
        can_advance = False
        dungeon = None
        state = GS.GAME
        
        console.add_output(f"ВОЛНА {wave}!")
        
        # Спавним врагов
        if wave % 5 == 0:
            spawn_boss(wave)
            console.add_output(f"ВОЛНА {wave} - БОСС!")
        else:
            for _ in range(spawn_count()):
                spawn_enemy()
            console.add_output(f"Заспавнено {len(enemies)} врагов")


# -------------------- Boss Special Attacks --------------------
def boss_special_attack(boss, attack_type, phase=0):
    px, py = player["x"], player["y"]
    bx, by = boss["x"], boss["y"]

    if phase >= 2:
        spd = ENEMY_BULLET_SPEED * 0.6 * BOSS_SPEED_MULT
        mult = 1.8
    elif phase >= 1:
        spd = ENEMY_BULLET_SPEED * 0.75 * BOSS_SPEED_MULT
        mult = 1.4
    else:
        spd = ENEMY_BULLET_SPEED * 0.9 * BOSS_SPEED_MULT
        mult = 1.0

    col = boss["color"]

    if attack_type == "cross_spiral":
        origin = time.time() * 1.0
        for i in range(int(10 * mult)):
            for ang in [0, math.pi / 2, math.pi, 3 * math.pi / 2]:
                final_ang = ang + origin + i * 0.1
                ebullets.append(create_bullet(boss, final_ang, spd * (0.7 + i * 0.02), col, 8))

    elif attack_type == "homing_ring":
        for i in range(int(20 * mult)):
            ang = i * (2 * math.pi / (20 * mult))
            ebullets.append(create_bullet(boss, ang, spd * 0.5, (100, 255, 200), 8, 
                                        homing=True, homing_strength=0.02))

    elif attack_type == "god_pattern":
        now = time.time()
        for i in range(int(16 * mult)):
            ang = now * 1.0 + i * math.pi * 0.1
            ebullets.append(create_bullet(boss, ang, spd * 0.7, (255, 255, 255), 7))

        for _ in range(int(10 * mult)):
            offset = random.uniform(-0.2, 0.2)
            ebullets.append(create_bullet(boss, offset, spd * 0.6, (255, 100, 100), 8))

        for i in range(int(20 * mult)):
            if i % 2 == 0:
                ang = i * (2 * math.pi / (20 * mult)) + now
                ebullets.append(create_bullet(boss, ang, spd * 0.5, (100, 200, 255), 6))

    elif attack_type == "ring_burst":
        boss_pattern_ring_burst(boss, phase)
    elif attack_type == "void_burst":
        boss_pattern_void_burst(boss, phase)
    elif attack_type == "homing_vortex":
        boss_pattern_homing_vortex(boss, phase)
    elif attack_type == "pinwheel":
        boss_pattern_pinwheel(boss, phase)


# -------------------- After Boss Victory --------------------
def after_boss_victory():
    global state, dungeon, wave, boss_defeated, souls, walls_active, can_advance

    # Поедатель душ - +1 душа за босса
    if player.get("soul_devourer", False):
        souls += 1
        player["soul_devourer_kills"] += 1
        console.add_output(f"Поедатель душ поглотил душу босса! Души: {souls}")

    # Обычный поедатель душ
    if player.get("soul_eater", False):
        souls += 1

    # Переходим к следующей волне без подземелья
    wave += 1
    boss_defeated = False
    walls_active = True
    can_advance = False
    dungeon = None
    state = GS.GAME
    
    console.add_output(f"ВОЛНА {wave}!")
    
    # Спавним врагов
    if wave % 5 == 0:
        spawn_boss(wave)
        console.add_output(f"ВОЛНА {wave} - БОСС!")
    else:
        for _ in range(spawn_count()):
            spawn_enemy()
        console.add_output(f"Заспавнено {len(enemies)} врагов")


# -------------------- Drawing Functions --------------------
def draw_hud():
    hp_col = (0, 255, 0) if player["hp"] > player["max_hp"] * 0.66 else \
        ((255, 200, 0) if player["hp"] > player["max_hp"] * 0.33 else (255, 80, 80))
    draw(f"HP {int(player['hp'])}/{player['max_hp']}", 10, 10, hp_col, FONT_S)

    draw(f"Souls {souls}", 10, 38, (180, 80, 255), FONT_S)
    draw(f"${money}", 10, 66, GOLD, FONT_S)

    if not player.get("hide_wave", False):
        draw(f"WAVE {wave}", 10, 94, current_arena.color, FONT_S)
    else:
        draw(f"???", 10, 94, GRAY, FONT_XS)

    draw(f"Score {player['score']}", 10, 122, WHITE, FONT_XS)
    draw(f"Lives {player['lives']}", 10, 144, WHITE, FONT_XS)

    if player.get("god_mode", False):
        draw("GOD MODE", 10, 166, (255, 50, 50), FONT_XS)

    if "cruelty" in player["effects"] and player["cruelty_bonus"] > 0:
        draw(f"Cruelty +{player['cruelty_bonus']}", 10, 188, (255, 50, 50), FONT_XS)

    if player["waves_without_hit"] > 0:
        draw(f"Clean {player['waves_without_hit']} waves", 10, 210, (100, 255, 100), FONT_XS)

    if player["credit_timer"] > 0:
        player["credit_timer"] -= 1 / 60
        if player["credit_timer"] <= 0 and player["hp"] <= 0:
            global game_over
            game_over = True
        draw(f"Credit: {int(player['credit_timer'])}s", 10, 232, YELLOW, FONT_XS)

    y = 254
    now = time.time()
    if player["actives"]:
        draw("Actives (CTRL):", 10, y, CYAN, FONT_XS)
        y += 18
        for i, k in enumerate(sorted(player["actives"])[:5], 1):
            a = actives[k]
            meta = player["actives"][k]
            ready = (now >= meta["last_t"] + a["cd"]) if a["cd_type"] == "time" else (wave >= meta["last_w"] + a["cd"])
            draw(f"{i}: {a['name']} - {'READY' if ready else 'CD'}", 12, y,
                 (210, 255, 210) if ready else GRAY, FONT_XS)
            y += 14

    y += 8
    listed = []
    for lab, cond in [("PIERCE", "pierce" in player["effects"]),
                      ("LASER", "laser" in player["effects"]),
                      ("GRENADE", "grenade" in player["effects"]),
                      ("BLOOD RAY", "blood_ray" in player["effects"]),
                      ("FIRE", "fire" in player["effects"]),
                      ("BOMB", "bomb" in player["effects"]),
                      ("HOMING", "homing" in player["effects"] or "homing_plus" in player["effects"]),
                      ("CRUELTY", "cruelty" in player["effects"]),
                      ("INFALLIBILITY", "infallibility" in player["effects"]),
                      ("PAC-MAN", "pacman" in player["effects"]),
                      ("D6", "d6_revive" in player["effects"]),
                      ("D6 II", "d6_II_revive" in player["effects"]),
                      ("TRAITOR", "traitor" in player["effects"]),
                      ("SOUL EATER", "soul_eater" in player["effects"]),
                      ("ONLY CURSED", "only_cursed" in player["effects"]),
                      ("ROT CURSE", "rot_curse" in player),
                      ("SOUL DEVOURER", "soul_devourer" in player),
                      ("GREED", "greed" in player),
                      ("MADE OF GLASS", "made_of_glass" in player)]:
        if cond:
            listed.append(lab)

    if player["vamp"] > 0:
        listed.append(f"VAMP{player['vamp']:.1f}")
    if player["regen"] > 0:
        listed.append(f"REGEN{player['regen']:.1f}")
    if player.get("parry_time_bonus", 0) > 0:
        listed.append(f"PARRY+{player['parry_time_bonus']:.1f}s")
    if player.get("difficulty", 0) != 0:
        diff = player["difficulty"]
        listed.append(f"DIFF{diff:+d}")

    draw("Effects:", 10, y, YELLOW, FONT_XS)
    y += 16
    to_show = listed if show_full else listed[:DISPLAY_LIMIT]
    draw(" ".join(to_show), 10, y, WHITE, FONT_XS)
    y += 14
    extra = len(listed) - len(to_show)
    if not show_full and extra > 0:
        draw(f"+{extra} more (TAB)", 10, y, GRAY, FONT_XS)

    y += 18
    if parry_cooldown > 0:
        time_left = max(0, parry_cooldown)
        draw(f"Parry CD: {time_left:.1f}s", 10, y, GRAY, FONT_XS)
    else:
        draw("Parry READY (SPACE)", 10, y, CYAN, FONT_XS)

    if parrying:
        window_left = max(0, PARRY_WINDOW - (time.time() - parry_window_start))
        if window_left > 0:
            draw(f"Parry window: {window_left:.2f}s", 10, y + 16, (0, 255, 255), FONT_XS)

    if player["shield_active"]:
        draw(f"Shield: {max(0, player['shield_until'] - time.time()):.1f}s", 10, y + 32, BLUE, FONT_XS)


def draw_game():
    import sys as _dsys
    _d = getattr(_dsys, "_drawgame", 0) + 1
    setattr(_dsys, "_drawgame", _d)
    if _d <= 3:
        print(f"DRAW_GAME called #{_d}, state={state}", flush=True)
    global walls_active

    current_arena.update(1 / 60)
    current_arena.draw_background(screen)

    now = time.time()
    update_special_effects(1/60)

    if not walls_active:
        pulse = (math.sin(now * 3) + 1) / 2
        color = (0, 255, 255)
        text = "СЛЕДУЮЩАЯ ВОЛНА →"

        text_surf = FONT_S.render(text, True, color)
        text_rect = text_surf.get_rect()
        text_rect.right = screen.get_width() - 30
        text_rect.top = 100
        screen.blit(text_surf, text_rect)

        arrow_x = screen.get_width() - 80
        arrow_y = 150
        offset = math.sin(now * 5) * 5

        for i in range(3):
            x = arrow_x - i * 20 + offset
            alpha_i = int(255 * (1 - i * 0.3))
            color_i = color[:3] + (alpha_i,)

            s = pygame.Surface((30, 30), pygame.SRCALPHA)
            pygame.draw.polygon(s, color_i,
                               [(0, 0), (25, 15), (0, 30)])
            screen.blit(s, (x, arrow_y - 15))

    for sw in player["shockwaves"][:]:
        sx, sy, start, dur = sw
        t = now - start
        if t < 0:
            continue
        if t > dur:
            player["shockwaves"].remove(sw)
            continue

        radius = int(40 + 200 * (t / dur))
        alpha = max(0, 200 - int(200 * (t / dur)))
        color = (100, 200, 255, alpha)

        s = pygame.Surface((radius * 2, radius * 2), pygame.SRCALPHA)
        pygame.draw.circle(s, color, (radius, radius), radius, 3)
        screen.blit(s, (sx - radius, sy - radius))

    if player.get("bhole") and now <= player["bhole"]["until"]:
        bx, by = player["bhole"]["x"], player["bhole"]["y"]
        pulse = math.sin(now * 3) * 5 + 15
        pygame.draw.circle(screen, (40, 40, 60), (int(bx), int(by)), int(pulse))
        pygame.draw.circle(screen, (80, 80, 120), (int(bx), int(by)), int(pulse), 2)

        for e in enemies:
            dx, dy = bx - e["x"], by - e["y"]
            d = max(1.0, math.hypot(dx, dy))
            if d < 300:
                e["x"] += dx / d * 0.8
                e["y"] += dy / d * 0.8

        for b in ebullets:
            dx, dy = bx - b["x"], by - b["y"]
            d = max(1.0, math.hypot(dx, dy))
            if d < 250:
                b["dx"] += dx / d * 0.1
                b["dy"] += dy / d * 0.1

    for b in bullets:
        size = b.get("size", 5)

        if b.get("blood"):
            color = (200, 0, 0)
            ang = math.atan2(b.get("dy", 0), b.get("dx", 1))
            draw_bullet(screen, BULLET_TYPE.FIRE, color, b["x"], b["y"], size + 1, ang)
        elif b.get("blood_ray"):
            ang = math.atan2(b.get("dy", 0), b.get("dx", 1))
            start_x = int(b["x"])
            start_y = int(b["y"])
            end_x = int(b["x"] - b["dx"] * 3)
            end_y = int(b["y"] - b["dy"] * 3)
            draw_bullet(screen, BULLET_TYPE.LASER, (220, 0, 0), b["x"], b["y"], size, ang)
        elif b.get("laser"):
            color = b.get("color", (255, 255, 0))
            ang = math.atan2(b.get("dy", 0), b.get("dx", 1))
            draw_bullet(screen, BULLET_TYPE.LASER, color, b["x"], b["y"], size, ang)
        else:
            bullet_type = b.get("bullet_type", BULLET_TYPE.NORMAL)
            bp = b.get("pattern")
            color = PATTERN_COLORS.get(bp, None)
            if color is None:
                color = BULLET_COLORS.get(bullet_type, (255, 220, 50))
            ang = math.atan2(b.get("dy", 0), b.get("dx", 1))
            draw_bullet(screen, bullet_type, color, b["x"], b["y"], size, ang)

    bullet_count = len(ebullets)

    if bullet_count > 200:
        bullet_size_mult = 0.9
    elif bullet_count > 100:
        bullet_size_mult = 1.0
    else:
        bullet_size_mult = 1.1

    for b in ebullets:
        base_size = b.get("size", 10)
        size = max(MIN_BULLET_SIZE, min(MAX_BULLET_SIZE, int(base_size * bullet_size_mult)))
        import sys as _sys
        _cnt = getattr(_sys, '_ebul_dbg', 0)
        setattr(_sys, '_ebul_dbg', _cnt + 1)
        if _cnt < 1000:
            import sys as _sys2
            _dbg_cnt2 = getattr(_sys2, '_dbg_cnt3', 0)
            setattr(_sys2, '_dbg_cnt3', _dbg_cnt2 + 1)
            if _dbg_cnt2 < 5000:
                print(f"EBULLdata: keys={list(b.keys())[:10]}, bp={b.get('pattern')}, bt={b.get('bullet_type', 'MISSING')}")

        bullet_type = b.get("bullet_type", BULLET_TYPE.NORMAL)
        bp = b.get("pattern")
        color = PATTERN_COLORS.get(bp, None)
        if color is None:
            color = BULLET_COLORS.get(bullet_type, (255, 80, 80))
        ang = math.atan2(b.get("dy", 1), b.get("dx", 0))
        draw_bullet(screen, bullet_type, color, b["x"], b["y"], size, ang)


    if bullet_count >= 50:
        player["bullet_hell_count"] += 1 / 60
        if player["bullet_hell_count"] >= 5 and not achievements["bullet_hell"]:
            achievements["bullet_hell"] = True

    for e in enemies:
        if e["hp"] < e["max_hp"]:
            w = 30
            fw = int(w * (e["hp"] / e["max_hp"]))
            pygame.draw.rect(screen, (80, 0, 0), (e["x"] - w // 2, e["y"] - 25, w, 4))
            pygame.draw.rect(screen, (0, 200, 0), (e["x"] - w // 2, e["y"] - 25, fw, 4))

        status_colors = []
        if e.get("bleeding", 0) > now:
            status_colors.append((200, 0, 0))
        if e.get("burning", 0) > now:
            status_colors.append((255, 100, 0))
        if e.get("poisoned", 0) > now:
            status_colors.append((80, 255, 80))
        if e.get("stoned", 0) > now:
            status_colors.append((150, 150, 150))

        r = e["hp"] / e["max_hp"]
        base_color = e["color"] if r >= 0.66 else ((255, 150, 0) if r >= 0.33 else (255, 60, 60))

        if e.get("stoned", 0) > now:
            base_color = (150, 150, 150)

        draw(e["char"], e["x"] - 5, e["y"] - 8, base_color, FONT_XS)

        for i, col in enumerate(status_colors):
            ang = i * (2 * math.pi / len(status_colors))
            px = e["x"] + math.cos(ang) * 15
            py = e["y"] + math.sin(ang) * 15
            pygame.draw.circle(screen, col, (int(px), int(py)), 3)

    for b in bosses:
        w = 150
        fw = int(w * (b["hp"] / b["max_hp"]))
        pygame.draw.rect(screen, (100, 0, 0), (b["x"] - w // 2, b["y"] - 35, w, 8))
        pygame.draw.rect(screen, (0, 220, 0), (b["x"] - w // 2, b["y"] - 35, fw, 8))

        if len(b["phases"]) > 1:
            phase_w = w // len(b["phases"])
            for i in range(len(b["phases"])):
                px = b["x"] - w // 2 + i * phase_w
                pygame.draw.rect(screen, (255, 255, 255), (px, b["y"] - 42, 2, 5))

        draw(b["name"], b["x"] - len(b["name"]) * 5 // 2, b["y"] - 50, WHITE, FONT_XS)

        r = b["hp"] / b["max_hp"]
        col = b["color"] if r >= 0.66 else ((255, 160, 60) if r >= 0.33 else (255, 80, 80))
        draw(b["char"], b["x"] - 7, b["y"] - 12, col, FONT_XS)

        if b.get("quake") and random.random() < 0.15:
            for i in range(5):
                ang = random.uniform(0, 2 * math.pi)
                dist = random.uniform(50, 200)
                sx = b["x"] + math.cos(ang) * dist
                sy = b["y"] + math.sin(ang) * dist
                player["shockwaves"].append((sx, sy, now, 1.5))

    for p in ppick:
        time_left = ITEM_DESPAWN_TIME - (now - p["spawn_time"])
        if time_left < 5 and int(now * 2) % 2 == 0:
            continue
        glow_r = 12 + int(math.sin(now * 4) * 3)
        glow = pygame.Surface((glow_r * 2, glow_r * 2), pygame.SRCALPHA)
        glow_color = (*p["color"][:3], 80)
        pygame.draw.circle(glow, glow_color, (glow_r, glow_r), glow_r)
        screen.blit(glow, (p["x"] - glow_r, p["y"] - glow_r))
        pygame.draw.circle(screen, p["color"], (int(p["x"]), int(p["y"])), 6, 2)
        draw(p["char"], p["x"] - 4, p["y"] - 5, WHITE, FONT_XS)

        name = powerups[p["key"]]["name"]
        draw(name, p["x"] - len(name) * 3, p["y"] - 22, p["color"], FONT_XS)

    for a in apick:
        time_left = ITEM_DESPAWN_TIME - (now - a["spawn_time"])
        if time_left < 5 and int(now * 2) % 2 == 0:
            continue
        glow_r = 12 + int(math.sin(now * 4 + 2) * 3)
        glow = pygame.Surface((glow_r * 2, glow_r * 2), pygame.SRCALPHA)
        glow_color = (*a["color"][:3], 80)
        pygame.draw.circle(glow, glow_color, (glow_r, glow_r), glow_r)
        screen.blit(glow, (a["x"] - glow_r, a["y"] - glow_r))
        pygame.draw.rect(screen, a["color"], (a["x"] - 8, a["y"] - 8, 16, 16), 2)
        draw(a["char"], a["x"] - 4, a["y"] - 5, WHITE, FONT_XS)

        name = actives[a["key"]]["name"]
        draw(name, a["x"] - len(name) * 3, a["y"] - 22, a["color"], FONT_XS)

    for h in hearts:
        time_left = ITEM_DESPAWN_TIME - (now - h["spawn_time"])
        if time_left < 5 and int(now * 2) % 2 == 0:
            continue
        glow_r = 10 + int(math.sin(now * 5) * 2)
        glow = pygame.Surface((glow_r * 2, glow_r * 2), pygame.SRCALPHA)
        pygame.draw.circle(glow, (255, 70, 70, 60), (glow_r, glow_r), glow_r)
        screen.blit(glow, (h["x"] - glow_r, h["y"] - glow_r))
        draw("+", h["x"] - 3, h["y"] - 4, (255, 70, 70), FONT_XS)

    for c in coins:
        time_left = ITEM_DESPAWN_TIME * 1.5 - (now - c["spawn_time"])
        if time_left < 5 and int(now * 2) % 2 == 0:
            continue
        glow_r = 8 + int(math.sin(now * 6 + 1) * 2)
        glow = pygame.Surface((glow_r * 2, glow_r * 2), pygame.SRCALPHA)
        pygame.draw.circle(glow, (255, 215, 0, 50), (glow_r, glow_r), glow_r)
        screen.blit(glow, (c["x"] - glow_r, c["y"] - glow_r))
        draw("$", c["x"] - 3, c["y"] - 4, GOLD, FONT_XS)

    if player["inv"] <= 0 or (now * 10) % 2 < 1:
        if player["shield_active"]:
            shield_r = 20 + math.sin(now * 5) * 3
            pygame.draw.circle(screen, (0, 120, 255, 100),
                             (int(player["x"]), int(player["y"])),
                             int(shield_r), 2)

        if player["aura_slow"] > 0:
            pygame.draw.circle(screen, (100, 200, 255, 50),
                             (int(player["x"]), int(player["y"])),
                             player["aura_r"], 1)

        draw("@", player["x"] - 3, player["y"] - 4, (0, 255, 255), FONT_XS)

    for p in particles[:]:
        p["x"] += p["dx"]
        p["y"] += p["dy"]
        p["life"] -= 1 / 60
        if p["life"] <= 0:
            particles.remove(p)
        else:
            alpha = int(255 * p["life"])
            pygame.draw.circle(screen, (*p["color"], alpha),
                             (int(p["x"]), int(p["y"])), 2)

    if parry_cooldown > 0:
        draw(f"Parry: {parry_cooldown:.1f}s", player["x"] - 20, player["y"] + 25, GRAY, FONT_XS)

    draw_hud()
    console.draw(screen)


def draw_dungeon():
    # Draw arena background first
    current_arena.update(1 / 60)
    current_arena.draw_background(screen)
    
    dungeon.draw(screen)

    # Draw enemies same style as main game
    now = time.time()
    for e in dungeon.enemies:
        status_colors = []
        if e.get("bleeding", 0) > now:
            status_colors.append((200, 0, 0))
        if e.get("burning", 0) > now:
            status_colors.append((255, 100, 0))
        if e.get("poisoned", 0) > now:
            status_colors.append((80, 255, 80))
        if e.get("stoned", 0) > now:
            status_colors.append((150, 150, 150))
        
        r = e["hp"] / e["max_hp"]
        enemy_color = e.get("color", (255, 100, 100))
        base_color = enemy_color if r >= 0.66 else ((255, 150, 0) if r >= 0.33 else (255, 60, 60))
        
        if e.get("cursed", False):
            base_color = (200, 50, 50)
        if e.get("stoned", 0) > now:
            base_color = (150, 150, 150)
        
        draw(e.get("char", "E"), e["x"] - 5, e["y"] - 8, base_color, FONT_XS)
        
        for i, col in enumerate(status_colors):
            ang = i * (2 * math.pi / len(status_colors))
            px = e["x"] + math.cos(ang) * 15
            py = e["y"] + math.sin(ang) * 15
            pygame.draw.circle(screen, col, (int(px), int(py)), 3)

        w = 30
        fw = int(w * (e["hp"] / e["max_hp"]))
        pygame.draw.rect(screen, (80, 0, 0), (e["x"] - w // 2, e["y"] - 25, w, 4))
        pygame.draw.rect(screen, (0, 200, 0), (e["x"] - w // 2, e["y"] - 25, fw, 4))

    # Draw player same as main game
    now = time.time()
    if player["inv"] <= 0 or (now * 10) % 2 < 1:
        if player.get("shield_active"):
            shield_r = 20 + math.sin(now * 5) * 3
            pygame.draw.circle(screen, (0, 120, 255, 100),
                             (int(player["x"]), int(player["y"])),
                             int(shield_r), 2)

        if player.get("aura_slow", 0) > 0:
            pygame.draw.circle(screen, (100, 200, 255, 50),
                             (int(player["x"]), int(player["y"])),
                             player.get("aura_r", 100), 1)

        draw("@", player["x"] - 3, player["y"] - 4, (0, 255, 255), FONT_XS)
    
    # Draw bullets same style as main game
    for b in bullets:
        size = b.get("size", 5)

        if b.get("blood"):
            color = (200, 0, 0)
            draw_bullet(screen, BULLET_TYPE.FIRE, color, b["x"], b["y"], size + 1, 0)
        elif b.get("blood_ray"):
            draw_bullet(screen, BULLET_TYPE.LASER, (220, 0, 0), b["x"], b["y"], size, 0)
        elif b.get("laser"):
            color = b.get("color", (255, 255, 0))
            ang = math.atan2(b.get("dy", 0), b.get("dx", 1))
            draw_bullet(screen, BULLET_TYPE.LASER, color, b["x"], b["y"], size, 0)
        else:
            bullet_type = b.get("bullet_type", BULLET_TYPE.NORMAL)
            bp = b.get("pattern")
            color = PATTERN_COLORS.get(bp, None)
            if color is None:
                color = BULLET_COLORS.get(bullet_type, (255, 220, 50))
            ang = math.atan2(b.get("dy", 0), b.get("dx", 1))
            import sys
            _dbg_pl = getattr(sys, '_dbg_pl', 0) + 1
            setattr(sys, '_dbg_pl', _dbg_pl)
            if _dbg_pl <= 6:
                sh2 = BULLET_TYPE_TO_SHAPE.get(bullet_type, BULLET_SHAPE.CIRCLE)
                print(f"PlayerBullet #{_dbg_pl}: bp={bp}, bt={bullet_type.name}, color={color}, shape={sh2.name}")
            draw_bullet(screen, bullet_type, color, b["x"], b["y"], size, ang)

    # Draw enemy bullets with shape mapping also
    for b in ebullets:
        base_size = b.get("size", 10)
        size = max(MIN_BULLET_SIZE, min(MAX_BULLET_SIZE, base_size))

        bullet_type = b.get("bullet_type", BULLET_TYPE.NORMAL)
        bp = b.get("pattern")
        color = PATTERN_COLORS.get(bp, None)
        if color is None:
            color = BULLET_COLORS.get(bullet_type, (255, 80, 80))
        ang = math.atan2(b.get("dy", 1), b.get("dx", 0))
        draw_bullet(screen, bullet_type, color, b["x"], b["y"], size, ang)

    for p in ppick:
        glow_r = 12 + int(math.sin(now * 4) * 3)
        glow = pygame.Surface((glow_r * 2, glow_r * 2), pygame.SRCALPHA)
        glow_color = (*p["color"][:3], 80)
        pygame.draw.circle(glow, glow_color, (glow_r, glow_r), glow_r)
        screen.blit(glow, (p["x"] - glow_r, p["y"] - glow_r))
        pygame.draw.circle(screen, p["color"], (int(p["x"]), int(p["y"])), 6, 2)
        draw(p["char"], p["x"] - 4, p["y"] - 5, WHITE, FONT_XS)
        name = powerups[p["key"]]["name"]
        draw(name, p["x"] - len(name) * 3, p["y"] - 22, p["color"], FONT_XS)

    for a in apick:
        glow_r = 12 + int(math.sin(now * 4 + 2) * 3)
        glow = pygame.Surface((glow_r * 2, glow_r * 2), pygame.SRCALPHA)
        glow_color = (*a["color"][:3], 80)
        pygame.draw.circle(glow, glow_color, (glow_r, glow_r), glow_r)
        screen.blit(glow, (a["x"] - glow_r, a["y"] - glow_r))
        pygame.draw.rect(screen, a["color"], (a["x"] - 8, a["y"] - 8, 16, 16), 2)
        draw(a["char"], a["x"] - 4, a["y"] - 5, WHITE, FONT_XS)
        name = actives[a["key"]]["name"]
        draw(name, a["x"] - len(name) * 3, a["y"] - 22, a["color"], FONT_XS)

    for h in hearts:
        glow_r = 10 + int(math.sin(now * 5) * 2)
        glow = pygame.Surface((glow_r * 2, glow_r * 2), pygame.SRCALPHA)
        pygame.draw.circle(glow, (255, 70, 70, 60), (glow_r, glow_r), glow_r)
        screen.blit(glow, (h["x"] - glow_r, h["y"] - glow_r))
        draw("+", h["x"] - 3, h["y"] - 4, (255, 70, 70), FONT_XS)

    for c in coins:
        glow_r = 8 + int(math.sin(now * 6 + 1) * 2)
        glow = pygame.Surface((glow_r * 2, glow_r * 2), pygame.SRCALPHA)
        pygame.draw.circle(glow, (255, 215, 0, 50), (glow_r, glow_r), glow_r)
        screen.blit(glow, (c["x"] - glow_r, c["y"] - glow_r))
        draw("$", c["x"] - 3, c["y"] - 4, GOLD, FONT_XS)

    # Draw particles same as main game
    for p in particles[:]:
        p["x"] += p["dx"]
        p["y"] += p["dy"]
        p["life"] -= 1 / 60
        if p["life"] <= 0:
            particles.remove(p)
        else:
            alpha = int(255 * p["life"])
            pygame.draw.circle(screen, (*p["color"], alpha),
                             (int(p["x"]), int(p["y"])), 2)

    dungeon.draw_minimap(screen, screen.get_width() - 220, 20)

    hp_col = (0, 255, 0) if player["hp"] > player["max_hp"] * 0.66 else \
        ((255, 200, 0) if player["hp"] > player["max_hp"] * 0.33 else (255, 80, 80))
    draw(f"HP {int(player['hp'])}/{player['max_hp']}", 10, 10, hp_col, FONT_S)

    draw(f"Souls {souls}", 10, 38, (180, 80, 255), FONT_S)
    draw(f"${money}", 10, 66, GOLD, FONT_S)

    if not player.get("hide_wave", False):
        draw(f"WAVE {wave}", 10, 94, current_arena.color, FONT_S)

    if player["credit_timer"] > 0:
        player["credit_timer"] -= 1 / 60
        if player["credit_timer"] <= 0 and player["hp"] <= 0:
            global game_over
            game_over = True
        draw(f"Credit: {int(player['credit_timer'])}s", 10, 122, YELLOW, FONT_XS)

    if len(dungeon.enemies) == 0:
        draw("ROOM CLEAR", screen.get_width() // 2 - 50, 30, GREEN, FONT_S)
    else:
        draw(f"ENEMIES: {len(dungeon.enemies)}", screen.get_width() // 2 - 50, 30, RED, FONT_S)

    if dungeon.is_exit() and len(dungeon.enemies) == 0:
        draw("EXIT →", screen.get_width() - 150, screen.get_height() // 2, CYAN, FONT_L)

    console.draw(screen)


# -------------------- Bestiary --------------------
def bestiary_data():
    enemies_info = []
    for k, d in ENEMIES.items():
        enemies_info.append((d["char"], d["name"],
                           f"HP:{d['hp']} AI:{d['ai'].name} PAT:{d['pat'].name}"))
    for k, d in ELITES.items():
        enemies_info.append((d["char"], d["name"],
                           f"HP:{d['hp']} elite PAT:{d['pat'].name}"))
    for k, d in ULTRAS.items():
        enemies_info.append((d["char"], d["name"],
                           f"HP:{d['hp']} ++ PAT:{d['pat'].name}"))

    bosses_info = []
    for w, b in sorted(BOSSES.items()):
        bosses_info.append((b["char"], f"{b['name']} (Wave {w})",
                          f"HP:{b['hp']} Phases:{len(b['ph'])} Special:{b.get('special_attack', 'None')}"))

    sin_info = []
    for k, b in SIN_BOSSES.items():
        sin_info.append((b["char"], f"{b['name']} (Грех)",
                       f"HP:{b['hp']} Pattern:{b['pattern'].name}"))

    items_info = []
    for k, d in powerups.items():
        items_info.append((d["char"], d["name"], d["desc"]))

    actives_info = []
    for k, d in actives.items():
        actives_info.append((d["char"], d["name"],
                           f"{d['desc']} | CD:{d['cd']} {'sec' if d['cd_type'] == 'time' else 'waves'}"))

    return enemies_info, bosses_info + sin_info, items_info, actives_info


def draw_bestiary():
    screen.fill((15, 15, 20))
    draw("BESTIARY", 30, 20, WHITE, FONT_L)
    draw("Tabs: 1 Enemies  2 Bosses  3 Items  4 Actives", 30, 70, GRAY, FONT_XS)
    draw("UP/DOWN: Scroll, ESC: Back", 30, 90, GRAY, FONT_XS)

    enemies_info, bosses_info, items_info, actives_info = bestiary_data()
    data = [enemies_info, bosses_info, items_info, actives_info][tab_idx]

    y = 120
    start = max(0, tab_scroll)
    end = min(len(data), start + 15)

    for i in range(start, end):
        ch, name, desc = data[i]
        draw(f"{ch}", 34, y, CYAN, FONT_S)
        draw(f"{name}", 70, y, WHITE, FONT_XS)
        draw(desc, 70, y + 18, GRAY, FONT_XS)
        y += 45

    if len(data) > 15:
        draw(f"Page {start // 15 + 1}/{(len(data) + 14) // 15}", screen.get_width() // 2 - 50, screen.get_height() - 40, GRAY, FONT_XS)


def draw_achievements():
    screen.fill((20, 15, 25))
    draw("ACHIEVEMENTS", 30, 20, WHITE, FONT_L)
    draw("ESC to return, UP/DOWN to scroll", 30, 70, GRAY, FONT_XS)

    completed, total, progress = get_achievement_progress()
    draw(f"Progress: {completed}/{total} ({progress * 100:.1f}%)", 30, 100, YELLOW, FONT_S)

    if progress >= 0.5 and not achievements["completionist"]:
        achievements["completionist"] = True
    if progress >= 0.75 and not achievements["master"]:
        achievements["master"] = True
    if progress >= 1.0 and not achievements["legendary"]:
        achievements["legendary"] = True

    y = 140
    start = max(0, ach_scroll)
    end = min(len(ACHIEVEMENTS_LIST), start + 12)

    items = list(ACHIEVEMENTS_LIST.items())
    for i in range(start, end):
        key, (name, desc) = items[i]
        color = GREEN if achievements[key] else GRAY
        draw("✓" if achievements[key] else "○", 34, y, color, FONT_S)
        draw(name, 70, y, color, FONT_XS)
        draw(desc, 70, y + 18, GRAY, FONT_XS)
        y += 50

    if len(ACHIEVEMENTS_LIST) > 12:
        draw(f"Page {start // 12 + 1}/{(len(ACHIEVEMENTS_LIST) + 11) // 12}", screen.get_width() // 2 - 50, screen.get_height() - 40, GRAY, FONT_XS)


def draw_menu():
    screen.fill((0, 0, 0))
    draw("Ultimate Roguelike Part I", screen.get_width() // 2 - 220, 100, WHITE, FONT_XL)
    draw("UR20", screen.get_width() // 2 - 80, 160, YELLOW, FONT_L)  # Изменено на UR20
    draw("Ascend through 100 waves", screen.get_width() // 2 - 200, 220, GRAY, FONT_S)

    completed, total, progress = get_achievement_progress()
    draw(f"Achievements: {completed}/{total}", screen.get_width() // 2 - 120, 260, CYAN, FONT_S)

    for i, m in enumerate(menu_items):
        color = (0, 255, 120) if i == menu_idx else WHITE
        draw(m, screen.get_width() // 2 - 60, 330 + i * 45, color, FONT_L)
    
    # Version in bottom left corner
    draw("20", 10, screen.get_height() - 30, GRAY, FONT_XS)  # Изменено на 20


def draw_settings():
    screen.fill((0, 0, 0))
    draw("Настройки", screen.get_width() // 2 - 80, 80, WHITE, FONT_L)
    opts = ["Режим управления", "Громкость музыки", "Громкость звуков", "Общая громкость", "Тест звука"]
    for i, m in enumerate(opts):
        color = (0, 255, 120) if i == settings_idx else WHITE
        draw(m, screen.get_width() // 2 - 110, 200 + i * 60, color, FONT_L)
    
    mode_preview = control_mode
    draw(f"Режим: {mode_preview}",
         screen.get_width() // 2 - 100, 240,
         (200, 200, 200), FONT_S)
    
    draw(f"Музыка: {int(music_volume * 100)}%",
         screen.get_width() // 2 - 100, 300,
         (200, 200, 200), FONT_S)
    
    draw(f"Звуки: {int(sfx_volume * 100)}%",
         screen.get_width() // 2 - 100, 360,
         (200, 200, 200), FONT_S)
    
    draw(f"Общая: {int(master_volume * 100)}%",
         screen.get_width() // 2 - 100, 420,
         (200, 200, 200), FONT_S)
    
    draw("ENTER — воспроизвести тест звука",
         screen.get_width() // 2 - 160, 540,
         (150, 200, 150), FONT_S)

    draw("← → изменить  ↑ ↓ выбор  ESC назад",
         screen.get_width() // 2 - 160, screen.get_height() - 60, GRAY, FONT_S)
    draw("20", 10, screen.get_height() - 30, GRAY, FONT_XS)


def draw_pause():
    s = pygame.Surface((screen.get_width(), screen.get_height()), pygame.SRCALPHA)
    s.fill((0, 0, 0, 180))
    screen.blit(s, (0, 0))

    draw("PAUSED", screen.get_width() // 2 - 70, screen.get_height() // 2 - 100, WHITE, FONT_XL)
    draw("ESC — resume   R — restart   Q — quit",
         screen.get_width() // 2 - 200, screen.get_height() // 2 - 40, GRAY, FONT_S)

    draw(f"Wave: {wave}", screen.get_width() // 2 - 50, screen.get_height() // 2, WHITE, FONT)
    draw(f"Score: {player['score']}", screen.get_width() // 2 - 50, screen.get_height() // 2 + 30, WHITE, FONT)
    draw(f"Souls: {souls}", screen.get_width() // 2 - 50, screen.get_height() // 2 + 60, WHITE, FONT)
    draw(f"Money: ${money}", screen.get_width() // 2 - 50, screen.get_height() // 2 + 90, GOLD, FONT)


# -------------------- Save/Load --------------------
def save_achievements():
    global achievements
    if ACHIEVEMENTS_DISABLED:
        return
    try:
        with open("achievements.json", "w") as f:
            json.dump(achievements, f)
    except:
        pass


def load_achievements():
    global achievements
    try:
        if os.path.exists("achievements.json"):
            with open("achievements.json", "r") as f:
                loaded = json.load(f)
                achievements.update(loaded)
    except:
        pass


# -------------------- Main --------------------
load_achievements()
# load_sound_effects()
reset()
running = True
start_game_time = time.time()

# Start music playback
play_next_music()

while running:
    import sys as _block_sys
    _blk = getattr(_block_sys, '_blk', 0) + 1
    setattr(_block_sys, '_blk', _blk)
    if _blk <= 3:
        print(f"WHILE LOOP iter {_blk}, state={state}, running={running}", flush=True)
    now = time.time()
    dt = min(0.033, now - last_time)
    last_time = now

    for ev in pygame.event.get():
        if ev.type == pygame.QUIT:
            running = False
        elif ev.type == pygame.KEYDOWN:
            if ev.key == pygame.K_F11:
                # Toggle fullscreen
                flags = screen.get_flags()
                if flags & pygame.FULLSCREEN:
                    screen = pygame.display.set_mode((W, H), pygame.RESIZABLE)
                else:
                    screen = pygame.display.set_mode((0, 0), pygame.FULLSCREEN)
                    # Update W and H to actual screen size
                    info = pygame.display.Info()
                    W, H = info.current_w, info.current_h
            elif ev.key == pygame.K_BACKQUOTE and console.enabled:
                console.toggle()
            elif console.active:
                if ev.key == pygame.K_RETURN:
                    console.execute()
                elif ev.key == pygame.K_BACKSPACE:
                    console.backspace()
                elif ev.key == pygame.K_UP:
                    console.history_up()
                elif ev.key == pygame.K_DOWN:
                    console.history_down()
                elif ev.key == pygame.K_ESCAPE:
                    console.toggle()
                elif ev.unicode.isprintable() and len(ev.unicode) > 0:
                    console.add_char(ev.unicode)
                continue
            else:
                if state == GS.MENU:
                    if ev.key == pygame.K_DOWN:
                        menu_idx = (menu_idx + 1) % len(menu_items)
                    elif ev.key == pygame.K_UP:
                        menu_idx = (menu_idx - 1) % len(menu_items)
                    elif ev.key in (pygame.K_RETURN, pygame.K_SPACE):
                        sel = menu_items[menu_idx]
                        if sel == "Start":
                            reset()
                            start_game_time = time.time()
                            state = GS.GAME
                        elif sel == "Bestiary":
                            tab_scroll = 0
                            state = GS.BESTIARY
                        elif sel == "Achievements":
                            ach_scroll = 0
                            state = GS.ACHIEVEMENTS
                        elif sel == "Settings":
                            settings_idx = 0
                            state = GS.SETTINGS
                        elif sel == "Quit":
                            running = False
                elif state == GS.DUNGEON:
                    if ev.key == pygame.K_ESCAPE:
                        state = GS.PAUSE
                    elif ev.key == pygame.K_TAB:
                        show_full = not show_full
                    elif ev.key in (pygame.K_1, pygame.K_2, pygame.K_3, pygame.K_4, pygame.K_5):
                        keys = list(player["actives"].keys())
                        idx = ev.key - pygame.K_1
                        if idx < len(keys):
                            activate_active(keys[idx])
                elif state in (GS.GAME, GS.PAUSE):
                    if ev.key == pygame.K_ESCAPE:
                        state = GS.PAUSE if state == GS.GAME else GS.GAME
                    elif ev.key == pygame.K_q and state == GS.PAUSE:
                        running = False
                    elif ev.key == pygame.K_r and state == GS.PAUSE:
                        reset()
                        start_game_time = time.time()
                        state = GS.GAME
                    elif state == GS.GAME:
                        if ev.key == pygame.K_TAB:
                            show_full = not show_full
                        if ev.key in (pygame.K_1, pygame.K_2, pygame.K_3, pygame.K_4, pygame.K_5):
                            keys = list(player["actives"].keys())
                            idx = ev.key - pygame.K_1
                            if idx < len(keys):
                                activate_active(keys[idx])
                elif state == GS.BESTIARY:
                    if ev.key == pygame.K_ESCAPE:
                        state = GS.MENU
                    elif ev.key == pygame.K_TAB:
                        tab_idx = (tab_idx + 1) % len(tab_names)
                        tab_scroll = 0
                    elif ev.key == pygame.K_UP:
                        tab_scroll = max(0, tab_scroll - 1)
                    elif ev.key == pygame.K_DOWN:
                        tab_scroll += 1
                elif state == GS.ACHIEVEMENTS:
                    if ev.key == pygame.K_ESCAPE:
                        state = GS.MENU
                    elif ev.key == pygame.K_UP:
                        ach_scroll = max(0, ach_scroll - 1)
                    elif ev.key == pygame.K_DOWN:
                        ach_scroll += 1
                elif state == GS.SETTINGS:
                    if ev.key == pygame.K_ESCAPE:
                        state = GS.MENU
                    elif ev.key == pygame.K_UP:
                        settings_idx = (settings_idx - 1) % 5
                    elif ev.key == pygame.K_DOWN:
                        settings_idx = (settings_idx + 1) % 5
                    elif ev.key == pygame.K_LEFT:
                        if settings_idx == 0:
                            control_mode = "Классический" if control_mode == "Новый" else "Новый"
                        elif settings_idx == 1:
                            music_volume = max(0.0, music_volume - 0.1)
                            pygame.mixer.music.set_volume(music_volume * master_volume)
                        elif settings_idx == 2:
                            sfx_volume = max(0.0, sfx_volume - 0.1)
                        elif settings_idx == 3:
                            master_volume = max(0.0, master_volume - 0.1)
                            pygame.mixer.music.set_volume(music_volume * master_volume)
                    elif ev.key == pygame.K_RIGHT:
                        if settings_idx == 0:
                            control_mode = "Новый" if control_mode == "Классический" else "Классический"
                        elif settings_idx == 1:
                            music_volume = min(1.0, music_volume + 0.1)
                            pygame.mixer.music.set_volume(music_volume * master_volume)
                        elif settings_idx == 2:
                            sfx_volume = min(1.0, sfx_volume + 0.1)
                        elif settings_idx == 3:
                            master_volume = min(1.0, master_volume + 0.1)
                            pygame.mixer.music.set_volume(music_volume * master_volume)
                    elif ev.key == pygame.K_RETURN and settings_idx == 4:
                        play_random_sound()
                elif state in (GS.OVER, GS.WIN):
                    if ev.key == pygame.K_r:
                        reset()
                        start_game_time = time.time()
                        state = GS.GAME
                    elif ev.key in (pygame.K_q, pygame.K_ESCAPE):
                        running = False
        elif ev.type == pygame.MOUSEBUTTONDOWN:
            if ev.button == 1 and control_mode == "Новый" and state in (GS.GAME, GS.DUNGEON):
                mouse_pressed = True
        elif ev.type == pygame.MOUSEBUTTONUP:
            if ev.button == 1:
                mouse_pressed = False

    # Music playback - every 3 minutes play next track
    if music_play_time > 0 and time.time() - music_play_time > 180:
        if not pygame.mixer.music.get_busy():
            play_next_music()

    if state == GS.MENU:
        draw_menu()
    elif state == GS.BESTIARY:
        draw_bestiary()
    elif state == GS.ACHIEVEMENTS:
        draw_achievements()
    elif state == GS.SETTINGS:
        draw_settings()
    elif state == GS.PAUSE:
        if dungeon:
            draw_dungeon()
        else:
            draw_game()
        draw_pause()
    elif state == GS.DUNGEON:
        # Пропускаем обновление игры если консоль открыта
        if console.paused:
            draw_dungeon()
            draw_hud()
            console.draw(screen)
            pygame.display.flip()
            continue
        
        handle_dungeon_input()
        update_bullets(dt)
        update_enemy_bullets(dt)
        update_dungeon_enemies(dt)
        collisions()
        pick_powerups()
        update_special_effects(dt)

        if player["regen"] > 0 and random.random() < 0.01:
            player["hp"] = min(player["hp"] + player["regen"], player["max_hp"])

        if player["hp"] <= 0 and souls <= 0 and player.get("credit_timer", 0) <= 0:
            state = GS.OVER
            save_achievements()

        draw_dungeon()
    elif state in (GS.OVER, GS.WIN):
        if dungeon:
            draw_dungeon()
        else:
            draw_game()
        title = "VICTORY! WAVE 100 CLEARED" if state == GS.WIN else "GAME OVER"
        draw(title, screen.get_width() // 2 - 180, screen.get_height() // 2 - 60, WHITE, FONT_L)
        draw(f"Final Wave: {wave}", screen.get_width() // 2 - 100, screen.get_height() // 2, WHITE, FONT)
        draw(f"Score: {player['score']}", screen.get_width() // 2 - 80, screen.get_height() // 2 + 30, WHITE, FONT)
        draw(f"Money: ${money}", screen.get_width() // 2 - 80, screen.get_height() // 2 + 60, GOLD, FONT)
        draw("R — restart, Q/ESC — quit", screen.get_width() // 2 - 140, screen.get_height() // 2 + 100, GRAY, FONT_S)

        if state == GS.WIN and wave >= 100 and not achievements["wave_100"]:
            achievements["wave_100"] = True
            save_achievements()

        if state == GS.WIN and player["lives"] == 3 and not achievements["perfect_game"]:
            achievements["perfect_game"] = True
            save_achievements()
    else:  # GS.GAME
        import sys as _gsys
        _gf = getattr(_gsys, '_gframe', 0) + 1
        setattr(_gsys, '_gframe', _gf)
        if _gf <= 3 or _gf % 500 == 0:
            print(f"GAME LOOP frame={_gf}, enemies={len(enemies)}, ebullets={len(ebullets)}, bullets={len(bullets)}, state={state}")
        # Пропускаем обновление игры если консоль открыта
        if console.paused:
            if dungeon:
                draw_dungeon()
            else:
                draw_game()
            draw_hud()
            console.draw(screen)
            pygame.display.flip()
            continue
        
        handle_input()
        update_special_effects(dt)

        update_bullets(dt)
        update_enemy_bullets(dt)
        update_enemies(dt)
        update_bosses(dt)

        collisions()

        pick_powerups()

        if player["regen"] > 0 and random.random() < 0.01:
            player["hp"] = min(player["hp"] + player["regen"], player["max_hp"])

        if "infallibility" in player["effects"]:
            if player["last_hit_wave"] != wave:
                player["dmg"] += 1
                player["last_hit_wave"] = wave

        if player["inv"] > 0:
            player["inv"] = max(0.0, player["inv"] - dt)
            player["invincibility_time"] += dt
            if player["invincibility_time"] >= 10 and not achievements["invincible"]:
                achievements["invincible"] = True
        else:
            player["invincibility_time"] = 0

        if player["shield_active"] and time.time() >= player["shield_until"]:
            player["shield_active"] = False

        game_time = now - start_game_time
        if wave >= 20 and game_time <= 300 and not achievements["speedrunner"]:
            achievements["speedrunner"] = True

        if player["waves_without_hit"] >= 5 and not achievements["immortal"]:
            achievements["immortal"] = True

        if len(player["status_effects_applied"]) >= 4 and not achievements["elementalist"]:
            achievements["elementalist"] = True

        if (player["dmg"] >= 20 or player["max_hp"] >= 20 or
            player["speed_mult"] >= 2.0 or player["luck"] >= 0.5) and not achievements["specialist"]:
            achievements["specialist"] = True

        if len(enemies) == 0 and len(bosses) == 0 and player["combo_kills"] == 0 and wave > 0:
            if not achievements.get("pacifist_check", False):
                achievements["pacifist"] = True
                achievements["pacifist_check"] = True

        if wave >= 50 and player["lives"] == 3 and not achievements["unstoppable"]:
            achievements["unstoppable"] = True

        if player["score"] >= 1000 and not achievements["sharpshooter"]:
            achievements["sharpshooter"] = True

        if time.time() < player.get("time_slow_until", 0):
            player["time_slowed_total"] += dt
            if player["time_slowed_total"] >= 30 and not achievements["time_lord"]:
                achievements["time_lord"] = True

        if player["score"] >= 5000 and not achievements["economist"]:
            achievements["economist"] = True

        if "INFINITY" in player["unique_items"] and player["infinity_taken_wave"] > 0:
            if wave >= player["infinity_taken_wave"] + 10 and not achievements["infinity"]:
                achievements["infinity"] = True

        if "GLASS_CANNON" in player["unique_items"] and player["lives"] == 3 and not achievements["glass_demon"]:
            achievements["glass_demon"] = True

        # Проверка на проклятые предметы
        if player.get("cursed_items", 0) >= 3 and not achievements["curse_bearer"]:
            achievements["curse_bearer"] = True

        # Проверка на стеклянного бога
        if player.get("made_of_glass", False) and player["hp"] == 1 and player["waves_without_hit"] >= 1 and not achievements["glass_God"]:
            achievements["glass_God"] = True

        if player["hp"] <= 0 and souls <= 0 and player.get("credit_timer", 0) <= 0:
            state = GS.OVER
            save_achievements()

        draw_game()

    console.draw(screen)
    pygame.display.flip()
    clock.tick(60)

save_achievements()
pygame.quit()
sys.exit()
