import * as THREE from 'three';
import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';

export class World {
    constructor(game) {
        this.game = game;
        this.scene = game.scene;
        this.interactables = [];
        this.colliders = [];
        this.sunLight = null;
        this.ambientLight = null;
        // Для миникарты
        this.buildingRects = [];
        // 3D модель машины
        this.carModel = null;
        this.carModelBaseRotation = 0; // корректировка ориентации модели
        this.parkedCars = []; // для замены припаркованных машин
        this.vehicleColliders = []; // динамические коллайдеры машин
        this.mapConfig = null;
    }

    async loadMapConfig() {
        try {
            const resp = await fetch('data/map-config.json');
            this.mapConfig = await resp.json();
        } catch(e) {
            console.warn('Map config not found, using defaults');
            this.mapConfig = null;
        }
    }

    async build() {
        this.interactables = [];
        this.colliders = [];
        this.buildingRects = [];
        this.vehicleColliders = [];
        await this.loadMapConfig();
        this.createGround();
        this.createLighting();
        this.createBuildings();
        this.createPark();
        this.createDumpsters();
        this.createBenches();
        this.createShop();
        this.createShelter();
        this.createStreetLamps();
        this.createTrashPiles();
        this.createFountain();
        this.createPhoneBooth();
        this.createParkingLot();
        this.createBusStop();
        this.createDecorations();
        this.createChurch();
        this.createConstructionSite();
        this.createJobBoard();
        this.createHospital();
        this.createMarket();
        this.createCampSpot();
    }

    createGround() {
        const groundGeo = new THREE.PlaneGeometry(300, 300);
        const groundMat = new THREE.MeshStandardMaterial({ color: 0x555555, roughness: 0.9 });
        const ground = new THREE.Mesh(groundGeo, groundMat);
        ground.rotation.x = -Math.PI / 2;
        ground.receiveShadow = true;
        this.scene.add(ground);

        // === ДОРОЖНАЯ СЕТКА (из конфига) ===
        const roads = this.mapConfig?.roads || [
            { x: 0, z: 0, width: 300, height: 12, rotation: 0, sidewalkWidth: 3 },
            { x: 0, z: 0, width: 12, height: 300, rotation: Math.PI / 2, sidewalkWidth: 3 },
        ];
        for (const road of roads) {
            this.createRoad(road.x, 0.01, road.z, road.width, road.height, road.rotation);
        }

        // === АВТОГЕНЕРАЦИЯ ТРОТУАРОВ И БОРДЮРОВ (с вырезами на перекрёстках) ===
        const sidewalkMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.8 });
        const curbMat = new THREE.MeshStandardMaterial({ color: 0x999999 });

        const ewRoads = roads.filter(r => Math.abs(r.rotation) < 0.1);
        const nsRoads = roads.filter(r => Math.abs(r.rotation) >= 0.1);

        for (const road of roads) {
            const sw = road.sidewalkWidth ?? 3;
            if (sw <= 0) continue;

            const isEW = Math.abs(road.rotation) < 0.1;

            if (isEW) {
                const halfH = road.height / 2;
                const roadStart = road.x - road.width / 2;
                const roadEnd = road.x + road.width / 2;

                // Находим пересечения с N-S дорогами → вырезы по X
                const gaps = [];
                for (const ns of nsRoads) {
                    const nsHalfW = ns.width / 2;
                    const nsSW = ns.sidewalkWidth ?? 3;
                    const nsZMin = ns.z - ns.height / 2;
                    const nsZMax = ns.z + ns.height / 2;
                    if (road.z >= nsZMin && road.z <= nsZMax &&
                        ns.x >= roadStart && ns.x <= roadEnd) {
                        gaps.push({ start: ns.x - nsHalfW - nsSW, end: ns.x + nsHalfW + nsSW });
                    }
                }
                gaps.sort((a, b) => a.start - b.start);

                // Сегменты тротуара между вырезами
                const segments = [];
                let pos = roadStart;
                for (const g of gaps) {
                    if (g.start > pos) segments.push({ start: pos, end: g.start });
                    pos = Math.max(pos, g.end);
                }
                if (pos < roadEnd) segments.push({ start: pos, end: roadEnd });

                for (const seg of segments) {
                    const len = seg.end - seg.start;
                    const cx = (seg.start + seg.end) / 2;

                    const swGeo = new THREE.BoxGeometry(len, 0.15, sw);
                    [1, -1].forEach(side => {
                        const swMesh = new THREE.Mesh(swGeo, sidewalkMat);
                        swMesh.position.set(cx, 0.075, road.z + side * (halfH + sw / 2));
                        swMesh.receiveShadow = true;
                        this.scene.add(swMesh);
                    });

                    const curbGeo = new THREE.BoxGeometry(len, 0.2, 0.15);
                    [halfH, halfH + sw, -halfH, -(halfH + sw)].forEach(offset => {
                        const curb = new THREE.Mesh(curbGeo, curbMat);
                        curb.position.set(cx, 0.1, road.z + offset);
                        this.scene.add(curb);
                    });
                }
            } else {
                const halfW = road.width / 2;
                const roadStart = road.z - road.height / 2;
                const roadEnd = road.z + road.height / 2;

                // Находим пересечения с E-W дорогами → вырезы по Z
                const gaps = [];
                for (const ew of ewRoads) {
                    const ewHalfH = ew.height / 2;
                    const ewSW = ew.sidewalkWidth ?? 3;
                    const ewXMin = ew.x - ew.width / 2;
                    const ewXMax = ew.x + ew.width / 2;
                    if (road.x >= ewXMin && road.x <= ewXMax &&
                        ew.z >= roadStart && ew.z <= roadEnd) {
                        gaps.push({ start: ew.z - ewHalfH - ewSW, end: ew.z + ewHalfH + ewSW });
                    }
                }
                gaps.sort((a, b) => a.start - b.start);

                const segments = [];
                let pos = roadStart;
                for (const g of gaps) {
                    if (g.start > pos) segments.push({ start: pos, end: g.start });
                    pos = Math.max(pos, g.end);
                }
                if (pos < roadEnd) segments.push({ start: pos, end: roadEnd });

                for (const seg of segments) {
                    const len = seg.end - seg.start;
                    const cz = (seg.start + seg.end) / 2;

                    const swGeo = new THREE.BoxGeometry(sw, 0.15, len);
                    [1, -1].forEach(side => {
                        const swMesh = new THREE.Mesh(swGeo, sidewalkMat);
                        swMesh.position.set(road.x + side * (halfW + sw / 2), 0.075, cz);
                        swMesh.receiveShadow = true;
                        this.scene.add(swMesh);
                    });

                    const curbGeo = new THREE.BoxGeometry(0.15, 0.2, len);
                    [halfW, halfW + sw, -halfW, -(halfW + sw)].forEach(offset => {
                        const curb = new THREE.Mesh(curbGeo, curbMat);
                        curb.position.set(road.x + offset, 0.1, cz);
                        this.scene.add(curb);
                    });
                }
            }
        }
    }

    createRoad(x, y, z, w, h, rot) {
        const roadGeo = new THREE.PlaneGeometry(w, h);
        const roadMat = new THREE.MeshStandardMaterial({ color: 0x2a2a2a, roughness: 0.85 });
        const road = new THREE.Mesh(roadGeo, roadMat);
        road.rotation.x = -Math.PI / 2;
        road.rotation.z = rot;
        road.position.set(x, y, z);
        road.receiveShadow = true;
        this.scene.add(road);

        // Пунктирная разметка по центру дороги
        const lineW = 1.5;
        const lineGap = 3;
        const roadLen = rot === 0 ? w : h;
        const step = lineW + lineGap;
        const count = Math.floor(roadLen / step);
        const totalLen = count * step;
        const lineMat = new THREE.MeshStandardMaterial({ color: 0xffffcc });

        for (let i = 0; i < count; i++) {
            const lineGeo = new THREE.PlaneGeometry(lineW, 0.15);
            const line = new THREE.Mesh(lineGeo, lineMat);
            line.rotation.x = -Math.PI / 2;
            line.rotation.z = rot;
            const offset = -totalLen / 2 + i * step + lineW / 2;
            if (rot === 0) {
                line.position.set(x + offset, y + 0.01, z);
            } else {
                line.position.set(x, y + 0.01, z + offset);
            }
            this.scene.add(line);
        }
    }

    createLighting() {
        this.ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
        this.scene.add(this.ambientLight);

        this.sunLight = new THREE.DirectionalLight(0xffffff, 1.0);
        this.sunLight.position.set(50, 80, 30);
        this.sunLight.castShadow = true;
        this.sunLight.shadow.mapSize.set(2048, 2048);
        this.sunLight.shadow.camera.left = -120;
        this.sunLight.shadow.camera.right = 120;
        this.sunLight.shadow.camera.top = 120;
        this.sunLight.shadow.camera.bottom = -120;
        this.sunLight.shadow.camera.near = 1;
        this.sunLight.shadow.camera.far = 350;
        this.scene.add(this.sunLight);

        const hemiLight = new THREE.HemisphereLight(0x87CEEB, 0x444444, 0.3);
        this.scene.add(hemiLight);
    }

    updateLighting(gameTime) {
        const hour = gameTime / 60;
        let sunIntensity, ambientIntensity, exposure;
        let skyColor;

        if (hour >= 5 && hour < 7) {
            const t = (hour - 5) / 2;
            sunIntensity = THREE.MathUtils.lerp(0.05, 0.8, t);
            ambientIntensity = THREE.MathUtils.lerp(0.08, 0.35, t);
            exposure = THREE.MathUtils.lerp(0.25, 0.9, t);
            skyColor = new THREE.Color().lerpColors(new THREE.Color(0x1a1a3e), new THREE.Color(0xff9966), t);
        } else if (hour >= 7 && hour < 9) {
            const t = (hour - 7) / 2;
            sunIntensity = THREE.MathUtils.lerp(0.8, 1.0, t);
            ambientIntensity = THREE.MathUtils.lerp(0.35, 0.4, t);
            exposure = THREE.MathUtils.lerp(0.9, 1.0, t);
            skyColor = new THREE.Color().lerpColors(new THREE.Color(0xff9966), new THREE.Color(0x87CEEB), t);
        } else if (hour >= 9 && hour < 17) {
            sunIntensity = 1.0;
            ambientIntensity = 0.4;
            exposure = 1.0;
            skyColor = new THREE.Color(0x87CEEB);
        } else if (hour >= 17 && hour < 19) {
            const t = (hour - 17) / 2;
            sunIntensity = THREE.MathUtils.lerp(1.0, 0.5, t);
            ambientIntensity = THREE.MathUtils.lerp(0.4, 0.2, t);
            exposure = THREE.MathUtils.lerp(1.0, 0.6, t);
            skyColor = new THREE.Color().lerpColors(new THREE.Color(0x87CEEB), new THREE.Color(0xff6633), t);
        } else if (hour >= 19 && hour < 21) {
            const t = (hour - 19) / 2;
            sunIntensity = THREE.MathUtils.lerp(0.5, 0.05, t);
            ambientIntensity = THREE.MathUtils.lerp(0.2, 0.08, t);
            exposure = THREE.MathUtils.lerp(0.6, 0.25, t);
            skyColor = new THREE.Color().lerpColors(new THREE.Color(0xff6633), new THREE.Color(0x0a0a1e), t);
        } else {
            sunIntensity = 0.05;
            ambientIntensity = 0.08;
            exposure = 0.25;
            skyColor = new THREE.Color(0x0a0a1e);
        }

        this.sunLight.intensity = sunIntensity;
        this.ambientLight.intensity = ambientIntensity;
        this.game.renderer.toneMappingExposure = exposure;
        this.scene.background = skyColor;
        if (this.scene.fog) this.scene.fog.color = skyColor;

        const sunAngle = ((hour - 6) / 12) * Math.PI;
        this.sunLight.position.set(Math.cos(sunAngle) * 60, Math.sin(sunAngle) * 80, 30);
    }

    createBuildings() {
        const configs = (this.mapConfig?.buildings || [
            { x: -45, z: -22, w: 14, h: 20, d: 10, color: 0x8b7355 },
            { x: -30, z: -22, w: 10, h: 15, d: 10, color: 0x696969 },
            { x: -15, z: -22, w: 12, h: 18, d: 10, color: 0x7b6b55 },
            { x: 15, z: -22, w: 12, h: 22, d: 10, color: 0x5b5b6b },
            { x: 30, z: -22, w: 14, h: 16, d: 10, color: 0x6b6b55 },
            { x: 40, z: -22, w: 10, h: 20, d: 10, color: 0x556b6b },
            { x: -40, z: -55, w: 16, h: 28, d: 12, color: 0x556b7b },
            { x: -20, z: -55, w: 12, h: 18, d: 10, color: 0x6b6b5b },
            { x: 20, z: -55, w: 10, h: 14, d: 10, color: 0x6b7b5b },
            { x: 38, z: -55, w: 14, h: 20, d: 12, color: 0x7b5b6b },
            { x: -45, z: 25, w: 16, h: 16, d: 10, color: 0x7b5b5b },
            { x: -30, z: 25, w: 10, h: 12, d: 10, color: 0x5b7b6b },
            { x: 15, z: 25, w: 12, h: 22, d: 10, color: 0x5b6b7b },
            { x: 30, z: 25, w: 14, h: 18, d: 10, color: 0x6b5b7b },
            { x: 42, z: 25, w: 10, h: 14, d: 10, color: 0x7b7b5b },
            { x: -45, z: 65, w: 14, h: 14, d: 10, color: 0x6b5b5b },
            { x: 15, z: 65, w: 12, h: 16, d: 10, color: 0x5b6b5b },
        ]).map(b => ({
            x: b.x, z: b.z, w: b.w, h: b.h, d: b.d,
            color: typeof b.color === 'string' ? parseInt(b.color.replace('#',''), 16) : b.color
        }));

        configs.forEach(cfg => {
            this.createBuilding(cfg.x, cfg.z, cfg.w, cfg.h, cfg.d, cfg.color);
            this.buildingRects.push({ x: cfg.x, z: cfg.z, w: cfg.w, d: cfg.d });
        });
    }

    createBuilding(x, z, w, h, d, color) {
        const group = new THREE.Group();

        const bodyGeo = new THREE.BoxGeometry(w, h, d);
        const bodyMat = new THREE.MeshStandardMaterial({ color, roughness: 0.8 });
        const body = new THREE.Mesh(bodyGeo, bodyMat);
        body.position.y = h / 2;
        body.castShadow = true;
        body.receiveShadow = true;
        group.add(body);

        // Окна (с рандомным свечением — кто-то дома, кто-то нет)
        const winW = 1.2, winH = 1.8;
        const floors = Math.floor(h / 4);
        const cols = Math.floor(w / 3.5);

        for (let f = 0; f < floors; f++) {
            for (let c = 0; c < cols; c++) {
                const lit = Math.random() > 0.4;
                const winMat = new THREE.MeshStandardMaterial({
                    color: lit ? 0xffeebb : 0x556677,
                    emissive: lit ? 0x554422 : 0x112233,
                    emissiveIntensity: lit ? 0.4 : 0.1,
                    roughness: 0.1,
                    metalness: 0.8
                });

                const winGeo = new THREE.PlaneGeometry(winW, winH);
                const wx = -w / 2 + 2 + c * 3.5;
                const wy = 3 + f * 4;

                const win1 = new THREE.Mesh(winGeo, winMat);
                win1.position.set(wx, wy, d / 2 + 0.01);
                group.add(win1);

                const win2 = new THREE.Mesh(winGeo, winMat);
                win2.position.set(wx, wy, -d / 2 - 0.01);
                win2.rotation.y = Math.PI;
                group.add(win2);
            }
        }

        // Крыша
        const roofGeo = new THREE.BoxGeometry(w + 0.5, 0.5, d + 0.5);
        const roofMat = new THREE.MeshStandardMaterial({ color: 0x3a3a3a });
        const roof = new THREE.Mesh(roofGeo, roofMat);
        roof.position.y = h;
        roof.castShadow = true;
        group.add(roof);

        // Дверь (на одном случайном фасаде)
        const doorGeo = new THREE.BoxGeometry(1.5, 2.5, 0.1);
        const doorMat = new THREE.MeshStandardMaterial({ color: 0x5c3a1e });
        const door = new THREE.Mesh(doorGeo, doorMat);
        door.position.set(0, 1.25, d / 2 + 0.06);
        group.add(door);

        group.position.set(x, 0, z);
        this.scene.add(group);

        this.colliders.push(new THREE.Box3(
            new THREE.Vector3(x - w / 2, 0, z - d / 2),
            new THREE.Vector3(x + w / 2, h, z + d / 2)
        ));

    }

    createPark() {
        const parkCfg = this.mapConfig?.structures?.park || { x: -30, z: 25, radius: 18 };
        const parkX = parkCfg.x;
        const parkZ = parkCfg.z;
        const parkRadius = parkCfg.radius;

        const parkGeo = new THREE.CircleGeometry(parkRadius, 32);
        const parkMat = new THREE.MeshStandardMaterial({ color: 0x3a7d3a, roughness: 0.9 });
        const park = new THREE.Mesh(parkGeo, parkMat);
        park.rotation.x = -Math.PI / 2;
        park.position.set(parkX, 0.02, parkZ);
        park.receiveShadow = true;
        this.scene.add(park);

        // Деревья (больше)
        const treePositions = [
            [parkX + 8, parkZ - 5], [parkX - 8, parkZ - 5], [parkX, parkZ + 10], [parkX + 10, parkZ + 7],
            [parkX - 10, parkZ + 5], [parkX + 5, parkZ - 7], [parkX - 5, parkZ + 13], [parkX - 12, parkZ - 3],
            [parkX + 12, parkZ + 3], [parkX - 8, parkZ - 10],
        ];
        treePositions.forEach(([tx, tz]) => this.createTree(tx, tz));

        // Кусты
        const bushPositions = [
            [parkX + 5, parkZ - 5], [parkX - 5, parkZ - 1], [parkX - 10, parkZ + 7], [parkX + 10, parkZ + 10],
            [parkX + 12, parkZ - 1], [parkX - 2, parkZ + 13],
        ];
        bushPositions.forEach(([bx, bz]) => this.createBush(bx, bz));

        // Дорожки парка
        const pathMat = new THREE.MeshStandardMaterial({ color: 0x8b7355, roughness: 0.9 });

        const path1 = new THREE.Mesh(new THREE.PlaneGeometry(2, 30), pathMat);
        path1.rotation.x = -Math.PI / 2;
        path1.position.set(parkX, 0.03, parkZ);
        this.scene.add(path1);

        const path2 = new THREE.Mesh(new THREE.PlaneGeometry(20, 2), pathMat);
        path2.rotation.x = -Math.PI / 2;
        path2.position.set(parkX, 0.03, parkZ);
        this.scene.add(path2);

        // Клумба в центре
        const flowerGeo = new THREE.CircleGeometry(3, 16);
        const flowerMat = new THREE.MeshStandardMaterial({ color: 0x228822 });
        const flowerBed = new THREE.Mesh(flowerGeo, flowerMat);
        flowerBed.rotation.x = -Math.PI / 2;
        flowerBed.position.set(parkX, 0.04, parkZ);
        this.scene.add(flowerBed);

        // Цветы
        const colors = [0xff4444, 0xffff44, 0xff88ff, 0xffaa22];
        for (let i = 0; i < 12; i++) {
            const angle = (i / 12) * Math.PI * 2;
            const r = 1.5 + Math.random();
            const fGeo = new THREE.SphereGeometry(0.15, 6, 4);
            const fMat = new THREE.MeshStandardMaterial({
                color: colors[Math.floor(Math.random() * colors.length)],
                emissive: 0x222200,
                emissiveIntensity: 0.1
            });
            const flower = new THREE.Mesh(fGeo, fMat);
            flower.position.set(
                parkX + Math.cos(angle) * r,
                0.15,
                parkZ + Math.sin(angle) * r
            );
            this.scene.add(flower);
        }
    }

    createTree(x, z) {
        const group = new THREE.Group();
        const h = 2.5 + Math.random() * 1.5;
        const crownR = 2 + Math.random() * 1;

        const trunkGeo = new THREE.CylinderGeometry(0.25, 0.45, h, 8);
        const trunkMat = new THREE.MeshStandardMaterial({ color: 0x5c3a1e });
        const trunk = new THREE.Mesh(trunkGeo, trunkMat);
        trunk.position.y = h / 2;
        trunk.castShadow = true;
        group.add(trunk);

        const crownGeo = new THREE.SphereGeometry(crownR, 8, 6);
        const shade = 0x1d5b1d + Math.floor(Math.random() * 0x102010);
        const crownMat = new THREE.MeshStandardMaterial({ color: shade, roughness: 0.9 });
        const crown = new THREE.Mesh(crownGeo, crownMat);
        crown.position.y = h + crownR * 0.6;
        crown.castShadow = true;
        group.add(crown);

        group.position.set(x, 0, z);
        this.scene.add(group);

        this.colliders.push(new THREE.Box3(
            new THREE.Vector3(x - 0.5, 0, z - 0.5),
            new THREE.Vector3(x + 0.5, 6, z + 0.5)
        ));
    }

    createBush(x, z) {
        const bushGeo = new THREE.SphereGeometry(0.8 + Math.random() * 0.4, 6, 5);
        const bushMat = new THREE.MeshStandardMaterial({ color: 0x2a6b2a, roughness: 0.95 });
        const bush = new THREE.Mesh(bushGeo, bushMat);
        bush.position.set(x, 0.5, z);
        bush.scale.y = 0.7;
        bush.castShadow = true;
        this.scene.add(bush);
    }

    createDumpsters() {
        const positions = this.mapConfig?.interactables?.dumpsters || [
            { x: -20, z: -15, rot: 0 },
            { x: 25, z: -15, rot: 0.3 },
            { x: -45, z: 12, rot: -0.2 },
            { x: 35, z: 12, rot: 0.5 },
            { x: -25, z: 35, rot: 0.1 },
            { x: 20, z: -48, rot: 0 },
            { x: -40, z: -48, rot: 0.4 },
            { x: 25, z: 35, rot: -0.3 },
        ];

        positions.forEach(pos => {
            const dumpster = this.createDumpster(pos.x, pos.z, pos.rot);
            this.interactables.push({
                type: 'dumpster',
                mesh: dumpster,
                position: new THREE.Vector3(pos.x, 0, pos.z),
                radius: 3,
                label: 'Обыскать мусорку',
                searched: false,
                searchCooldown: 0
            });
        });
    }

    createDumpster(x, z, rot) {
        const group = new THREE.Group();
        const bodyMat = new THREE.MeshStandardMaterial({ color: 0x2e5b2e, roughness: 0.7 });

        const body = new THREE.Mesh(new THREE.BoxGeometry(2, 1.4, 1.2), bodyMat);
        body.position.y = 0.7;
        body.castShadow = true;
        group.add(body);

        const lid = new THREE.Mesh(new THREE.BoxGeometry(2.1, 0.1, 1.3), bodyMat);
        lid.position.set(0, 1.45, 0);
        group.add(lid);

        // Немного мусора вокруг
        const trashMat = new THREE.MeshStandardMaterial({ color: 0x555544 });
        for (let i = 0; i < 3; i++) {
            const t = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.1, 0.15), trashMat);
            t.position.set((Math.random() - 0.5) * 2.5, 0.05, (Math.random() - 0.5) * 2);
            t.rotation.y = Math.random() * Math.PI;
            group.add(t);
        }

        group.position.set(x, 0, z);
        group.rotation.y = rot;
        this.scene.add(group);

        this.colliders.push(new THREE.Box3(
            new THREE.Vector3(x - 1, 0, z - 0.6),
            new THREE.Vector3(x + 1, 1.5, z + 0.6)
        ));

        return group;
    }

    createBenches() {
        const positions = this.mapConfig?.interactables?.benches || [
            { x: -30, z: 20, rot: 0 },
            { x: -25, z: 32, rot: Math.PI },
            { x: 10, z: 12, rot: Math.PI },
            { x: -15, z: -12, rot: 0 },
            { x: 35, z: 12, rot: Math.PI },
            { x: -35, z: 35, rot: Math.PI },
        ];

        positions.forEach(pos => {
            const bench = this.createBench(pos.x, pos.z, pos.rot);
            this.interactables.push({
                type: 'bench',
                mesh: bench,
                position: new THREE.Vector3(pos.x, 0, pos.z),
                radius: 2.5,
                label: 'Отдохнуть на скамейке'
            });
        });
    }

    createBench(x, z, rot) {
        const group = new THREE.Group();
        const woodMat = new THREE.MeshStandardMaterial({ color: 0x8b6914, roughness: 0.85 });
        const metalMat = new THREE.MeshStandardMaterial({ color: 0x444444, metalness: 0.6 });

        const seat = new THREE.Mesh(new THREE.BoxGeometry(2, 0.1, 0.6), woodMat);
        seat.position.y = 0.5;
        seat.castShadow = true;
        group.add(seat);

        const back = new THREE.Mesh(new THREE.BoxGeometry(2, 0.6, 0.08), woodMat);
        back.position.set(0, 0.85, -0.28);
        back.rotation.x = -0.15;
        group.add(back);

        [-0.8, 0.8].forEach(lx => {
            const leg = new THREE.Mesh(new THREE.BoxGeometry(0.08, 0.5, 0.6), metalMat);
            leg.position.set(lx, 0.25, 0);
            group.add(leg);
        });

        group.position.set(x, 0, z);
        group.rotation.y = rot;
        this.scene.add(group);
        return group;
    }

    createShop() {
        const group = new THREE.Group();
        const shopCfg = this.mapConfig?.structures?.shop || {};
        const shopX = shopCfg.x ?? -25, shopZ = shopCfg.z ?? -12;
        const sw = shopCfg.w ?? 10, sh = shopCfg.h ?? 5, sd = shopCfg.d ?? 8;
        const hw = sw / 2, hd = sd / 2;

        const bodyMat = new THREE.MeshStandardMaterial({ color: 0xaa8855, roughness: 0.7 });
        const body = new THREE.Mesh(new THREE.BoxGeometry(sw, sh, sd), bodyMat);
        body.position.y = sh / 2;
        body.castShadow = true;
        body.receiveShadow = true;
        group.add(body);

        // Вывеска с подсветкой
        const signMat = new THREE.MeshStandardMaterial({
            color: 0x2244aa,
            emissive: 0x2244aa,
            emissiveIntensity: 0.6
        });
        const sign = new THREE.Mesh(new THREE.BoxGeometry(sw * 0.6, 1.2, 0.2), signMat);
        sign.position.set(0, sh - 0.2, hd + 0.1);
        group.add(sign);

        // Свет вывески
        const signLight = new THREE.PointLight(0x4466ff, 0.5, 8);
        signLight.position.set(0, sh - 0.5, hd + 1);
        group.add(signLight);

        const door = new THREE.Mesh(
            new THREE.BoxGeometry(1.8, 2.8, 0.15),
            new THREE.MeshStandardMaterial({ color: 0x6b4226 })
        );
        door.position.set(0, 1.4, hd + 0.05);
        group.add(door);

        // Витрины
        const winMat = new THREE.MeshStandardMaterial({
            color: 0x88ccff, transparent: true, opacity: 0.5, roughness: 0.1
        });
        [-hw * 0.7, hw * 0.7].forEach(wx => {
            const win = new THREE.Mesh(new THREE.PlaneGeometry(2.5, 2), winMat);
            win.position.set(wx, sh / 2, hd + 0.01);
            group.add(win);
        });

        // Навес
        const awningMat = new THREE.MeshStandardMaterial({ color: 0xcc3333 });
        const awning = new THREE.Mesh(new THREE.BoxGeometry(sw + 0.5, 0.1, 2), awningMat);
        awning.position.set(0, sh - 1, hd + 1);
        group.add(awning);

        group.position.set(shopX, 0, shopZ);
        this.scene.add(group);

        this.colliders.push(new THREE.Box3(
            new THREE.Vector3(shopX - hw, 0, shopZ - hd),
            new THREE.Vector3(shopX + hw, sh, shopZ + hd)
        ));

        this.buildingRects.push({ x: shopX, z: shopZ, w: sw, d: sd });

        this.interactables.push({
            type: 'shop',
            mesh: group,
            position: new THREE.Vector3(shopX, 0, shopZ + hd + 1),
            radius: 3,
            label: 'Войти в магазин'
        });
    }

    createShelter() {
        const group = new THREE.Group();
        const shelterCfg = this.mapConfig?.structures?.shelter || {};
        const sx = shelterCfg.x ?? -35, sz = shelterCfg.z ?? 35;
        const sw = shelterCfg.w ?? 8, sd = shelterCfg.d ?? 6;
        const hw = sw / 2, hd = sd / 2;
        const wallH = 3.5;

        const wallMat = new THREE.MeshStandardMaterial({ color: 0x6b5b4b, roughness: 0.9 });

        const backWall = new THREE.Mesh(new THREE.BoxGeometry(sw, wallH, 0.3), wallMat);
        backWall.position.set(0, wallH / 2, -hd);
        backWall.castShadow = true;
        group.add(backWall);

        [-hw, hw].forEach(sideX => {
            const sideWall = new THREE.Mesh(new THREE.BoxGeometry(0.3, wallH, sd), wallMat);
            sideWall.position.set(sideX, wallH / 2, 0);
            sideWall.castShadow = true;
            group.add(sideWall);
        });

        const roof = new THREE.Mesh(new THREE.BoxGeometry(sw + 1, 0.2, sd + 1), wallMat);
        roof.position.y = wallH + 0.1;
        roof.castShadow = true;
        group.add(roof);

        // Матрас + подушка
        const mat = new THREE.Mesh(
            new THREE.BoxGeometry(2, 0.2, 3),
            new THREE.MeshStandardMaterial({ color: 0x555577 })
        );
        mat.position.set(-1, 0.1, -0.5);
        group.add(mat);

        const pillow = new THREE.Mesh(
            new THREE.BoxGeometry(0.6, 0.15, 0.4),
            new THREE.MeshStandardMaterial({ color: 0x666688 })
        );
        pillow.position.set(-1, 0.25, -1.8);
        group.add(pillow);

        // Коробки
        const boxMat = new THREE.MeshStandardMaterial({ color: 0x8b6914 });
        const box1 = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.6, 0.6), boxMat);
        box1.position.set(hw - 1, 0.3, -2);
        group.add(box1);

        const box2 = new THREE.Mesh(new THREE.BoxGeometry(0.6, 0.4, 0.5), boxMat);
        box2.position.set(hw - 0.7, 0.2, -1.2);
        group.add(box2);

        // Костёр + камни
        const fireGeo = new THREE.ConeGeometry(0.3, 0.6, 6);
        const fireMat = new THREE.MeshStandardMaterial({
            color: 0xff6600,
            emissive: 0xff4400,
            emissiveIntensity: 1.5
        });
        const fire = new THREE.Mesh(fireGeo, fireMat);
        fire.position.set(hw - 2, 0.3, 0);
        group.add(fire);

        // Камни вокруг костра
        const stoneMat = new THREE.MeshStandardMaterial({ color: 0x666666 });
        for (let i = 0; i < 6; i++) {
            const angle = (i / 6) * Math.PI * 2;
            const stone = new THREE.Mesh(
                new THREE.SphereGeometry(0.15, 5, 4),
                stoneMat
            );
            stone.position.set(hw - 2 + Math.cos(angle) * 0.5, 0.1, Math.sin(angle) * 0.5);
            stone.scale.y = 0.6;
            group.add(stone);
        }

        // Свет от костра
        const fireLight = new THREE.PointLight(0xff6600, 2, 12);
        fireLight.position.set(hw - 2, 1, 0);
        fireLight.castShadow = true;
        group.add(fireLight);

        group.position.set(sx, 0, sz);
        group.rotation.y = Math.PI; // открыто на юг, к парку
        this.scene.add(group);

        this.colliders.push(new THREE.Box3(
            new THREE.Vector3(sx - hw - 0.2, 0, sz - hd - 0.2),
            new THREE.Vector3(sx + hw + 0.2, wallH + 0.1, sz + hd + 0.2)
        ));

        this.interactables.push({
            type: 'shelter',
            mesh: group,
            position: new THREE.Vector3(sx, 0, sz - 1),
            radius: 5,
            label: 'Поспать в укрытии'
        });

        // Костёр: локальный (hw-2, 0, 0), группа повёрнута на PI → мировая позиция (sx - (hw-2), 0, sz)
        this.interactables.push({
            type: 'campfire',
            mesh: fire,
            position: new THREE.Vector3(sx - (hw - 2), 0, sz),
            radius: 3,
            label: 'Погреться у костра'
        });
    }

    createTrashPiles() {
        const positions = this.mapConfig?.interactables?.trashPiles || [
            { x: -40, z: 12 },
            { x: 35, z: -15 },
            { x: -10, z: -38 },
            { x: 25, z: 38 },
        ];

        positions.forEach(pos => {
            const group = new THREE.Group();
            const mats = [0x555544, 0x444433, 0x554444, 0x445544];

            for (let i = 0; i < 8; i++) {
                const size = 0.2 + Math.random() * 0.4;
                const geo = new THREE.BoxGeometry(size, size * 0.5, size);
                const mat = new THREE.MeshStandardMaterial({
                    color: mats[Math.floor(Math.random() * mats.length)]
                });
                const piece = new THREE.Mesh(geo, mat);
                piece.position.set(
                    (Math.random() - 0.5) * 1.5,
                    size * 0.25,
                    (Math.random() - 0.5) * 1.5
                );
                piece.rotation.y = Math.random() * Math.PI;
                group.add(piece);
            }

            group.position.set(pos.x, 0, pos.z);
            this.scene.add(group);

            this.interactables.push({
                type: 'trashpile',
                mesh: group,
                position: new THREE.Vector3(pos.x, 0, pos.z),
                radius: 2.5,
                label: 'Порыться в мусоре',
                searchCooldown: 0
            });
        });
    }

    createFountain() {
        const group = new THREE.Group();
        const cfg = this.mapConfig?.structures?.fountain || {};
        const fx = cfg.x ?? -30, fz = cfg.z ?? 25;

        // База
        const baseMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.7 });
        const base = new THREE.Mesh(new THREE.CylinderGeometry(1.5, 1.8, 0.5, 16), baseMat);
        base.position.y = 0.25;
        group.add(base);

        // Колонна
        const column = new THREE.Mesh(new THREE.CylinderGeometry(0.2, 0.25, 1.2, 8), baseMat);
        column.position.y = 1.1;
        group.add(column);

        // Верхняя чаша
        const bowlGeo = new THREE.SphereGeometry(0.5, 12, 6, 0, Math.PI * 2, 0, Math.PI / 2);
        const bowl = new THREE.Mesh(bowlGeo, baseMat);
        bowl.position.y = 1.7;
        bowl.rotation.x = Math.PI;
        group.add(bowl);

        // Вода
        const waterMat = new THREE.MeshStandardMaterial({
            color: 0x3388bb,
            transparent: true,
            opacity: 0.6,
            roughness: 0.1,
            metalness: 0.3
        });
        const water = new THREE.Mesh(new THREE.CircleGeometry(1.3, 16), waterMat);
        water.rotation.x = -Math.PI / 2;
        water.position.y = 0.48;
        group.add(water);

        group.position.set(fx, 0, fz);
        this.scene.add(group);

        this.interactables.push({
            type: 'fountain',
            mesh: group,
            position: new THREE.Vector3(fx, 0, fz),
            radius: 2.5,
            label: 'Попить воды'
        });
    }

    createPhoneBooth() {
        const group = new THREE.Group();
        const cfg = this.mapConfig?.structures?.phoneBooth || {};
        const px = cfg.x ?? 10, pz = cfg.z ?? -8;
        const pw = cfg.w ?? 1.2, pd = cfg.d ?? 1.2;
        const hw = pw / 2, hd = pd / 2;
        const boothH = 2.5;

        const boothMat = new THREE.MeshStandardMaterial({ color: 0x2244aa });
        const booth = new THREE.Mesh(new THREE.BoxGeometry(pw, boothH, pd), boothMat);
        booth.position.y = boothH / 2;
        booth.castShadow = true;
        group.add(booth);

        const phoneMat = new THREE.MeshStandardMaterial({ color: 0x333333 });
        const phone = new THREE.Mesh(new THREE.BoxGeometry(0.2, 0.3, 0.1), phoneMat);
        phone.position.set(0, 1.5, hd - 0.04);
        group.add(phone);

        group.position.set(px, 0, pz);
        this.scene.add(group);

        this.colliders.push(new THREE.Box3(
            new THREE.Vector3(px - hw, 0, pz - hd),
            new THREE.Vector3(px + hw, boothH, pz + hd)
        ));

        this.interactables.push({
            type: 'phone',
            mesh: group,
            position: new THREE.Vector3(px, 0, pz + 1),
            radius: 2,
            label: 'Таксофон'
        });
    }

    createParkingLot() {
        const cfg = this.mapConfig?.structures?.parking || {};
        const lotX = cfg.x ?? 35, lotZ = cfg.z ?? 12;
        const lotW = cfg.w ?? 20, lotD = cfg.d ?? 15;
        const lot = new THREE.Mesh(
            new THREE.PlaneGeometry(lotW, lotD),
            new THREE.MeshStandardMaterial({ color: 0x2a2a2a })
        );
        lot.rotation.x = -Math.PI / 2;
        lot.position.set(lotX, 0.01, lotZ);
        lot.receiveShadow = true;
        this.scene.add(lot);

        // Разметка парковки (относительно центра)
        const lineMat = new THREE.MeshStandardMaterial({ color: 0xeeeeee });
        const slotW = 4;
        const lineCount = Math.floor(lotW / slotW) + 1;
        for (let i = 0; i < lineCount; i++) {
            const line = new THREE.Mesh(new THREE.PlaneGeometry(0.1, lotD * 0.35), lineMat);
            line.rotation.x = -Math.PI / 2;
            line.position.set(lotX - lotW / 2 + i * slotW, 0.02, lotZ);
            this.scene.add(line);
        }

        // Машины на парковке (в центрах слотов)
        const carConfigs = [
            { x: lotX - lotW / 2 + slotW / 2, z: lotZ, color: 0xcc2222 },
            { x: lotX, z: lotZ, color: 0x2255cc },
            { x: lotX + lotW / 2 - slotW / 2, z: lotZ, color: 0x888888 },
        ];

        carConfigs.forEach(cfg => {
            const car = this.carModel
                ? this.carModel.clone()
                : this.createCarMeshFallback(cfg.color);

            car.position.set(cfg.x, 0, cfg.z);
            if (this.carModel) car.rotation.y = this.carModelBaseRotation;
            this.scene.add(car);

            this.parkedCars.push({ mesh: car, x: cfg.x, z: cfg.z, rotY: 0 });

            this.colliders.push(new THREE.Box3(
                new THREE.Vector3(cfg.x - 1, 0, cfg.z - 2),
                new THREE.Vector3(cfg.x + 1, 2, cfg.z + 2)
            ));
        });
    }

    createBusStop() {
        const group = new THREE.Group();
        const cfg = this.mapConfig?.structures?.busStop || {};
        const bx = cfg.x ?? -20, bz = cfg.z ?? 7;
        const bw = cfg.w ?? 5, bd = cfg.d ?? 2;
        const bhw = bw / 2, bhd = bd / 2;

        // Крыша
        const roofMat = new THREE.MeshStandardMaterial({ color: 0x336699, transparent: true, opacity: 0.7 });
        const roof = new THREE.Mesh(new THREE.BoxGeometry(bw, 0.1, bd), roofMat);
        roof.position.y = 2.8;
        group.add(roof);

        // Столбы
        const poleMat = new THREE.MeshStandardMaterial({ color: 0x888888, metalness: 0.5 });
        [-(bhw - 0.2), bhw - 0.2].forEach(px => {
            const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.06, 0.06, 2.8, 6), poleMat);
            pole.position.set(px, 1.4, -(bhd - 0.1));
            group.add(pole);
        });

        // Задняя стенка
        const backMat = new THREE.MeshStandardMaterial({ color: 0x336699, transparent: true, opacity: 0.4 });
        const back = new THREE.Mesh(new THREE.BoxGeometry(bw, 2.5, 0.05), backMat);
        back.position.set(0, 1.35, -(bhd - 0.05));
        group.add(back);

        // Скамейка внутри
        const benchMat = new THREE.MeshStandardMaterial({ color: 0x888888 });
        const bench = new THREE.Mesh(new THREE.BoxGeometry(bw * 0.6, 0.08, 0.5), benchMat);
        bench.position.set(0, 0.5, -0.5);
        group.add(bench);

        group.position.set(bx, 0, bz);
        group.rotation.y = Math.PI; // лицом к дороге
        this.scene.add(group);

        this.interactables.push({
            type: 'bench',
            mesh: group,
            position: new THREE.Vector3(bx, 0, bz),
            radius: 3,
            label: 'Посидеть на остановке'
        });
    }

    createStreetLamps() {
        const positions = this.mapConfig?.decorations?.lamps || [
            [-20, -7], [0, -7], [20, -7], [-40, -7], [40, -7],
            [-20, 7], [0, 7], [20, 7], [40, 7],
            [-20, -44], [0, -44], [20, -44], [-40, -44],
            [-20, 48], [0, 48], [20, 48],
            [48, -20], [48, 20],
            [-58, -20], [-58, 20],
        ];

        // Дороги для определения направления фонарей
        const lampRoads = this.mapConfig?.roads || [];

        positions.forEach(([x, z]) => {
            const group = new THREE.Group();

            const poleMat = new THREE.MeshStandardMaterial({ color: 0x444444, metalness: 0.6 });
            const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.12, 5, 8), poleMat);
            pole.position.y = 2.5;
            group.add(pole);

            // Горизонтальный кронштейн
            const arm = new THREE.Mesh(new THREE.BoxGeometry(1.5, 0.06, 0.06), poleMat);
            arm.position.set(0.75, 5, 0);
            group.add(arm);

            const lampGeo = new THREE.BoxGeometry(0.5, 0.15, 0.3);
            const lampMat = new THREE.MeshStandardMaterial({
                color: 0xffffcc,
                emissive: 0xffdd88,
                emissiveIntensity: 0.5
            });
            const lamp = new THREE.Mesh(lampGeo, lampMat);
            lamp.position.set(1.5, 4.9, 0);
            group.add(lamp);

            const light = new THREE.PointLight(0xffdd88, 0.6, 18);
            light.position.set(1.5, 4.8, 0);
            group.add(light);

            // Поворот кронштейна к ближайшей дороге
            let minDist = Infinity;
            let rotY = 0;
            for (const rd of lampRoads) {
                const isEW = Math.abs(rd.rotation) < 0.1;
                let dist;
                if (isEW) {
                    const xMin = rd.x - rd.width / 2;
                    const xMax = rd.x + rd.width / 2;
                    if (x < xMin - 5 || x > xMax + 5) continue;
                    dist = Math.abs(z - rd.z);
                } else {
                    const zMin = rd.z - rd.height / 2;
                    const zMax = rd.z + rd.height / 2;
                    if (z < zMin - 5 || z > zMax + 5) continue;
                    dist = Math.abs(x - rd.x);
                }
                if (dist < minDist) {
                    minDist = dist;
                    const dx = isEW ? 0 : rd.x - x;
                    const dz = isEW ? rd.z - z : 0;
                    rotY = Math.atan2(dz, dx);
                }
            }

            group.position.set(x, 0, z);
            group.rotation.y = rotY;
            this.scene.add(group);
        });
    }

    createDecorations() {
        // Мусор на земле (больше)
        const trashGeo = new THREE.BoxGeometry(0.3, 0.08, 0.2);
        const trashColors = [0x666666, 0x555544, 0x665544, 0x444455];
        for (let i = 0; i < 60; i++) {
            const trashMat = new THREE.MeshStandardMaterial({
                color: trashColors[Math.floor(Math.random() * trashColors.length)]
            });
            const trash = new THREE.Mesh(trashGeo, trashMat);
            trash.position.set(
                (Math.random() - 0.5) * 120,
                0.04,
                (Math.random() - 0.5) * 120
            );
            trash.rotation.y = Math.random() * Math.PI;
            trash.scale.set(0.5 + Math.random(), 1, 0.5 + Math.random());
            this.scene.add(trash);
        }

        // Забор парка
        const parkCfg = this.mapConfig?.structures?.park || { x: -30, z: 25, radius: 18 };
        const fenceMat = new THREE.MeshStandardMaterial({ color: 0x333333, metalness: 0.5 });
        for (let i = 0; i < 24; i++) {
            const angle = (i / 24) * Math.PI * 2;
            if (i >= 9 && i <= 12) continue; // Проход
            const fx = parkCfg.x + Math.cos(angle) * (parkCfg.radius + 1);
            const fz = parkCfg.z + Math.sin(angle) * (parkCfg.radius + 1);
            const post = new THREE.Mesh(new THREE.CylinderGeometry(0.04, 0.04, 1, 6), fenceMat);
            post.position.set(fx, 0.5, fz);
            this.scene.add(post);

            // Горизонтальные перекладины
            if (i < 23) {
                const nextAngle = ((i + 1) / 24) * Math.PI * 2;
                const nx = parkCfg.x + Math.cos(nextAngle) * (parkCfg.radius + 1);
                const nz = parkCfg.z + Math.sin(nextAngle) * (parkCfg.radius + 1);
                const len = Math.sqrt((nx - fx) ** 2 + (nz - fz) ** 2);
                const bar = new THREE.Mesh(new THREE.BoxGeometry(len, 0.03, 0.03), fenceMat);
                bar.position.set((fx + nx) / 2, 0.7, (fz + nz) / 2);
                bar.rotation.y = Math.atan2(nx - fx, nz - fz) + Math.PI / 2;
                this.scene.add(bar);
            }
        }

        // Пожарные гидранты
        const hydrants = this.mapConfig?.decorations?.hydrants || [[-18,-9],[25,-9],[35,8]];
        const hydrantMat = new THREE.MeshStandardMaterial({ color: 0xcc2222 });
        hydrants.forEach(([hx, hz]) => {
            const hydrant = new THREE.Mesh(new THREE.CylinderGeometry(0.15, 0.2, 0.7, 8), hydrantMat);
            hydrant.position.set(hx, 0.35, hz);
            this.scene.add(hydrant);
        });

        // Урны
        const bins = this.mapConfig?.decorations?.bins || [[-8,-9],[8,-9],[25,9],[-25,9]];
        const binMat = new THREE.MeshStandardMaterial({ color: 0x444444 });
        bins.forEach(([bx, bz]) => {
            const bin = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.2, 0.8, 8), binMat);
            bin.position.set(bx, 0.4, bz);
            this.scene.add(bin);
        });
    }

    createChurch() {
        const group = new THREE.Group();
        const cfg = this.mapConfig?.structures?.church || {};
        const cx = cfg.x ?? 30, cz = cfg.z ?? 60;
        const cw = cfg.w ?? 10, ch = cfg.h ?? 8, cd = cfg.d ?? 14;
        const hw = cw / 2, hd = cd / 2;

        const wallMat = new THREE.MeshStandardMaterial({ color: 0xccbb99, roughness: 0.8 });

        // Основное здание
        const body = new THREE.Mesh(new THREE.BoxGeometry(cw, ch, cd), wallMat);
        body.position.y = ch / 2;
        body.castShadow = true;
        body.receiveShadow = true;
        group.add(body);

        // Крыша (двускатная)
        const roofGeo = new THREE.ConeGeometry(Math.max(hw, hd), 3, 4);
        const roofMat = new THREE.MeshStandardMaterial({ color: 0x554433 });
        const roof = new THREE.Mesh(roofGeo, roofMat);
        roof.position.y = ch + 1.5;
        roof.rotation.y = Math.PI / 4;
        roof.castShadow = true;
        group.add(roof);

        // Колокольня
        const tower = new THREE.Mesh(new THREE.BoxGeometry(3, 6, 3), wallMat);
        tower.position.set(0, ch + 3, -hd + 3);
        tower.castShadow = true;
        group.add(tower);

        const towerRoof = new THREE.Mesh(new THREE.ConeGeometry(2.5, 3, 4), roofMat);
        towerRoof.position.set(0, ch + 7.5, -hd + 3);
        towerRoof.rotation.y = Math.PI / 4;
        group.add(towerRoof);

        // Крест
        const crossMat = new THREE.MeshStandardMaterial({ color: 0xffd700, metalness: 0.6 });
        const crossV = new THREE.Mesh(new THREE.BoxGeometry(0.15, 1.2, 0.15), crossMat);
        crossV.position.set(0, ch + 9.6, -hd + 3);
        group.add(crossV);
        const crossH = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.15, 0.15), crossMat);
        crossH.position.set(0, ch + 9.9, -hd + 3);
        group.add(crossH);

        // Дверь
        const doorMat = new THREE.MeshStandardMaterial({ color: 0x5c3a1e });
        const door = new THREE.Mesh(new THREE.BoxGeometry(2, 3.5, 0.15), doorMat);
        door.position.set(0, 1.75, hd + 0.05);
        group.add(door);

        // Окна-витражи
        const vitrageMat = new THREE.MeshStandardMaterial({
            color: 0x4488cc, emissive: 0x223344, emissiveIntensity: 0.3, transparent: true, opacity: 0.7
        });
        [-hw * 0.6, hw * 0.6].forEach(wx => {
            const win = new THREE.Mesh(new THREE.PlaneGeometry(1.5, 3), vitrageMat);
            win.position.set(wx, ch / 2, hd + 0.02);
            group.add(win);
        });

        // Тёплый свет из окон
        const churchLight = new THREE.PointLight(0xffddaa, 0.4, 15);
        churchLight.position.set(0, 3, hd + 1);
        group.add(churchLight);

        group.position.set(cx, 0, cz);
        this.scene.add(group);

        this.colliders.push(new THREE.Box3(
            new THREE.Vector3(cx - hw, 0, cz - hd),
            new THREE.Vector3(cx + hw, ch + 6, cz + hd)
        ));

        this.buildingRects.push({ x: cx, z: cz, w: cw, d: cd });

        this.interactables.push({
            type: 'church',
            mesh: group,
            position: new THREE.Vector3(cx, 0, cz + hd + 1),
            radius: 3,
            label: 'Войти в церковь'
        });
    }

    createConstructionSite() {
        const group = new THREE.Group();
        const cfg = this.mapConfig?.structures?.construction || {};
        const sx = cfg.x ?? 70, sz = cfg.z ?? 60;
        const siteRadius = cfg.radius ?? 12;

        // Забор строительной площадки
        const fenceMat = new THREE.MeshStandardMaterial({ color: 0xcc8833 });
        for (let i = 0; i < 20; i++) {
            const angle = (i / 20) * Math.PI * 2;
            const fx = Math.cos(angle) * siteRadius;
            const fz = Math.sin(angle) * siteRadius;
            const post = new THREE.Mesh(new THREE.BoxGeometry(0.1, 2, 0.1), fenceMat);
            post.position.set(fx, 1, fz);
            group.add(post);

            if (i < 19 && !(i >= 15 && i <= 16)) { // Проход
                const next = ((i + 1) / 20) * Math.PI * 2;
                const nx = Math.cos(next) * 12;
                const nz = Math.sin(next) * 12;
                const len = Math.sqrt((nx - fx) ** 2 + (nz - fz) ** 2);
                const plank = new THREE.Mesh(new THREE.BoxGeometry(len, 1.5, 0.05), fenceMat);
                plank.position.set((fx + nx) / 2, 1, (fz + nz) / 2);
                plank.rotation.y = Math.atan2(nx - fx, nz - fz) + Math.PI / 2;
                group.add(plank);
            }
        }

        // Недостроенное здание (каркас)
        const concreteMat = new THREE.MeshStandardMaterial({ color: 0x888888, roughness: 0.9 });
        // Колонны
        [[-4, -4], [4, -4], [-4, 4], [4, 4]].forEach(([px, pz]) => {
            const column = new THREE.Mesh(new THREE.BoxGeometry(0.6, 8, 0.6), concreteMat);
            column.position.set(px, 4, pz);
            column.castShadow = true;
            group.add(column);
        });

        // Перекрытия
        const slab = new THREE.Mesh(new THREE.BoxGeometry(9, 0.3, 9), concreteMat);
        slab.position.y = 4;
        slab.castShadow = true;
        slab.receiveShadow = true;
        group.add(slab);

        const slab2 = new THREE.Mesh(new THREE.BoxGeometry(9, 0.3, 9), concreteMat);
        slab2.position.y = 8;
        slab2.castShadow = true;
        group.add(slab2);

        // Кран (упрощённый)
        const craneMat = new THREE.MeshStandardMaterial({ color: 0xddaa22 });
        const craneBase = new THREE.Mesh(new THREE.CylinderGeometry(0.4, 0.5, 18, 8), craneMat);
        craneBase.position.set(8, 9, 0);
        craneBase.castShadow = true;
        group.add(craneBase);

        const craneArm = new THREE.Mesh(new THREE.BoxGeometry(16, 0.4, 0.4), craneMat);
        craneArm.position.set(2, 18, 0);
        group.add(craneArm);

        // Строительные материалы
        const brickMat = new THREE.MeshStandardMaterial({ color: 0xaa5533 });
        for (let i = 0; i < 3; i++) {
            const pile = new THREE.Mesh(
                new THREE.BoxGeometry(1.5, 0.6, 0.8),
                brickMat
            );
            pile.position.set(-6 + i * 2.5, 0.3, -7);
            group.add(pile);
        }

        // Песчаная куча
        const sandMat = new THREE.MeshStandardMaterial({ color: 0xccaa66 });
        const sand = new THREE.Mesh(new THREE.ConeGeometry(2, 1.5, 8), sandMat);
        sand.position.set(6, 0.75, -6);
        group.add(sand);

        group.position.set(sx, 0, sz);
        this.scene.add(group);

        this.colliders.push(
            new THREE.Box3(new THREE.Vector3(sx - 4.3, 0, sz - 4.3), new THREE.Vector3(sx - 3.7, 8, sz - 3.7)),
            new THREE.Box3(new THREE.Vector3(sx + 3.7, 0, sz - 4.3), new THREE.Vector3(sx + 4.3, 8, sz - 3.7)),
            new THREE.Box3(new THREE.Vector3(sx - 4.3, 0, sz + 3.7), new THREE.Vector3(sx - 3.7, 8, sz + 4.3)),
            new THREE.Box3(new THREE.Vector3(sx + 3.7, 0, sz + 3.7), new THREE.Vector3(sx + 4.3, 8, sz + 4.3)),
        );

        this.buildingRects.push({ x: sx, z: sz, w: 9, d: 9 });

        this.interactables.push({
            type: 'dumpster',
            mesh: group,
            position: new THREE.Vector3(sx - 5, 0, sz - 7),
            radius: 3,
            label: 'Обыскать стройматериалы',
            searched: false,
            searchCooldown: 0
        });

        this.interactables.push({
            type: 'shelter',
            mesh: group,
            position: new THREE.Vector3(sx, 0, sz),
            radius: 4,
            label: 'Поспать на стройке'
        });
    }

    createJobBoard() {
        const cfg = this.mapConfig?.structures?.jobBoard || {};
        const x = cfg.x ?? 20, z = cfg.z ?? -8;
        const group = new THREE.Group();

        // Столб
        const poleMat = new THREE.MeshStandardMaterial({ color: 0x654321 });
        const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.05, 0.05, 2, 6), poleMat);
        pole.position.y = 1;
        pole.castShadow = true;
        group.add(pole);

        // Доска
        const boardMat = new THREE.MeshStandardMaterial({ color: 0xd4a060 });
        const board = new THREE.Mesh(new THREE.BoxGeometry(1.2, 0.8, 0.05), boardMat);
        board.position.y = 1.6;
        board.castShadow = true;
        group.add(board);

        // Листки на доске
        const paperMat = new THREE.MeshStandardMaterial({ color: 0xeeeecc });
        for (let i = 0; i < 3; i++) {
            const paper = new THREE.Mesh(new THREE.BoxGeometry(0.25, 0.3, 0.01), paperMat);
            paper.position.set(-0.35 + i * 0.35, 1.6, 0.03);
            group.add(paper);
        }

        // Надпись (маленький квадрат сверху)
        const signMat = new THREE.MeshStandardMaterial({ color: 0x2e7d32 });
        const sign = new THREE.Mesh(new THREE.BoxGeometry(0.8, 0.15, 0.06), signMat);
        sign.position.set(0, 2.05, 0);
        group.add(sign);

        group.position.set(x, 0, z);
        this.scene.add(group);

        this.interactables.push({
            type: 'jobboard',
            mesh: group,
            position: new THREE.Vector3(x, 0, z),
            radius: 3,
            label: 'Доска объявлений (работа)'
        });
    }

    createHospital() {
        const group = new THREE.Group();
        const cfg = this.mapConfig?.structures?.hospital || {};
        const hx = cfg.x ?? -45, hz = cfg.z ?? -55;
        const hw = cfg.w ?? 12, hh = cfg.h ?? 7, hd = cfg.d ?? 10;
        const halfW = hw / 2, halfD = hd / 2;

        const wallMat = new THREE.MeshStandardMaterial({ color: 0xdddddd, roughness: 0.7 });
        const body = new THREE.Mesh(new THREE.BoxGeometry(hw, hh, hd), wallMat);
        body.position.y = hh / 2;
        body.castShadow = true;
        body.receiveShadow = true;
        group.add(body);

        // Крыша
        const roofMat = new THREE.MeshStandardMaterial({ color: 0x555555 });
        const roof = new THREE.Mesh(new THREE.BoxGeometry(hw + 0.5, 0.3, hd + 0.5), roofMat);
        roof.position.y = hh;
        group.add(roof);

        // Красный крест
        const crossMat = new THREE.MeshStandardMaterial({ color: 0xcc2222, emissive: 0x881111, emissiveIntensity: 0.3 });
        const crossV = new THREE.Mesh(new THREE.BoxGeometry(0.6, 2.5, 0.1), crossMat);
        crossV.position.set(0, hh - 1.5, halfD + 0.06);
        group.add(crossV);
        const crossH = new THREE.Mesh(new THREE.BoxGeometry(2, 0.6, 0.1), crossMat);
        crossH.position.set(0, hh - 1.5, halfD + 0.06);
        group.add(crossH);

        // Дверь
        const doorMat = new THREE.MeshStandardMaterial({ color: 0x88ccff, transparent: true, opacity: 0.6 });
        const door = new THREE.Mesh(new THREE.BoxGeometry(2.5, 3, 0.1), doorMat);
        door.position.set(0, 1.5, halfD + 0.05);
        group.add(door);

        // Свет у входа
        const light = new THREE.PointLight(0xffffff, 0.5, 10);
        light.position.set(0, 4, halfD + 1);
        group.add(light);

        group.position.set(hx, 0, hz);
        this.scene.add(group);

        this.colliders.push(new THREE.Box3(
            new THREE.Vector3(hx - halfW, 0, hz - halfD),
            new THREE.Vector3(hx + halfW, hh, hz + halfD)
        ));

        this.buildingRects.push({ x: hx, z: hz, w: hw, d: hd });

        this.interactables.push({
            type: 'hospital',
            mesh: group,
            position: new THREE.Vector3(hx, 0, hz + halfD + 1),
            radius: 3,
            label: 'Войти в больницу'
        });
    }

    createMarket() {
        const group = new THREE.Group();
        const cfg = this.mapConfig?.structures?.market || {};
        const mx = cfg.x ?? 35, mz = cfg.z ?? -55;
        const mw = cfg.w ?? 14, mh = cfg.h ?? 4, md = cfg.d ?? 10;
        const hw = mw / 2, hd = md / 2;

        // Основное здание рынка
        const wallMat = new THREE.MeshStandardMaterial({ color: 0x8b6914, roughness: 0.8 });
        const body = new THREE.Mesh(new THREE.BoxGeometry(mw, mh, md), wallMat);
        body.position.y = mh / 2;
        body.castShadow = true;
        group.add(body);

        // Навес
        const awningMat = new THREE.MeshStandardMaterial({ color: 0xcc6633 });
        const awning = new THREE.Mesh(new THREE.BoxGeometry(mw + 2, 0.1, 3), awningMat);
        awning.position.set(0, mh - 0.5, hd + 1);
        group.add(awning);

        // Столбы навеса
        const poleMat = new THREE.MeshStandardMaterial({ color: 0x654321 });
        [-hw, 0, hw].forEach(px => {
            const pole = new THREE.Mesh(new THREE.CylinderGeometry(0.08, 0.08, mh - 0.5, 6), poleMat);
            pole.position.set(px, (mh - 0.5) / 2, hd + 2.2);
            group.add(pole);
        });

        // Прилавки
        const counterMat = new THREE.MeshStandardMaterial({ color: 0xaa8855 });
        const counterSpacing = mw / 3;
        [-counterSpacing, 0, counterSpacing].forEach(cx => {
            const counter = new THREE.Mesh(new THREE.BoxGeometry(3, 1, 1.5), counterMat);
            counter.position.set(cx, 0.5, hd + 0.5);
            group.add(counter);
        });

        // Товары на прилавках (цветные кубики)
        const colors = [0xff4444, 0x44ff44, 0xffff44, 0xff8844, 0x44aaff];
        for (let i = 0; i < 15; i++) {
            const item = new THREE.Mesh(
                new THREE.BoxGeometry(0.3, 0.3, 0.3),
                new THREE.MeshStandardMaterial({ color: colors[Math.floor(Math.random() * colors.length)] })
            );
            item.position.set(
                -hw * 0.7 + Math.random() * mw * 0.7,
                1.15,
                hd + 0.2 + Math.random() * 0.5
            );
            group.add(item);
        }

        // Вывеска
        const signMat = new THREE.MeshStandardMaterial({ color: 0xdd8833, emissive: 0x553311, emissiveIntensity: 0.4 });
        const sign = new THREE.Mesh(new THREE.BoxGeometry(mw * 0.4, 1, 0.15), signMat);
        sign.position.set(0, mh + 0.2, hd + 0.1);
        group.add(sign);

        group.position.set(mx, 0, mz);
        this.scene.add(group);

        this.colliders.push(new THREE.Box3(
            new THREE.Vector3(mx - hw, 0, mz - hd),
            new THREE.Vector3(mx + hw, mh, mz + hd)
        ));

        this.buildingRects.push({ x: mx, z: mz, w: mw, d: md });

        this.interactables.push({
            type: 'market',
            mesh: group,
            position: new THREE.Vector3(mx, 0, mz + hd + 1),
            radius: 4,
            label: 'Рынок — купить экипировку'
        });
    }

    createCampSpot() {
        // Место для лагеря игрока (если ещё не построен)
        const cfg = this.mapConfig?.structures?.campSpot || {};
        const cx = cfg.x ?? -20, cz = cfg.z ?? 38;
        this.interactables.push({
            type: 'camp_spot',
            position: new THREE.Vector3(cx, 0, cz),
            radius: 3,
            label: 'Место для лагеря'
        });
    }

    // Движущиеся машины на дорогах
    // Загрузка 3D-модели машины
    loadCarModel() {
        const loader = new GLTFLoader();
        loader.load('textures/Mercedes E-Class W210 (1997).glb', (gltf) => {
            const model = gltf.scene;

            // Вычисляем размеры модели
            const box = new THREE.Box3().setFromObject(model);
            const size = box.getSize(new THREE.Vector3());
            const center = box.getCenter(new THREE.Vector3());

            // Определяем самую длинную ось (длина машины)
            const maxDim = Math.max(size.x, size.y, size.z);
            const targetLength = 4.2; // длина машины в игровых единицах
            const s = targetLength / maxDim;

            // Создаём обёртку для центрирования
            const wrapper = new THREE.Group();
            model.scale.set(s, s, s);

            // Центрируем модель: X/Z по центру, Y — дно на уровне 0
            model.position.set(
                -center.x * s,
                -box.min.y * s,
                -center.z * s
            );

            // Тени
            model.traverse((child) => {
                if (child.isMesh) {
                    child.castShadow = true;
                    child.receiveShadow = true;
                }
            });

            wrapper.add(model);

            // Определяем ориентацию: если модель длиннее по X, поворачиваем на 90°
            // чтобы «нос» смотрел в +Z (по умолчанию в нашей системе)
            if (size.x > size.z) {
                this.carModelBaseRotation = Math.PI / 2;
            }

            this.carModel = wrapper;
            console.log(`Car model loaded: ${size.x.toFixed(1)}x${size.y.toFixed(1)}x${size.z.toFixed(1)} → scaled ${s.toFixed(3)}`);

            // Заменяем все процедурные машины на модель
            this.replaceVehicleMeshes();

        }, (progress) => {
            if (progress.total > 0) {
                const pct = Math.round(progress.loaded / progress.total * 100);
                if (pct % 25 === 0) console.log(`Loading car model: ${pct}%`);
            }
        }, (error) => {
            console.warn('Car model failed to load, keeping procedural cars:', error);
        });
    }

    // Заменить все машины на 3D-модель
    replaceVehicleMeshes() {
        if (!this.carModel) return;

        // Движущиеся машины
        if (this.vehicles) {
            this.vehicles.forEach(v => {
                const old = v.mesh;
                const pos = old.position.clone();
                const visible = old.visible;

                // Пересчитываем rotation с учётом базового поворота модели
                const r = v.route;
                let rotY;
                if (r.axis === 'x') {
                    rotY = (r.dir > 0 ? Math.PI / 2 : -Math.PI / 2) + this.carModelBaseRotation;
                } else {
                    rotY = (r.dir > 0 ? 0 : Math.PI) + this.carModelBaseRotation;
                }

                this.scene.remove(old);

                const newCar = this.carModel.clone();
                newCar.position.copy(pos);
                newCar.rotation.y = rotY;
                newCar.visible = visible;

                this.scene.add(newCar);
                v.mesh = newCar;
                v.baseRotation = this.carModelBaseRotation;
            });
        }

        // Припаркованные машины
        if (this.parkedCars) {
            this.parkedCars.forEach(p => {
                this.scene.remove(p.mesh);

                const newCar = this.carModel.clone();
                newCar.position.set(p.x, 0, p.z);
                newCar.rotation.y = p.rotY + this.carModelBaseRotation;

                this.scene.add(newCar);
                p.mesh = newCar;
            });
        }
    }

    createVehicles() {
        this.vehicles = [];

        // Маршруты строго по дорогам
        const routes = this.mapConfig?.vehicles?.routes || [
            { axis: 'x', lane: -3, start: -130, end: 130, dir: 1 },
            { axis: 'x', lane: 3,  start: 130, end: -130, dir: -1 },
            { axis: 'x', lane: -42, start: -130, end: 130, dir: 1 },
            { axis: 'x', lane: -38, start: 130, end: -130, dir: -1 },
            { axis: 'z', lane: -3, start: -130, end: 130, dir: 1 },
            { axis: 'z', lane: 3,  start: 130, end: -130, dir: -1 },
            { axis: 'z', lane: 47, start: -45, end: 45, dir: 1 },
            { axis: 'z', lane: 53, start: 45, end: -45, dir: -1 },
            { axis: 'z', lane: -63, start: -45, end: 45, dir: 1 },
            { axis: 'z', lane: -57, start: 45, end: -45, dir: -1 },
        ];

        const carColors = [0xcc2222, 0x2255cc, 0x22cc22, 0x888888, 0xdddd22, 0xcc8822, 0xffffff, 0x222222];

        for (let i = 0; i < 12; i++) {
            const route = routes[i % routes.length];
            const color = carColors[Math.floor(Math.random() * carColors.length)];

            // Используем загруженную модель или процедурный fallback
            const car = this.carModel
                ? this.carModel.clone()
                : this.createCarMeshFallback(color);

            // Начальная позиция
            const offset = -i * 35 - Math.random() * 20;
            const baseRot = this.carModel ? this.carModelBaseRotation : 0;

            if (route.axis === 'x') {
                const startX = route.dir > 0 ? route.start + offset : route.end - offset;
                car.position.set(startX, 0, route.lane);
                car.rotation.y = (route.dir > 0 ? Math.PI / 2 : -Math.PI / 2) + baseRot;
            } else {
                const startZ = route.dir > 0 ? route.start + offset : route.end - offset;
                car.position.set(route.lane, 0, startZ);
                car.rotation.y = (route.dir > 0 ? 0 : Math.PI) + baseRot;
            }

            this.scene.add(car);

            // Динамический коллайдер для движущейся машины
            const collider = new THREE.Box3();
            this.vehicleColliders.push(collider);

            this.vehicles.push({
                mesh: car,
                route,
                speed: 8 + Math.random() * 6,
                delay: i * 4 + Math.random() * 8,
                baseRotation: baseRot,
                collider,
            });
        }

        // Если модель ещё не загружена, начинаем загрузку
        if (!this.carModel) {
            this.loadCarModel();
        }
    }

    // Процедурная машина (fallback пока модель грузится)
    createCarMeshFallback(color) {
        const car = new THREE.Group();
        const carBody = new THREE.Mesh(
            new THREE.BoxGeometry(2, 1, 3.5),
            new THREE.MeshStandardMaterial({ color, metalness: 0.4 })
        );
        carBody.position.y = 0.7;
        carBody.castShadow = true;
        car.add(carBody);

        const cabin = new THREE.Mesh(
            new THREE.BoxGeometry(1.6, 0.7, 1.8),
            new THREE.MeshStandardMaterial({ color: 0x88bbcc, transparent: true, opacity: 0.5 })
        );
        cabin.position.y = 1.45;
        car.add(cabin);

        const wheelMat = new THREE.MeshStandardMaterial({ color: 0x222222 });
        [[-0.9, -1.1], [-0.9, 1.1], [0.9, -1.1], [0.9, 1.1]].forEach(([wx, wz]) => {
            const wheel = new THREE.Mesh(new THREE.CylinderGeometry(0.25, 0.25, 0.15, 8), wheelMat);
            wheel.rotation.z = Math.PI / 2;
            wheel.position.set(wx, 0.25, wz);
            car.add(wheel);
        });

        const headlightMat = new THREE.MeshStandardMaterial({
            color: 0xffffcc, emissive: 0xffffcc, emissiveIntensity: 0.5
        });
        [-0.6, 0.6].forEach(lx => {
            const headlight = new THREE.Mesh(new THREE.SphereGeometry(0.1, 6, 4), headlightMat);
            headlight.position.set(lx, 0.6, 1.8);
            car.add(headlight);
        });

        return car;
    }

    updateVehicles(dt) {
        if (!this.vehicles) return;

        // Половинные размеры машины для коллайдера
        const hw = 1.0, hh = 1.5, hl = 2.0; // width/2, height, length/2

        this.vehicles.forEach(v => {
            if (v.delay > 0) {
                v.delay -= dt;
                v.mesh.visible = false;
                // Убираем коллайдер пока машина невидима
                v.collider.makeEmpty();
                return;
            }
            v.mesh.visible = true;

            const pos = v.mesh.position;
            const r = v.route;

            if (r.axis === 'x') {
                pos.x += r.dir * v.speed * dt;
                if (r.dir > 0 && pos.x > 135) {
                    pos.x = -135;
                    v.delay = 3 + Math.random() * 8;
                } else if (r.dir < 0 && pos.x < -135) {
                    pos.x = 135;
                    v.delay = 3 + Math.random() * 8;
                }
                // Коллайдер: машина едет по X — длинная сторона вдоль X
                v.collider.set(
                    new THREE.Vector3(pos.x - hl, 0, pos.z - hw),
                    new THREE.Vector3(pos.x + hl, hh, pos.z + hw)
                );
            } else {
                pos.z += r.dir * v.speed * dt;
                if (r.dir > 0 && pos.z > 135) {
                    pos.z = -135;
                    v.delay = 3 + Math.random() * 8;
                } else if (r.dir < 0 && pos.z < -135) {
                    pos.z = 135;
                    v.delay = 3 + Math.random() * 8;
                }
                // Коллайдер: машина едет по Z — длинная сторона вдоль Z
                v.collider.set(
                    new THREE.Vector3(pos.x - hw, 0, pos.z - hl),
                    new THREE.Vector3(pos.x + hw, hh, pos.z + hl)
                );
            }
        });
    }
}
