d10e0a6e5a
This major rewrite grew out of a desire to be able to have stalactites placed according to a global noise function, which meant the whole decoration approach had to be changed to allow for data to be efficiently carried over between nodes. In the process I cleaned up a lot of old inefficient code, some of it written back when I was still learning LUA in general. The legacy.lua file contains the old deprecated code so in theory any mods depending on subterrane should still function as they did before.
142 lines
4.4 KiB
Lua
142 lines
4.4 KiB
Lua
local sidelen = mapgen_helper.block_size
|
|
|
|
local snap_to_minp = function(ydepth)
|
|
return ydepth - (ydepth+32) % sidelen -- put ydepth at the minp.y of mapblocks
|
|
end
|
|
|
|
function subterrane:register_cave_spawn(cave_layer_def, start_depth)
|
|
minetest.register_on_newplayer(function(player)
|
|
local ydepth = snap_to_minp(start_depth or cave_layer_def.y_max)
|
|
local spawned = false
|
|
while spawned ~= true do
|
|
spawned = spawnplayer(cave_layer_def, player, ydepth)
|
|
ydepth = ydepth - sidelen
|
|
if ydepth < cave_layer_def.y_min then
|
|
ydepth = snap_to_minp(cave_layer_def.y_max)
|
|
end
|
|
end
|
|
end)
|
|
|
|
minetest.register_on_respawnplayer(function(player)
|
|
local ydepth = snap_to_minp(start_depth or cave_layer_def.y_max)
|
|
local spawned = false
|
|
while spawned ~= true do
|
|
spawned = spawnplayer(cave_layer_def, player, ydepth)
|
|
ydepth = ydepth - sidelen
|
|
if ydepth < cave_layer_def.y_min then
|
|
ydepth = snap_to_minp(cave_layer_def.y_max)
|
|
end
|
|
end
|
|
return true
|
|
end)
|
|
end
|
|
|
|
local outside_region = 0
|
|
local inside_ground = 1
|
|
local inside_cavern = 2
|
|
|
|
-- Spawn player underground in a giant cavern
|
|
function spawnplayer(cave_layer_def, player, ydepth)
|
|
subterrane.set_defaults(cave_layer_def)
|
|
|
|
local YMIN = cave_layer_def.y_min
|
|
local YMAX = cave_layer_def.y_max
|
|
local BLEND = math.min(cave_layer_def.boundary_blend_range, (YMAX-YMIN)/2)
|
|
|
|
local TCAVE = cave_layer_def.cave_threshold
|
|
|
|
local np_cave = cave_layer_def.perlin_cave
|
|
local np_wave = cave_layer_def.perlin_wave
|
|
|
|
local y_blend_min = YMIN + BLEND * 1.5
|
|
local y_blend_max = YMAX - BLEND * 1.5
|
|
|
|
local column_def = cave_layer_def.columns
|
|
local double_frequency = cave_layer_def.double_frequency
|
|
|
|
local options = {}
|
|
|
|
for chunk = 1, 64 do
|
|
minetest.log("info", "[subterrane] searching for spawn "..chunk)
|
|
|
|
local minp = {x = sidelen * math.random(-32, 32) - 32, z = sidelen * math.random(-32, 32) - 32, y = ydepth}
|
|
local maxp = {x = minp.x + sidelen - 1, z = minp.z + sidelen - 1, y = ydepth + sidelen - 1}
|
|
|
|
local nvals_cave, cave_area = mapgen_helper.perlin3d("subterrane:cave", minp, maxp, np_cave) --cave noise for structure
|
|
local nvals_wave = mapgen_helper.perlin3d("subterrane:wave", minp, maxp, np_wave) --wavy structure of cavern ceilings and floors
|
|
|
|
-- pre-average everything
|
|
for vi, value in ipairs(nvals_cave) do
|
|
nvals_cave[vi] = (value + nvals_wave[vi])/2
|
|
end
|
|
local column_points = nil
|
|
local column_weight = nil
|
|
|
|
local previous_y = minp.y
|
|
local previous_node_state = outside_region
|
|
|
|
for vi, x, y, z in cave_area:iterp_yxz(vector.add(minp, 2), vector.subtract(maxp, 2)) do
|
|
|
|
if y < previous_y then
|
|
previous_node_state = outside_region
|
|
end
|
|
previous_y = y
|
|
|
|
local cave_local_threshold
|
|
if y < y_blend_min then
|
|
cave_local_threshold = TCAVE + ((y_blend_min - y) / BLEND) ^ 2
|
|
elseif y > y_blend_max then
|
|
cave_local_threshold = TCAVE + ((y - y_blend_max) / BLEND) ^ 2
|
|
else
|
|
cave_local_threshold = TCAVE
|
|
end
|
|
|
|
local cave_value = nvals_cave[vi]
|
|
if double_frequency then
|
|
cave_value = math.abs(cave_value)
|
|
end
|
|
|
|
-- inside a giant cavern
|
|
if cave_value > cave_local_threshold then
|
|
local column_value = 0
|
|
local inside_column = false
|
|
if column_def then
|
|
if column_points == nil then
|
|
column_points = subterrane.get_column_points(minp, maxp, column_def)
|
|
column_weight = column_def.weight
|
|
end
|
|
column_value = subterrane.get_column_value({x=x, y=y, z=z}, column_points)
|
|
end
|
|
inside_column = column_value > 0 and cave_value - column_value * column_weight < cave_local_threshold
|
|
if previous_node_state == inside_ground and not inside_column then
|
|
-- we just entered the cavern from below. Do a quick check for head space.
|
|
local val_above = nvals_cave[vi+cave_area.ystride]
|
|
if double_frequency then
|
|
val_above = math.abs(val_above)
|
|
end
|
|
if val_above > cave_local_threshold then
|
|
table.insert(options, {x=x, y=y+1, z=z})
|
|
end
|
|
end
|
|
if not inside_column then
|
|
previous_node_state = inside_cavern
|
|
else
|
|
previous_node_state = inside_ground
|
|
end
|
|
|
|
else
|
|
previous_node_state = inside_ground
|
|
end
|
|
end
|
|
|
|
if table.getn(options) > 20 then -- minimum 20 to ensure the player is put in a place with a modicum of size
|
|
local choice = math.random(1, table.getn(options))
|
|
local spawnpoint = options[ choice ]
|
|
minetest.log("action", "[subterrane] spawning player " .. minetest.pos_to_string(spawnpoint))
|
|
player:setpos(spawnpoint)
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|