You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
113 lines
3.4 KiB
TypeScript
113 lines
3.4 KiB
TypeScript
import alea from 'alea'
|
|
import { createNoise2D, type NoiseFunction2D } from 'simplex-noise'
|
|
import createBlockGenerator from './blockGen'
|
|
import createBlockExtender from './blockExt'
|
|
|
|
import {blockTypes, blockTypes as T, level as L, type Block, type BlockType} from './def'
|
|
|
|
// describes a changed block, eg digged or placed by the player
|
|
type DamagedBlock = {
|
|
type: 'damage'
|
|
x: number
|
|
y: number
|
|
damage: number
|
|
}
|
|
type ChangedBlock = {
|
|
type: 'exchange'
|
|
x: number
|
|
y: number
|
|
newType: BlockType
|
|
}
|
|
type Change = DamagedBlock | ChangedBlock
|
|
|
|
const MAX_LIGHT = 100 // maximum level where light shines
|
|
|
|
export default function createLevel(width: number, height: number, seed = 'extremely random seed') {
|
|
const prng = alea(seed)
|
|
const noise2D = createNoise2D(prng)
|
|
const rand: NoiseFunction2D = (x, y) => 0.5 + 0.5 * noise2D(x, y)
|
|
|
|
// stores the current grid of blocks, visible on the screen
|
|
const _grid: Block[][] = new Array(height)
|
|
// stores the limit to where light still shines,
|
|
// for each column currently visible on the screen
|
|
const _lightBarrier: number[] = [...new Array(width)].map(() => MAX_LIGHT)
|
|
const _changes: Change[][] = []
|
|
|
|
const blockGen = createBlockGenerator(rand)
|
|
const blockExt = createBlockExtender(rand)
|
|
|
|
// Apply changes, coming from the player (tocktocktock-plopp!)
|
|
function change(change: Change) {
|
|
const x = change.x
|
|
if (!_changes[x]) _changes[x] = [change]
|
|
else _changes[x].push(change)
|
|
}
|
|
|
|
function applyPlayerChanges(columnOffset: number, levelOffset: number) {
|
|
for (let col = columnOffset; col < columnOffset + width; col++) {
|
|
const changes = _changes[col]
|
|
if (changes) {
|
|
const maxLevel = levelOffset + height
|
|
changes.forEach(c => {
|
|
if (c.type !== 'exchange' || c.y < levelOffset || c.y >= maxLevel) return
|
|
_grid[c.y - levelOffset][c.x - columnOffset] = blockTypes[c.newType]
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
// takes the current columnOffset and generates all blocks from the very top
|
|
// until a block is generated that blocks light. The height of that block is
|
|
// stored in the lightBarrier list
|
|
function calcLightBarrier(columnOffset: number) {
|
|
let previousBlock: Block = T.air
|
|
|
|
for (let col = 0; col < width; col++) {
|
|
for (let level = 0; level < MAX_LIGHT; level++) {
|
|
let block = blockGen.generateBlock(level, col + columnOffset)
|
|
block = blockExt.extendBlock(level, col, columnOffset, block, previousBlock)
|
|
|
|
const changes = _changes[columnOffset + col]
|
|
if (changes) {
|
|
const change = changes.find(c => c.y === level)
|
|
if (change && change.type === 'exchange') block = blockTypes[change.newType]
|
|
}
|
|
|
|
previousBlock = block
|
|
|
|
if (!block.transparent) {
|
|
_lightBarrier[col] = level
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function generate(columnOffset: number, levelOffset: number) {
|
|
for (let i = 0; i < height; i++) {
|
|
const level = levelOffset + i
|
|
const row: Block[] = Array(width)
|
|
const previousRow = i ? _grid[i-1] : [] as Block[]
|
|
|
|
blockGen.fillRow(level, columnOffset, row)
|
|
blockExt.extendRow(level, columnOffset, row, previousRow)
|
|
|
|
_grid[i] = row
|
|
}
|
|
applyPlayerChanges(columnOffset, levelOffset)
|
|
}
|
|
|
|
function sunLight(columnOffset: number) {
|
|
calcLightBarrier(columnOffset)
|
|
return _lightBarrier
|
|
}
|
|
|
|
function grid(x: number, y: number) {
|
|
generate(x, y)
|
|
return _grid
|
|
}
|
|
|
|
return { grid, sunLight, change }
|
|
}
|