'use strict'; const TOOLS = ['paint', 'box', 'picker', 'eraser', 'fill']; const TOOL_LABELS = { paint: '画笔:在网格上点击或拖拽绘制(需先在右侧选瓦片)', box: '框选:拖拽矩形区域,松开后用当前瓦片填满', picker: '吸管:点击已有格子,吸取瓦片为当前画笔', eraser: '橡皮擦:点击或拖拽删除当前层的格子', fill: '油漆桶:填充相连的同类型格子(需先选瓦片)', }; const TOOL_CURSORS = { paint: 'crosshair', box: 'crosshair', picker: 'copy', eraser: 'not-allowed', fill: 'cell', }; function layerMap(state) { return state.editLayer === 'ground' ? state.config.ground : state.config.border; } function getCell(state, key) { const map = layerMap(state); return map[key]; } function hasCell(state, key) { return getCell(state, key) !== undefined; } function removeCell(state, key) { if (state.editLayer === 'ground') delete state.config.ground[key]; else delete state.config.border[key]; } function setCell(state, key, tileKey) { if (state.editLayer === 'ground') state.config.ground[key] = tileKey; else state.config.border[key] = tileKey; } function canPaintBrush(state) { return state.brush && state.brush.layer === state.editLayer; } function fillRectangle(state, x0, y0, x1, y1, tileKey) { const minX = Math.min(x0, x1); const maxX = Math.max(x0, x1); const minY = Math.min(y0, y1); const maxY = Math.max(y0, y1); let n = 0; for (let x = minX; x <= maxX; x++) { for (let y = minY; y <= maxY; y++) { setCell(state, `${x},${y}`, tileKey); n++; } } return n; } function eraseRectangle(state, x0, y0, x1, y1) { const minX = Math.min(x0, x1); const maxX = Math.max(x0, x1); const minY = Math.min(y0, y1); const maxY = Math.max(y0, y1); let n = 0; for (let x = minX; x <= maxX; x++) { for (let y = minY; y <= maxY; y++) { const key = `${x},${y}`; if (hasCell(state, key)) { removeCell(state, key); n++; } } } return n; } /** 四连通洪水填充(仅当前编辑层) */ function floodFill(state, startKey, fillTileKey) { const map = layerMap(state); const target = map[startKey]; if (target === fillTileKey) return 0; const q = [startKey]; const seen = new Set([startKey]); let n = 0; while (q.length) { const key = q.shift(); map[key] = fillTileKey; n++; const [xs, ys] = key.split(','); const x = parseInt(xs, 10); const y = parseInt(ys, 10); for (const [nx, ny] of [[x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]]) { const nk = `${nx},${ny}`; if (seen.has(nk)) continue; const v = map[nk]; const same = target === undefined ? v === undefined : v === target; if (!same) continue; seen.add(nk); q.push(nk); } } return n; } /** 洪水清除(橡皮擦油漆桶:删相连同值区域) */ function floodErase(state, startKey) { const map = layerMap(state); if (map[startKey] === undefined) return 0; const target = map[startKey]; const q = [startKey]; const seen = new Set([startKey]); let n = 0; while (q.length) { const key = q.shift(); delete map[key]; n++; const [xs, ys] = key.split(','); const x = parseInt(xs, 10); const y = parseInt(ys, 10); for (const [nx, ny] of [[x + 1, y], [x - 1, y], [x, y + 1], [x, y - 1]]) { const nk = `${nx},${ny}`; if (seen.has(nk) || map[nk] !== target) continue; seen.add(nk); q.push(nk); } } return n; } function findPaletteTile(state, tileKey, layer) { return state.tiles.find((t) => t.tileKey === tileKey && t.layer === layer) || null; } module.exports = { TOOLS, TOOL_LABELS, TOOL_CURSORS, layerMap, getCell, hasCell, removeCell, setCell, canPaintBrush, fillRectangle, eraseRectangle, floodFill, floodErase, findPaletteTile, };