This commit is contained in:
wfz
2026-04-15 20:58:16 +08:00
commit 8ab9a9fa70
33 changed files with 6140 additions and 0 deletions

250
frontend/src/utils/grid.js Normal file
View File

@@ -0,0 +1,250 @@
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
}