import { GRID_SIZE, CELL_SIZE, WALL_SIZE } from './constants.js' export class Grid { constructor(mapData) { this.width = GRID_SIZE this.height = GRID_SIZE this.cells = [] this.parseMap(mapData) } parseMap(mapData) { this.cells = [] for (let y = 0; y < this.height; y++) { this.cells[y] = [] for (let x = 0; x < this.width; x++) { this.cells[y][x] = mapData[y] ? mapData[y][x] || 0 : 0 } } } isWall(gridX, gridY) { if (gridX < 0 || gridX >= this.width || gridY < 0 || gridY >= this.height) return true return this.cells[gridY][gridX] === 1 } isSpawnPoint(gridX, gridY) { if (gridX < 0 || gridX >= this.width || gridY < 0 || gridY >= this.height) return false return this.cells[gridY][gridX] === 2 } worldToGrid(wx, wy) { return { x: Math.floor(wx / CELL_SIZE), y: Math.floor(wy / CELL_SIZE) } } gridToWorld(gx, gy) { return { x: gx * CELL_SIZE + CELL_SIZE / 2, y: gy * CELL_SIZE + CELL_SIZE / 2 } } isWalkable(wx, wy, size) { const half = size / 2 const corners = [ { x: wx - half, y: wy - half }, { x: wx + half, y: wy - half }, { x: wx - half, y: wy + half }, { x: wx + half, y: wy + half } ] for (const corner of corners) { const g = this.worldToGrid(corner.x, corner.y) if (this.isWall(g.x, g.y)) return false } return true } getSpawnPoints() { const points = [] for (let y = 0; y < this.height; y++) { for (let x = 0; x < this.width; x++) { if (this.cells[y][x] === 2) { points.push({ x, y }) } } } return points } findPath(startX, startY, endX, endY) { const sg = this.worldToGrid(startX, startY) const eg = this.worldToGrid(endX, endY) if (this.isWall(eg.x, eg.y)) return null const openSet = [] const closedSet = new Set() const cameFrom = new Map() const heuristic = (ax, ay, bx, by) => Math.abs(ax - bx) + Math.abs(ay - by) const startKey = `${sg.x},${sg.y}` openSet.push({ x: sg.x, y: sg.y, g: 0, h: heuristic(sg.x, sg.y, eg.x, eg.y), f: heuristic(sg.x, sg.y, eg.x, eg.y) }) const directions = [ { dx: 0, dy: -1 }, { dx: 0, dy: 1 }, { dx: -1, dy: 0 }, { dx: 1, dy: 0 }, { dx: -1, dy: -1 }, { dx: 1, dy: -1 }, { dx: -1, dy: 1 }, { dx: 1, dy: 1 } ] let iterations = 0 const maxIterations = 2000 while (openSet.length > 0 && iterations < maxIterations) { iterations++ openSet.sort((a, b) => a.f - b.f) const current = openSet.shift() const currentKey = `${current.x},${current.y}` if (current.x === eg.x && current.y === eg.y) { const path = [] let key = currentKey while (cameFrom.has(key)) { const [cx, cy] = key.split(',').map(Number) const wp = this.gridToWorld(cx, cy) path.unshift({ x: wp.x, y: wp.y }) key = cameFrom.get(key) } return path } closedSet.add(currentKey) for (const dir of directions) { const nx = current.x + dir.dx const ny = current.y + dir.dy const nKey = `${nx},${ny}` if (closedSet.has(nKey)) continue if (this.isWall(nx, ny)) continue if (nx < 0 || nx >= this.width || ny < 0 || ny >= this.height) continue if (dir.dx !== 0 && dir.dy !== 0) { if (this.isWall(current.x + dir.dx, current.y) || this.isWall(current.x, current.y + dir.dy)) { continue } } const isDiagonal = dir.dx !== 0 && dir.dy !== 0 const moveCost = isDiagonal ? 1.414 : 1 const g = current.g + moveCost const existing = openSet.find(n => n.x === nx && n.y === ny) if (existing) { if (g < existing.g) { existing.g = g existing.f = g + existing.h cameFrom.set(nKey, currentKey) } } else { const h = heuristic(nx, ny, eg.x, eg.y) openSet.push({ x: nx, y: ny, g, h, f: g + h }) cameFrom.set(nKey, currentKey) } } } return null } } export function generateDefaultMap() { const map = [] for (let y = 0; y < GRID_SIZE; y++) { map[y] = [] for (let x = 0; x < GRID_SIZE; x++) { if (x === 0 || x === GRID_SIZE - 1 || y === 0 || y === GRID_SIZE - 1) { map[y][x] = 1 } else { map[y][x] = 0 } } } const wallSegments = [ { x1: 5, y1: 5, x2: 5, y2: 10 }, { x1: 10, y1: 3, x2: 15, y2: 3 }, { x1: 20, y1: 5, x2: 20, y2: 12 }, { x1: 8, y1: 15, x2: 14, y2: 15 }, { x1: 25, y1: 10, x2: 25, y2: 18 }, { x1: 3, y1: 20, x2: 8, y2: 20 }, { x1: 15, y1: 20, x2: 15, y2: 26 }, { x1: 22, y1: 22, x2: 28, y2: 22 }, { x1: 5, y1: 25, x2: 10, y2: 25 }, { x1: 12, y1: 8, x2: 12, y2: 12 }, { x1: 18, y1: 16, x2: 22, y2: 16 }, { x1: 27, y1: 5, x2: 27, y2: 9 }, { x1: 8, y1: 27, x2: 13, y2: 27 }, { x1: 18, y1: 26, x2: 18, y2: 30 } ] for (const seg of wallSegments) { if (seg.x1 === seg.x2) { for (let y = seg.y1; y <= seg.y2; y++) { if (seg.x1 > 0 && seg.x1 < GRID_SIZE - 1 && y > 0 && y < GRID_SIZE - 1) { map[y][seg.x1] = 1 } } } else { for (let x = seg.x1; x <= seg.x2; x++) { if (x > 0 && x < GRID_SIZE - 1 && seg.y1 > 0 && seg.y1 < GRID_SIZE - 1) { map[seg.y1][x] = 1 } } } } const spawnPoints = [ { x: 2, y: 2 }, { x: 29, y: 2 }, { x: 2, y: 29 }, { x: 29, y: 29 } ] for (const sp of spawnPoints) { map[sp.y][sp.x] = 2 for (let dy = -1; dy <= 1; dy++) { for (let dx = -1; dx <= 1; dx++) { const ny = sp.y + dy const nx = sp.x + dx if (ny > 0 && ny < GRID_SIZE - 1 && nx > 0 && nx < GRID_SIZE - 1) { if (map[ny][nx] === 1) map[ny][nx] = 0 } } } } const zombieSpawns = [ { x: 16, y: 2 }, { x: 2, y: 16 }, { x: 29, y: 16 }, { x: 16, y: 29 }, { x: 16, y: 16 } ] for (const sp of zombieSpawns) { for (let dy = -1; dy <= 1; dy++) { for (let dx = -1; dx <= 1; dx++) { const ny = sp.y + dy const nx = sp.x + dx if (ny > 0 && ny < GRID_SIZE - 1 && nx > 0 && nx < GRID_SIZE - 1) { if (map[ny][nx] === 1) map[ny][nx] = 0 } } } } return map }