// ==================== ENTRY POINT ==================== import { state } from './core/state.js'; import { SERVER_URL } from './config.js'; import { showStartMenu, isTgWebApp, tgUser } from './ui/start-menu.js'; import { initCanvas } from './core/canvas.js'; import { initSocket } from './multiplayer/socket.js'; import { loadSound, playSound } from './audio/sound-engine.js'; import { initTextures, tex, itemTex } from './render/textures.js'; import { getBlock, setBlock, removeBlock } from './world/world-storage.js'; import { genColumn, surfaceGyAt } from './world/generation.js'; import { initDB, loadGame, applySave, saveGame } from './game/save.js'; import { loop } from './game/loop.js'; import { initChat } from './ui/chat.js'; import { initFurnace } from './ui/furnace.js'; import { initMinimap } from './ui/minimap.js'; import { initSaveControls, updateSaveButtonVisibility } from './ui/save-controls.js'; import { initMarket } from './ui/market.js'; import { initRespawn } from './ui/respawn.js'; import { initShare } from './ui/share.js'; import { initControls } from './input/controls.js'; import { initMouseHandlers } from './input/mouse-handler.js'; import { initModes } from './game/modes.js'; import { initVoice } from './multiplayer/voice-chat.js'; import { rebuildHotbar } from './ui/hotbar.js'; import { toggleCraft, closeCraft, toggleInventory, closeInventory } from './ui/craft.js'; // ==================== 1) CONFIG — parse URL ==================== const urlParams = new URLSearchParams(window.location.search); state.SERVER_URL = SERVER_URL; state.TELEGRAM_BOT_USERNAME = 'Grechkacraft_bot'; state.TELEGRAM_APP_SHORT_NAME = 'minegrechka'; // Set TG-related state if (isTgWebApp) { state.myTgId = tgUser?.id || null; } state.heroImg.src = 'https://supamg.mkn8n.ru/storage/v1/object/public/img//grechka.png'; // ==================== 2) START MENU ==================== // Show the start menu before initializing the game. // The menu returns a Promise that resolves with the worldId. async function startGame() { const worldId = await showStartMenu(); // Set worldId and playerName state.worldId = worldId; if (!state.playerName) { state.playerName = localStorage.getItem('minegrechka_playerName') || 'Игрок'; localStorage.setItem('minegrechka_playerName', state.playerName); } console.log('[Launch] worldId:', state.worldId, 'player:', state.playerName, 'tg:', isTgWebApp); // Validate TG initData if in TG context if (isTgWebApp && window.Telegram && Telegram.WebApp.initData) { try { const resp = await fetch(state.SERVER_URL + '/api/tg/auth', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ initData: Telegram.WebApp.initData }) }); const data = await resp.json(); if (data.ok && data.tg_id) { console.log('[TG] Authenticated, tg_id:', data.tg_id); state.myTgId = data.tg_id; } } catch (e) { console.warn('[TG] Auth failed:', e); } } // Update URL with world code try { const newUrl = new URL(window.location.href); newUrl.searchParams.set('world', state.worldId); if (typeof history !== 'undefined' && history.replaceState) { history.replaceState(null, '', newUrl.toString()); } } catch (e) {} // ==================== 3) CANVAS — init from canvas module ==================== initCanvas(); // ==================== 4) STATE — set DOM refs ==================== state.hpEl = document.getElementById('hp'); state.foodEl = document.getElementById('food'); state.sxEl = document.getElementById('sx'); state.syEl = document.getElementById('sy'); state.todEl = document.getElementById('tod'); state.worldIdEl = document.getElementById('worldId'); state.playerCountEl = document.getElementById('playerCount'); state.deathEl = document.getElementById('death'); state.hotbarEl = document.getElementById('hotbar'); state.craftPanel = document.getElementById('craftPanel'); state.recipesEl = document.getElementById('recipes'); state.inventoryPanel = document.getElementById('inventoryPanel'); state.inventoryGrid = document.getElementById('inventoryGrid'); state.marketPanel = document.getElementById('marketPanel'); state.marketContent = document.getElementById('marketContent'); state.tradePanel = document.getElementById('tradePanel'); state.tradeContent = document.getElementById('tradeContent'); state.multiplayerStatus = document.getElementById('multiplayerStatus'); // ==================== 5) TEXTURES — initTextures() ==================== initTextures(); state.tex = tex; state.itemTex = itemTex; // ==================== 6) CRAFT/INVENTORY BUTTONS ==================== const craftBtn = document.getElementById('craftBtn'); const invToggle = document.getElementById('invToggle'); const craftClose = document.getElementById('craftClose'); const inventoryClose = document.getElementById('inventoryClose'); if (craftBtn) craftBtn.onclick = () => toggleCraft(); if (craftClose) craftClose.onclick = () => closeCraft(); if (invToggle) invToggle.onclick = () => toggleInventory(); if (inventoryClose) inventoryClose.onclick = () => closeInventory(); // ==================== 7) CONTROLS — initControls ==================== initControls(); // ==================== 8) MOUSE — initMouseHandlers ==================== initMouseHandlers(); // ==================== 9) UI MODULES — init all UI ==================== initChat(); initFurnace(); initMinimap(); initSaveControls(); initMarket(); initRespawn(); initShare(); initModes(); // ==================== 10) SOCKET — initSocket ==================== initSocket(); // ==================== 11) VOICE — initVoice ==================== initVoice(); // ==================== 12) SAVE — loadGame, applySave ==================== rebuildHotbar(); initDB().then(async () => { const loadedSave = await loadGame(); if (loadedSave) { await applySave(loadedSave); console.log('Загружено сохранение, HP:', state.player.hp); if (state.player.hp <= 0) { console.log('WARNING: HP <= 0 после загрузки, возрождаемся'); state.player.hp = 100; state.player.hunger = 100; state.player.o2 = 100; state.player.x = state.spawnPoint.x; state.player.y = state.spawnPoint.y; state.player.vx = state.player.vy = 0; state.player.invuln = 0; state.player.fallStartY = state.player.y; } } else { console.log('Сохранение не найдено, начинаем новую игру'); state.player.hp = 100; state.player.hunger = 100; state.player.o2 = 100; state.player.vx = state.player.vy = 0; state.player.invuln = 0; // старт — на поверхности const startGX = 6; for (let dx = -3; dx <= 3; dx++) genColumn(startGX + dx); const surfaceY = surfaceGyAt(startGX); let safeGY = surfaceY - 1; const aboveBlock = getBlock(startGX, surfaceY - 1); if (aboveBlock && aboveBlock.t === 'water') { for (let gy = state.SEA_GY - 1; gy >= 0; gy--) { const b = getBlock(startGX, gy); if (!b || b.dead || b.t === 'air' || b.t === 'water') continue; safeGY = gy - 1; break; } } state.player.y = safeGY * state.TILE; state.player.x = startGX * state.TILE; state.player.fallStartY = state.player.y; state.spawnPoint.x = state.player.x; state.spawnPoint.y = state.player.y; console.log('Новая игра: startGX=', startGX, 'surfaceY=', surfaceY, 'player.y=', state.player.y); // Генерируем карту вокруг стартовой позиции for (let gx = startGX - 50; gx <= startGX + 50; gx++) { genColumn(gx); } } // Автосейв при скрытии страницы document.addEventListener('visibilitychange', () => { if (document.hidden) { saveGame(); } }); // Автосейв перед закрытием страницы window.addEventListener('beforeunload', () => { saveGame(); }); }).catch(err => { console.error('Ошибка инициализации:', err); const startGX = 6; genColumn(startGX); state.player.y = (surfaceGyAt(startGX) - 1) * state.TILE; state.player.fallStartY = state.player.y; for (let gx = startGX - 50; gx <= startGX + 50; gx++) { genColumn(gx); } }); // ==================== 13) LOOP — startLoop ==================== requestAnimationFrame(loop); } // Start the game startGame().catch(err => { console.error('Fatal error starting game:', err); });