Files
OpenClaw-bot-review/lib/pixel-office/layout/tileMap.ts
T
xmanrui eebe431560 feat: implement Pixel Office Phase 2 - layout editor, persistence, sound, sub-agent visualization
- Add layout editor with floor/wall paint, erase, furniture place/move/rotate/delete tools
- Add editor state management (undo/redo, selection, drag, ghost preview)
- Add EditorToolbar and EditActionBar UI components
- Add layout persistence API (GET/POST to ~/.openclaw/pixel-office/layout.json)
- Add notification sound system (Web Audio API ascending chime on agent completion)
- Add sub-agent visualization via session JSONL parsing
- Fix agent activity API to read from agents.list config and correct session paths
- Add agent name labels above character sprites in canvas renderer
- Add i18n translations for editor features (zh/en)
2026-02-25 01:39:44 +08:00

106 lines
3.0 KiB
TypeScript

import { TileType } from '../types'
/** Check if a tile is walkable (floor, carpet, or doorway, and not blocked by furniture) */
export function isWalkable(
col: number,
row: number,
tileMap: TileType[][],
blockedTiles: Set<string>,
): boolean {
const rows = tileMap.length
const cols = rows > 0 ? tileMap[0].length : 0
if (row < 0 || row >= rows || col < 0 || col >= cols) return false
const t = tileMap[row][col]
if (t === TileType.WALL || t === TileType.VOID) return false
if (blockedTiles.has(`${col},${row}`)) return false
return true
}
/** Get walkable tile positions (grid coords) for wandering */
export function getWalkableTiles(
tileMap: TileType[][],
blockedTiles: Set<string>,
): Array<{ col: number; row: number }> {
const rows = tileMap.length
const cols = rows > 0 ? tileMap[0].length : 0
const tiles: Array<{ col: number; row: number }> = []
for (let r = 0; r < rows; r++) {
for (let c = 0; c < cols; c++) {
if (isWalkable(c, r, tileMap, blockedTiles)) {
tiles.push({ col: c, row: r })
}
}
}
return tiles
}
/** BFS pathfinding on 4-connected grid (no diagonals). Returns path excluding start, including end. */
export function findPath(
startCol: number,
startRow: number,
endCol: number,
endRow: number,
tileMap: TileType[][],
blockedTiles: Set<string>,
): Array<{ col: number; row: number }> {
if (startCol === endCol && startRow === endRow) return []
const key = (c: number, r: number) => `${c},${r}`
const startKey = key(startCol, startRow)
const endKey = key(endCol, endRow)
// End must be walkable (or be a chair tile which may be adjacent to desk)
// We allow the end tile even if it's not strictly walkable for chair positions
const endWalkable = isWalkable(endCol, endRow, tileMap, blockedTiles)
if (!endWalkable) {
// If the end is a desk tile, we still can't path there
return []
}
const visited = new Set<string>()
visited.add(startKey)
const parent = new Map<string, string>()
const queue: Array<{ col: number; row: number }> = [{ col: startCol, row: startRow }]
const dirs = [
{ dc: 0, dr: -1 }, // up
{ dc: 0, dr: 1 }, // down
{ dc: -1, dr: 0 }, // left
{ dc: 1, dr: 0 }, // right
]
while (queue.length > 0) {
const curr = queue.shift()!
const currKey = key(curr.col, curr.row)
if (currKey === endKey) {
// Reconstruct path
const path: Array<{ col: number; row: number }> = []
let k = endKey
while (k !== startKey) {
const [c, r] = k.split(',').map(Number)
path.unshift({ col: c, row: r })
k = parent.get(k)!
}
return path
}
for (const d of dirs) {
const nc = curr.col + d.dc
const nr = curr.row + d.dr
const nk = key(nc, nr)
if (visited.has(nk)) continue
if (!isWalkable(nc, nr, tileMap, blockedTiles)) continue
visited.add(nk)
parent.set(nk, currKey)
queue.push({ col: nc, row: nr })
}
}
// No path found
return []
}