153 lines
5.2 KiB
Lua
153 lines
5.2 KiB
Lua
-- NOTE the API of this file should probably change?
|
|
|
|
local lume = require "lib.lume"
|
|
local simplex_map = require "lib.simplex_map"
|
|
|
|
local biomes = {
|
|
-- biomes are checked in order, with the first candidate always being selected
|
|
[800] = {
|
|
name = "sea ice",
|
|
altitude = {min = -math.huge, max = 0},
|
|
temperature = {min = -math.huge, max = -2},
|
|
rainfall = {min = -math.huge, max = math.huge},
|
|
color = {0.6, 0.6, 1, 1},
|
|
},
|
|
[900] = {
|
|
name = "deep ocean",
|
|
altitude = {min = -math.huge, max = -5000},
|
|
temperature = {min = -math.huge, max = math.huge},
|
|
rainfall = {min = -math.huge, max = math.huge},
|
|
color = {0, 0, 0.25, 1},
|
|
},
|
|
[1000] = {
|
|
name = "ocean (definition, 0 meters)",
|
|
altitude = {min = -math.huge, max = 0},
|
|
temperature = {min = -math.huge, max = math.huge},
|
|
rainfall = {min = -math.huge, max = math.huge},
|
|
color = {0, 0, 0.5, 1},
|
|
},
|
|
[1500] = {
|
|
name = "frozen (definition, 0 C)",
|
|
altitude = {min = -math.huge, max = math.huge},
|
|
temperature = {min = -math.huge, max = 0},
|
|
rainfall = {min = -math.huge, max = math.huge},
|
|
color = {1, 1, 1, 1},
|
|
},
|
|
[2000] = {
|
|
name = "snowline (middle estimate)",
|
|
altitude = {min = 4000, max = math.huge},
|
|
temperature = {min = -math.huge, max = math.huge},
|
|
rainfall = {min = -math.huge, max = math.huge},
|
|
color = {1, 1, 1, 1},
|
|
},
|
|
[2100] = {
|
|
name = "alpine zone",
|
|
altitude = {min = 3500, max = math.huge},
|
|
temperature = {min = -math.huge, max = math.huge},
|
|
rainfall = {min = -math.huge, max = math.huge},
|
|
color = {0.8, 0.8, 0.8, 1},
|
|
},
|
|
[2200] = {
|
|
name = "subalpine zone",
|
|
altitude = {min = 3000, max = math.huge},
|
|
temperature = {min = -math.huge, max = math.huge},
|
|
rainfall = {min = -math.huge, max = math.huge},
|
|
color = {0.67, 0.67, 0.67, 1},
|
|
},
|
|
[2300] = {
|
|
name = "montane zone",
|
|
altitude = {min = 2500, max = math.huge},
|
|
temperature = {min = -math.huge, max = math.huge},
|
|
rainfall = {min = -math.huge, max = math.huge},
|
|
color = {0.67, 0.67, 0.5, 1},
|
|
},
|
|
}
|
|
|
|
local generate = function()
|
|
local map = {}
|
|
map.size = 500
|
|
map.tile_size = 1 -- TODO tile_size really shouldn't be the responsibility of the map generator - it's a rendering detail, not a map feature
|
|
map.altitude = simplex_map.generate{ -- will be converted to meters
|
|
size = map.size, scale = 0.001, min = 0, max = 1,
|
|
octaves = 7, lacunarity = 2, gain = 0.5,
|
|
}
|
|
map.temperature = simplex_map.generate{ -- in celsius
|
|
-- -20 to 45 chosen by looking at the temperature range shown on https://openclimatemap.org/
|
|
size = map.size, scale = 0.005, min = 20, max = 65, -- approximate target extremes of -89.2 to 56.7
|
|
octaves = 2, lacunarity = 2, gain = 0.5,
|
|
}
|
|
map.rainfall = simplex_map.generate{ -- in millimeters of rainfall
|
|
size = map.size, scale = 0.005, -- min = 18, max = 3240,
|
|
octaves = 1, lacunarity = 2, gain = 0.5,
|
|
}
|
|
|
|
local function earthlike_altitude_adjustment(altitude)
|
|
-- TODO replace with a simpler function?
|
|
return 6099 - 114909 * altitude + 830891 * altitude^2 - 2.72e6 * altitude^3 + 4.19e6 * altitude^4 - 3.02e6 * altitude^5 + 815866 * altitude^6
|
|
end
|
|
|
|
for x = 1, map.size do
|
|
for y = 1, map.size do
|
|
map.altitude[x][y] = earthlike_altitude_adjustment(map.altitude[x][y])
|
|
end
|
|
end
|
|
map.altitude.min = simplex_map.min(map.altitude)
|
|
map.altitude.max = simplex_map.max(map.altitude)
|
|
|
|
local biome_order = {}
|
|
for order in pairs(biomes) do
|
|
biome_order[#biome_order + 1] = order
|
|
end
|
|
table.sort(biome_order)
|
|
|
|
local radius_squared = (map.size / 2)^2
|
|
map.biome = {}
|
|
for x = 1, map.size do
|
|
map.biome[x] = {}
|
|
for y = 1, map.size do
|
|
if lume.distance(x, y, map.size / 2, map.size / 2, true) < radius_squared then
|
|
local altitude = map.altitude[x][y]
|
|
local temperature = map.temperature[x][y]
|
|
local rainfall = map.rainfall[x][y]
|
|
|
|
-- modify temperature based on altitude
|
|
if altitude > 0 then
|
|
temperature = temperature - 0.0065 * altitude -- -6.5 C / km
|
|
else
|
|
temperature = temperature + altitude / 1000 -- -1 C / km (deep water adjustment)
|
|
end
|
|
-- modify temperature by latitude
|
|
local polar_distance = math.abs(y - (map.size / 2))^1.3 -- experimenting with slight non-linearity
|
|
local max_temperature_delta = 10
|
|
temperature = temperature - (polar_distance / map.size * 2 * max_temperature_delta)
|
|
-- 'save' temperature changes
|
|
map.temperature[x][y] = temperature
|
|
|
|
-- choose biome
|
|
for _, order in ipairs(biome_order) do
|
|
local biome = biomes[order]
|
|
if altitude >= biome.altitude.min and altitude <= biome.altitude.max then
|
|
if temperature >= biome.temperature.min and temperature <= biome.temperature.max then
|
|
if rainfall >= biome.rainfall.min and rainfall <= biome.rainfall.max then
|
|
map.biome[x][y] = biome.color
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
-- clear non-planet zones
|
|
map.altitude[x][y] = nil
|
|
map.temperature[x][y] = nil
|
|
map.rainfall[x][y] = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
return map
|
|
end
|
|
|
|
return {
|
|
generate = generate,
|
|
}
|