diff options
Diffstat (limited to 'js/magic-bird/map.js')
-rw-r--r-- | js/magic-bird/map.js | 209 |
1 files changed, 209 insertions, 0 deletions
diff --git a/js/magic-bird/map.js b/js/magic-bird/map.js new file mode 100644 index 0000000..212bbfa --- /dev/null +++ b/js/magic-bird/map.js @@ -0,0 +1,209 @@ +const canvas = document.getElementById('map'); +const ctx = canvas.getContext('2d'); +canvas.width = window.innerWidth; +canvas.height = window.innerHeight; + +const cellSize = 16; // Size of each cell in pixels +const gridWidth = 100; // Number of cells in width +const gridHeight = 100; // Number of cells in height + +let grid = []; +let history = []; // undo stack +let offsetX = 0; +let offsetY = 0; +let selectedNumber = 0; + +const initializeGrid = () => { + grid = Array.from({ length: gridHeight }, () => Array(gridWidth).fill(0)); +}; + +// a hack to preload map assets +const preLoad = [ + 'imgs/extracted-1688-map/MapParts/trees/50.png', + 'imgs/extracted-1688-map/MapParts/hills/8.png', + 'imgs/extracted-1688-map/MapParts/mountains/18.png', + 'imgs/extracted-1688-map/MapParts/towns/9.png', + 'imgs/extracted-1688-map/MapParts/cities/33.png', +].map(src => { + let images = new Image(); + images.src = src; + return images; +}); + +const drawGrid = () => { + ctx.clearRect(0, 0, canvas.width, canvas.height); + + if ( + offsetX < 0 || + offsetX > gridWidth * cellSize - canvas.width || + offsetY < 0 || + offsetY > gridHeight * cellSize - canvas.height + ) { + ctx.fillStyle = '#91f7bd'; + ctx.fillRect(0, 0, canvas.width, canvas.height); + } + + grid.forEach((row, i) => { + row.forEach((state, j) => { + let color = '#000'; + let imageSrc = ''; + switch (state) { + case 0: + color = 'beige'; + break; + case 1: + imageSrc = 'imgs/extracted-1688-map/MapParts/trees/50.png' + break; + case 2: + imageSrc = 'imgs/extracted-1688-map/MapParts/hills/8.png'; + break; + case 3: + imageSrc = 'imgs/extracted-1688-map/MapParts/mountains/18.png'; + break; + case 4: + imageSrc = 'imgs/extracted-1688-map/MapParts/towns/9.png'; + break; + case 5: + imageSrc = 'imgs/extracted-1688-map/MapParts/cities/33.png'; + break; + case 6: + color = '#f0f'; + break; + case 7: + color = '#0ff'; + break; + case 8: + color = '#f80'; + break; + case 9: + color = '#08f'; + break; + } + if (imageSrc) { + ctx.fillStyle = 'beige'; + ctx.fillRect(j * cellSize - offsetX, i * cellSize - offsetY, cellSize, cellSize); + ctx.strokeStyle = '#ddd'; + ctx.strokeRect(j * cellSize - offsetX, i * cellSize - offsetY, cellSize, cellSize); + const image = new Image(); + image.src = imageSrc; + ctx.drawImage(image, j * cellSize - offsetX, i * cellSize - offsetY, cellSize, cellSize); + } else { + ctx.fillStyle = color; + ctx.fillRect(j * cellSize - offsetX, i * cellSize - offsetY, cellSize, cellSize); + ctx.strokeStyle = '#ddd'; + ctx.strokeRect(j * cellSize - offsetX, i * cellSize - offsetY, cellSize, cellSize); + } + }); + }); +}; + +const saveGrid = () => { + localStorage.setItem('grid', JSON.stringify(grid)); +}; + +const loadGrid = () => { + const savedGrid = localStorage.getItem('grid'); + if (savedGrid) { + grid = JSON.parse(savedGrid); + } +}; + +const handleKeyDown = (e) => { + switch (e.key) { + case 'ArrowUp': + offsetY -= cellSize; + break; + case 'ArrowDown': + offsetY += cellSize; + break; + case 'ArrowLeft': + offsetX -= cellSize; + break; + case 'ArrowRight': + offsetX += cellSize; + break; + case 'h': + alert(`Controls: + - Arrow keys to move the map + - Click to place a tile + - Number keys, 0 - 9, to select a tile type + - Z to undo + - C to clear the grid + - E to export the grid as json + - S to export the grid as a png + - H to display this help text + `); + break; + case 'z': + if (history.length > 0) { + const lastChange = history.pop(); // Pop last change from history + grid[lastChange.y][lastChange.x] = lastChange.state; // Revert state + drawGrid(); + } + break; + case 'c': + if (window.confirm('Are you sure you want to clear the grid?')) { + initializeGrid(); + drawGrid(); + } + break; + case 'e': + const mapName = prompt('Please enter a name for the map:'); + if (mapName) { + if (window.confirm(`Are you sure you want to export the grid as ${mapName}?`)) { + exportGridAsJson(mapName); + } + } + break; + case 's': + const imgName = prompt('Please enter a name for the image:'); + if (imgName) { + exportGridAsPng(imgName); + } + break; + default: + if (e.key >= '0' && e.key <= '9') { + selectedNumber = parseInt(e.key); + } + break; + } + drawGrid(); +}; + +const handleCanvasClick = (e) => { + const x = Math.floor((e.clientX + offsetX) / cellSize); + const y = Math.floor((e.clientY + offsetY) / cellSize); + history.push({ x, y, state: grid[y][x] }); // Push current state to history + grid[y][x] = selectedNumber; // Set state to selectedNumber + drawGrid(); +}; + +const exportGridAsJson = (mapName) => { + const dataStr = "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(grid)); + const downloadAnchorNode = document.createElement('a'); + downloadAnchorNode.setAttribute("href", dataStr); + downloadAnchorNode.setAttribute("download", `${mapName}.json`); + document.body.appendChild(downloadAnchorNode); // evidently this is required for firefox + downloadAnchorNode.click(); + downloadAnchorNode.remove(); +}; + +const exportGridAsPng = (imgName) => { + const dataUrl = canvas.toDataURL('image/png'); + const downloadAnchorNode = document.createElement('a'); + downloadAnchorNode.setAttribute("href", dataUrl); + downloadAnchorNode.setAttribute("download", `${imgName}.png`); + document.body.appendChild(downloadAnchorNode); + downloadAnchorNode.click(); + downloadAnchorNode.remove(); +}; + +window.addEventListener('keydown', handleKeyDown); +canvas.addEventListener('click', handleCanvasClick); +window.onload = () => { + initializeGrid(); + loadGrid(); + drawGrid(); +}; + +window.onunload = saveGrid; \ No newline at end of file |