Files
nissefolk/src/utils/noise.ts
2026-03-20 08:11:31 +00:00

78 lines
2.3 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { createNoise2D } from 'simplex-noise'
import { WORLD_TILES } from '../config'
import { TileType } from '../types'
/** Simple seeded PRNG (mulberry32) */
function mulberry32(seed: number): () => number {
return () => {
seed |= 0
seed = (seed + 0x6D2B79F5) | 0
let t = Math.imul(seed ^ (seed >>> 15), 1 | seed)
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t
return ((t ^ (t >>> 14)) >>> 0) / 4294967296
}
}
function classify(e: number, m: number): TileType {
if (e < 0.22) return TileType.DEEP_WATER
if (e < 0.30) return TileType.SHALLOW_WATER
if (e < 0.38) return TileType.SAND
if (e > 0.82) return TileType.ROCK
// land split by moisture
if (m > 0.62 && e > 0.48) return TileType.FOREST
if (m > 0.38) return TileType.DARK_GRASS
return TileType.GRASS
}
export function generateTerrain(seed: number): number[] {
const prng1 = mulberry32(seed)
const prng2 = mulberry32(seed ^ 0xDEADBEEF)
const elevNoise = createNoise2D(prng1)
const moistNoise = createNoise2D(prng2)
const size = WORLD_TILES
const tiles = new Array<number>(size * size)
for (let y = 0; y < size; y++) {
for (let x = 0; x < size; x++) {
// Multi-octave elevation
const nx = x / size
const ny = y / size
const e =
(elevNoise(nx * 4, ny * 4) * 1.0 +
elevNoise(nx * 8, ny * 8) * 0.5 +
elevNoise(nx * 16, ny * 16) * 0.25) / 1.75
const eNorm = (e + 1) / 2 // -1..1 → 0..1
const m = moistNoise(nx * 6 + 10, ny * 6 + 10)
const mNorm = (m + 1) / 2
tiles[y * size + x] = classify(eNorm, mNorm)
}
}
return tiles
}
/** Find a walkable spawn tile near the world center */
export function findSpawn(tiles: number[]): { tileX: number; tileY: number } {
const center = Math.floor(WORLD_TILES / 2)
const walkable = new Set([TileType.GRASS, TileType.DARK_GRASS, TileType.SAND])
for (let r = 0; r < center; r++) {
for (let dy = -r; dy <= r; dy++) {
for (let dx = -r; dx <= r; dx++) {
if (Math.abs(dx) !== r && Math.abs(dy) !== r) continue
const tx = center + dx
const ty = center + dy
if (tx < 0 || ty < 0 || tx >= WORLD_TILES || ty >= WORLD_TILES) continue
if (walkable.has(tiles[ty * WORLD_TILES + tx])) {
return { tileX: tx, tileY: ty }
}
}
}
}
return { tileX: center, tileY: center }
}