299 lines
12 KiB
Plaintext
299 lines
12 KiB
Plaintext
// ==================== ENTRY POINT ====================
|
|
import { state } from './core/state.js';
|
|
import { initSocket } from './multiplayer/socket.js';
|
|
import { sendBlockChange, sendPlayerPosition } from './multiplayer/socket-helpers.js';
|
|
import { loadSound, playSound } from './audio/sound-engine.js';
|
|
import { BLOCKS } from './data/blocks.js';
|
|
import { ITEMS } from './data/items.js';
|
|
import { TOOLS } from './data/tools.js';
|
|
import { SMELTING_RECIPES } from './data/recipes.js';
|
|
import { initTextures } from './render/textures.js';
|
|
import { getBlock, setBlock, removeBlock } from './world/world-storage.js';
|
|
import { genColumn, surfaceGyAt, ensureGenAroundCamera, regenerateVisibleChunks } 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 { 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 { resolveY, resolveX } from './physics/collision.js';
|
|
import { calculateDamage } from './entities/player.js';
|
|
import { updateWaterFlag } from './physics/water-detect.js';
|
|
import { updateWaterPhysics } from './physics/water.js';
|
|
import { explodeAt, activateTNT } from './world/tnt.js';
|
|
import { useTool } from './data/tools.js';
|
|
import { rebuildHotbar } from './ui/hotbar.js';
|
|
import { closeTrade } from './ui/trade.js';
|
|
import { toggleMarket, closeMarket } from './ui/market.js';
|
|
|
|
// ==================== 1) CONFIG — parse URL/getName ====================
|
|
const urlParams = new URLSearchParams(window.location.search);
|
|
state.SERVER_URL = urlParams.get('server') || 'https://apigrech.mkn8n.ru';
|
|
state.TELEGRAM_BOT_USERNAME = 'Grechkacraft_bot';
|
|
state.TELEGRAM_APP_SHORT_NAME = 'minegrechka';
|
|
|
|
// Защита от mixed content
|
|
if (location.protocol === 'https:' && state.SERVER_URL.startsWith('http://')) {
|
|
console.warn('⚠️ Mixed content warning: page is HTTPS but server URL is HTTP');
|
|
alert('⚠️ Предупреждение: страница загружена по HTTPS, но сервер использует HTTP. Это может вызвать проблемы.');
|
|
}
|
|
|
|
// Player name
|
|
state.playerName = localStorage.getItem('minegrechka_playerName') || null;
|
|
if (!state.playerName) {
|
|
state.playerName = prompt('Введите ваше имя для игры:') || 'Игрок';
|
|
localStorage.setItem('minegrechka_playerName', state.playerName);
|
|
console.log('Player name set:', state.playerName);
|
|
}
|
|
|
|
// World ID from URL or generate new
|
|
console.log('Current URL:', window.location.href);
|
|
const worldParam = urlParams.get('world');
|
|
console.log('world param:', worldParam);
|
|
|
|
state.worldId = (worldParam && worldParam.trim() !== '') ? worldParam : null;
|
|
console.log('worldId after params:', state.worldId, 'type:', typeof state.worldId);
|
|
|
|
if (!state.worldId) {
|
|
state.worldId = Math.random().toString(36).substring(2, 10);
|
|
console.log('Generated worldId:', state.worldId);
|
|
|
|
try {
|
|
const newUrl = new URL(window.location.href);
|
|
newUrl.searchParams.set('world', state.worldId);
|
|
const newUrlString = newUrl.toString();
|
|
console.log('New URL to set:', newUrlString);
|
|
|
|
if (typeof window.history !== 'undefined' && typeof window.history.replaceState === 'function') {
|
|
window.history.replaceState(null, '', newUrlString);
|
|
console.log('URL after replaceState:', window.location.href);
|
|
} else {
|
|
console.error('History API not supported!');
|
|
}
|
|
} catch (e) {
|
|
console.error('Error updating URL:', e);
|
|
}
|
|
|
|
console.log('Generated new worldId for browser:', state.worldId);
|
|
}
|
|
|
|
console.log('Final worldId:', state.worldId, 'Player name:', state.playerName);
|
|
|
|
// ==================== 2) CANVAS — get elements, resize ====================
|
|
state.gameEl = document.getElementById('game');
|
|
state.canvas = document.getElementById('c');
|
|
state.ctx = state.canvas.getContext('2d');
|
|
|
|
// offscreen light map
|
|
state.lightC = document.createElement('canvas');
|
|
state.lightCtx = state.lightC.getContext('2d');
|
|
|
|
state.dpr = Math.max(1, window.devicePixelRatio || 1);
|
|
|
|
function resize() {
|
|
state.W = state.gameEl.clientWidth;
|
|
state.H = state.gameEl.clientHeight;
|
|
state.canvas.width = state.W * state.dpr;
|
|
state.canvas.height = state.H * state.dpr;
|
|
state.lightC.width = state.W * state.dpr;
|
|
state.lightC.height = state.H * state.dpr;
|
|
state.ctx.setTransform(state.dpr, 0, 0, state.dpr, 0, 0);
|
|
}
|
|
window.addEventListener('resize', resize);
|
|
resize();
|
|
|
|
// ==================== 3) STATE — init player, inv, etc ====================
|
|
state.otherPlayers = new Map();
|
|
state.serverMobs = new Map();
|
|
state.grid = new Map();
|
|
state.blocks = [];
|
|
state.generated = new Set();
|
|
state.serverOverrides = new Map();
|
|
state.activeFurnaces = new Map();
|
|
state.activeTNT = new Set();
|
|
state.toolDurability = new Map();
|
|
state.worldSeed = Math.floor(Math.random() * 1000000);
|
|
|
|
// Clouds
|
|
state.clouds = Array.from({ length: 10 }, () => ({
|
|
x: Math.random() * 2000,
|
|
y: -200 - Math.random() * 260,
|
|
w: 80 + Math.random() * 120,
|
|
s: 12 + Math.random() * 20
|
|
}));
|
|
|
|
// Weather
|
|
state.weatherTimer = 0;
|
|
state.weatherChangeInterval = 60 + Math.random() * 120;
|
|
|
|
// Player spawn
|
|
state.player.x = 6 * state.TILE;
|
|
state.player.y = 0;
|
|
state.spawnPoint.x = 6 * state.TILE;
|
|
state.spawnPoint.y = 0;
|
|
|
|
// Hero image
|
|
state.heroImg = new Image();
|
|
state.heroImg.src = 'https://supamg.mkn8n.ru/storage/v1/object/public/img//grechka.png';
|
|
|
|
// UI elements
|
|
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.multiplayerStatus = document.getElementById('multiplayerStatus');
|
|
|
|
// Market and trade panel DOM refs
|
|
state.marketPanel = document.getElementById('marketPanel');
|
|
state.marketContent = document.getElementById('marketContent');
|
|
state.tradePanel = document.getElementById('tradePanel');
|
|
state.tradeContent = document.getElementById('tradeContent');
|
|
|
|
// ==================== 4) TEXTURES — initTextures() ====================
|
|
initTextures();
|
|
|
|
// ==================== 5) AUDIO — loadSound for each ====================
|
|
loadSound('splash', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//splash.mp3');
|
|
loadSound('sand1', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//sand1.mp3');
|
|
loadSound('snow1', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//snow1.mp3');
|
|
loadSound('stone1', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//stone1.mp3');
|
|
loadSound('wood1', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//wood1.mp3');
|
|
loadSound('cloth1', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//cloth1.mp3');
|
|
loadSound('fire', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//fire.mp3');
|
|
loadSound('hit1', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//hit1.mp3');
|
|
loadSound('attack', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//attack.mp3');
|
|
loadSound('hurt_chicken', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//hurt1_chicken.mp3');
|
|
loadSound('stone_build', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//stone1%20(1).mp3');
|
|
loadSound('wood_build', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//wood1%20(1).mp3');
|
|
loadSound('click', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//click.mp3');
|
|
loadSound('explode1', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//explode1.mp3');
|
|
loadSound('glass1', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//glass1.mp3');
|
|
loadSound('eat1', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//eat1.mp3');
|
|
loadSound('step', 'https://supamg.mkn8n.ru/storage/v1/object/public/sounds//sand2.mp3');
|
|
|
|
// ==================== 6) CONTROLS — initControls ====================
|
|
initControls();
|
|
|
|
// ==================== 7) MOUSE — initMouseHandlers ====================
|
|
initMouseHandlers();
|
|
|
|
// ==================== 8) UI MODULES — init all UI ====================
|
|
initChat();
|
|
initFurnace();
|
|
initMinimap();
|
|
initSaveControls();
|
|
initRespawn();
|
|
initShare();
|
|
initModes();
|
|
|
|
// ==================== MARKET & TRADE UI ====================
|
|
// Market close
|
|
const marketClose = document.getElementById('marketClose');
|
|
if (marketClose) {
|
|
marketClose.addEventListener('click', () => {
|
|
closeMarket();
|
|
});
|
|
}
|
|
// Trade close
|
|
const tradeClose = document.getElementById('tradeClose');
|
|
if (tradeClose) {
|
|
tradeClose.addEventListener('click', () => {
|
|
closeTrade();
|
|
});
|
|
}
|
|
|
|
// ==================== 9) SOCKET — initSocket ====================
|
|
initSocket();
|
|
|
|
// ==================== 10) VOICE — initVoice ====================
|
|
initVoice();
|
|
|
|
// ==================== 11) 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); |