split editor

This commit is contained in:
2025-10-20 21:07:28 +05:00
parent 023ccd03d8
commit 88643415aa
34 changed files with 3796 additions and 1184 deletions

253
racing-tools/editor.js Normal file
View File

@@ -0,0 +1,253 @@
// Состояние редактора
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();