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


<!DOCTYPE html>
<html lang="ru">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <title>Greenhouse 3D: Interactive Grow Room</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
    <style>
        :root {
            --phone-bg: #0b120c;
            --phone-neon: #39ff14;
            --danger: #ff3333;
        }

        body, html {
            margin: 0; padding: 0; width: 100%; height: 100%;
            overflow: hidden; background-color: #050705;
            font-family: 'Segoe UI', Roboto, sans-serif;
            user-select: none; -webkit-user-select: none;
        }

        #canvas-3d {
            position: absolute; top: 0; left: 0; width: 100%; height: 100%; z-index: 1;
        }

        /* HUD сверху */
        #hud {
            position: absolute; top: 20px; left: 50%; transform: translateX(-50%);
            z-index: 10; display: flex; gap: 20px;
            background: rgba(10, 15, 10, 0.85); padding: 12px 30px;
            border-radius: 30px; border: 1px solid rgba(57, 255, 20, 0.2);
            backdrop-filter: blur(8px); pointer-events: none;
        }
        .hud-item { text-align: center; color: #fff; min-width: 80px; }
        .hud-label { font-size: 10px; color: #666; text-transform: uppercase; font-weight: bold; }
        .hud-val { font-size: 16px; font-weight: bold; font-family: monospace; }

        /* Сигнальное табло */
        #action-toast {
            position: absolute; top: 90px; left: 50%; transform: translateX(-50%);
            z-index: 10; background: rgba(211, 47, 47, 0.9); color: white;
            padding: 8px 20px; border-radius: 8px; font-size: 13px; font-weight: bold;
            display: none; box-shadow: 0 0 15px rgba(255,0,0,0.4);
        }

        /* Кнопка "Достать телефон" */
        #phone-toggle-btn {
            position: absolute; bottom: 25px; right: 25px; z-index: 15;
            background: linear-gradient(135deg, #142312, #070d07);
            color: var(--phone-neon); border: 2px solid var(--phone-neon);
            padding: 15px 25px; font-size: 14px; font-weight: bold; border-radius: 12px;
            cursor: pointer; box-shadow: 0 0 20px rgba(57,255,20,0.15);
            text-transform: uppercase; letter-spacing: 1px; transition: all 0.3s;
        }
        #phone-toggle-btn:hover { background: var(--phone-neon); color: #000; box-shadow: 0 0 30px var(--phone-neon); }

        /* Экран Смартфона */
        #smartphone {
            position: absolute; bottom: -650px; right: 25px; width: 330px; height: 560px;
            background-color: var(--phone-bg); border-radius: 32px; z-index: 20;
            border: 4px solid #142012; box-shadow: 0 25px 60px rgba(0,0,0,0.8);
            transition: bottom 0.5s cubic-bezier(0.175, 0.885, 0.32, 1.275);
            display: flex; flex-direction: column; overflow: hidden;
        }
        #smartphone.active { bottom: 95px; }

        .phone-header {
            background: #0f1a0f; padding: 15px; text-align: center;
            border-bottom: 1px solid #1d331c; color: var(--phone-neon);
            font-family: monospace; font-size: 14px; font-weight: bold;
        }
        .phone-content { padding: 15px; flex-grow: 1; overflow-y: auto; color: #cbd5e1; }
        .app-card { background: #132212; border: 1px solid #1d331c; padding: 12px; border-radius: 10px; margin-bottom: 12px; }
        .app-btn {
            width: 100%; background: var(--phone-neon); color: #000; border: none;
            padding: 10px; font-weight: bold; border-radius: 6px; cursor: pointer;
            margin-top: 8px; text-transform: uppercase; font-size: 11px;
        }
        .app-btn.bribe { background: #ff5252; color: white; }
        .phone-input {
            width: 80%; background: #070d07; border: 1px solid #1d331c; color: #fff;
            padding: 6px; text-align: center; border-radius: 4px; font-weight: bold; margin-top: 5px;
        }

        #phone-log {
            background: #050a05; border-radius: 6px; padding: 8px;
            font-family: monospace; font-size: 10px; color: #39ff14; height: 70px; overflow-y: auto;
        }

        /* Инструкция */
        #table-instructions {
            position: absolute; bottom: 25px; left: 25px; z-index: 5;
            color: #5d7a5b; font-size: 12px; font-family: monospace; line-height: 1.6;
            background: rgba(0,0,0,0.5); padding: 10px; border-radius: 8px; pointer-events: none;
        }
    </style>
</head>
<body>

    <div id="canvas-3d"></div>

    <div id="hud">
        <div class="hud-item"><div class="hud-label">Бюджет ($)</div><div id="hud-money" class="hud-val">15,000</div></div>
        <div class="hud-item"><div class="hud-label">Урожай (кг)</div><div id="hud-stock" class="hud-val">0.0</div></div>
        <div class="hud-item"><div class="hud-label">Семена</div><div id="hud-seeds" class="hud-val">5 шт</div></div>
        <div class="hud-item"><div class="hud-label">Улики</div><div id="hud-wanted" class="hud-val" style="color:var(--danger)">0%</div></div>
    </div>

    <div id="action-toast">ВЛАЖНОСТЬ КОРНЕЙ КРИТИЧЕСКАЯ! ОТКАЧАЙ ВОДУ СИНИМ ВЕНТИЛЕМ!</div>

    <div id="table-instructions">
        ТЕХНОЛОГИЧЕСКИЙ ГРОУБОКС:<br>
        1. Перетащи МЫШКОЙ Зеленый Мешок с семенами в Горшок, чтобы посадить куст.<br>
        2. Куст начнет расти в 3D. Перетащи Желтую Канистру с удобрением для буста соцветий.<br>
        3. Кликай на Синий Круглый Вентиль помпы справа, чтобы вовремя снижать влажность почвы.
    </div>

    <button id="phone-toggle-btn" onclick="togglePhone()">Открыть телефон</button>

    <div id="smartphone">
        <div class="phone-header">GREEN_NET OS v4.1</div>
        <div class="phone-content">
            
            <div class="app-card">
                <div style="font-weight:bold; color:var(--phone-neon); font-size:13px; margin-bottom:5px;">GREEN_MARKET // ОПТ</div>
                <div style="font-size:11px;">Склад готового продукта: <strong id="p-weight" style="color:#fff">0.0</strong> кг</div>
                <div id="p-client-info" style="font-size:11px; color:#aaa; margin: 5px 0; font-style:italic;">"Ожидание созревания куста..."</div>
                <div style="margin-top: 8px; font-size:11px;">
                    Цена за 1 кг ($): <br>
                    <input type="number" id="p-offer-price" class="phone-input" value="6000">
                </div>
                <button class="app-btn" onclick="phoneSellBatch()">Продать партию</button>
            </div>

            <div class="app-card">
                <div style="font-weight:bold; color:#ffb300; font-size:13px; margin-bottom:5px;">ПРИКРЫТИЕ И СНАБЖЕНИЕ</div>
                <button class="app-btn" style="background:#ffb300;" onclick="phoneBuySupplies()">Заказать семена элит (+5шт) (-$3k)</button>
                <button class="app-btn bribe" onclick="phoneBribePolice()">Дать взятку шерифу (-$2k)</button>
            </div>

            <div id="phone-log">Вход в сеть выполнен. Прокси-сервер: Амстердам.</div>
        </div>
    </div>

<script>
    // Баланс игры
    let state = {
        money: 15000, seeds: 5, wanted: 0, stock: 0, purity: 0, // purity тут выступает как ТГК / качество
        isCooking: false, progress: 0, temp: 24, pressure: 50, // pressure тут выступает как влажность грунта
        catalystAdded: false, loop: null
    };

    // 3D Графика
    let scene, camera, renderer;
    let pot, plant, seedBag, fertilizerCan, valve, gaugeHand;
    let raycaster, mouse, dragObject = null, isDragging = false;
    let plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0);

    function init3DGame() {
        const container = document.getElementById('canvas-3d');
        
        scene = new THREE.Scene();
        scene.background = new THREE.Color(0x050805);
        
        camera = new THREE.PerspectiveCamera(45, window.innerWidth / window.innerHeight, 0.1, 100);
        camera.position.set(0, 7, 12);
        camera.lookAt(0, 1, 0);

        renderer = new THREE.WebGLRenderer({ antialias: true });
        renderer.setSize(window.innerWidth, window.innerHeight);
        container.appendChild(renderer.domElement);

        raycaster = new THREE.Raycaster();
        mouse = new THREE.Vector2();

        // Освещение гроубокса
        let ambient = new THREE.AmbientLight(0x112211);
        scene.add(ambient);

        // Фитолампа (Ультрафиолетовый / Пурпурный свет)
        let uvLight = new THREE.PointLight(0xdc26ff, 2.5, 25);
        uvLight.position.set(0, 6, 0);
        scene.add(uvLight);

        let daylight = new THREE.PointLight(0xffffff, 0.8, 30);
        daylight.position.set(3, 8, 3);
        scene.add(daylight);

        // Стол
        let tableGeo = new THREE.BoxGeometry(16, 0.2, 8);
        let tableMat = new THREE.MeshStandardMaterial({ color: 0x141a13, roughness: 0.8 });
        let table = new THREE.Mesh(tableGeo, tableMat);
        table.position.y = -0.1;
        scene.add(table);

        // ОБЪЕКТЫ
        // 1. Горшок с землей по центру
        pot = new THREE.Group();
        let potGeo = new THREE.CylinderGeometry(1.6, 1.1, 1.8, 24);
        let potMat = new THREE.MeshStandardMaterial({ color: 0x2b1e17, roughness: 0.9 });
        let potMesh = new THREE.Mesh(potGeo, potMat);
        
        let soilGeo = new THREE.CylinderGeometry(1.5, 1.4, 0.2, 24);
        let soilMat = new THREE.MeshStandardMaterial({ color: 0x140d0a, roughness: 1.0 });
        let soilMesh = new THREE.Mesh(soilGeo, soilMat);
        soilMesh.position.y = 0.8;
        
        pot.add(potMesh); pot.add(soilMesh);
        pot.position.set(0, 0.9, 0);
        scene.add(pot);

        // 3D Растение (Модель, которая будет расти физически)
        plant = new THREE.Group();
        plant.position.set(0, 1, 0);
        scene.add(plant);

        // 2. Мешок семян (Перетаскиваемый объект слева)
        let bagGroup = new THREE.Group();
        let bagGeo = new THREE.BoxGeometry(1.1, 1.5, 0.7);
        let bagMat = new THREE.MeshStandardMaterial({ color: 0x2e7d32, roughness: 0.5 }); // Зеленый мешок
        let bagMesh = new THREE.Mesh(bagGeo, bagMat);
        bagGroup.add(bagMesh);
        bagGroup.position.set(-4.5, 0.75, 1);
        bagGroup.userData = { type: "seeds", originX: -4.5, originZ: 1 };
        seedBag = bagGroup;
        scene.add(seedBag);

        // 3. Удобрения (Желтая канистра справа впереди)
        let fertGroup = new THREE.Group();
        let fertGeo = new THREE.CylinderGeometry(0.5, 0.5, 1.4, 16);
        let fertMat = new THREE.MeshStandardMaterial({ color: 0xfbc02d, roughness: 0.2 }); // Желтая бутылка
        let fertMesh = new THREE.Mesh(fertGeo, fertMat);
        fertGroup.add(fertMesh);
        fertGroup.position.set(-2.5, 0.7, 2);
        fertGroup.userData = { type: "fertilizer", originX: -2.5, originZ: 2 };
        fertilizerCan = fertGroup;
        scene.add(fertilizerCan);

        // 4. Гигрометр (Прибор контроля влажности почвы)
        let gauge = new THREE.Group();
        let backGeo = new THREE.CylinderGeometry(0.9, 0.9, 0.3, 32);
        backGeo.rotateX(Math.PI / 2);
        let gaugeBack = new THREE.Mesh(backGeo, new THREE.MeshStandardMaterial({ color: 0x223021 }));
        let arrowGeo = new THREE.BoxGeometry(0.7, 0.08, 0.05);
        arrowGeo.translate(0.35, 0, 0);
        gaugeHand = new THREE.Mesh(arrowGeo, new THREE.MeshBasicMaterial({ color: 0x00e5ff }));
        gaugeHand.position.z = 0.18;
        gauge.add(gaugeBack); gauge.add(gaugeHand);
        gauge.position.set(4, 1.2, -1);
        scene.add(gauge);

        // Синий вентиль гидропонной помпы (Для кликов)
        let valveGroup = new THREE.Group();
        let wheelGeo = new THREE.TorusGeometry(0.6, 0.15, 8, 24);
        wheelGeo.rotateX(Math.PI / 2);
        let wheel = new THREE.Mesh(wheelGeo, new THREE.MeshStandardMaterial({ color: 0x0288d1, roughness: 0.4 }));
        let coreGeo = new THREE.CylinderGeometry(0.15, 0.15, 0.6, 8);
        let core = new THREE.Mesh(coreGeo, new THREE.MeshStandardMaterial({ color: 0x777 }));
        core.position.y = -0.2;
        valveGroup.add(wheel); valveGroup.add(core);
        valveGroup.position.set(3.5, 0.4, 1.5);
        valveGroup.userData = { type: "valve" };
        valve = valveGroup;
        scene.add(valve);

        // Обработчики мыши
        window.addEventListener('mousedown', onPointerDown);
        window.addEventListener('mousemove', onPointerMove);
        window.addEventListener('mouseup', onPointerUp);
        window.addEventListener('resize', onWindowResize);

        animateLab();
    }

    // Рендеринг веток куста в 3D
    function rebuildPlant3D(progress) {
        // Очищаем старые листья
        while(plant.children.length > 0){ 
            plant.remove(plant.children[0]); 
        }

        if (progress <= 0) return;

        // Создаем новые ветки на основе прогресса роста
        let height = (progress / 100) * 2.5;
        let stemGeo = new THREE.CylinderGeometry(0.05, 0.15, height, 8);
        stemGeo.translate(0, height/2, 0);
        let stemMat = new THREE.MeshStandardMaterial({ color: 0x388e3c, roughness: 0.9 });
        let stem = new THREE.Mesh(stemGeo, stemMat);
        plant.add(stem);

        // Листья/Соцветия (Зеленые сферы)
        let leafCount = Math.floor(progress / 15) + 1;
        let leafMat = new THREE.MeshStandardMaterial({ 
            color: state.catalystAdded ? 0x1b5e20 : 0x4caf50, 
            roughness: 0.6 
        });

        for(let i=0; i<leafCount; i++) {
            let size = 0.2 + (i * 0.08);
            let leafGeo = new THREE.SphereGeometry(size, 8, 8);
            let leaf = new THREE.Mesh(leafGeo, leafMat);
            leaf.position.set(
                Math.sin(i * 2) * 0.4,
                (height * (i / leafCount)) + 0.2,
                Math.cos(i * 2) * 0.4
            );
            plant.add(leaf);
        }
    }

    // --- DRAG & DROP В 3D ---
    function onPointerDown(e) {
        mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
        raycaster.setFromCamera(mouse, camera);
        
        let intersects = raycaster.intersectObjects([seedBag, fertilizerCan, valve], true);
        
        if (intersects.length > 0) {
            let obj = intersects[0].object;
            while (obj.parent && !obj.userData.type) { obj = obj.parent; }
            
            if (obj.userData.type === "valve") {
                valve.rotation.y += 0.8;
                reduceMoisture();
            } else if (obj.userData.type === "seeds" || obj.userData.type === "fertilizer") {
                isDragging = true;
                dragObject = obj;
            }
        }
    }

    function onPointerMove(e) {
        if (!isDragging || !dragObject) return;
        
        mouse.x = (e.clientX / window.innerWidth) * 2 - 1;
        mouse.y = -(e.clientY / window.innerHeight) * 2 + 1;
        raycaster.setFromCamera(mouse, camera);
        
        let raySpace = new THREE.Vector3();
        raycaster.ray.intersectPlane(plane, raySpace);
        
        dragObject.position.x = raySpace.x;
        dragObject.position.z = raySpace.z;
        dragObject.position.y = (dragObject.userData.type === "seeds") ? 1.4 : 1.2;

        // Проверка сближения с горшком (0, 0)
        let dist = Math.sqrt(dragObject.position.x * dragObject.position.x + dragObject.position.z * dragObject.position.z);
        if (dist < 1.4) {
            if (dragObject.userData.type === "seeds" && !state.isCooking) {
                startPlantGrowth();
                returnToOrigin(dragObject);
            } else if (dragObject.userData.type === "fertilizer" && state.isCooking && !state.catalystAdded) {
                addFertilizer();
                returnToOrigin(dragObject);
            }
        }
    }

    function onPointerUp() {
        if (dragObject) returnToOrigin(dragObject);
        isDragging = false; dragObject = null;
    }

    function returnToOrigin(obj) {
        obj.position.x = obj.userData.originX;
        obj.position.z = obj.userData.originZ;
        obj.position.y = 0.7;
    }

    // --- СИСТЕМНАЯ ЛОГИКА КУЛЬТИВАЦИИ ---
    function startPlantGrowth() {
        if (state.seeds < 1) { phoneLog("Маркет: У вас кончились семена сортовых культур!"); return; }
        if (state.stock > 0) { phoneLog("Внимание: На складе лежит старый урожай, продайте его!"); return; }

        state.seeds--; state.isCooking = true; state.progress = 0; state.pressure = 40; state.purity = 70;
        state.catalystAdded = false;

        isDragging = false; dragObject = null;
        updateUIScreen();

        state.loop = setInterval(() => {
            state.progress += 2.5;
            state.pressure += Math.random() * 9.5 - 3.8; // Рост влажности субстрата

            // Движение стрелки прибора
            let radAngle = ((state.pressure / 100) * Math.PI) - (Math.PI / 2);
            gaugeHand.rotation.z = -radAngle;

            // Перестройка 3D модели дерева в реальном времени
            rebuildPlant3D(state.progress);

            // Проверка критических порогов влажности
            let toast = document.getElementById('action-toast');
            if (state.pressure > 70 || state.pressure < 30) {
                state.purity -= 0.6; // Качество падает
                toast.style.display = 'block';
            } else {
                toast.style.display = 'none';
            }

            // Гибель урожая
            if (state.pressure >= 100 || state.pressure <= 0) {
                toast.style.display = 'none';
                phoneLog("Система: Корневая система погибла из-за дисбаланса влаги!");
                rebuildPlant3D(0);
                stopLabLoop();
                return;
            }

            if (state.progress >= 100) finishPlantGrowth();
            updateUIScreen();
        }, 350);
    }

    function addFertilizer() {
        state.catalystAdded = true;
        isDragging = false; dragObject = null;
        if (state.pressure >= 35 && state.pressure <= 65) {
            state.purity += 18.5; // Значительный буст качества ТГК
            phoneLog("Гроубокс: Стимулятор цветения усвоен на 100%.");
        } else {
            state.purity += 5;
            phoneLog("Гроубокс: Избыток минералов обжег соцветия куста.");
        }
        updateUIScreen();
    }

    function reduceMoisture() {
        if (!state.isCooking) return;
        state.pressure -= 22; // Помпа откачивает воду
        if (state.pressure < 5) state.pressure = 5;
    }

    function stopLabLoop() {
        clearInterval(state.loop); state.isCooking = false;
        document.getElementById('action-toast').style.display = 'none';
        let radAngle = ((50 / 100) * Math.PI) - (Math.PI / 2);
        gaugeHand.rotation.z = -radAngle;
        updateUIScreen();
    }

    function finishPlantGrowth() {
        stopLabLoop();
        state.stock = 1.5 + (state.purity / 35);
        phoneLog(`Маркет: Собрано готовых сухих соцветий: ${state.stock.toFixed(2)} кг. Мощность: ${state.purity.toFixed(1)}%`);
        
        let clients = ["Клуб Amsterdam", "Кофишоп Barney", "Местный дилер"];
        document.getElementById('p-client-info').innerText = `Заказчик: ${clients[Math.floor(Math.random()*clients.length)]} (Требуется качество: ${state.purity.toFixed(0)}%)`;
        updateUIScreen();
    }

    // --- УПРАВЛЕНИЕ СМАРТФОНОМ ---
    function togglePhone() {
        document.getElementById('smartphone').classList.toggle('active');
    }

    function phoneSellBatch() {
        if (state.stock <= 0) { phoneLog("Ошибка: Склад пуст!"); return; }
        let offer = parseFloat(document.getElementById('p-offer-price').value);
        let maxLimitPrice = 5000 * (state.purity / 60);

        if (offer <= maxLimitPrice) {
            let win = state.stock * offer;
            state.money += win;
            state.wanted += Math.random() * 9 + 3;
            phoneLog(`Криптосеть: Товар принят. На счет переведено +$${Math.round(win)}`);
            state.stock = 0;
            rebuildPlant3D(0); // Срезаем куст в горшке
            document.getElementById('p-client-info').innerText = "Ожидание нового урожая...";
        } else {
            phoneLog("Отказ: Диспетчер отклонил цену. Слишком дорого для этой селекции.");
            state.wanted += 2;
        }
        updateUIScreen();
    }

    function phoneBuySupplies() {
        if (state.money < 3000) { phoneLog("Ошибка счета: Баланс заблокирован!"); return; }
        state.money -= 3000; state.seeds += 5;
        phoneLog("Поставщик: Тайник с 5 семенами заложен за гаражами.");
        updateUIScreen();
    }

    function phoneBribePolice() {
        if (state.money < 2000) { phoneLog("Адвокат: Мало денег для взятки."); return; }
        state.money -= 2000; state.wanted = Math.max(0, state.wanted - 30);
        phoneLog("Информатор: Патрульные маршруты изменены, улики утеряны.");
        updateUIScreen();
    }

    function phoneLog(msg) {
        let l = document.getElementById('phone-log');
        l.innerHTML += `<br>» ${msg}`; l.scrollTop = l.scrollHeight;
    }

    // --- СЕРВИСНЫЕ ИНТЕРФЕЙСЫ ---
    function updateUIScreen() {
        document.getElementById('hud-money').innerText = state.money.toLocaleString();
        document.getElementById('hud-stock').innerText = state.stock.toFixed(2);
        document.getElementById('hud-seeds').innerText = state.seeds + ' шт';
        document.getElementById('hud-wanted').innerText = Math.round(state.wanted) + '%';
        document.getElementById('p-weight').innerText = state.stock.toFixed(2);

        if (state.wanted >= 100) {
            alert("ИГРА ОКОНЧЕНА. Полиция вскрыла двери подвала и конфисковала гидропонную оранжерею!");
            location.reload();
        }
    }

    function animateLab() {
        requestAnimationFrame(animateLab);
        // Небольшая плавная анимация покачивания куста
        if(state.isCooking && plant) {
            plant.rotation.y = Math.sin(Date.now() * 0.001) * 0.08;
        }
        renderer.render(scene, camera);
    }

    function onWindowResize() {
        camera.aspect = window.innerWidth / window.innerHeight;
        camera.updateProjectionMatrix();
        renderer.setSize(window.innerWidth, window.innerHeight);
    }

    window.onload = () => {
        init3DGame();
        updateUIScreen();
    };
</script>
</body>
</html>