const express = require('express');
const app = express();
const http = require('http').createServer(app);
const io = require('socket.io')(http);
const path = require('path');
const fs = require('fs');

// Глобальная защита от падения сервера
process.on('uncaughtException', (err) => {
    console.error('SERVER ERROR CAUGHT:', err);
});

app.use(express.static(path.join(__dirname, 'public')));

if (!fs.existsSync('./saves')){
    fs.mkdirSync('./saves');
}

const BLOCK_SIZE = 32;
const WORLD_WIDTH = 400;
const WORLD_HEIGHT = 150;
const DAY_DURATION = 600000; // 10 минут
const GRAVITY = 0.5;

const BLOCKS = {
    AIR: 0, DIRT: 1, STONE: 2, GRASS: 3, WOOD: 4, LEAVES: 5,
    WORKBENCH: 6, FURNACE: 7, SAND: 8, COAL_ORE: 9, IRON_ORE: 10,
    GOLD_ORE: 11, COPPER_ORE: 12, GLASS: 13, BRICK: 14, TORCH: 15,
    WATER: 16, CHEST: 17
};

// Блоки, через которые можно проходить (фоновые, как деревья в Террарии)
const PASSABLE_BLOCKS = [BLOCKS.AIR, BLOCKS.WOOD, BLOCKS.LEAVES, BLOCKS.TORCH];

function isPassable(blockType) {
    return PASSABLE_BLOCKS.includes(blockType);
}
const ITEMS = {
    WOODEN_SWORD: 100, WOODEN_PICKAXE: 101, WOODEN_AXE: 102,
    STONE_SWORD: 103, STONE_PICKAXE: 104, STONE_AXE: 105,
    IRON_SWORD: 106, IRON_PICKAXE: 107, IRON_AXE: 108,
    COAL: 109, IRON_BAR: 110, GOLD_BAR: 111, COPPER_BAR: 112,
    HEALTH_POTION: 113, MANA_POTION: 114
};

// Характеристики мобов
const MOB_STATS = {
    slime: { 
        hp: 20, 
        damage: 1, 
        speed: 1.5, 
        jumpForce: 6, 
        attackRange: 30, 
        aggroRange: 150,
        attackCooldown: 1000,
        width: 32, 
        height: 32 
    },
    zombie: { 
        hp: 40, 
        damage: 2, 
        speed: 1.2, 
        jumpForce: 8, 
        attackRange: 35, 
        aggroRange: 200,
        attackCooldown: 800,
        width: 32, 
        height: 48 
    },
    skeleton: { 
        hp: 30, 
        damage: 2, 
        speed: 1.8, 
        jumpForce: 7, 
        attackRange: 40, 
        aggroRange: 250,
        attackCooldown: 1200,
        width: 32, 
        height: 48 
    },
    demon: { 
        hp: 60, 
        damage: 3, 
        speed: 2.0, 
        jumpForce: 10, 
        attackRange: 45, 
        aggroRange: 300,
        attackCooldown: 600,
        width: 32, 
        height: 36 
    }
};

const lobbies = {};

function getEmptyInventory() {
    let inv = {};
    [...Object.values(BLOCKS), ...Object.values(ITEMS)].forEach(id => inv[id] = 0);
    return inv;
}

function generateWorld() {
    let world = Array(WORLD_HEIGHT).fill().map(() => Array(WORLD_WIDTH).fill(BLOCKS.AIR));
    const surfaceHeight = [];
    
    // Базовый уровень поверхности
    const baseLevel = 50;
    
    // Уровень воды
    const waterLevel = baseLevel + 12;
    
    // Генерация поверхности с разнообразным рельефом
    for (let x = 0; x < WORLD_WIDTH; x++) {
        let height = baseLevel;
        height += Math.sin(x * 0.02) * 8;
        height += Math.sin(x * 0.05) * 4;
        height += Math.cos(x * 0.1) * 2;
        
        // Озеро слева (x: 15-70) - глубокая впадина
        if (x >= 15 && x <= 70) {
            const lakeCenter = 42;
            const distFromCenter = Math.abs(x - lakeCenter);
            const lakeHalfWidth = 28;
            
            if (distFromCenter < lakeHalfWidth) {
                const depthFactor = 1 - (distFromCenter / lakeHalfWidth);
                const maxDepth = 20;
                height = waterLevel + 3 + Math.floor(maxDepth * depthFactor * depthFactor);
            }
        }
        
        surfaceHeight[x] = Math.floor(height);
    }
    
    // Заполнение блоками
    for (let x = 0; x < WORLD_WIDTH; x++) {
        const surface = surfaceHeight[x];
        for (let y = surface; y < WORLD_HEIGHT; y++) {
            if (y === surface) {
                // Песок вокруг озера и на дне
                if (x >= 10 && x <= 75) {
                    world[y][x] = BLOCKS.SAND;
                } else {
                    world[y][x] = Math.random() < 0.1 ? BLOCKS.SAND : BLOCKS.GRASS;
                }
            } else if (y < surface + 4 + Math.floor(Math.random() * 2)) {
                if (x >= 10 && x <= 75) {
                    world[y][x] = BLOCKS.SAND;
                } else {
                    world[y][x] = BLOCKS.DIRT;
                }
            } else {
                world[y][x] = BLOCKS.STONE;
                const depth = y - surface;
                if (Math.random() < 0.025 && depth > 8) world[y][x] = BLOCKS.COAL_ORE;
                else if (Math.random() < 0.012 && depth > 15) world[y][x] = BLOCKS.COPPER_ORE;
                else if (Math.random() < 0.010 && depth > 25) world[y][x] = BLOCKS.IRON_ORE;
                else if (Math.random() < 0.006 && depth > 40) world[y][x] = BLOCKS.GOLD_ORE;
            }
        }
    }
    
    // Заливаем озеро водой
    for (let x = 15; x <= 70; x++) {
        const bottomY = surfaceHeight[x];
        if (bottomY > waterLevel) {
            for (let y = waterLevel; y < bottomY; y++) {
                if (y >= 0 && y < WORLD_HEIGHT) {
                    world[y][x] = BLOCKS.WATER;
                }
            }
        }
    }
    
    // Пещеры
    for (let i = 0; i < 30; i++) {
        let cx = Math.floor(Math.random() * (WORLD_WIDTH - 40)) + 20;
        let cy = Math.floor(Math.random() * 50) + 70;
        const caveLength = Math.floor(Math.random() * 40) + 20;
        
        for (let j = 0; j < caveLength; j++) {
            const radius = Math.floor(Math.random() * 3) + 2;
            for (let dy = -radius; dy <= radius; dy++) {
                for (let dx = -radius; dx <= radius; dx++) {
                    if (dx*dx + dy*dy <= radius*radius) {
                        const nx = cx + dx;
                        const ny = cy + dy;
                        if (ny >= 60 && ny < WORLD_HEIGHT && nx >= 0 && nx < WORLD_WIDTH) {
                            if (world[ny][nx] !== BLOCKS.WATER) {
                                world[ny][nx] = BLOCKS.AIR;
                            }
                        }
                    }
                }
            }
            cx += Math.floor(Math.random() * 5) - 2;
            cy += Math.floor(Math.random() * 5) - 2;
            cx = Math.max(5, Math.min(WORLD_WIDTH - 5, cx));
            cy = Math.max(60, Math.min(WORLD_HEIGHT - 5, cy));
        }
    }
    
    // Деревья справа от озера
    for (let x = 85; x < WORLD_WIDTH - 10; x += Math.floor(Math.random() * 8) + 6) {
        const surface = surfaceHeight[x];
        if (!world[surface] || world[surface][x] !== BLOCKS.GRASS) continue;
        
        if (surface < WORLD_HEIGHT - 1 && surface > 10) {
            const treeHeight = Math.floor(Math.random() * 4) + 5;
            for (let y = surface - treeHeight; y < surface; y++) { 
                if (y >= 0) world[y][x] = BLOCKS.WOOD; 
            }
            // Крона (овальная форма - широкая и высокая)
            for (let dy = -4; dy <= 1; dy++) {
                for (let dx = -3; dx <= 3; dx++) {
                    const ty = surface - treeHeight + dy; 
                    const tx = x + dx;
                    // Овальная форма: шире по X, выше по Y
                    const distX = dx / 3.5; // Ширина ~7 блоков
                    const distY = (dy + 1.5) / 2.5; // Высота ~5 блоков
                    const dist = Math.sqrt(distX * distX + distY * distY);
                    if (dist > 1) continue;
                    
                    if (ty >= 0 && ty < WORLD_HEIGHT && tx >= 0 && tx < WORLD_WIDTH) {
                        if (world[ty][tx] === BLOCKS.AIR) {
                            world[ty][tx] = BLOCKS.LEAVES;
                        }
                    }
                }
            }
        }
    }
    
    // Немного деревьев слева от озера
    for (let x = 3; x < 10; x += Math.floor(Math.random() * 4) + 3) {
        const surface = surfaceHeight[x];
        if (!world[surface] || world[surface][x] !== BLOCKS.GRASS) continue;
        
        if (surface < WORLD_HEIGHT - 1 && surface > 10) {
            const treeHeight = Math.floor(Math.random() * 3) + 4;
            for (let y = surface - treeHeight; y < surface; y++) { 
                if (y >= 0) world[y][x] = BLOCKS.WOOD; 
            }
            // Крона (овальная форма)
            for (let dy = -3; dy <= 1; dy++) {
                for (let dx = -2; dx <= 2; dx++) {
                    const ty = surface - treeHeight + dy; 
                    const tx = x + dx;
                    // Овальная форма
                    const distX = dx / 2.5;
                    const distY = (dy + 1) / 2;
                    const dist = Math.sqrt(distX * distX + distY * distY);
                    if (dist > 1) continue;
                    
                    if (ty >= 0 && ty < WORLD_HEIGHT && tx >= 0 && tx < WORLD_WIDTH) {
                        if (world[ty][tx] === BLOCKS.AIR) {
                            world[ty][tx] = BLOCKS.LEAVES;
                        }
                    }
                }
            }
        }
    }
    
    return world;
}

function saveLobby(lobbyId) {
    if (!lobbies[lobbyId]) return;
    const savedPlayersData = lobbies[lobbyId].savedPlayers || {};
    for (let socketId in lobbies[lobbyId].players) {
        const p = lobbies[lobbyId].players[socketId];
        savedPlayersData[p.nickname] = { 
            inventory: p.inventory, 
            hotbar: p.hotbarSlots,
            health: p.health || 20,
            team: p.team || null
        };
    }
    const data = {
        world: lobbies[lobbyId].world,
        savedPlayers: savedPlayersData,
        timeOfDay: lobbies[lobbyId].timeOfDay,
        dayCount: lobbies[lobbyId].dayCount || 1, // Сохраняем счётчик дней
        chests: lobbies[lobbyId].chests || {} // Сохраняем сундуки
    };
    fs.writeFile(`./saves/${lobbyId}.json`, JSON.stringify(data), (err) => { if (err) console.error(`Err save:`, err); });
}

function loadLobby(lobbyId) {
    try {
        if (fs.existsSync(`./saves/${lobbyId}.json`)) {
            const raw = fs.readFileSync(`./saves/${lobbyId}.json`);
            const data = JSON.parse(raw);
            return {
                world: data.world,
                savedPlayers: data.savedPlayers || {},
                players: {},
                mobs: [],
                chests: data.chests || {},
                droppedItems: [], // Выпавшие предметы
                nextDropId: 1,
                timeOfDay: data.timeOfDay || 0,
                dayCount: data.dayCount || 1, // Счётчик дней
                lastUpdate: Date.now()
            };
        }
    } catch (e) { console.error("Load err:", e); }
    return null;
}

// Проверка столкновения двух прямоугольников
function checkCollision(x1, y1, w1, h1, x2, y2, w2, h2) {
    return x1 < x2 + w2 && x1 + w1 > x2 && y1 < y2 + h2 && y1 + h1 > y2;
}

// Расстояние между двумя объектами
function getDistance(obj1, obj2) {
    const cx1 = obj1.x + (obj1.width || 24) / 2;
    const cy1 = obj1.y + (obj1.height || 48) / 2;
    const cx2 = obj2.x + obj2.width / 2;
    const cy2 = obj2.y + obj2.height / 2;
    return Math.sqrt(Math.pow(cx1 - cx2, 2) + Math.pow(cy1 - cy2, 2));
}

// Найти ближайшего игрока к мобу
function findNearestPlayer(mob, players) {
    let nearest = null;
    let minDist = Infinity;
    
    for (let id in players) {
        const player = players[id];
        if (player.health <= 0) continue; // Пропускаем мёртвых игроков
        
        const dist = getDistance(player, mob);
        if (dist < minDist) {
            minDist = dist;
            nearest = { id, player, distance: dist };
        }
    }
    
    return nearest;
}

// Проверка столкновения с блоками (игнорирует фоновые блоки)
function checkBlockCollision(x, y, width, height, world) {
    const left = Math.floor(x / BLOCK_SIZE);
    const right = Math.floor((x + width - 1) / BLOCK_SIZE);
    const top = Math.floor(y / BLOCK_SIZE);
    const bottom = Math.floor((y + height - 1) / BLOCK_SIZE);
    
    for (let by = top; by <= bottom; by++) {
        for (let bx = left; bx <= right; bx++) {
            if (by >= 0 && by < WORLD_HEIGHT && bx >= 0 && bx < WORLD_WIDTH) {
                const block = world[by] && world[by][bx];
                if (block && !isPassable(block)) return true;
            }
        }
    }
    return false;
}

setInterval(() => { Object.keys(lobbies).forEach(id => saveLobby(id)); }, 30000);

// === ТЕКУЧЕСТЬ ВОДЫ ===
const WATER_UPDATE_INTERVAL = 100; // Быстрее обновление
const WATER_MAX_SPREAD = 7; // Максимальное распространение в стороны

function updateWaterForLobby(lobby, lobbyId) {
    const world = lobby.world;
    const waterUpdates = [];
    
    // Функция подсчёта расстояния до источника воды (блок воды сверху или большое количество воды рядом)
    function countNearbyWaterSource(x, y) {
        let waterAbove = 0;
        // Проверяем воду сверху (это источник)
        for (let checkY = y - 1; checkY >= Math.max(0, y - 10); checkY--) {
            if (world[checkY] && world[checkY][x] === BLOCKS.WATER) {
                waterAbove++;
            } else {
                break;
            }
        }
        return waterAbove;
    }
    
    // Функция проверки, есть ли рядом источник воды
    function hasWaterSource(x, y) {
        // Проверяем воду сверху
        if (y > 0 && world[y - 1] && world[y - 1][x] === BLOCKS.WATER) {
            return true;
        }
        
        // Проверяем сколько воды рядом по горизонтали в пределах WATER_MAX_SPREAD
        let waterLeft = 0;
        let waterRight = 0;
        
        for (let i = 1; i <= WATER_MAX_SPREAD; i++) {
            if (x - i >= 0 && world[y] && world[y][x - i] === BLOCKS.WATER) {
                // Проверяем есть ли у этой воды источник сверху
                if (y > 0 && world[y - 1] && world[y - 1][x - i] === BLOCKS.WATER) {
                    return true;
                }
                waterLeft++;
            } else {
                break;
            }
        }
        
        for (let i = 1; i <= WATER_MAX_SPREAD; i++) {
            if (x + i < WORLD_WIDTH && world[y] && world[y][x + i] === BLOCKS.WATER) {
                // Проверяем есть ли у этой воды источник сверху
                if (y > 0 && world[y - 1] && world[y - 1][x + i] === BLOCKS.WATER) {
                    return true;
                }
                waterRight++;
            } else {
                break;
            }
        }
        
        return false;
    }
    
    // Функция подсчёта расстояния до ближайшего источника
    function getDistanceToSource(x, y) {
        // Ищем воду с источником сверху
        for (let dist = 0; dist <= WATER_MAX_SPREAD; dist++) {
            // Проверяем слева
            if (x - dist >= 0 && world[y] && world[y][x - dist] === BLOCKS.WATER) {
                if (y > 0 && world[y - 1] && world[y - 1][x - dist] === BLOCKS.WATER) {
                    return dist;
                }
            }
            // Проверяем справа
            if (x + dist < WORLD_WIDTH && world[y] && world[y][x + dist] === BLOCKS.WATER) {
                if (y > 0 && world[y - 1] && world[y - 1][x + dist] === BLOCKS.WATER) {
                    return dist;
                }
            }
        }
        return WATER_MAX_SPREAD + 1;
    }
    
    // Сканируем весь мир
    for (let y = WORLD_HEIGHT - 2; y >= 0; y--) {
        if (!world[y]) continue;
        for (let x = 0; x < WORLD_WIDTH; x++) {
            if (world[y][x] === BLOCKS.WATER) {
                // Течём вниз - всегда
                if (y + 1 < WORLD_HEIGHT && world[y + 1] && world[y + 1][x] === BLOCKS.AIR) {
                    waterUpdates.push({ x, y: y + 1, type: BLOCKS.WATER });
                }
                
                // Течём в стороны только если снизу твёрдый блок
                if (y + 1 < WORLD_HEIGHT && world[y + 1] && world[y + 1][x] !== BLOCKS.AIR && world[y + 1][x] !== BLOCKS.WATER) {
                    // Проверяем расстояние до источника
                    const distToSource = getDistanceToSource(x, y);
                    
                    // Распространяемся только если не достигли максимума
                    if (distToSource < WATER_MAX_SPREAD) {
                        // Влево
                        if (x - 1 >= 0 && world[y][x - 1] === BLOCKS.AIR) {
                            const newDist = getDistanceToSource(x - 1, y);
                            if (newDist <= WATER_MAX_SPREAD) {
                                waterUpdates.push({ x: x - 1, y, type: BLOCKS.WATER });
                            }
                        }
                        // Вправо
                        if (x + 1 < WORLD_WIDTH && world[y][x + 1] === BLOCKS.AIR) {
                            const newDist = getDistanceToSource(x + 1, y);
                            if (newDist <= WATER_MAX_SPREAD) {
                                waterUpdates.push({ x: x + 1, y, type: BLOCKS.WATER });
                            }
                        }
                    }
                }
            }
        }
    }
    
    // Применяем обновления и отправляем клиентам
    const maxUpdates = 50; // Больше обновлений за тик
    for (let i = 0; i < Math.min(waterUpdates.length, maxUpdates); i++) {
        const update = waterUpdates[i];
        if (world[update.y] && world[update.y][update.x] === BLOCKS.AIR) {
            world[update.y][update.x] = update.type;
            // Отправляем обновление всем игрокам в лобби
            io.to(lobbyId).emit('blockUpdated', { x: update.x, y: update.y, type: update.type });
        }
    }
}

// === ГЛАВНЫЙ ИГРОВОЙ ЦИКЛ ===
setInterval(() => {
    try {
        const dt = 33; 
        const now = Date.now();
        
        Object.keys(lobbies).forEach(lobbyId => {
            const lobby = lobbies[lobbyId];
            if (!lobby) return;

            // Счётчик дней
            const prevTimeOfDay = lobby.timeOfDay;
            lobby.timeOfDay += dt / DAY_DURATION;
            if (lobby.timeOfDay >= 1) {
                lobby.timeOfDay = 0;
                lobby.dayCount = (lobby.dayCount || 1) + 1; // Увеличиваем счётчик дней
            }
            if (!lobby.dayCount) lobby.dayCount = 1;

            // --- СПАВН МОБОВ ---
            const isNight = lobby.timeOfDay > 0.40 && lobby.timeOfDay < 0.70;
            const dayCount = lobby.dayCount || 1;
            
            // Днём очень мало мобов (1-2 слизня), ночью больше
            let maxMobs = isNight ? Math.min(3 + dayCount, 12) : 2;
            let spawnChance = isNight ? 0.015 : 0.002;

            const playerIds = Object.keys(lobby.players);
            if (lobby.mobs.length < maxMobs && Math.random() < spawnChance && playerIds.length > 0) {
                // Выбираем случайного игрока для спавна рядом с ним
                const randomPlayer = lobby.players[playerIds[Math.floor(Math.random() * playerIds.length)]];
                
                // Спавн за пределами экрана (500-800 пикселей от игрока)
                const minDistance = 500;
                const maxDistance = 800;
                const spawnDistance = minDistance + Math.random() * (maxDistance - minDistance);
                const spawnDirection = Math.random() > 0.5 ? 1 : -1;
                
                let spawnX = randomPlayer.x + spawnDirection * spawnDistance;
                
                // Ограничиваем границами мира
                spawnX = Math.max(BLOCK_SIZE * 5, Math.min(spawnX, (WORLD_WIDTH - 5) * BLOCK_SIZE));
                
                // Проверяем что спавн далеко от ВСЕХ игроков И от других мобов
                let tooClose = false;
                for (let pid of playerIds) {
                    const p = lobby.players[pid];
                    const dist = Math.abs(p.x - spawnX);
                    if (dist < 450) {
                        tooClose = true;
                        break;
                    }
                }
                
                // Проверяем что не слишком близко к другим мобам (чтобы не сбивались в кучу)
                if (!tooClose) {
                    for (let mob of lobby.mobs) {
                        const dist = Math.abs(mob.x - spawnX);
                        if (dist < 150) {
                            tooClose = true;
                            break;
                        }
                    }
                }
                
                if (tooClose) return; // Не спавним если слишком близко
                
                // Находим высоту спавна НА ЗЕМЛЕ (ищем твёрдый блок, игнорируя деревья)
                let spawnY = 0;
                const bx = Math.floor(spawnX / BLOCK_SIZE);
                let foundGround = false;
                for(let y = 0; y < WORLD_HEIGHT; y++) {
                    const block = lobby.world[y] && lobby.world[y][bx];
                    if (block && !isPassable(block) && block !== BLOCKS.WATER) { 
                        spawnY = (y - 1) * BLOCK_SIZE; // Спавн прямо над землёй
                        foundGround = true;
                        break; 
                    }
                }
                
                if (!foundGround) return; // Не спавним если нет земли
                
                // Определение типа моба с прогрессией по дням
                // День 1-4: только слизни
                // День 5-9: слизни + зомби
                // День 10+: слизни + зомби + скелеты
                // Демоны не спавнятся обычным способом
                let type = 'slime';
                
                if (isNight) {
                    const rand = Math.random();
                    
                    if (dayCount >= 10) {
                        // День 10+: слизни, зомби, скелеты
                        if (rand < 0.35) type = 'zombie';
                        else if (rand < 0.55) type = 'skeleton';
                        else type = 'slime';
                    } else if (dayCount >= 5) {
                        // День 5-9: слизни и зомби
                        if (rand < 0.45) type = 'zombie';
                        else type = 'slime';
                    } else {
                        // День 1-4: только слизни
                        type = 'slime';
                    }
                } else {
                    // Днём только слизни (очень редко)
                    type = 'slime';
                }

                const stats = MOB_STATS[type];
                
                lobby.mobs.push({
                    id: Date.now() + Math.random(),
                    x: spawnX,
                    y: spawnY - stats.height + BLOCK_SIZE, // Корректная позиция на земле
                    vx: 0,
                    vy: 0,
                    width: stats.width,
                    height: stats.height,
                    health: stats.hp,
                    maxHealth: stats.hp,
                    type: type,
                    direction: 1,
                    lastAttack: 0,
                    targetPlayer: null,
                    onGround: true // Спавнится на земле
                });
            }

            // --- ИИ И ФИЗИКА МОБОВ ---
            lobby.mobs.forEach(mob => {
                const stats = MOB_STATS[mob.type] || MOB_STATS.slime;
                
                // Поиск ближайшего игрока
                const nearest = findNearestPlayer(mob, lobby.players);
                
                if (nearest && nearest.distance < stats.aggroRange) {
                    mob.targetPlayer = nearest.id;
                    const player = nearest.player;
                    
                    // Движение к игроку
                    const dx = player.x - mob.x;
                    
                    if (Math.abs(dx) > stats.attackRange) {
                        // Двигаемся к игроку
                        mob.vx = dx > 0 ? stats.speed : -stats.speed;
                        mob.direction = dx > 0 ? 1 : -1;
                    } else {
                        // Близко к игроку - атакуем
                        mob.vx *= 0.8; // Замедляемся
                        
                        // Проверяем кулдаун атаки
                        if (now - mob.lastAttack > stats.attackCooldown) {
                            // Проверяем коллизию для атаки
                            const attackDist = getDistance(player, mob);
                            
                            if (attackDist < stats.attackRange + 20) {
                                mob.lastAttack = now;
                                
                                // Наносим урон игроку
                                player.health = (player.health || 20) - stats.damage;
                                
                                // Отталкиваем игрока
                                const knockbackX = (player.x - mob.x) > 0 ? 8 : -8;
                                const knockbackY = -5;
                                
                                // Отправляем событие урона
                                io.to(nearest.id).emit('playerDamaged', {
                                    health: player.health,
                                    knockbackX: knockbackX,
                                    knockbackY: knockbackY,
                                    attackerId: mob.id,
                                    damage: stats.damage
                                });
                                
                                // Если игрок умер
                                if (player.health <= 0) {
                                    io.to(nearest.id).emit('playerDied');
                                }
                            }
                        }
                    }
                    
                    // Прыжок если игрок выше или есть препятствие
                    if (mob.onGround) {
                        const playerAbove = player.y < mob.y - 32;
                        const obstacleAhead = checkBlockCollision(
                            mob.x + mob.direction * 20, 
                            mob.y - 10, 
                            mob.width, 
                            mob.height - 10, 
                            lobby.world
                        );
                        
                        if (playerAbove || obstacleAhead) {
                            mob.vy = -stats.jumpForce;
                            mob.onGround = false;
                        }
                    }
                } else {
                    // Нет цели - бродим случайно
                    mob.targetPlayer = null;
                    
                    if (Math.random() < 0.02) {
                        mob.vx = (Math.random() - 0.5) * stats.speed * 2;
                        mob.direction = mob.vx > 0 ? 1 : -1;
                    }
                    
                    // Случайные прыжки для слизней
                    if (mob.type === 'slime' && mob.onGround && Math.random() < 0.03) {
                        mob.vy = -stats.jumpForce;
                        mob.onGround = false;
                    }
                }
                
                // Гравитация
                mob.vy += GRAVITY;
                if (mob.vy > 15) mob.vy = 15;
                
                // Применяем скорость
                mob.x += mob.vx;
                mob.y += mob.vy;
                
                // Трение
                mob.vx *= 0.95;

                // Коллизия с землёй (игнорируем фоновые блоки)
                const bx = Math.floor((mob.x + mob.width/2) / BLOCK_SIZE);
                const by = Math.floor((mob.y + mob.height) / BLOCK_SIZE);
                
                mob.onGround = false;
                if (by >= 0 && by < WORLD_HEIGHT && bx >= 0 && bx < WORLD_WIDTH) {
                    const groundBlock = lobby.world[by] && lobby.world[by][bx];
                    if (groundBlock && !isPassable(groundBlock)) {
                        mob.y = by * BLOCK_SIZE - mob.height;
                        mob.vy = 0;
                        mob.onGround = true;
                    }
                }
                
                // Коллизия со стенами (игнорируем фоновые блоки)
                const wallBx = Math.floor((mob.x + mob.width/2 + mob.direction * mob.width/2) / BLOCK_SIZE);
                const wallBy = Math.floor((mob.y + mob.height/2) / BLOCK_SIZE);
                if (wallBy >= 0 && wallBy < WORLD_HEIGHT && wallBx >= 0 && wallBx < WORLD_WIDTH) {
                    const wallBlock = lobby.world[wallBy] && lobby.world[wallBy][wallBx];
                    if (wallBlock && !isPassable(wallBlock)) {
                        mob.vx = 0;
                        mob.x -= mob.direction * 2;
                    }
                }

                // Границы мира
                if (mob.x < 0) { mob.x = 0; mob.vx *= -1; }
                if (mob.x > (WORLD_WIDTH - 1) * BLOCK_SIZE) { mob.x = (WORLD_WIDTH - 1) * BLOCK_SIZE; mob.vx *= -1; }
                if (mob.y > (WORLD_HEIGHT + 10) * BLOCK_SIZE) mob.health = 0;
            });

            // Удаляем мёртвых мобов
            lobby.mobs = lobby.mobs.filter(m => m.health > 0);

            // Обновляем текучесть воды
            updateWaterForLobby(lobby, lobbyId);

            // Отправляем обновление состояния
            io.to(lobbyId).emit('gameStateUpdate', {
                timeOfDay: lobby.timeOfDay,
                dayCount: lobby.dayCount || 1,
                mobs: lobby.mobs.map(m => ({
                    id: m.id,
                    x: m.x,
                    y: m.y,
                    width: m.width,
                    height: m.height,
                    health: m.health,
                    maxHealth: m.maxHealth,
                    type: m.type,
                    direction: m.direction,
                    targetPlayer: m.targetPlayer
                }))
            });
        });
    } catch (err) {
        console.error("Game loop error:", err);
    }
}, 33);

io.on('connection', (socket) => {
    let currentLobby = null;

    socket.on('getLobbies', () => {
        try {
            const savedFiles = fs.readdirSync('./saves').filter(f => f.endsWith('.json')).map(f => f.replace('.json', ''));
            const activeIds = Object.keys(lobbies);
            const allIds = [...new Set([...savedFiles, ...activeIds])];
            socket.emit('lobbyList', allIds.map(id => ({
                id, 
                players: lobbies[id] ? Object.keys(lobbies[id].players).length : 0,
                status: lobbies[id] ? 'Active' : 'Saved'
            })));
        } catch(e) { socket.emit('lobbyList', []); }
    });
    
    // Удаление мира
    socket.on('deleteWorld', (lobbyId) => {
        try {
            // Проверяем, что в лобби никого нет
            if (lobbies[lobbyId] && Object.keys(lobbies[lobbyId].players).length > 0) {
                socket.emit('errorMsg', 'Нельзя удалить мир, в котором есть игроки!');
                return;
            }
            
            // Удаляем из памяти
            if (lobbies[lobbyId]) {
                delete lobbies[lobbyId];
            }
            
            // Удаляем файл сохранения
            const savePath = `./saves/${lobbyId}.json`;
            if (fs.existsSync(savePath)) {
                fs.unlinkSync(savePath);
            }
            
            socket.emit('worldDeleted', lobbyId);
            // Обновляем список лобби для всех
            io.emit('lobbyListUpdated');
        } catch(e) {
            console.error('Error deleting world:', e);
            socket.emit('errorMsg', 'Ошибка при удалении мира');
        }
    });

    socket.on('createLobby', (data) => handleJoin(socket, data));
    socket.on('joinLobby', (data) => handleJoin(socket, data));

    function handleJoin(socket, data) {
        const { lobbyId, nickname } = data;
        let safeNick = (nickname || "Player").trim().substring(0, 15);
        if (safeNick.length === 0) safeNick = "Player";

        if (!lobbies[lobbyId]) {
            const loaded = loadLobby(lobbyId);
            if (loaded) {
                lobbies[lobbyId] = loaded;
            } else {
                lobbies[lobbyId] = {
                    world: generateWorld(),
                    players: {},
                    savedPlayers: {},
                    mobs: [],
                    chests: {},
                    droppedItems: [], // Выпавшие предметы
                    nextDropId: 1,
                    timeOfDay: 0,
                    lastUpdate: Date.now()
                };
            }
        }

        const players = lobbies[lobbyId].players;
        for (let id in players) {
            if (players[id].nickname === safeNick) {
                socket.emit('errorMsg', 'Игрок с таким именем уже есть в лобби!');
                return;
            }
        }
        joinLobby(socket, lobbyId, safeNick);
    }

    function joinLobby(socket, lobbyId, safeNick) {
        currentLobby = lobbyId;
        socket.join(lobbyId);
        
        const spawnX = Math.floor(WORLD_WIDTH / 2);
        let spawnY = 0;
        const world = lobbies[lobbyId].world;
        for (let y = 0; y < WORLD_HEIGHT; y++) {
            if (world[y] && world[y][spawnX] !== BLOCKS.AIR) {
                spawnY = (y - 2) * BLOCK_SIZE;
                break;
            }
        }

        let playerInv = getEmptyInventory();
        let playerHotbar = [0, 0, 0, 0, 0, 0, 0, 0, 0]; // Пустой хотбар
        let playerHealth = 20;

        if (lobbies[lobbyId].savedPlayers[safeNick]) {
            playerInv = lobbies[lobbyId].savedPlayers[safeNick].inventory;
            playerHotbar = lobbies[lobbyId].savedPlayers[safeNick].hotbar;
            playerHealth = lobbies[lobbyId].savedPlayers[safeNick].health || 20;
        }

        lobbies[lobbyId].players[socket.id] = {
            x: spawnX * BLOCK_SIZE,
            y: spawnY,
            width: 24,
            height: 48,
            vx: 0, vy: 0, direction: 1, walkCycle: 0, heldItem: null,
            nickname: safeNick,
            inventory: playerInv,
            hotbarSlots: playerHotbar,
            health: playerHealth,
            maxHealth: 20,
            lastDamageTime: 0,
            team: null // null, 'red', 'blue', 'green', 'yellow'
        };

        socket.emit('gameStart', {
            world: lobbies[lobbyId].world,
            players: lobbies[lobbyId].players,
            selfId: socket.id,
            spawn: { x: spawnX * BLOCK_SIZE, y: spawnY },
            nickname: safeNick,
            inventory: playerInv,
            hotbarSlots: playerHotbar,
            health: playerHealth,
            chests: lobbies[lobbyId].chests || {}
        });
        
        // Отправляем список существующих дропов
        if (lobbies[lobbyId].droppedItems && lobbies[lobbyId].droppedItems.length > 0) {
            socket.emit('droppedItemsSync', lobbies[lobbyId].droppedItems);
        }

        socket.to(lobbyId).emit('playerJoined', {
            id: socket.id,
            ...lobbies[lobbyId].players[socket.id]
        });
    }

    socket.on('playerMove', (data) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const player = lobbies[currentLobby].players[socket.id];
        if (player) {
            player.x = data.x; player.y = data.y; player.vx = data.vx; player.vy = data.vy;
            player.direction = data.direction; player.walkCycle = data.walkCycle; player.heldItem = data.heldItem;
            if (data.team !== undefined) player.team = data.team;
            socket.to(currentLobby).emit('playerMoved', { id: socket.id, x: player.x, y: player.y, direction: player.direction, walkCycle: player.walkCycle, heldItem: player.heldItem, nickname: player.nickname, team: player.team });
        }
    });
    
    // Смена команды
    socket.on('setTeam', (team) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const player = lobbies[currentLobby].players[socket.id];
        if (player) {
            player.team = team;
            // Оповещаем всех игроков о смене команды
            io.to(currentLobby).emit('playerTeamChanged', { id: socket.id, team: team });
        }
    });
    
    // PvP атака
    socket.on('attackPlayer', (data) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const attacker = lobbies[currentLobby].players[socket.id];
        const target = lobbies[currentLobby].players[data.targetId];
        
        if (!attacker || !target) return;
        
        // Проверка команд - нельзя атаковать союзников
        if (attacker.team && attacker.team === target.team) return;
        
        // Проверка кулдауна урона цели
        const now = Date.now();
        if (now - (target.lastDamageTime || 0) < 1000) return;
        target.lastDamageTime = now;
        
        // Наносим урон
        target.health = (target.health || 20) - data.damage;
        if (target.health < 0) target.health = 0;
        
        // Отправляем событие урона цели
        io.to(data.targetId).emit('pvpDamage', {
            damage: data.damage,
            knockbackX: data.knockbackX,
            knockbackY: data.knockbackY,
            attackerName: attacker.nickname,
            attackerId: socket.id
        });
        
        // Если цель убита
        if (target.health <= 0) {
            io.to(currentLobby).emit('chatMessage', { 
                nickname: '⚔️ PvP', 
                text: `${attacker.nickname} убил ${target.nickname}!` 
            });
        }
    });

    socket.on('updateBlock', (data) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const { x, y, type } = data;
        
        // Запрет на размещение воды игроками
        if (type === BLOCKS.WATER) return;
        
        if(y >= 0 && y < WORLD_HEIGHT && x >= 0 && x < WORLD_WIDTH) {
            const world = lobbies[currentLobby].world;
            if (world[y]) {
                const currentBlock = world[y][x];
                
                // Запрет на ломание воды
                if (type === 0 && currentBlock === BLOCKS.WATER) return;
                
                // Если ломаем блок (type === 0)
                if (type === 0) {
                    world[y][x] = type;
                    io.to(currentLobby).emit('blockUpdated', { x, y, type });
                    return;
                }
                
                // Если ставим блок - проверяем что можно ставить на воздух или воду
                if (currentBlock === BLOCKS.AIR || currentBlock === BLOCKS.WATER) {
                    // Проверка что рядом есть блок (не воздух и не вода)
                    let hasNeighbor = false;
                    const neighbors = [[x-1, y], [x+1, y], [x, y-1], [x, y+1]];
                    
                    for (const [nx, ny] of neighbors) {
                        if (ny >= 0 && ny < WORLD_HEIGHT && nx >= 0 && nx < WORLD_WIDTH) {
                            const neighborBlock = world[ny] && world[ny][nx];
                            if (neighborBlock && neighborBlock !== BLOCKS.AIR && neighborBlock !== BLOCKS.WATER) {
                                hasNeighbor = true;
                                break;
                            }
                        }
                    }
                    
                    if (hasNeighbor) {
                        world[y][x] = type;
                        io.to(currentLobby).emit('blockUpdated', { x, y, type });
                    }
                }
            }
        }
    });

    socket.on('damageMob', (mobId) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const lobby = lobbies[currentLobby];
        const mobIndex = lobby.mobs.findIndex(m => m.id === mobId);
        
        if (mobIndex !== -1) {
            const mob = lobby.mobs[mobIndex];
            const player = lobby.players[socket.id];
            
            let damage = 1;
            if(player && player.heldItem) {
                if(player.heldItem === ITEMS.WOODEN_SWORD) damage = 3;
                if(player.heldItem === ITEMS.STONE_SWORD) damage = 5;
                if(player.heldItem === ITEMS.IRON_SWORD) damage = 7;
            }

            mob.health -= damage;
            
            if(player) {
                mob.vx = (mob.x - player.x > 0 ? 1 : -1) * 8;
                mob.vy = -6;
            }

            if (mob.health <= 0) {
                lobby.mobs.splice(mobIndex, 1);
                
                // Дроп с мобов
                if (player) {
                    const mobType = mob.type;
                    // Можно добавить дроп предметов
                }
            }
            
            io.to(currentLobby).emit('gameStateUpdate', { 
                timeOfDay: lobby.timeOfDay, 
                mobs: lobby.mobs.map(m => ({
                    id: m.id, x: m.x, y: m.y, width: m.width, height: m.height,
                    health: m.health, maxHealth: m.maxHealth, type: m.type, direction: m.direction
                }))
            });
        }
    });

    // Синхронизация здоровья игрока
    socket.on('syncHealth', (health) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const player = lobbies[currentLobby].players[socket.id];
        if (player) {
            player.health = health;
        }
    });

    // Респавн игрока
    socket.on('playerRespawn', () => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const player = lobbies[currentLobby].players[socket.id];
        if (player) {
            player.health = player.maxHealth;
            // Можно добавить телепорт на спавн
        }
    });

    socket.on('syncInventory', (data) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const p = lobbies[currentLobby].players[socket.id];
        if (p) {
            p.inventory = data.inventory;
            p.hotbarSlots = data.hotbarSlots;
        }
    });
    
    // Обновление содержимого сундука
    socket.on('updateChest', (data) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const { x, y, items } = data;
        const key = `${x},${y}`;
        
        if (!lobbies[currentLobby].chests) {
            lobbies[currentLobby].chests = {};
        }
        
        lobbies[currentLobby].chests[key] = { items };
        
        // Отправляем обновление всем игрокам в лобби
        socket.to(currentLobby).emit('chestUpdated', { x, y, items });
    });
    
    // Запрос содержимого сундука
    socket.on('getChest', (data) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const { x, y } = data;
        const key = `${x},${y}`;
        
        const chests = lobbies[currentLobby].chests || {};
        const chest = chests[key] || { items: {} };
        
        socket.emit('chestData', { x, y, items: chest.items });
    });
    
    // Удаление сундука (перенос вещей в инвентарь)
    socket.on('breakChest', (data) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const { x, y } = data;
        const key = `${x},${y}`;
        
        const chests = lobbies[currentLobby].chests || {};
        const chest = chests[key];
        
        if (chest && chest.items) {
            const player = lobbies[currentLobby].players[socket.id];
            if (player) {
                // Переносим вещи из сундука в инвентарь игрока
                for (let itemId in chest.items) {
                    const count = chest.items[itemId];
                    if (count > 0) {
                        player.inventory[itemId] = (player.inventory[itemId] || 0) + count;
                    }
                }
                // Отправляем обновлённый инвентарь игроку
                socket.emit('inventoryUpdated', { inventory: player.inventory });
            }
        }
        
        // Удаляем сундук из данных
        delete chests[key];
        
        // Оповещаем всех о разрушении сундука
        io.to(currentLobby).emit('chestRemoved', { x, y });
    });
    
    // === СИНХРОНИЗАЦИЯ ДРОПА ПРЕДМЕТОВ ===
    
    // Когда игрок создаёт дроп (ломает блок, выбрасывает предмет)
    socket.on('itemDropped', (data) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const lobby = lobbies[currentLobby];
        
        const drop = {
            id: lobby.nextDropId++,
            x: data.x,
            y: data.y,
            vx: data.vx || (Math.random() - 0.5) * 4,
            vy: data.vy || -3,
            itemId: data.itemId,
            count: data.count || 1,
            createdBy: socket.id,
            pickupDelay: 500, // 0.5 секунды до подбора
            createdAt: Date.now()
        };
        
        if (!lobby.droppedItems) lobby.droppedItems = [];
        lobby.droppedItems.push(drop);
        
        // Отправляем всем игрокам в лобби (включая отправителя для синхронизации id)
        io.to(currentLobby).emit('itemDroppedSync', drop);
    });
    
    // Когда игрок подбирает дроп
    socket.on('itemPickedUp', (dropId) => {
        if (!currentLobby || !lobbies[currentLobby]) return;
        const lobby = lobbies[currentLobby];
        
        if (!lobby.droppedItems) return;
        
        const index = lobby.droppedItems.findIndex(d => d.id === dropId);
        if (index !== -1) {
            const drop = lobby.droppedItems[index];
            // Проверяем таймер подбора на сервере
            const timePassed = Date.now() - (drop.createdAt || 0);
            if (timePassed < (drop.pickupDelay || 0)) {
                return; // Ещё нельзя подбирать
            }
            
            lobby.droppedItems.splice(index, 1);
            // Оповещаем всех игроков что дроп подобран
            io.to(currentLobby).emit('itemRemovedSync', dropId);
        }
    });

    socket.on('chatMessage', (msg) => {
        if (currentLobby && lobbies[currentLobby].players[socket.id]) {
            const nickname = lobbies[currentLobby].players[socket.id].nickname;
            const safeMsg = msg.replace(/</g, "&lt;").replace(/>/g, "&gt;");
            io.to(currentLobby).emit('chatMessage', { nickname: nickname, text: safeMsg });
        }
    });

    socket.on('pingCheck', () => { socket.emit('pongCheck'); });

    socket.on('disconnect', () => {
        if (currentLobby && lobbies[currentLobby]) {
            const p = lobbies[currentLobby].players[socket.id];
            if(p) {
                lobbies[currentLobby].savedPlayers[p.nickname] = {
                    inventory: p.inventory,
                    hotbar: p.hotbarSlots,
                    health: p.health
                };
                io.to(currentLobby).emit('playerDisconnected', { id: socket.id, nickname: p.nickname });
                delete lobbies[currentLobby].players[socket.id];
                saveLobby(currentLobby);
            }
        }
    });
});

const PORT = process.env.PORT || 3000;
const HOST = '0.0.0.0';

http.listen(PORT, HOST, () => {
    console.log(`Сервер запущен! LAN: http://192.168.18.50:${PORT}`);
});