| 183 | } |
| 184 | |
| 185 | function computeVisibility(): void { |
| 186 | for (let i = 0; i < MAP_WIDTH * MAP_HEIGHT; i++) { |
| 187 | visible[i] = false; |
| 188 | } |
| 189 | |
| 190 | // Simple raycasting FOV |
| 191 | const steps = 360; |
| 192 | for (let a = 0; a < steps; a++) { |
| 193 | const angle = (a / steps) * Math.PI * 2; |
| 194 | const dx = Math.cos(angle); |
| 195 | const dy = Math.sin(angle); |
| 196 | let rx = player.x + 0.5; |
| 197 | let ry = player.y + 0.5; |
| 198 | for (let d = 0; d < FOV_RADIUS; d++) { |
| 199 | const tx = Math.floor(rx); |
| 200 | const ty = Math.floor(ry); |
| 201 | if (tx < 0 || tx >= MAP_WIDTH || ty < 0 || ty >= MAP_HEIGHT) break; |
| 202 | const idx = ty * MAP_WIDTH + tx; |
| 203 | visible[idx] = true; |
| 204 | explored[idx] = true; |
| 205 | if (map[idx] === TILE_WALL) break; |
| 206 | rx = rx + dx; |
| 207 | ry = ry + dy; |
| 208 | } |
| 209 | } |
| 210 | } |
| 211 | |
| 212 | function enemyAt(x: number, y: number): number { |
| 213 | for (let i = 0; i < enemies.length; i++) { |