import { BEACH, ICECAP, LIGHT_FOREST, MOUNTAIN, WATER } from "./data"; import { IslandGrid } from "./grid"; export type IsDone = boolean; export type LobeGenerator = () => IsDone; export type LobeGeneratorConstructor = ( islands: IslandGrid, basePos: number ) => LobeGenerator; /** form mountain */ export const SMALL_MOUNTAIN: LobeGeneratorConstructor = (islands: IslandGrid, basePos: number) => () => { const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER); const edgeTiles = islandTiles.filter((pos) => islands.data[pos] <= BEACH); islands.dropWithin(edgeTiles); const mountainTiles = islands.floodSearch( basePos, (tile) => tile > MOUNTAIN ); islands.dropWithin(mountainTiles); return true; }; /** form mountain with icecap */ export const BIG_MOUNTAIN: LobeGeneratorConstructor = (islands: IslandGrid, basePos: number) => () => { const mountainTiles = islands.floodSearch( basePos, (tile) => tile > MOUNTAIN ); islands.dropWithin(mountainTiles); return mountainTiles.some((pos) => islands.data[pos] == ICECAP); }; /** form low-lying beach */ export const SMALL_BEACH: LobeGeneratorConstructor = (islands: IslandGrid, basePos: number) => () => { const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER); const shoreTiles = islandTiles.filter((pos) => islands.data[pos] <= WATER); islands.dropWithin(shoreTiles); return true; }; /** form low-lying beach that meets another island */ export const BIG_BEACH: LobeGeneratorConstructor = ( islands: IslandGrid, basePos: number ) => { let dropped = 0; return () => { const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER); const shoreTiles = islandTiles.filter((pos) => islands.data[pos] <= WATER); islands.dropWithin(shoreTiles); dropped++; const landTiles = islandTiles.filter((pos) => islands.data[pos] > WATER); return landTiles.length > dropped; }; }; /** form forested zone wherever available */ export const SCATTERED_FOREST: LobeGeneratorConstructor = (islands: IslandGrid, basePos: number) => () => { const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER); // grow shore const shoreTiles = islandTiles.filter((pos) => islands.data[pos] <= WATER); islands.dropWithin(shoreTiles); // seed beach const beachTiles = islandTiles.filter((pos) => islands.data[pos] == BEACH); islands.dropWithin(beachTiles); islands.dropWithin(beachTiles); // expand forest const forestLobe = islands.floodSearch(basePos, (tile) => tile > WATER); const forestTiles = forestLobe.filter( (pos) => islands.data[pos] == LIGHT_FOREST ); islands.dropWithin(forestTiles); return true; }; /** form forested zone that tries to grow as one mass */ export const CONTIGUOUS_FOREST: LobeGeneratorConstructor = (islands: IslandGrid, basePos: number) => () => { const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER); // grow shore const shoreTiles = islandTiles.filter((pos) => islands.data[pos] <= WATER); islands.dropWithin(shoreTiles); // seed beach const beachTiles = islandTiles.filter((pos) => islands.data[pos] == BEACH); islands.dropWithin(beachTiles); // locate & expand central forest edge const forestLobe = islands.floodSearch(basePos, (tile) => tile > BEACH); const forestTiles = forestLobe.filter( (pos) => islands.data[pos] == LIGHT_FOREST ); islands.dropWithin(forestTiles); islands.dropWithin(forestTiles); return true; }; /** form forested zone that can raise additional peaks */ export const HILLY_FOREST: LobeGeneratorConstructor = (islands: IslandGrid, basePos: number) => () => { const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER); // grow shore const shoreTiles = islandTiles.filter((pos) => islands.data[pos] <= WATER); islands.dropWithin(shoreTiles); // seed beach const beachTiles = islandTiles.filter((pos) => islands.data[pos] == BEACH); islands.dropWithin(beachTiles); // locate central forest const centralForest = islands.floodSearch(basePos, (tile) => tile > BEACH); islands.dropWithin(centralForest); // expand edge const edgeTiles = centralForest.filter( (pos) => islands.data[pos] == LIGHT_FOREST ); islands.dropWithin(edgeTiles); const hillTiles = centralForest.filter( (pos) => islands.data[pos] >= MOUNTAIN ); return hillTiles.length > 10; }; /** form low-lying beach with eroded sections */ export const ERODED_BEACH: LobeGeneratorConstructor = (islands: IslandGrid, basePos: number) => () => { const islandTiles = islands.floodSearch(basePos, (tile) => tile > WATER); const shoreTiles = islandTiles.filter((pos) => islands.data[pos] <= WATER); islands.dropWithin(shoreTiles); // erode if (islandTiles.length > 1) { const erodePos = islandTiles[islands.rng() % islandTiles.length]; islands.data[erodePos] = Math.max(islands.data[erodePos] - 1, WATER); } return true; }; /** form low-lying beach with eroded sections */ export const NO_ISLAND: LobeGeneratorConstructor = (islands: IslandGrid, basePos: number) => () => { 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 = [ SCATTERED_FOREST, CONTIGUOUS_FOREST, HILLY_FOREST, ]; export const SMALL_ISLANDS = [SMALL_BEACH, SCATTERED_FOREST, ERODED_BEACH]; export const ALL_ISLANDS = [ SMALL_MOUNTAIN, BIG_MOUNTAIN, SMALL_BEACH, BIG_BEACH, SCATTERED_FOREST, CONTIGUOUS_FOREST, HILLY_FOREST, ERODED_BEACH, ]; export const VOIDS = [NO_ISLAND, SINKHOLE, WIDE_SINKHOLE];