Created
June 28, 2023 00:59
-
-
Save snuffyDev/076ea425f698b31d5a0b935e4e4626b1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import type { ExtendedEntityV2 } from "$lib/types/core/map"; | |
import type { Position2D } from "$lib/types/position"; | |
type Portal = { | |
position: Position2D; | |
connectedRegion: number; | |
}; | |
type Region = { | |
portals: Portal[]; | |
connectedRegions: number[]; | |
tiles: Position2D[]; | |
}; | |
function preprocessLevel(level: ExtendedEntityV2[][]): Region[] { | |
const regions: Region[] = []; | |
const visitedTiles: Set<string> = new Set(); | |
function findConnectedRegion(tile: Position2D, regionIndex: number): void { | |
const region = regions[regionIndex]; | |
const { x, z } = tile; | |
const key = `${x},${z}`; | |
if (visitedTiles.has(key)) { | |
return; | |
} | |
visitedTiles.add(key); | |
region.tiles.push(tile); | |
const adjacentTiles: Position2D[] = [ | |
{ x: x, z: z - 1 }, // left | |
{ x: x, z: z + 1 }, // right | |
{ x: x - 1, z: z }, // front | |
{ x: x + 1, z: z } // back | |
]; | |
for (const adjacentTile of adjacentTiles) { | |
const { x: adjX, z: adjZ } = adjacentTile; | |
if (adjX >= 0 && adjX < level.length && adjZ >= 0 && adjZ < level[adjZ].length) { | |
const adjacentTileData = level[adjZ][adjX]; | |
const adjacentTileKey = `${adjX},${adjZ}`; | |
if (adjacentTileData.component !== "Object" || visitedTiles.has(adjacentTileKey)) { | |
continue; | |
} | |
if ( | |
adjacentTileData.component === "Door" && | |
adjacentTileData.attributes?.state === "closed" | |
) { | |
const connectedRegion = regionIndex; | |
region.portals.push({ | |
position: adjacentTile, | |
connectedRegion | |
}); | |
region.connectedRegions.push(connectedRegion); | |
} else if (adjacentTileData.blocking !== false) { | |
region.tiles.push(adjacentTileData.position); | |
visitedTiles.add(adjacentTileKey); | |
if (visitedTiles.has(adjacentTileKey)) { | |
continue; | |
} | |
findConnectedRegion(adjacentTile, regionIndex); | |
} else if (adjacentTileData.component) { | |
// if (visitedTiles.has(adjacentTileKey)) { | |
// continue; | |
// } | |
region.tiles.push(adjacentTileData.position); | |
visitedTiles.add(adjacentTileKey); | |
// findConnectedRegion(adjacentTile, regionIndex); | |
if (visitedTiles.has(adjacentTileKey)) { | |
continue; | |
} | |
} | |
} | |
} | |
} | |
function findPortalsInRegion(regionIndex: number): void { | |
const region = regions[regionIndex]; | |
for (const portal of region.portals) { | |
const { position, connectedRegion } = portal; | |
findConnectedRegion(position, connectedRegion); | |
} | |
} | |
for (let x = 0; x < level.length; x++) { | |
for (let z = 0; z < level[x].length; z++) { | |
const tile = level[z][x]; | |
if (!visitedTiles.has(`${x},${z}`) && tile.component === "Door") { | |
const region: Region = { | |
portals: [], | |
connectedRegions: [], | |
tiles: [] | |
}; | |
regions.push(region); | |
findConnectedRegion({ x, z }, regions.length - 1); | |
} | |
} | |
} | |
for (let i = 0; i < regions.length; i++) { | |
findPortalsInRegion(i); | |
} | |
return regions; | |
} | |
export { Portal, Region, preprocessLevel }; | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export function raycast(origin: Position2D, rotation: number): string[] { | |
const visiblePositions: string[] = []; | |
let stepSize = 0.5; // This determines the resolution of the raycast | |
let maxSteps = 10000; // This is a safeguard to prevent infinite loops in case of errors | |
forLoop: for (let step = 0; step < maxSteps; step++) { | |
let index = 0; | |
let x = origin.x + step * stepSize * Math.cos(rotation); | |
let z = origin.z + step * stepSize * Math.sin(rotation); | |
let tilePos: Position2D = { x: Math.floor(x + 0.5), z: Math.floor(z + 0.5) }; | |
test: while (++index < 2) { | |
const entity = CurrentLevel.checkCollisionWithWorld(tilePos, true); | |
if (entity !== null) { | |
visiblePositions.push(`${tilePos.x}${tilePos.z}`); | |
if (entity) { | |
break forLoop; | |
} | |
continue test; | |
} | |
} | |
} | |
return visiblePositions; | |
} | |
export function castRays( | |
viewer: Position2D, | |
viewerRotation: number, | |
models: Model[], | |
fov: number, | |
callback: (model: Model, state: boolean) => void | |
): void { | |
// Calculate the rotation angles for the leftmost and rightmost rays | |
// Calculate the rotation angles for the leftmost and rightmost rays | |
const leftRotation = viewerRotation - fov / 2; | |
const rightRotation = viewerRotation + fov / 2; | |
// Calculate the number of rays to cast within the field of view | |
const numRays = ~~(Math.ceil((window.innerWidth / window.innerHeight) * 180) * Math.PI); | |
const rayVisibleEntities: string[] = []; | |
// Cast the rays | |
for (let i = 0; i < numRays; i++) { | |
// Interpolate the rotation angle for this ray | |
const rotation = normalizeAngle( | |
leftRotation + ((rightRotation - leftRotation) * i) / (numRays - 1) | |
); | |
// add the visible entities to the list | |
rayVisibleEntities.push(...raycast(viewer, rotation)); | |
} | |
// Check each model's visibility | |
for (const entity of models) { | |
const position = entity.getLocalPosition(); | |
const isVisible = rayVisibleEntities.includes(`${position.x}${position.z}`); | |
callback(entity, isVisible); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment