// ==================== МИНИКАРТА ==================== import { state } from '../core/state.js'; import { BLOCKS } from '../data/blocks.js'; import { getBlock } from '../world/world-storage.js'; import { playSound } from '../audio/sound-engine.js'; import { isNight } from '../entities/mob-ai.js'; const minimapWrap = document.getElementById('minimapWrap'); const minimapCanvas = document.getElementById('minimap'); const minimapCtx = minimapCanvas.getContext('2d'); // Цвета блоков для миникарты (по 1 пикселю на блок) const MINIMAP_COLORS = { grass: '#4a8c2a', dirt: '#6b3410', stone: '#6a6a6a', sand: '#d4b84a', gravel: '#888', clay: '#5a9ad4', wood: '#a63d00', planks: '#c46a10', leaves: '#2a8a3a', glass: '#aaddee', water: '#2980b9', coal: '#1a1a2a', copper_ore: '#a05535', iron_ore: '#b0b0b0', gold_ore: '#d4a017', diamond_ore: '#0090d0', brick: '#8a2010', tnt: '#c02020', campfire: '#d08020', torch: '#d0a020', bedrock: '#1a1a1a', flower: '#d03050', bed: '#c03060', ladder: '#a04000', boat: '#6b3410' }; export function initMinimap() { document.getElementById('mapToggle').onclick = () => { playSound('click'); state.minimapOpen = !state.minimapOpen; minimapWrap.style.display = state.minimapOpen ? 'block' : 'none'; }; } export function renderMinimap() { if (!state.minimapOpen) return; const mW = minimapCanvas.width; const mH = minimapCanvas.height; const scale = 2; // пикселей на блок const TILE = state.TILE; const player = state.player; // Область карты — центрирована на игроке const pGX = Math.floor(player.x / TILE); const pGY = Math.floor(player.y / TILE); const viewW = Math.floor(mW / scale); const viewH = Math.floor(mH / scale); const startGX = pGX - Math.floor(viewW / 2); const startGY = pGY - Math.floor(viewH / 2); // Очищаем minimapCtx.fillStyle = isNight() ? '#070816' : '#87CEEB'; minimapCtx.fillRect(0, 0, mW, mH); // Рисуем блоки const imgData = minimapCtx.createImageData(mW, mH); const data = imgData.data; for (let dx = 0; dx < viewW; dx++) { for (let dy = 0; dy < viewH; dy++) { const gx = startGX + dx; const gy = startGY + dy; const b = getBlock(gx, gy); if (!b || b.dead || b.t === 'air') continue; const color = MINIMAP_COLORS[b.t]; if (!color) continue; // Парсим hex цвет const r = parseInt(color.slice(1, 3), 16); const g = parseInt(color.slice(3, 5), 16); const bl = parseInt(color.slice(5, 7), 16); // Заполняем scale x scale пикселей for (let sx = 0; sx < scale; sx++) { for (let sy = 0; sy < scale; sy++) { const px = dx * scale + sx; const py = dy * scale + sy; if (px >= mW || py >= mH) continue; const idx = (py * mW + px) * 4; data[idx] = r; data[idx + 1] = g; data[idx + 2] = bl; data[idx + 3] = 255; } } } } minimapCtx.putImageData(imgData, 0, 0); // Игрок — белый пиксель по центру minimapCtx.fillStyle = '#fff'; minimapCtx.fillRect(Math.floor(mW / 2) - 2, Math.floor(mH / 2) - 2, 4, 4); // Другие игроки — жёлтые точки for (const [sid, p] of state.otherPlayers) { const dx = Math.floor(p.x / TILE) - startGX; const dy = Math.floor(p.y / TILE) - startGY; if (dx >= 0 && dx < viewW && dy >= 0 && dy < viewH) { minimapCtx.fillStyle = '#f1c40f'; minimapCtx.fillRect(dx * scale - 1, dy * scale - 1, 3, 3); } } // Мобы — красные (враждебные) / зелёные (животные) const allMobsForMap = state.isMultiplayer ? Array.from(state.serverMobs.values()) : state.mobs; for (const m of allMobsForMap) { const dx = Math.floor(m.x / TILE) - startGX; const dy = Math.floor(m.y / TILE) - startGY; if (dx >= 0 && dx < viewW && dy >= 0 && dy < viewH) { const hostile = m.kind === 'zombie' || m.kind === 'creeper' || m.kind === 'skeleton'; minimapCtx.fillStyle = hostile ? '#e74c3c' : '#2ecc71'; minimapCtx.fillRect(dx * scale, dy * scale, 2, 2); } } }