lualandmg/init.lua

432 lines
10 KiB
Lua

lualandmg = {}
local mod_path = minetest.get_modpath("lualandmg")
-- Default noise params
lualandmg.np_base = {
offset = 0,
scale = 1,
spread = {x=256, y=256, z=256},
octaves = 5,
seed = 42692,
persist = 0.5
}
lualandmg.np_mountains = {
offset = 0,
scale = 1,
spread = {x=256, y=256, z=256},
octaves = 5,
seed = 3853,
persist = 0.5
}
lualandmg.np_trees = {
offset = 0,
scale = 1,
spread = {x=64, y=64, z=64},
octaves = 1,
seed = -5432,
persist = 0.6
}
lualandmg.np_caves = {
offset = 0,
scale = 1,
spread = {x=32, y=16, z=32},
octaves = 4,
seed = -11842,
persist = 0.4,
flags = "eased"
}
-- A value between: 0.0 and 100.0
lualandmg.np_temperature = {
offset = 0,
scale = 1,
spread = {x=512, y=512, z=512},
octaves = 2,
seed = 921498,
persist = 0.5
}
dofile(mod_path.."/settings.lua")
dofile(mod_path.."/nodes.lua")
dofile(mod_path.."/functions.lua")
dofile(mod_path.."/treegen.lua")
dofile(mod_path.."/biomedef.lua")
-- Apply noise modifier settings
local np_list = {"np_base", "np_mountains", "np_trees", "np_temperature"}
for _,v in pairs(np_list) do
lualandmg[v].spread = vector.multiply(
lualandmg[v].spread, lualandmg.noise_spread)
end
minetest.set_mapgen_setting("mgname", "singlenode", true)
minetest.register_chatcommand("regenerate", {
description = "Regenerates <size * 8>^3 nodes around you",
params = "<size * 8>",
privs = {server=true},
func = function(name, param)
local size = tonumber(param) or 1
if size > 8 then
size = 8 -- Limit: 8*8 -> 64
elseif size < 1 then
return false, "Nothing to do."
end
size = size * 8
local player = minetest.get_player_by_name(name)
local pos = vector.floor(vector.divide(player:getpos(), size))
local minp = vector.multiply(pos, size)
local maxp = vector.add(minp, size - 1)
lualandmg.generate(minp, maxp, math.random(0, 9999), true)
return true, "Done!"
end
})
local c_air = minetest.get_content_id("air")
local c_water = minetest.get_content_id("default:water_source")
local c_lava = minetest.get_content_id("default:lava_source")
local c_stone = minetest.get_content_id("default:stone")
local c_ice = minetest.get_content_id("default:ice")
local c_cactus = minetest.get_content_id("default:cactus")
local heightmap = {}
local heatmap = {}
local old_funct = minetest.get_mapgen_object
function minetest.get_mapgen_object(what)
if what == "heightmap" then
return heightmap
end
if what == "heatmap" then
return heatmap
end
return old_funct(what)
end
function lualandmg.generate(minp, maxp, seed, regen)
local is_surface = maxp.y > -80
local t1 = os.clock()
local sidelen1d = maxp.x - minp.x + 1
local sidelen3d = {x=sidelen1d, y=sidelen1d, z=sidelen1d}
heightmap = {}
heatmap = {}
local terrain_scale = lualandmg.terrain_scale
local trees = lualandmg.registered_trees
local surface = {}
local mudflow_check = {}
local nvals_base, nvals_mountains, nvals_trees, nvals_temp
if is_surface then
-- Update the perlin maps if required
if lualandmg.pm_sidelen ~= sidelen1d then
lualandmg.pm_base = minetest.get_perlin_map(lualandmg.np_base, sidelen3d)
lualandmg.pm_mountains = minetest.get_perlin_map(lualandmg.np_mountains, sidelen3d)
lualandmg.pm_trees = minetest.get_perlin_map(lualandmg.np_trees, sidelen3d)
lualandmg.pm_temp = minetest.get_perlin_map(lualandmg.np_temperature, sidelen3d)
lualandmg.pm_sidelen = sidelen1d
end
nvals_base = lualandmg.pm_base:get_2d_map_flat({x=minp.x, y=minp.z})
nvals_mountains = lualandmg.pm_mountains:get_2d_map_flat({x=minp.x, y=minp.z})
nvals_trees = lualandmg.pm_trees:get_2d_map_flat({x=minp.x, y=minp.z})
nvals_temp = lualandmg.pm_temp:get_2d_map_flat({x=minp.x, y=minp.z})
end
-- Pre-calculate terrain height, biome and trees
local nixz = 1
if is_surface then
local biomes = lualandmg.registered_biomes
local decorations = lualandmg.registered_decorations
for z = minp.z, maxp.z do
for x = minp.x, maxp.x do
local surf = nvals_base[nixz] * 20 + 16
local mountain_y = nvals_mountains[nixz] * 80 - 30
local tree_factor = nvals_trees[nixz] * 5 + 4
local temperature = nvals_temp[nixz] * 43 + 17
local tree = nil
-- 'j_*' used for jitter/spread values
local j_temperature = temperature + math.random(-3, 3)
if mountain_y > 0 then
surf = surf + mountain_y
end
if surf < 0 then
-- Deeper oceans
surf = surf * 2.5
end
surf = math.floor((surf * terrain_scale) + 0.5)
if math.abs(temperature - 45) < 5 then
-- Prevent lakes in deserts
surf = math.floor(
math.max(-math.abs(temperature - 45) * 5, surf) + 0.5)
end
local g_stone, g_middle, g_top, g_cover
for i, v in ipairs(biomes) do
if j_temperature > v.temperature_min or i == #biomes then
g_stone = v.stone
g_middle = v.middle
g_top = v.top
g_cover = v.cover
break
end
end
if not g_cover then
for i, v in pairs(decorations) do
if j_temperature >= v.temperature_min and
j_temperature <= v.temperature_max and
math.random(v.chance) == 1 then
if lualandmg.is_valid_ground(v.node_under, g_top) then
g_cover = v.name
break
end
end
end
end
-- Tweak the noise a bit
tree_factor = tree_factor * tree_factor
if tree_factor < 0.4 then
tree_factor = 0.4
end
for i, v in pairs(trees) do
if j_temperature >= v.temperature_min and
j_temperature <= v.temperature_max and
math.random(math.ceil(v.chance * tree_factor)) == 1 then
if lualandmg.is_valid_ground(v.node_under, g_top) then
tree = v
g_cover = nil
break
end
end
end
surface[nixz] = {surf, tree, temperature,
g_stone, g_middle, g_top, g_cover}
nixz = nixz + 1
end
end
nvals_base = nil
nvals_mountains = nil
nvals_trees = nil
end
local vm, emin, emax
if regen then
vm = minetest.get_voxel_manip()
emin, emax = vm:read_from_map(minp, maxp)
else
vm, emin, emax = minetest.get_mapgen_object("voxelmanip")
end
local area = VoxelArea:new{MinEdge=emin, MaxEdge=emax}
local data = vm:get_data()
if regen then
-- Erase the entire area
for z = minp.z, maxp.z do
for y = minp.y, maxp.y do
local vi = area:index(minp.x, y, z)
for x = minp.x, maxp.x do
data[vi] = c_air
vi = vi + 1
end
end
end
end
local nvals_caves = minetest.get_perlin_map(lualandmg.np_caves, sidelen3d):get_3d_map_flat(minp)
local nixyz = 1
nixz = 1
-- Lava occurrence coefficient, maximal lava at Y=-4096
local lava_coeff = math.max(minp.y / 4096, -1) / 5
for z = minp.z, maxp.z do
for y = minp.y, maxp.y do
local vi = area:index(minp.x, y, z)
for x = minp.x, maxp.x do
local surf, tree, temp = 0, 0, 0
local g_stone = c_stone
local g_middle, g_top, g_cover
if is_surface then
local cache = surface[nixz]
surf = cache[1]
tree = cache[2]
temp = cache[3]
g_stone = cache[4]
g_middle = cache[5]
g_top = cache[6]
g_cover = cache[7]
end
local cave = nvals_caves[nixyz]
if cave > 1.1 + lava_coeff and y < -20
and not (is_surface and temp < 5) then
-- Cave, filled with lava
if cave > 1.3 + lava_coeff then
data[vi] = c_lava
end
elseif cave < -0.9 + lava_coeff / 2 and y <= surf + 1 then
-- Empty cave
if is_surface then
mudflow_check[nixz] = true
end
elseif y == surf and y < 0 then
-- Sea ground
data[vi] = g_middle
elseif y == surf then
-- Surface, maybe with a tree above
if tree then
tree.action(
vector.new(x, y + 1, z),
vm, data, area, seed
)
data[vi] = g_middle
else
data[vi] = g_top
end
elseif y == surf + 1 and y > 0 and g_cover then
if data[vi] == c_air then
if data[area:index(x, y - 1, z)] == g_top then
data[vi] = g_cover
end
end
elseif surf - y <= (3 - surf / 30) and y < surf then
-- Soil/sand under the surface
data[vi] = g_middle
elseif y > surf and y <= 0 then
-- Water
if temp + math.random(-2, 2) < -18 then
data[vi] = c_ice
elseif temp < 45 then
data[vi] = c_water
end
elseif y < surf then
data[vi] = g_stone
end
nixyz = nixyz + 1
nixz = nixz + 1
vi = vi + 1
end
nixz = nixz - sidelen1d
end
nixz = nixz + sidelen1d
end
nvals_caves = nil
local t2 = os.clock()
local log_message = minetest.pos_to_string(minp).." terrain = "..
math.ceil((t2 - t1) * 1000)
if is_surface then
-- Do the mudflow!
nixz = 1
local height = 2
for z = minp.z, maxp.z do
for x = minp.x, maxp.x do
if mudflow_check[nixz] then
local cache = surface[nixz]
local r_surf = cache[1]
local surf = r_surf + height
local g_stone = cache[4]
local g_middle = cache[5]
local g_top = cache[6]
-- out of range
if r_surf - 16 > maxp.y then
surf = minp.y + 1
end
-- node at surface got removed
local max_depth = 5
local vi, node, ground
local depth = 0
local covered, water = false, false
for y = surf, minp.y + 1, -1 do
vi = area:index(x, y, z)
node = data[vi]
local is_air = (node == c_air)
if node == c_water then
water = true
end
if depth >= max_depth then
ground = y + max_depth
break
end
if is_air then
if water then
-- Fill up caves to prevent massive flood
data[vi] = c_water
elseif depth > 0 then
covered = true
data[vi] = g_stone
end
depth = 0
elseif y <= r_surf then
depth = depth + 1
end
end
if ground and ground ~= surf then
vi = area:index(x, ground, z)
if ground >= 0 and not covered then
data[vi] = g_top
-- Update terrain height for heightmap
cache[1] = ground
else
data[vi] = g_middle
end
vi = area:index(x, ground - 1, z)
data[vi] = g_middle
end
end
heightmap[nixz] = surface[nixz][1]
heatmap[nixz] = nvals_temp[nixz] * 50 + 50
nixz = nixz + 1
end
end
log_message = log_message..", mudflow = "..
math.ceil((os.clock() - t2) * 1000)
end
vm:set_data(data)
if regen then
vm:set_param2_data({})
else
vm:set_lighting({day=0, night=0})
end
minetest.generate_ores(vm)
vm:calc_lighting()
vm:write_to_map()
vm:update_liquids()
log_message = log_message..", total = "..
math.ceil((os.clock() - t1) * 1000).." [ms]"
minetest.log("action", log_message)
end
minetest.after(0, table.insert,
minetest.registered_on_generateds, 1, lualandmg.generate)