diff --git a/island/generators.ts b/island/generators.ts index 01b3dfa..11c46e7 100644 --- a/island/generators.ts +++ b/island/generators.ts @@ -168,6 +168,50 @@ export const NO_ISLAND: LobeGeneratorConstructor = return true; }; +const SINKHOLE_BURNOUT = 1500; + +/** carve out elevation */ +export const SINKHOLE: LobeGeneratorConstructor = ( + islands: IslandGrid, + basePos: number +) => { + // emergency cutoff in case sinkhole ends up fighting a generator that terminates at a given elevation + let ticks = 0; + return () => { + if (ticks++ < SINKHOLE_BURNOUT) { + const sunk = islands.floodSearch(basePos, (tile) => tile < 0); + + islands.sinkhole(islands.choose(sunk)); + } + + return true; + }; +}; + +/** carve out elevation more aggressively */ +export const WIDE_SINKHOLE: LobeGeneratorConstructor = ( + islands: IslandGrid, + basePos: number +) => { + // emergency cutoff in case sinkhole ends up fighting a generator that terminates at a given elevation + let ticks = 0; + return () => { + if (ticks++ < SINKHOLE_BURNOUT) { + const sunk = islands.floodSearch(basePos, (tile) => tile < 0); + + const sunkEdge = sunk.filter((pos) => islands.data[pos] >= WATER); + + if (sunkEdge.length > 0) { + islands.sinkhole(islands.choose(sunkEdge)); + } else { + islands.sinkhole(islands.choose(sunk)); + } + } + + return true; + }; +}; + export const BIG_ISLANDS = [BIG_MOUNTAIN, BIG_BEACH, HILLY_FOREST]; export const ROCKY_ISLANDS = [SMALL_MOUNTAIN, BIG_MOUNTAIN, HILLY_FOREST]; export const GREEN_ISLANDS = [ @@ -186,3 +230,4 @@ export const ALL_ISLANDS = [ HILLY_FOREST, ERODED_BEACH, ]; +export const VOIDS = [NO_ISLAND, SINKHOLE, WIDE_SINKHOLE]; diff --git a/island/grid.ts b/island/grid.ts index 8e72aec..50b34aa 100644 --- a/island/grid.ts +++ b/island/grid.ts @@ -7,7 +7,9 @@ import { LobeGenerator, NO_ISLAND, ROCKY_ISLANDS, + SINKHOLE, SMALL_ISLANDS, + VOIDS, } from "./generators"; export class IslandGrid { @@ -30,10 +32,10 @@ export class IslandGrid { this.choose(SMALL_ISLANDS), this.choose(ALL_ISLANDS), this.choose(ALL_ISLANDS), - NO_ISLAND, - NO_ISLAND, - NO_ISLAND, - NO_ISLAND, + this.choose(VOIDS), + this.choose(VOIDS), + this.choose(VOIDS), + this.choose(VOIDS), NO_ISLAND, NO_ISLAND, ]); @@ -126,10 +128,36 @@ export class IslandGrid { } // flat, increase elevation - const newValue = ++this.data[pos]; - if (newValue == ICECAP) { - this.done = true; + ++this.data[pos]; + } + + public sinkhole(pos: number): void { + const higherNeighbors: number[] = []; + + const check = (adjPos: number) => { + if (this.data[adjPos] > this.data[pos]) { + higherNeighbors.push(adjPos); + } + }; + + // try to pull from neighbors + + check((pos - this.width - 1) % this.data.length); + check((pos - this.width) % this.data.length); + check((pos - this.width + 1) % this.data.length); + check((pos - 1) % this.data.length); + check((pos + 1) % this.data.length); + check((pos + this.width - 1) % this.data.length); + check((pos + this.width) % this.data.length); + check((pos + this.width + 1) % this.data.length); + + if (higherNeighbors.length > 0) { + const uphill = higherNeighbors[this.rng() % higherNeighbors.length]; + return this.sinkhole(uphill); } + + // flat, decrease elevation + this.data[pos] = Math.max(this.data[pos] - 1, -3); } public choose(list: T[]) { diff --git a/island/render.ts b/island/render.ts index f9eb92d..1f74a9a 100644 --- a/island/render.ts +++ b/island/render.ts @@ -9,6 +9,11 @@ export function renderIslands( for (let x = 0; x < islands.width; x++) { const tile = islands.data[islands.xy(x, y)]; switch (tile) { + case -3: + case -2: + case -1: + cx.fillStyle = "#000088"; + break; case WATER: cx.fillStyle = "blue"; break;