'use strict'; // Browser: use window globals; Node: use require const _Const = typeof require !== 'undefined' ? require('./constants') : { TILE, COLS, ROWS, TILE_TYPES, COLORS }; // Create a layout grid with border walls and optional interior walls/torches function createBorderLayout(walls, torches) { const layout = []; for (let y = 0; y < _Const.ROWS; y++) { const row = []; for (let x = 0; x < _Const.COLS; x++) { if (y === 0 || y === _Const.ROWS - 1 || x === 0 || x === _Const.COLS - 1) { row.push(_Const.TILE_TYPES.WALL); } else { row.push(_Const.TILE_TYPES.FLOOR); } } layout.push(row); } if (walls) { for (const [wx, wy] of walls) { if (wy >= 0 && wy < _Const.ROWS && wx >= 0 && wx < _Const.COLS) { layout[wy][wx] = _Const.TILE_TYPES.WALL; } } } if (torches) { for (const [tx, ty] of torches) { if (ty >= 0 && ty < _Const.ROWS && tx >= 0 && tx < _Const.COLS) { layout[ty][tx] = _Const.TILE_TYPES.TORCH; } } } return layout; } // Create empty layout (all floor, border walls) function createEmptyLayout() { return createBorderLayout([], []); } // Draw a heart shape on canvas function drawHeart(ctx, cx, cy, size, filled) { ctx.save(); ctx.fillStyle = filled ? _Const.COLORS.heart : _Const.COLORS.heartEmpty; ctx.beginPath(); const s = size * 0.5; ctx.moveTo(cx, cy + s * 0.3); ctx.bezierCurveTo(cx, cy - s * 0.5, cx - s * 1.2, cy - s * 0.5, cx - s, cy + s * 0.1); ctx.bezierCurveTo(cx - s, cy + s * 0.7, cx, cy + s * 1.1, cx, cy + s * 1.1); ctx.bezierCurveTo(cx, cy + s * 0.7, cx + s, cy + s * 0.1, cx + s, cy + s * 0.1); ctx.bezierCurveTo(cx + s * 1.2, cy - s * 0.5, cx, cy - s * 0.5, cx, cy + s * 0.3); ctx.closePath(); ctx.fill(); ctx.restore(); } // Draw a heart container (full 2-HP heart) function drawHeartContainer(ctx, cx, cy) { drawHeart(ctx, cx, cy, 14, true); } // Manhattan distance function manhattanDist(x1, y1, x2, y2) { return Math.abs(x1 - x2) + Math.abs(y1 - y2); } // Check if position is within bounds function inBounds(x, y) { return x >= 0 && x < _Const.COLS && y >= 0 && y < _Const.ROWS; } // Deep clone a 2D array function cloneLayout(layout) { return layout.map(row => [...row]); } // Clamp value between min and max function clamp(val, min, max) { return Math.max(min, Math.min(max, val)); } // Linear interpolation function lerp(a, b, t) { return a + (b - a) * t; } // Check if tile is passable (not wall) function isPassable(layout, x, y) { if (!inBounds(x, y)) return false; const tile = layout[y][x]; return tile !== _Const.TILE_TYPES.WALL && tile !== _Const.TILE_TYPES.LAVA; } // Check if tile is a wall function isWall(layout, x, y) { if (!inBounds(x, y)) return true; return layout[y][x] === _Const.TILE_TYPES.WALL; } // Check if tile is a soft wall (breakable with bombs/hammer) function isSoftWall(layout, x, y) { if (!inBounds(x, y)) return false; return layout[y][x] === _Const.TILE_TYPES.SOFTWALL; } // Get floor color for theme function getFloorColor(theme, x, y) { const base = theme.floorColor || _Const.COLORS.floor; const dark = theme.floorDarkColor || _Const.COLORS.floorDark; return ((x + y) % 2 === 0) ? base : dark; } // Get wall colors for theme function getWallColors(theme) { return { wall: theme.wallColor || _Const.COLORS.wall, top: theme.wallTopColor || _Const.COLORS.wallTop, front: theme.wallFrontColor || _Const.COLORS.wallFront, bottom: theme.wallBottomColor || _Const.COLORS.wallBottom, }; } // Format number with commas function formatNumber(n) { return n.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ','); } if (typeof module !== 'undefined' && module.exports) { module.exports = { createBorderLayout, createEmptyLayout, drawHeart, drawHeartContainer, manhattanDist, inBounds, cloneLayout, clamp, lerp, isPassable, isWall, isSoftWall, getFloorColor, getWallColors, formatNumber }; }