about summary refs log blame commit diff stats
path: root/js/magic-bird/map.js
blob: 212bbfa3d3bbec4284357fb262379ef3d6b0eade (plain) (tree)
















































































































































































































                                                                                                         
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;