Initial commit: working code closely reproducing core's biomegen
commit
3da0f675cc
|
@ -0,0 +1,58 @@
|
|||
-- biomelist.lua
|
||||
|
||||
local function make_biomelist()
|
||||
local biomes = {}
|
||||
|
||||
local cid = minetest.get_content_id
|
||||
|
||||
for _, a in pairs(minetest.registered_biomes) do
|
||||
local b = {}
|
||||
b.name = a.name
|
||||
biomes[b.name] = b
|
||||
|
||||
if a.node_dust then
|
||||
b.node_dust_name = a.node_dust
|
||||
b.node_dust = cid(a.node_dust)
|
||||
end
|
||||
|
||||
b.node_top = cid(a.node_top or "mapgen_stone")
|
||||
b.depth_top = a.depth_top or 0
|
||||
|
||||
b.node_filler = cid(a.node_filler or "mapgen_stone")
|
||||
b.depth_filler = a.depth_filler or 0
|
||||
|
||||
b.node_stone = cid(a.node_stone or "mapgen_stone")
|
||||
|
||||
b.node_water_top = cid(a.node_water_top or "mapgen_water_source")
|
||||
b.depth_water_top = a.depth_water_top or 0
|
||||
|
||||
b.node_water = cid(a.node_water or "mapgen_water_source")
|
||||
b.node_river_water = cid(a.node_river_water or "mapgen_river_water_source")
|
||||
|
||||
b.node_riverbed = cid(a.node_riverbed or "mapgen_stone")
|
||||
b.depth_riverbed = a.depth_riverbed or 0
|
||||
|
||||
-- b.node_cave_liquid = ...
|
||||
-- b.node_dungeon = ...
|
||||
-- b.node_dungeon_alt = ...
|
||||
-- b.node_dungeon_stair = ...
|
||||
|
||||
b.min_pos = a.min_pos or {x=-31000, y=-31000, z=-31000}
|
||||
if a.y_min then
|
||||
b.min_pos.y = math.max(b.min_pos.y, a.y_min)
|
||||
end
|
||||
b.max_pos = a.max_pos or {x=31000, y=31000, z=31000}
|
||||
if a.y_max then
|
||||
b.max_pos.y = math.min(b.max_pos.y, a.y_max)
|
||||
end
|
||||
|
||||
b.vertical_blend = a.vertical_blend or 0
|
||||
|
||||
b.heat_point = a.heat_point or 50
|
||||
b.humidity_point = a.humidity_point or 50
|
||||
end
|
||||
|
||||
return biomes
|
||||
end
|
||||
|
||||
return make_biomelist
|
|
@ -0,0 +1,209 @@
|
|||
-- decorations.lua
|
||||
|
||||
local emptynodes = {
|
||||
air = true,
|
||||
ignore = true,
|
||||
}
|
||||
|
||||
local function generateDecoSimple(deco, vm, pr, p, ceiling)
|
||||
print(deco.name, minetest.pos_to_string(p))
|
||||
local emin, emax = vm:get_emerged_area()
|
||||
|
||||
local place_offset_y = deco.place_offset_y
|
||||
if ceiling then
|
||||
if p.y - place_offset_y - deco.height_max < emin.y then
|
||||
return 0
|
||||
elseif p.y - 1 - place_offset_y > emax.y then
|
||||
return 0
|
||||
end
|
||||
else
|
||||
if p.y + place_offset_y + deco.height_max > emax.y then
|
||||
return 0
|
||||
elseif p.y + 1 + place_offset_y < emin.y then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
local decos = deco.decoration
|
||||
local nodename = decos[pr:next(1,#decos)]
|
||||
local height = deco.vary_height and pr:next(deco.height, deco.height_max) or deco.height
|
||||
local param2 = deco.vary_param2 and pr:next(deco.param2, deco.param2_max) or deco.param2
|
||||
local force_placement = deco.flags.force_placement == true
|
||||
|
||||
local direction = ceiling and -1 or 1
|
||||
p.y = p.y + place_offset_y * direction
|
||||
for i=1, height do
|
||||
p.y = p.y + direction
|
||||
local node = vm:get_node_at(p)
|
||||
if not force_placement and not emptynodes[node.name] then
|
||||
break
|
||||
end
|
||||
node.name = nodename
|
||||
node.param2 = param2
|
||||
vm:set_node_at(p, node)
|
||||
end
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
local function get_schematic_size(schem)
|
||||
if type(schem) == "table" then
|
||||
return schem.size
|
||||
elseif type(schem) == "string" then
|
||||
local mts = io.open(schem)
|
||||
if not mts then
|
||||
return {x=0, y=0, z=0}
|
||||
end
|
||||
mts:seek('set', 6)
|
||||
local sx1, sx2, sy1, sy2, sz1, sz2 = mts:read(6):byte()
|
||||
mts:close()
|
||||
return {x=sx1*256+sx2, y=sy1*256+sy2, z=sz1*256+sz2}
|
||||
end
|
||||
|
||||
return {x=0, y=0, z=0}
|
||||
end
|
||||
|
||||
local function generateDecoSchematic(deco, vm, pr, p, ceiling)
|
||||
print(deco.name, minetest.pos_to_string(p))
|
||||
local force_placement = deco.flags.force_placement == true
|
||||
local direction = ceiling and -1 or 1
|
||||
if not deco.flags.place_center_y then
|
||||
if ceiling then
|
||||
local size = get_schematic_size(schem)
|
||||
p.y = p.y - deco.place_offset_y - size.y + 1
|
||||
else
|
||||
p.y = p.y + deco.place_offset_y
|
||||
end
|
||||
end
|
||||
|
||||
minetest.place_schematic_on_vmanip(vm, p, deco.schematic, deco.rotation, deco.replacements, force_placement, deco.schem_flags)
|
||||
|
||||
return 1
|
||||
end
|
||||
|
||||
local function parse_node_list(raw_list)
|
||||
if not raw_list then
|
||||
return {}
|
||||
end
|
||||
local ilist = {}
|
||||
if type(raw_list) == "string" then
|
||||
raw_list = {raw_list}
|
||||
end
|
||||
|
||||
local cid = minetest.get_content_id
|
||||
for i, node in ipairs(raw_list) do
|
||||
if node:sub(1, 6) == "group:" then
|
||||
local groupname = node:sub(7, -1)
|
||||
for name, ndef in pairs(minetest.registered_nodes) do
|
||||
if ndef.groups and ndef.groups[groupname] and ndef.groups[groupname] > 0 then
|
||||
ilist[cid(name)] = true
|
||||
end
|
||||
end
|
||||
else
|
||||
ilist[cid(node)] = true
|
||||
end
|
||||
end
|
||||
|
||||
return ilist
|
||||
end
|
||||
|
||||
local function make_decolist()
|
||||
local decos = {}
|
||||
|
||||
local cid = minetest.get_content_id
|
||||
|
||||
for i, a in pairs(minetest.registered_decorations) do
|
||||
local b = {}
|
||||
decos[i] = b
|
||||
|
||||
b.name = a.name or "unnamed " .. i
|
||||
|
||||
b.deco_type = a.deco_type or "simple"
|
||||
|
||||
b.place_on = parse_node_list(a.place_on)
|
||||
|
||||
b.sidelen = a.sidelen or 8
|
||||
b.fill_ratio = a.fill_ratio or 0.02
|
||||
local np = a.noise_params
|
||||
b.use_noise = false
|
||||
if np then
|
||||
b.use_noise = true
|
||||
b.noise = minetest.get_perlin(np)
|
||||
end
|
||||
|
||||
b.use_biomes = false
|
||||
if a.biomes then
|
||||
local biomes_raw = a.biomes
|
||||
b.use_biomes = true
|
||||
if type(biomes_raw) == "table" then
|
||||
local biomes = {}
|
||||
b.biomes = biomes
|
||||
for i, biome in pairs(biomes_raw) do
|
||||
if type(biome) == "number" then
|
||||
biome = minetest.get_biome_name(biome)
|
||||
end
|
||||
biomes[biome] = true
|
||||
end
|
||||
else
|
||||
if type(biomes_raw) == "number" then
|
||||
biomes_raw = minetest.get_biome_name(biomes_raw)
|
||||
end
|
||||
b.biomes = {[biomes_raw] = true}
|
||||
end
|
||||
end
|
||||
|
||||
b.y_min = a.y_min or -31000
|
||||
b.y_max = a.y_max or 31000
|
||||
|
||||
b.spawn_by = parse_node_list(a.spawn_by)
|
||||
b.num_spawn_by = a.num_spawn_by or 0
|
||||
|
||||
local flags_raw = a.flags or ""
|
||||
local flags = {}
|
||||
b.flags = flags
|
||||
for i, flag in ipairs(flags_raw:split()) do
|
||||
flag = flag:trim()
|
||||
local status = true
|
||||
if flag:sub(1,2) == "no" then
|
||||
flag = flag:sub(3,-1)
|
||||
status = false
|
||||
end
|
||||
flags[flag] = status
|
||||
end
|
||||
|
||||
if b.deco_type == "simple" then
|
||||
local deco = a.decoration
|
||||
if type(deco) == "string" then
|
||||
deco = {deco}
|
||||
end
|
||||
b.decoration = deco
|
||||
b.height = a.height or 1
|
||||
b.height_max = math.max(a.height_max or b.height, b.height)
|
||||
b.vary_height = b.height < b.height_max
|
||||
b.param2 = a.param2 or 0
|
||||
b.param2_max = math.max(a.params2_max or b.param2, b.height)
|
||||
b.vary_param2 = b.param2 < b.param2_max
|
||||
b.place_offset_y = a.place_offset_y or 0
|
||||
b.generate = generateDecoSimple
|
||||
elseif b.deco_type == "schematic" then
|
||||
b.schematic = a.schematic
|
||||
b.replacements = a.replacements or {}
|
||||
b.rotation = a.rotation or 0
|
||||
b.place_offset_y = a.place_offset_y or 0
|
||||
|
||||
local schem_flags = {}
|
||||
for _, flag in ipairs({'place_center_x', 'place_center_y', 'place_center_z'}) do
|
||||
if flags[flag] then
|
||||
table.insert(schem_flags, flag)
|
||||
end
|
||||
end
|
||||
b.schem_flags = table.concat(schem_flags, ',')
|
||||
|
||||
b.generate = generateDecoSchematic
|
||||
end
|
||||
end
|
||||
|
||||
return decos
|
||||
end
|
||||
|
||||
return make_decolist
|
|
@ -0,0 +1,592 @@
|
|||
-- biomegen/init.lua
|
||||
|
||||
biomegen = {}
|
||||
|
||||
local make_biomelist = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/biomelist.lua")
|
||||
local make_decolist = dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/decorations.lua")
|
||||
|
||||
local np_filler_depth = {
|
||||
offset = 0,
|
||||
scale = 1.2,
|
||||
spread = {x=150, y=150, z=150},
|
||||
seed = 261,
|
||||
octaves = 3,
|
||||
persist = 0.7,
|
||||
lacunarity = 2.0,
|
||||
}
|
||||
|
||||
local nobj_filler_depth, nobj_heat, nobj_heat_blend, nobj_humid, nobj_humid_blend
|
||||
local nvals_filler_depth = {}
|
||||
local nvals_heat = {}
|
||||
local nvals_heat_blend = {}
|
||||
local nvals_humid = {}
|
||||
local nvals_humid_blend = {}
|
||||
|
||||
local water_level = tonumber(minetest.get_mapgen_setting('water_level'))
|
||||
local elevation_chill = 0
|
||||
function biomegen.set_elevation_chill(ec)
|
||||
elevation_chill = ec
|
||||
end
|
||||
|
||||
local init = false
|
||||
|
||||
local c_ignore
|
||||
local c_air
|
||||
local c_stone
|
||||
local c_water
|
||||
local c_rwater
|
||||
|
||||
--[[local function noiseparams(name)
|
||||
local np = minetest.get_mapgen_setting_noiseparams(name)
|
||||
print(name)
|
||||
print(dump2(np))
|
||||
|
||||
np.offset = tonumber(np.offset)
|
||||
np.scale = tonumber(np.scale)
|
||||
local sp = np.spread
|
||||
sp.x = tonumber(sp.x)
|
||||
sp.y = tonumber(sp.y)
|
||||
sp.z = tonumber(sp.z)
|
||||
np.seed = tonumber(np.scale)
|
||||
np.octaves = tonumber(np.scale)
|
||||
np.persist = tonumber(np.scale)
|
||||
np.lacunarity = tonumber(np.scale)
|
||||
|
||||
return np
|
||||
end]]
|
||||
|
||||
local biomes, decos
|
||||
|
||||
local function initialize(chulens)
|
||||
print("Initializing")
|
||||
|
||||
init = true
|
||||
|
||||
local noiseparams = minetest.get_mapgen_setting_noiseparams
|
||||
|
||||
local chulens2d = {x=chulens.x, y=chulens.z, z=1}
|
||||
local np_heat = noiseparams('mg_biome_np_heat')
|
||||
np_heat.offset = np_heat.offset + water_level*elevation_chill
|
||||
nobj_filler_depth = minetest.get_perlin_map(np_filler_depth, chulens2d)
|
||||
nobj_heat = minetest.get_perlin_map(np_heat, chulens2d)
|
||||
nobj_heat_blend = minetest.get_perlin_map(noiseparams('mg_biome_np_heat_blend'), chulens2d)
|
||||
nobj_humid = minetest.get_perlin_map(noiseparams('mg_biome_np_humidity'), chulens2d)
|
||||
nobj_humid_blend = minetest.get_perlin_map(noiseparams('mg_biome_np_humidity_blend'), chulens2d)
|
||||
|
||||
c_ignore = minetest.get_content_id("ignore")
|
||||
c_air = minetest.get_content_id("air")
|
||||
c_stone = minetest.get_content_id("mapgen_stone")
|
||||
c_water = minetest.get_content_id("mapgen_water_source")
|
||||
c_rwater = minetest.get_content_id("mapgen_river_water_source")
|
||||
|
||||
biomes = make_biomelist()
|
||||
decos = make_decolist()
|
||||
end
|
||||
|
||||
local biomemap = {}
|
||||
local heatmap = {}
|
||||
local humidmap = {}
|
||||
|
||||
function biomegen.calculateNoises(minp)
|
||||
local minp2d = {x=minp.x, y=minp.z}
|
||||
nobj_filler_depth:get_2d_map_flat(minp2d, nvals_filler_depth)
|
||||
|
||||
nobj_heat:get_2d_map_flat(minp2d, nvals_heat)
|
||||
nobj_heat_blend:get_2d_map_flat(minp2d, nvals_heat_blend)
|
||||
|
||||
nobj_humid:get_2d_map_flat(minp2d, nvals_humid)
|
||||
nobj_humid_blend:get_2d_map_flat(minp2d, nvals_humid_blend)
|
||||
|
||||
for i, heat in ipairs(nvals_heat) do -- use nvals_heat to iterate, could have been another one
|
||||
heatmap[i] = heat + nvals_heat_blend[i]
|
||||
humidmap[i] = nvals_humid[i] + nvals_humid_blend[i]
|
||||
end
|
||||
end
|
||||
|
||||
function biomegen.getBiomeAtIndex(i, pos)
|
||||
local heat = heatmap[i] - math.max(pos.y, water_level)*elevation_chill
|
||||
local humid = humidmap[i]
|
||||
return biomegen.calcBiomeFromNoise(heat, humid, pos)
|
||||
end
|
||||
|
||||
function biomegen.calcBiomeFromNoise(heat, humid, pos)
|
||||
local biome_closest = nil
|
||||
local biome_closest_blend = nil
|
||||
local dist_min = 31000
|
||||
local dist_min_blend = 31000
|
||||
|
||||
for i, biome in pairs(biomes) do
|
||||
local min_pos, max_pos = biome.min_pos, biome.max_pos
|
||||
if pos.y >= min_pos.y and pos.y <= max_pos.y+biome.vertical_blend
|
||||
and pos.x >= min_pos.x and pos.x <= max_pos.x
|
||||
and pos.z >= min_pos.z and pos.z <= max_pos.z then
|
||||
local d_heat = heat - biome.heat_point
|
||||
local d_humid = humid - biome.humidity_point
|
||||
local dist = d_heat*d_heat + d_humid*d_humid -- Pythagorean distance
|
||||
|
||||
if pos.y <= max_pos.y then -- Within y limits of biome
|
||||
if dist < dist_min then
|
||||
dist_min = dist
|
||||
biome_closest = biome
|
||||
end
|
||||
elseif dist < dist_min_blend then -- Blend area above biome
|
||||
dist_min_blend = dist
|
||||
biome_closest_blend = biome
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Carefully tune pseudorandom seed variation to avoid single node dither
|
||||
-- and create larger scale blending patterns similar to horizontal biome
|
||||
-- blend.
|
||||
local seed = math.floor(pos.y + (heat+humid) * 0.9)
|
||||
local rng = PseudoRandom(seed)
|
||||
|
||||
if biome_closest_blend and dist_min_blend <= dist_min
|
||||
and rng:next(0, biome_closest_blend.vertical_blend) >= pos.y - biome_closest_blend.max_pos.y then
|
||||
return biome_closest_blend
|
||||
end
|
||||
|
||||
return biome_closest
|
||||
end
|
||||
|
||||
function biomegen.generateBiomes(data, a, minp, maxp)
|
||||
local chulens = {x=maxp.x-minp.x+1, y=maxp.y-minp.y+1, z=maxp.z-minp.z+1}
|
||||
|
||||
local index = 1
|
||||
|
||||
if not init then
|
||||
initialize(chulens)
|
||||
end
|
||||
|
||||
biomegen.calculateNoises(minp)
|
||||
|
||||
for z=minp.z, maxp.z do
|
||||
for x=minp.x, maxp.x do
|
||||
local biome = nil
|
||||
local water_biome = nil
|
||||
|
||||
local depth_top = 0
|
||||
local base_filler = 0
|
||||
local depth_water_top = 0
|
||||
local depth_riverbed = 0
|
||||
|
||||
local biome_y_min = -31000
|
||||
local vi = a:index(x, maxp.y, z)
|
||||
local ystride = a.ystride
|
||||
|
||||
local c_above = data[vi+ystride]
|
||||
local air_above = c_above == c_air
|
||||
local river_water_above = c_above == c_rwater
|
||||
local water_above = c_above == c_water or river_water_above
|
||||
|
||||
biomemap[index] = nil
|
||||
|
||||
local nplaced = (air_above or water_above) and 0 or 31000
|
||||
|
||||
for y=maxp.y, minp.y, -1 do
|
||||
local c = data[vi]
|
||||
local is_stone_surface = (c == c_stone) and
|
||||
(air_above or water_above or not biome or y < biome_y_min)
|
||||
local is_water_surface = (c == c_water or c == c_rwater) and
|
||||
(air_above or not biome or y < biome_y_min)
|
||||
|
||||
if is_stone_surface or is_water_surface then
|
||||
biome = biomegen.getBiomeAtIndex(index, {x=x, y=y, z=z})
|
||||
|
||||
if not biomemap[index] and is_stone_surface then
|
||||
biomemap[index] = biome
|
||||
end
|
||||
|
||||
if not water_biome and is_water_surface then
|
||||
water_biome = biome
|
||||
end
|
||||
|
||||
depth_top = biome.depth_top
|
||||
base_filler = math.max(depth_top + biome.depth_filler + nvals_filler_depth[index], 0)
|
||||
depth_water_top = biome.depth_water_top
|
||||
depth_riverbed = biome.depth_riverbed
|
||||
biome_y_min = biome.min_pos.y
|
||||
end
|
||||
|
||||
if c == c_stone then
|
||||
local c_below = data[vi-ystride]
|
||||
if c_below == c_air or c_below == c_rwater or c_below == c_water then
|
||||
nplaced = 31000
|
||||
end
|
||||
if river_water_above then
|
||||
if nplaced < depth_riverbed then
|
||||
data[vi] = biome.node_riverbed
|
||||
nplaced = nplaced + 1
|
||||
else
|
||||
nplaced = 31000
|
||||
river_water_above = false
|
||||
end
|
||||
elseif nplaced < depth_top then
|
||||
data[vi] = biome.node_top
|
||||
nplaced = nplaced + 1
|
||||
elseif nplaced < base_filler then
|
||||
data[vi] = biome.node_filler
|
||||
nplaced = nplaced + 1
|
||||
else
|
||||
data[vi] = biome.node_stone
|
||||
nplaced = 31000
|
||||
end
|
||||
|
||||
air_above = false
|
||||
water_above = false
|
||||
elseif c == c_water then
|
||||
if y > water_level-depth_water_top then
|
||||
data[vi] = biome.node_water_top
|
||||
else
|
||||
data[vi] = biome.node_water
|
||||
end
|
||||
nplaced = 0
|
||||
air_above = false
|
||||
water_above = true
|
||||
elseif c == c_rwater then
|
||||
data[vi] = biome.node_river_water
|
||||
nplaced = 0
|
||||
air_above = false
|
||||
water_above = true
|
||||
river_water_above = true
|
||||
elseif c == c_air then
|
||||
nplaced = 0
|
||||
air_above = true
|
||||
water_above = false
|
||||
else
|
||||
nplaced = 31000
|
||||
air_above = false
|
||||
water_above = false
|
||||
end
|
||||
|
||||
vi = vi - ystride
|
||||
end
|
||||
|
||||
if not biomemap[index] then
|
||||
biomemap[index] = water_biome
|
||||
end
|
||||
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Walkable, liquid, and dustable: memoization tables for better performance
|
||||
local walkable = setmetatable({}, {
|
||||
__index = function(t, c)
|
||||
local is_walkable = false
|
||||
local ndef = minetest.registered_nodes[minetest.get_name_from_content_id(c)]
|
||||
if ndef and ndef.walkable then
|
||||
is_walkable = true
|
||||
end
|
||||
|
||||
print(ndef.name .. " walkable: " .. tostring(is_walkable))
|
||||
t[c] = is_walkable
|
||||
return is_walkable
|
||||
end,
|
||||
})
|
||||
|
||||
local liquid = setmetatable({}, {
|
||||
__index = function(t, c)
|
||||
local is_liquid = false
|
||||
local ndef = minetest.registered_nodes[minetest.get_name_from_content_id(c)]
|
||||
if ndef and ndef.liquidtype then
|
||||
is_liquid = ndef.liquidtype ~= "none"
|
||||
end
|
||||
|
||||
print(ndef.name .. " liquid: " .. tostring(is_liquid))
|
||||
t[c] = is_liquid
|
||||
return is_liquid
|
||||
end,
|
||||
})
|
||||
|
||||
local function canPlaceDeco(deco, data, vi, pattern)
|
||||
--print("Testing deco " .. deco.name)
|
||||
if not deco.place_on[data[vi]] then
|
||||
--print(deco.name, "Not the right node", minetest.get_name_from_content_id(data[vi]))
|
||||
return false
|
||||
elseif deco.num_spawn_by <= 0 then
|
||||
--print("No specific parameters")
|
||||
return true
|
||||
end
|
||||
|
||||
local spawn_by = deco.spawn_by
|
||||
local nneighs = deco.num_spawn_by
|
||||
for i, incr in ipairs(pattern) do
|
||||
vi = vi + incr
|
||||
if spawn_by[data[vi]] then
|
||||
nneighs = nneighs - 1
|
||||
if nneighs < 1 then
|
||||
--print("Neighbours found")
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--print(deco.name, "Not the required neighbours")
|
||||
return false
|
||||
end
|
||||
|
||||
local function placeDeco(deco, data, a, vm, minp, maxp, blockseed)
|
||||
local ps = PcgRandom(blockseed + 53)
|
||||
local carea_size = maxp.x - minp.x + 1
|
||||
|
||||
local sidelen = deco.sidelen
|
||||
if carea_size % sidelen > 0 then
|
||||
sidelen = carea_size
|
||||
end
|
||||
local divlen = carea_size / sidelen - 1
|
||||
local area = sidelen*sidelen
|
||||
local ystride, zstride = a.ystride, a.zstride
|
||||
local pattern = {1, zstride, -1, -1, -zstride, -zstride, 1, 1, ystride, zstride, zstride, -1, -1, -zstride, -zstride, 1} -- Successive increments to iterate over 16 neighbouring nodes
|
||||
|
||||
for z0=0, divlen do
|
||||
for x0=0, divlen do
|
||||
local p2d_center = {x=minp.x+sidelen*(x0+0.5), y=minp.z+sidelen*(z0+0.5)}
|
||||
local p2d_min = {x=minp.x+sidelen*x0, y=minp.z+sidelen*z0}
|
||||
local p2d_max = {x=minp.x+sidelen*(x0+1)-1, y=minp.z+sidelen*(z0+1)-1}
|
||||
|
||||
local cover = false
|
||||
local nval = deco.use_noise and deco.noise:get_2d(p2d_center) or deco.fill_ratio
|
||||
local deco_count = 0
|
||||
|
||||
if nval >= 10 then
|
||||
cover = true
|
||||
deco_count = area
|
||||
else
|
||||
local deco_count_f = area * nval
|
||||
if deco_count_f >= 1 then
|
||||
deco_count = deco_count_f
|
||||
elseif deco_count_f > 0 and ps:next(1, 1000) <= deco_count_f * 1000 then
|
||||
deco_count = 1
|
||||
end
|
||||
end
|
||||
|
||||
local x = p2d_min.x - 1
|
||||
local z = p2d_min.y
|
||||
|
||||
for i=1, deco_count do
|
||||
if not cover then
|
||||
x = ps:next(p2d_min.x, p2d_max.x)
|
||||
z = ps:next(p2d_min.y, p2d_max.y)
|
||||
else
|
||||
x = x + 1
|
||||
if x == p2d_max.x + 1 then
|
||||
z = z + 1
|
||||
x = p2d_min.x
|
||||
end
|
||||
end
|
||||
local mapindex = carea_size * (z - minp.z) + (x - minp.x)
|
||||
|
||||
if deco.flags["all_floors"] == true and deco.flags["all_ceilings"] == true then
|
||||
local biome_ok = true
|
||||
if deco.use_biomes and #biomemap > 0 then
|
||||
local biome_here = biomemap[mapindex]
|
||||
if biome_here and not deco.biomes[biome_here.name] then
|
||||
biome_ok = false
|
||||
end
|
||||
end
|
||||
|
||||
if biome_ok then
|
||||
local size = (maxp.x - minp.x + 1) / 2
|
||||
local floors = {}
|
||||
local ceilings = {}
|
||||
|
||||
local is_walkable = false
|
||||
local vi = a:index(x, maxp.y, z)
|
||||
local walkable_above = walkable[data[vi]]
|
||||
for y = maxp.y-1, minp.y, -1 do
|
||||
vi = vi - ystride
|
||||
is_walkable = walkable[data[vi]]
|
||||
if is_walkable and not walkable_above then
|
||||
table.insert(floors, y)
|
||||
elseif walkable_above and not walkable then
|
||||
table.insert(ceilings, y)
|
||||
end
|
||||
|
||||
walkable_above = is_walkable
|
||||
end
|
||||
|
||||
if deco.flags["all_floors"] then
|
||||
for _, y in ipairs(floors) do
|
||||
if y >= biome.y_min and y <= biome.y_max then
|
||||
local pos = {x=x, y=y, z=z}
|
||||
if canPlaceDeco(deco, data, vi, pattern) then
|
||||
deco:generate(vm, ps, pos, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if deco.flags["all_ceilings"] then
|
||||
for _, y in ipairs(ceilings) do
|
||||
if y >= biome.y_min and y <= biome.y_max then
|
||||
local pos = {x=x, y=y, z=z}
|
||||
if canPlaceDeco(deco, data, vi, pattern) then
|
||||
deco:generate(vm, ps, pos, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local y = -31000
|
||||
if deco.flags["liquid_surface"] == true then
|
||||
local vi = a:index(x, maxp.y, z)
|
||||
for yi=maxp.y, minp.y, -1 do
|
||||
local c = data[vi]
|
||||
if walkable[c] then
|
||||
break
|
||||
elseif liquid[c] then
|
||||
y = yi
|
||||
break
|
||||
end
|
||||
vi = vi - ystride
|
||||
end
|
||||
else
|
||||
local vi = a:index(x, maxp.y, z)
|
||||
for yi=maxp.y, minp.y, -1 do
|
||||
if walkable[data[vi]] then
|
||||
y = yi
|
||||
break
|
||||
end
|
||||
vi = vi - ystride
|
||||
end
|
||||
end
|
||||
|
||||
if y >= deco.y_min and y <= deco.y_max and y >= minp.y and y <= maxp.y then
|
||||
local biome_ok = true
|
||||
if deco.use_biomes and #biomemap > 0 then
|
||||
local biome_here = biomemap[mapindex]
|
||||
if biome_here and not deco.biomes[biome_here.name] then
|
||||
biome_ok = false
|
||||
end
|
||||
end
|
||||
|
||||
if biome_ok then
|
||||
local pos = {x=x, y=y, z=z}
|
||||
if canPlaceDeco(deco, data, a:index(x,y,z), pattern) then
|
||||
deco:generate(vm, ps, pos, false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
local function getBlockseed(p, seed)
|
||||
return seed + p.z * 38134234 + p.y * 42123 + p.x * 23
|
||||
end
|
||||
|
||||
function biomegen.placeAllDecos(data, a, vm, minp, maxp, seed)
|
||||
local emin = vm:get_emerged_area()
|
||||
local blockseed = getBlockseed(emin, seed)
|
||||
|
||||
local nplaced = 0
|
||||
|
||||
for i, deco in pairs(decos) do
|
||||
nplaced = nplaced + placeDeco(deco, data, a, vm, minp, maxp, blockseed)
|
||||
end
|
||||
|
||||
return nplaced
|
||||
end
|
||||
|
||||
local dustable = setmetatable({}, {
|
||||
__index = function(t, c)
|
||||
local is_dustable = false
|
||||
local ndef = minetest.registered_nodes[minetest.get_name_from_content_id(c)]
|
||||
if ndef and ndef.walkable then
|
||||
local dtype = ndef.drawtype
|
||||
if dtype and dtype == "normal" or dtype == "allfaces" or dtype == "allfaces_optional" or dtype == "glasslike" or dtype == "glasslike_framed" or dtype == "glasslike_framed_optional" then
|
||||
is_dustable = true
|
||||
end
|
||||
end
|
||||
|
||||
print(ndef.name .. " dustable: " .. tostring(is_dustable))
|
||||
t[c] = is_dustable
|
||||
return is_dustable
|
||||
end,
|
||||
})
|
||||
|
||||
function biomegen.dustTopNodes(vm, data, a, minp, maxp)
|
||||
if maxp.y < water_level then
|
||||
return
|
||||
end
|
||||
|
||||
local full_maxp = a.MaxEdge
|
||||
|
||||
local index = 1
|
||||
local ystride = a.ystride
|
||||
|
||||
for z = minp.z, maxp.z do
|
||||
for x = minp.x, maxp.x do
|
||||
local biome = biomemap[index]
|
||||
|
||||
if biome and biome.node_dust then
|
||||
local vi = a:index(x, full_maxp.y, z)
|
||||
local c_full_max = data[vi]
|
||||
local y_start
|
||||
|
||||
if c_full_max == c_air then
|
||||
y_start = full_maxp.y - 1
|
||||
elseif c_full_max == c_ignore then
|
||||
vi = a:index(x, maxp.y+1, z)
|
||||
local c_max = data[vi]
|
||||
|
||||
if c_max == c_air then
|
||||
y_start = maxp.y
|
||||
end
|
||||
end
|
||||
|
||||
if y_start then -- workaround for the 'continue' statement
|
||||
vi = a:index(x, y_start, z)
|
||||
local y = y_start
|
||||
for y0=y_start, minp.y-1, -1 do
|
||||
if data[vi] ~= c_air then
|
||||
y = y0
|
||||
break
|
||||
end
|
||||
vi = vi - ystride
|
||||
end
|
||||
local c = data[vi]
|
||||
if dustable[c] and c ~= biome.node_dust then
|
||||
local pos = {x=x, y=y+1, z=z}
|
||||
vm:set_node_at(pos, {name=biome.node_dust_name})
|
||||
--print(minetest.pos_to_string(pos), "Dust")
|
||||
end
|
||||
end
|
||||
end
|
||||
index = index + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[minetest.register_on_generated(function(minp, maxp, seed)
|
||||
if not init then
|
||||
local chulens = {x=maxp.x-minp.x+1, y=maxp.y-minp.y+1, z=maxp.z-minp.z+1}
|
||||
initialize(chulens)
|
||||
for i, biome in pairs(minetest.registered_biomes) do
|
||||
print(i)
|
||||
print(dump2(biome))
|
||||
end
|
||||
end
|
||||
end)]]
|
||||
|
||||
minetest.register_chatcommand('biome', {
|
||||
params = "",
|
||||
description = "Get local biome and related climate parameters",
|
||||
privs = {},
|
||||
func = function(name, params)
|
||||
local player = minetest.get_player_by_name(name)
|
||||
local pos = player:get_pos()
|
||||
local bdata = minetest.get_biome_data(pos)
|
||||
local biome = minetest.get_biome_name(bdata.biome)
|
||||
|
||||
return true, ("Biome: %s, Heat: %f, Humidity: %f"):format(biome, bdata.heat, bdata.humidity)
|
||||
end,
|
||||
})
|
Loading…
Reference in New Issue