// Состояние редактора let width = 15; let height = 15; let map = []; let selectedType = 0; let isDrawing = false; let scale = 1; let offsetX = 0; let offsetY = 0; let isPanning = false; let startPanX = 0; let startPanY = 0; // Canvas элементы const canvas = document.getElementById('mapCanvas'); const ctx = canvas.getContext('2d'); // Инициализация function init() { width = parseInt(document.getElementById('width').value); height = parseInt(document.getElementById('height').value); initMap(); resizeCanvas(); drawMap(); } // Инициализация карты function initMap() { map = Array(height).fill(null).map(() => Array(width).fill(0)); } function resizeCanvas() { const wrapper = canvas.parentElement; canvas.width = Math.max(1000, wrapper.clientWidth || 1000); canvas.height = Math.max(1000, wrapper.clientHeight || 1000); } // Отрисовка карты function drawMap() { ctx.setTransform(1, 0, 0, 1, 0, 0); ctx.clearRect(0, 0, canvas.width, canvas.height); ctx.translate(offsetX, offsetY); ctx.scale(scale, scale); drawCells(ctx, map, width, height); drawGrid(ctx, width, height); drawMarkers(ctx, map, width, height); } // Выбор типа ячейки function selectCellType(type) { selectedType = type; // Обновляем UI document.querySelectorAll('.cell-type').forEach(el => { el.classList.remove('active'); }); document.querySelector(`[data-type="${type}"]`).classList.add('active'); } // Получение координат ячейки по клику function getCellCoords(event) { const rect = canvas.getBoundingClientRect(); const canvasX = (event.clientX - rect.left - offsetX) / scale; const canvasY = (event.clientY - rect.top - offsetY) / scale; const x = Math.floor(canvasX / CELL_SIZE); const y = Math.floor(canvasY / CELL_SIZE); if (x >= 0 && x < width && y >= 0 && y < height) { return { x, y }; } return null; } // Установка типа ячейки function setCellType(x, y) { if (x >= 0 && x < width && y >= 0 && y < height) { map[y][x] = selectedType; drawMap(); } } // Обработчики событий мыши canvas.addEventListener('mousedown', (e) => { if (e.button === 2 || e.shiftKey) { isPanning = true; startPanX = e.clientX - offsetX; startPanY = e.clientY - offsetY; canvas.style.cursor = 'grabbing'; } else { isDrawing = true; const coords = getCellCoords(e); if (coords) { setCellType(coords.x, coords.y); } } }); canvas.addEventListener('mousemove', (e) => { if (isPanning) { offsetX = e.clientX - startPanX; offsetY = e.clientY - startPanY; drawMap(); } else if (isDrawing) { const coords = getCellCoords(e); if (coords) { setCellType(coords.x, coords.y); } } }); canvas.addEventListener('mouseup', () => { isDrawing = false; isPanning = false; canvas.style.cursor = 'default'; }); canvas.addEventListener('mouseleave', () => { isDrawing = false; isPanning = false; canvas.style.cursor = 'default'; }); canvas.addEventListener('wheel', (e) => { e.preventDefault(); const rect = canvas.getBoundingClientRect(); const mouseX = e.clientX - rect.left; const mouseY = e.clientY - rect.top; const zoom = e.deltaY < 0 ? 1.1 : 0.9; const newScale = Math.min(Math.max(0.1, scale * zoom), 5); offsetX = mouseX - (mouseX - offsetX) * (newScale / scale); offsetY = mouseY - (mouseY - offsetY) * (newScale / scale); scale = newScale; drawMap(); }); canvas.addEventListener('contextmenu', (e) => e.preventDefault()); // Изменение размера карты function resizeMap() { const newWidth = parseInt(document.getElementById('width').value); const newHeight = parseInt(document.getElementById('height').value); if (newWidth < 5 || newWidth > 100 || newHeight < 5 || newHeight > 100) { alert('Размеры должны быть от 5 до 100'); return; } const newMap = Array(newHeight).fill(null).map(() => Array(newWidth).fill(0)); // Копируем существующие данные const minHeight = Math.min(height, newHeight); const minWidth = Math.min(width, newWidth); for (let y = 0; y < minHeight; y++) { for (let x = 0; x < minWidth; x++) { newMap[y][x] = map[y][x]; } } width = newWidth; height = newHeight; map = newMap; resizeCanvas(); drawMap(); } // Очистка карты function clearMap() { if (confirm('Вы уверены, что хотите очистить всю карту?')) { initMap(); drawMap(); } } // Экспорт карты в JSON function exportMap() { const data = { map: map }; const json = JSON.stringify(data, null, 2); const blob = new Blob([json], { type: 'application/json' }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = `racing-map-${width}x${height}.json`; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); // Также выводим в консоль для быстрого копирования console.log('Экспортированная карта:', json); alert('Карта экспортирована! JSON также выведен в консоль браузера (F12)'); } // Импорт карты из JSON function importMap(event) { const file = event.target.files[0]; if (!file) return; const reader = new FileReader(); reader.onload = (e) => { try { const data = JSON.parse(e.target.result); const validated = validateMap(data); // Импортируем карту height = validated.height; width = validated.width; map = validated.map; // Обновляем UI document.getElementById('width').value = width; document.getElementById('height').value = height; resizeCanvas(); drawMap(); alert('Карта успешно импортирована!'); } catch (error) { alert('Ошибка при импорте: ' + error.message); console.error('Ошибка импорта:', error); } }; reader.readAsText(file); // Сброс input для возможности повторного импорта того же файла event.target.value = ''; } // Обработчики изменения размеров document.getElementById('width').addEventListener('keypress', (e) => { if (e.key === 'Enter') resizeMap(); }); document.getElementById('height').addEventListener('keypress', (e) => { if (e.key === 'Enter') resizeMap(); }); window.addEventListener('resize', () => { resizeCanvas(); drawMap(); }); init();