2024-01-13 12:10:37 -05:00
|
|
|
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;
|
|
|
|
|
2024-01-13 14:02:40 -05:00
|
|
|
/** 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] == WATER || islands.data[pos] == BEACH
|
|
|
|
);
|
|
|
|
islands.dropWithin(edgeTiles);
|
|
|
|
|
|
|
|
const mountainTiles = islands.floodSearch(
|
|
|
|
basePos,
|
|
|
|
(tile) => tile > MOUNTAIN
|
|
|
|
);
|
|
|
|
islands.dropWithin(mountainTiles);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2024-01-13 12:10:37 -05:00
|
|
|
/** 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 */
|
2024-01-13 12:27:40 -05:00
|
|
|
export const SMALL_BEACH: LobeGeneratorConstructor =
|
2024-01-13 12:10:37 -05:00
|
|
|
(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;
|
|
|
|
};
|
|
|
|
|
2024-01-13 12:27:40 -05:00
|
|
|
/** 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;
|
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2024-01-13 13:49:20 -05:00
|
|
|
/** form forested zone wherever available */
|
|
|
|
export const SCATTERED_FOREST: LobeGeneratorConstructor =
|
2024-01-13 12:10:37 -05:00
|
|
|
(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);
|
2024-01-13 13:49:20 -05:00
|
|
|
islands.dropWithin(beachTiles);
|
2024-01-13 12:10:37 -05:00
|
|
|
|
|
|
|
// expand forest
|
2024-01-13 13:49:20 -05:00
|
|
|
const forestLobe = islands.floodSearch(basePos, (tile) => tile > WATER);
|
2024-01-13 12:10:37 -05:00
|
|
|
const forestTiles = forestLobe.filter(
|
|
|
|
(pos) => islands.data[pos] == LIGHT_FOREST
|
|
|
|
);
|
|
|
|
islands.dropWithin(forestTiles);
|
2024-01-13 13:49:20 -05:00
|
|
|
|
|
|
|
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
|
|
|
|
);
|
2024-01-13 12:10:37 -05:00
|
|
|
islands.dropWithin(forestTiles);
|
|
|
|
islands.dropWithin(forestTiles);
|
|
|
|
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2024-01-13 13:49:20 -05:00
|
|
|
/** 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
|
|
|
|
);
|
2024-01-13 14:19:32 -05:00
|
|
|
return hillTiles.length > 10;
|
2024-01-13 13:49:20 -05:00
|
|
|
};
|
|
|
|
|
2024-01-13 12:10:37 -05:00
|
|
|
/** form low-lying beach with eroded sections */
|
2024-01-13 13:49:20 -05:00
|
|
|
export const ERODED_BEACH: LobeGeneratorConstructor =
|
2024-01-13 12:10:37 -05:00
|
|
|
(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;
|
|
|
|
};
|
2024-01-13 12:19:17 -05:00
|
|
|
|
2024-01-13 12:55:17 -05:00
|
|
|
/** form low-lying beach with eroded sections */
|
|
|
|
export const NO_ISLAND: LobeGeneratorConstructor =
|
|
|
|
(islands: IslandGrid, basePos: number) => () => {
|
|
|
|
return true;
|
|
|
|
};
|
|
|
|
|
2024-01-13 13:49:20 -05:00
|
|
|
export const BIG_ISLANDS = [BIG_MOUNTAIN, BIG_BEACH, HILLY_FOREST];
|
2024-01-13 14:02:40 -05:00
|
|
|
export const ROCKY_ISLANDS = [SMALL_MOUNTAIN, BIG_MOUNTAIN, HILLY_FOREST];
|
2024-01-13 13:49:20 -05:00
|
|
|
export const GREEN_ISLANDS = [
|
|
|
|
SCATTERED_FOREST,
|
|
|
|
CONTIGUOUS_FOREST,
|
|
|
|
HILLY_FOREST,
|
|
|
|
];
|
|
|
|
export const SMALL_ISLANDS = [SMALL_BEACH, SCATTERED_FOREST, ERODED_BEACH];
|
2024-01-13 12:27:40 -05:00
|
|
|
export const ALL_ISLANDS = [
|
2024-01-13 14:02:40 -05:00
|
|
|
SMALL_MOUNTAIN,
|
2024-01-13 12:27:40 -05:00
|
|
|
BIG_MOUNTAIN,
|
|
|
|
SMALL_BEACH,
|
2024-01-13 12:55:17 -05:00
|
|
|
BIG_BEACH,
|
2024-01-13 13:49:20 -05:00
|
|
|
SCATTERED_FOREST,
|
|
|
|
CONTIGUOUS_FOREST,
|
|
|
|
HILLY_FOREST,
|
|
|
|
ERODED_BEACH,
|
2024-01-13 12:27:40 -05:00
|
|
|
];
|