251 lines
6.3 KiB
JavaScript
251 lines
6.3 KiB
JavaScript
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
|
|
}
|