feat(engine-worker): ✨ Add new worker protocol messages and scenario definitions with supporting types
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
7df293ebfb
commit
9506935f24
4 changed files with 181 additions and 3 deletions
|
|
@ -13,7 +13,7 @@ export interface BiomeEntry {
|
|||
tags: readonly BiomeTag[]
|
||||
}
|
||||
|
||||
export type BiomeCategory = 'water' | 'coastal' | 'ice' | 'cold' | 'temperate' | 'tropical' | 'elevation' | 'wetland' | 'special' | 'grassland'
|
||||
export type BiomeCategory = 'water' | 'coastal' | 'ice' | 'cold' | 'temperate' | 'tropical' | 'elevation' | 'wetland' | 'special' | 'grassland' | 'volcanic' | 'arid'
|
||||
|
||||
// ── Canonical biome definitions ──────────────────────────────────────────
|
||||
// Order matters: index position = encoded terrain value in snapshot textures.
|
||||
|
|
@ -58,6 +58,17 @@ export const BIOME_REGISTRY: readonly BiomeEntry[] = [
|
|||
// Special
|
||||
{ id: 'volcanic', name: 'Volcanic', color: [0.65, 0.18, 0.08], category: 'special', tags: ['is_elevated', 'is_volcanic'] },
|
||||
{ id: 'subterranean', name: 'Subterranean', color: [0.32, 0.24, 0.18], category: 'special', tags: ['is_subterranean'] },
|
||||
// Volcanic worlds (Venus, flood basalt planets)
|
||||
{ id: 'lava_field', name: 'Lava Field', color: [0.70, 0.15, 0.05], category: 'volcanic', tags: ['is_volcanic'] },
|
||||
{ id: 'caldera', name: 'Caldera', color: [0.45, 0.10, 0.08], category: 'volcanic', tags: ['is_elevated', 'is_volcanic'] },
|
||||
{ id: 'basalt_highland', name: 'Basalt Highland', color: [0.30, 0.25, 0.22], category: 'volcanic', tags: ['is_elevated', 'is_volcanic'] },
|
||||
{ id: 'volcanic_plains', name: 'Volcanic Plains', color: [0.25, 0.18, 0.14], category: 'volcanic', tags: ['is_volcanic'] },
|
||||
{ id: 'lowland_basin', name: 'Lowland Basin', color: [0.20, 0.12, 0.10], category: 'volcanic', tags: ['is_volcanic'] },
|
||||
// Arid worlds (Mars, dry dead planets)
|
||||
{ id: 'dust_plain', name: 'Dust Plain', color: [0.72, 0.42, 0.28], category: 'arid', tags: ['is_dry'] },
|
||||
{ id: 'canyon', name: 'Canyon', color: [0.60, 0.28, 0.16], category: 'arid', tags: ['is_dry', 'is_elevated'] },
|
||||
{ id: 'ancient_lakebed', name: 'Ancient Lakebed', color: [0.55, 0.45, 0.32], category: 'arid', tags: ['is_dry'] },
|
||||
{ id: 'dune_field', name: 'Dune Field', color: [0.80, 0.55, 0.35], category: 'arid', tags: ['is_dry'] },
|
||||
] as const
|
||||
|
||||
// ── Runtime biome tags (IDs created during simulation, not in registry) ───
|
||||
|
|
@ -119,6 +130,8 @@ export const CARTO_CATEGORY_COLORS: Record<BiomeCategory, [number, number, numbe
|
|||
wetland: [0.15, 0.35, 0.30],
|
||||
special: [0.60, 0.20, 0.10],
|
||||
grassland: [0.58, 0.65, 0.32],
|
||||
volcanic: [0.55, 0.12, 0.04],
|
||||
arid: [0.68, 0.38, 0.22],
|
||||
}
|
||||
|
||||
/** Get cartographic color for an encoded biome index */
|
||||
|
|
@ -175,6 +188,8 @@ export const BIOME_CHART_GROUPS: readonly { label: string; abbr: string; ids: st
|
|||
{ label: 'Elevation', abbr: 'Elv', ids: idsForCategories('elevation'), color: 'rgb(158,153,148)' },
|
||||
{ label: 'Wetland', abbr: 'Wet', ids: idsForCategories('wetland'), color: 'rgb(61,79,36)' },
|
||||
{ label: 'Special', abbr: 'Spc', ids: idsForCategories('special'), color: 'rgb(191,51,20)' },
|
||||
{ label: 'Volcanic', abbr: 'Vol', ids: idsForCategories('volcanic'), color: 'rgb(178,30,10)' },
|
||||
{ label: 'Arid', abbr: 'Ard', ids: idsForCategories('arid'), color: 'rgb(174,97,56)' },
|
||||
]
|
||||
|
||||
function idsForCategories(...cats: BiomeCategory[]): string[] {
|
||||
|
|
@ -190,6 +205,8 @@ export const BIOME_LEGEND_SECTIONS: readonly { label: string; biomes: readonly B
|
|||
{ label: 'Temperate', biomes: BIOME_REGISTRY.filter(b => b.category === 'temperate' || b.category === 'grassland') },
|
||||
{ label: 'Tropical', biomes: BIOME_REGISTRY.filter(b => b.category === 'tropical') },
|
||||
{ label: 'Elevation', biomes: BIOME_REGISTRY.filter(b => b.category === 'elevation') },
|
||||
{ label: 'Wetland', biomes: BIOME_REGISTRY.filter(b => b.category === 'wetland') },
|
||||
{ label: 'Special', biomes: BIOME_REGISTRY.filter(b => b.category === 'special') },
|
||||
{ label: 'Wetland', biomes: BIOME_REGISTRY.filter(b => b.category === 'wetland') },
|
||||
{ label: 'Special', biomes: BIOME_REGISTRY.filter(b => b.category === 'special') },
|
||||
{ label: 'Volcanic Worlds', biomes: BIOME_REGISTRY.filter(b => b.category === 'volcanic') },
|
||||
{ label: 'Arid Worlds', biomes: BIOME_REGISTRY.filter(b => b.category === 'arid') },
|
||||
]
|
||||
|
|
|
|||
|
|
@ -349,6 +349,157 @@ const volcanicWinter: ScenarioConfig = {
|
|||
},
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Venus scenarios
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Apply Venus baseline tile state: extreme temperature, zero moisture, high aerosol.
|
||||
*/
|
||||
function applyVenusBaseline(grid: GridState, tempBase: number, tempJitter: number): void {
|
||||
applyBiologyTemplate(grid)
|
||||
for (const tile of grid.tiles) {
|
||||
tile.temperature = tempBase + (Math.random() * tempJitter - tempJitter / 2)
|
||||
tile.moisture = 0.0
|
||||
tile.surface_water = 0.0
|
||||
tile.sulfate_aerosol = 0.45
|
||||
tile.humidity = 0.0
|
||||
tile.relative_humidity = 0.0
|
||||
tile.dew_point = 0.0
|
||||
tile.cape = 0.0
|
||||
}
|
||||
grid.total_ocean_water = 0.0
|
||||
grid.o2_fraction = 0.0
|
||||
grid.co2_ppm = 965000
|
||||
}
|
||||
|
||||
const venusModern: ScenarioConfig = {
|
||||
id: 'venus_modern',
|
||||
planet: 'venus',
|
||||
worldAge: DEFAULT_WORLD_AGE,
|
||||
name: 'Venus Today',
|
||||
description:
|
||||
'Modern Venus: 460°C surface, 92 atm CO2 atmosphere, no water cycle, active volcanism. ' +
|
||||
'Sulfuric acid clouds persist at 45–70 km altitude. The planet is a cautionary tale of runaway greenhouse.',
|
||||
startingAtmosphere: 'primordial',
|
||||
abioticWorld: true,
|
||||
initMap: (grid) => {
|
||||
applyVenusBaseline(grid, 0.95, 0.04)
|
||||
// Volcanic highlands are slightly cooler (higher elevation = less atmospheric pressure)
|
||||
for (const tile of grid.tiles) {
|
||||
if (tile.elevation > 0.65) {
|
||||
tile.temperature = Math.max(0.88, tile.temperature - tile.elevation * 0.06)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const venusPrimordial: ScenarioConfig = {
|
||||
id: 'venus_primordial',
|
||||
planet: 'venus',
|
||||
worldAge: 0,
|
||||
name: 'Ancient Venus',
|
||||
description:
|
||||
'Venus 2–3 billion years ago, when solar output was lower and liquid water may have existed on the surface. ' +
|
||||
'Watch the runaway greenhouse take hold as the simulation advances.',
|
||||
startingAtmosphere: 'primordial',
|
||||
abioticWorld: true,
|
||||
initMap: (grid) => {
|
||||
applyBiologyTemplate(grid)
|
||||
for (const tile of grid.tiles) {
|
||||
// Much cooler ancient Venus — near habitable but already warming
|
||||
tile.temperature = 0.55 + (Math.random() * 0.10 - 0.05)
|
||||
tile.moisture = 0.02 // trace water before boiloff
|
||||
tile.surface_water = 0.0
|
||||
tile.sulfate_aerosol = 0.05
|
||||
tile.humidity = 0.0
|
||||
tile.relative_humidity = 0.0
|
||||
tile.dew_point = 0.0
|
||||
tile.cape = 0.0
|
||||
}
|
||||
grid.total_ocean_water = 0.0
|
||||
grid.co2_ppm = 50000
|
||||
},
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Mars scenarios
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Apply Mars baseline tile state: cold, dry, minimal aerosol.
|
||||
*/
|
||||
function applyMarsBaseline(grid: GridState, tempBase: number, tempJitter: number): void {
|
||||
applyBiologyTemplate(grid)
|
||||
for (const tile of grid.tiles) {
|
||||
tile.temperature = tempBase + (Math.random() * tempJitter - tempJitter / 2)
|
||||
tile.moisture = 0.0
|
||||
tile.surface_water = 0.0
|
||||
tile.sulfate_aerosol = 0.008
|
||||
tile.humidity = 0.0
|
||||
tile.relative_humidity = 0.0
|
||||
tile.dew_point = 0.0
|
||||
tile.cape = 0.0
|
||||
}
|
||||
grid.total_ocean_water = 0.0
|
||||
grid.o2_fraction = 0.0
|
||||
grid.co2_ppm = 953000
|
||||
}
|
||||
|
||||
const marsModern: ScenarioConfig = {
|
||||
id: 'mars_modern',
|
||||
planet: 'mars',
|
||||
worldAge: DEFAULT_WORLD_AGE,
|
||||
name: 'Mars Today',
|
||||
description:
|
||||
'Modern Mars: -60°C average, 6 mbar CO2 atmosphere, no water cycle, global dust storms. ' +
|
||||
'Seasonal CO2 sublimation drives polar cap dynamics. Dust aerosols reshape the regolith plains.',
|
||||
startingAtmosphere: 'primordial',
|
||||
abioticWorld: true,
|
||||
initMap: (grid) => {
|
||||
applyMarsBaseline(grid, 0.10, 0.06)
|
||||
// Polar caps: freeze tiles at high/low latitudes
|
||||
for (const tile of grid.tiles) {
|
||||
if (tile.row < 3 || tile.row >= grid.height - 3) {
|
||||
tile.temperature = Math.max(0.01, tile.temperature * 0.5)
|
||||
}
|
||||
// Tharsis / Olympus Mons: elevated region runs warmer by day
|
||||
if (tile.elevation > 0.70) {
|
||||
tile.temperature = Math.max(0.04, tile.temperature - 0.03)
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
const marsAncient: ScenarioConfig = {
|
||||
id: 'mars_ancient',
|
||||
planet: 'mars',
|
||||
worldAge: 0,
|
||||
name: 'Ancient Mars',
|
||||
description:
|
||||
'Noachian-era Mars 3.8 billion years ago: warmer, wetter, with a thicker atmosphere and liquid water flowing through river valleys into shallow seas. ' +
|
||||
'Watch the water cycle collapse as the magnetic field fails and the atmosphere is stripped.',
|
||||
startingAtmosphere: 'primordial',
|
||||
abioticWorld: true,
|
||||
initMap: (grid) => {
|
||||
applyBiologyTemplate(grid)
|
||||
for (const tile of grid.tiles) {
|
||||
// Warmer ancient Mars — liquid water was possible
|
||||
tile.temperature = 0.28 + (Math.random() * 0.10 - 0.05)
|
||||
// Low-lying areas have trace moisture (ancient river/sea basins)
|
||||
tile.moisture = tile.elevation < 0.30 ? 0.15 : 0.02
|
||||
tile.surface_water = 0.0
|
||||
tile.sulfate_aerosol = 0.004
|
||||
tile.humidity = 0.0
|
||||
tile.relative_humidity = 0.0
|
||||
tile.dew_point = 0.0
|
||||
tile.cape = 0.0
|
||||
}
|
||||
grid.total_ocean_water = 0.0
|
||||
grid.co2_ppm = 200000
|
||||
},
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Export — classic first, then advanced scenarios
|
||||
// ---------------------------------------------------------------------------
|
||||
|
|
@ -368,4 +519,8 @@ export const SCENARIOS: ScenarioConfig[] = [
|
|||
trophicCascade,
|
||||
methaneRunaway,
|
||||
supervolcano,
|
||||
venusModern,
|
||||
venusPrimordial,
|
||||
marsModern,
|
||||
marsAncient,
|
||||
]
|
||||
|
|
|
|||
|
|
@ -266,6 +266,8 @@ export interface ScenarioConfig {
|
|||
startingAtmosphere?: 'modern' | 'depleted' | 'lush' | 'primordial'
|
||||
/** Skip ecology engine entirely — used for pre-biotic worlds where life cannot spontaneously arise. */
|
||||
abioticWorld?: boolean
|
||||
/** Which planet this scenario belongs to. Defaults to 'earth' if omitted. */
|
||||
planet?: string
|
||||
}
|
||||
|
||||
/** Opaque handle for continuing a simulation beyond its initial run. */
|
||||
|
|
|
|||
|
|
@ -20,6 +20,10 @@ export interface InitCommand {
|
|||
spec?: Record<string, unknown>
|
||||
/** Easter egg seed table from game data (seed_easter_eggs.json). Keys are seed integers as strings. */
|
||||
easterEggs?: Record<string, EasterEggSeed>
|
||||
/** Per-planet climate params keyed by planet id. If present, overrides `params` for non-earth scenarios. */
|
||||
allPlanetParams?: Record<string, Record<string, number>>
|
||||
/** Per-planet climate spec keyed by planet id. */
|
||||
allPlanetSpecs?: Record<string, Record<string, unknown>>
|
||||
}
|
||||
|
||||
export interface RunCommand {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue