diff --git a/API.md b/API.md index 34a0c2c..bce791c 100644 --- a/API.md +++ b/API.md @@ -126,6 +126,12 @@ The biomes are defined as follows: node_roof = "foo:bluestone", -- Node forming the ceiling of the cave + node_air = "foo:air", + -- Nodes filling the inside of the cave. By default, this is air. + -- You can replace it with e.g. water to make the entire cave a water cave. + -- Keep in mind that cave biomes can blend, so it might not always look + -- very smooth. + node_shell = "foo:permafrost", depth_shell = 3, -- Node forming a layer around the entire cave and thickness of this layer diff --git a/init.lua b/init.lua index 3472e66..18a2427 100644 --- a/init.lua +++ b/init.lua @@ -11,6 +11,9 @@ noordstar_caves = {} -- Load features to influence cave shapes load("shape") +-- Load features to influence cave biomes +load("biome") + -- Start engine to generate caves load("engine") diff --git a/lua/biome.lua b/lua/biome.lua index 0373b30..202879d 100644 --- a/lua/biome.lua +++ b/lua/biome.lua @@ -59,6 +59,9 @@ local function clean_def(def) if type(def.node_roof) == "string" then d.node_roof = def.node_roof end + if type(def.node_air) == "string" then + d.node_air = def.node_air + end if type(def.node_shell) == "string" then d.node_shell = def.node_shell end diff --git a/lua/engine.lua b/lua/engine.lua index 1c3e6f1..5d6f655 100644 --- a/lua/engine.lua +++ b/lua/engine.lua @@ -2,6 +2,38 @@ -- Constants and magic numbers local mapgen_buffer = 16 +-- Noise params for heat_point +local heat_noise_params = + { offset = 50 + , scale = 50 + , spread = { x = 200, y = 200, z = 200 } + , seed = 320523 + , octaves = 2 + , persistence = 0.1 + , lacunarity = 2.0 + , flags = "" + } + +-- Noise params for humidity_point +local humidity_noise_params = + { offset = 50 + , scale = 50 + , spread = { x = 200, y = 200, z = 200 } + , seed = 9923473 + , octaves = 2 + , persistence = 0.1 + , lacunarity = 2.0 + , flags = "" + } + +local default_biome = + { name = "noordstar_caves:none" + , heat_point = -1e6 + , humidity_point = -1e6 + , minp = { x = -1e5, y = -1e5, z = -1e5 } + , maxp = { x = 1e5, y = 1e5, z = 1e5 } + } + -- Convert 3d relative coordinates to an index on a flat array local function from_3d_to_flat(dx, dy, dz, nx, ny) return (nx * ny * dz) + (nx * dy) + dx + 1 @@ -421,6 +453,46 @@ local function get_flat_cave_node_types(minp, maxp) end) end +-- Transform categorized blocks into +local function biome_def_to_content_id(def, nt, original_block) + local function get_node(name, alt) + return function() + if name == nil then + return alt + else + return minetest.get_content_id(name) + end + end + end + + local node_air = get_node(def.node_air, minetest.get_content_id("air")) + local node_floor = get_node(def.node_floor, original_block) + local node_stone = original_block + local node_roof = get_node(def.node_roof, original_block) + local node_unknown = original_block + local node_wall = get_node(def.node_wall, original_block) + + if nt == node_type.unknown then + return node_unknown + elseif nt == node_type.floor then + return node_floor() + elseif nt == node_type.wall then + return node_wall() + elseif nt == node_type.roof then + return node_roof() + elseif nt == node_type.content then + return node_air() + elseif nt == node_type.stone then + return node_stone + else + return original_block + end +end + +local function biome_distance(def, heat, humidity) + return (def.heat_point - heat)^2 + (def.humidity_point - humidity)^2 +end + minetest.register_on_generated(function(minp, maxp, blockseed) local vminp = { x = minp.x - mapgen_buffer @@ -440,29 +512,35 @@ minetest.register_on_generated(function(minp, maxp, blockseed) -- Get threshold values local node_types = get_flat_cave_node_types(vminp, vmaxp) - local node_air = minetest.get_content_id("air") - local node_floor = minetest.get_content_id("mcl_core:glass_green") - local node_wall = minetest.get_content_id("mcl_core:glass_purple") - local node_roof = minetest.get_content_id("mcl_core:glass_red") - local node_other = minetest.get_content_id("mcl_core:glass") + local heat_points = get_flat_from_noise_params(minp, maxp, heat_noise_params) + local humidity_points = get_flat_from_noise_params(minp, maxp, humidity_noise_params) + -- Map block values local nids = Flat3dArray:from_func(vminp, vmaxp, function(i, pos) local nt = node_types:get_pos(pos) if not is_valid_pos(pos, minp, maxp) then return flat_data:get_pos(pos) - elseif nt == node_type.unknown then - return node_other - elseif nt == node_type.floor then - return node_floor - elseif nt == node_type.wall then - return node_wall - elseif nt == node_type.roof then - return node_roof - elseif nt == node_type.content then - return node_air - elseif nt == node_type.stone then - return flat_data:get_pos(pos) + else + local biome = default_biome + + local heat = heat_points:get_pos(pos) + local humidity = humidity_points:get_pos(pos) + + for _, def in pairs(noordstar_caves.registered_biomes) do + if pos.x < def.minp.x then + elseif pos.y < def.minp.y then + elseif pos.z < def.minp.z then + elseif pos.x > def.maxp.x then + elseif pos.y > def.maxp.y then + elseif pos.z > def.maxp.z then + elseif biome_distance(def, heat, humidity) > biome_distance(biome, heat, humidity) then + else + biome = def + end + end + + return biome_def_to_content_id(biome, nt, flat_data:get_pos(pos)) end end)