geomoria/geomorph.lua

355 lines
12 KiB
Lua

-- Geomoria geomorph.lua
-- Copyright Duane Robertson (duane@duanerobertson.com), 2017
-- Distributed under the LGPLv2.1 (https://www.gnu.org/licenses/old-licenses/lgpl-2.1.en.html)
--newnode = geomoria_mod.clone_node("air")
--newnode.light_source = 14
--minetest.register_node("geomoria:bright_air", newnode)
geomoria_mod.geomorph = function(minp, maxp, data, p2data, area, node, heightmap)
if not (minp and maxp and data and p2data and area and node and type(data) == 'table' and type(p2data) == 'table') then
return
end
if minp.y >= 200 then
return
end
local csize = vector.add(vector.subtract(maxp, minp), 1)
local write = false
local wetness = 0
local avg = (minp.y + maxp.y) / 2
local out_of_range
if avg > geomoria_mod.geomoria_depth * 80 - 32 then
out_of_range = true
end
local index = 0
local index3d = 0
local plan_name = geomoria_mod.plans_keys[math.random(#geomoria_mod.plans_keys)]
local plan = geomoria_mod.plans[plan_name]
local rot = math.random(4) - 1
local exit_stair = (minp.z % (csize.z * 10)) < csize.z and (minp.x % (csize.x * 10)) < csize.x
if out_of_range then
plan = geomoria_mod.stair_height
rot = 0
elseif exit_stair then
plan = geomoria_mod.stair_base
rot = 0
end
if not out_of_range then
for _, item in pairs(plan) do
if item.act == 'fill' or item.act == 'stair' or item.act == 'ladder' or item.act == 'cylinder' or item.act == 'sphere' then
local coords = item.coords
if not (coords and item.node and type(coords) == 'table' and type(item.node) == 'string' and #coords == 6) then
print('Geomoria: Invalid plan')
return
end
local min_x, max_x, min_z, max_z, dy
if rot == 0 then
min_x, max_x = coords[1], coords[1] + coords[2] - 1
min_z, max_z = coords[5], coords[5] + coords[6] - 1
elseif rot == 1 then
min_x, max_x = coords[5], coords[5] + coords[6] - 1
min_z, max_z = csize.x - (coords[1] + coords[2]), csize.x - coords[1] - 1
elseif rot == 2 then
min_x, max_x = csize.x - (coords[1] + coords[2]), csize.x - coords[1] - 1
min_z, max_z = csize.z - (coords[5] + coords[6]), csize.z - coords[5] - 1
elseif rot == 3 then
min_x, max_x = csize.z - (coords[5] + coords[6]), csize.z - coords[5] - 1
min_z, max_z = coords[1], coords[1] + coords[2] - 1
end
local min_y = coords[3]
local max_y = coords[3] + coords[4] - 1
if item.act == 'stair' then
max_y = max_y + 4
end
for dz = min_z - 2, max_z + 2 do
for dy = min_y - 2, max_y + 2 do
for dx = min_x - 2, max_x + 2 do
if dx >= 0 and dx <= 79 and dy >= 0 and dy <= 79 and dz >= 0 and dz <= 79 then
local ivm = area:index(minp.x + dx, minp.y + dy, minp.z + dz)
data[ivm] = node['default:stone']
end
end
end
end
end
end
end
for _, item in pairs(plan) do
if item.param then
if item.param == 'wet' then
wetness = 1
elseif item.param == 'dry' then
wetness = -1
end
--elseif item.hook and type(item.hook) == 'function' then
-- local n, pos = item.hook(item.hook_params)
-- if type(n) == 'number' and type(pos) == 'table' and type(pos.x) == 'number' and type(pos.y) == 'number' and type(pos.z) == 'number' then
-- local ivm = area:index(minp.x + pos.x, minp.y + pos.y, minp.z + pos.z)
-- data[ivm] = node[item.line]
-- else
-- print("Geomoria: Could not interpret hook return values")
-- end
elseif item.act == 'fill' or item.act == 'stair' or item.act == 'ladder' or item.act == 'cylinder' or item.act == 'sphere' then
local coords = item.coords
local p2 = item.param2
if p2 and item.act == 'ladder' then
-- 2 X+ 3 X- 4 Z+ 5 Z-
local tran = '4253'
local fp2 = tran:find(tostring(p2)) - 1
p2 = (fp2 + rot) % 4
p2 = tonumber(tran:sub(p2 + 1, p2 + 1))
elseif p2 and p2 < 4 then
--p2 = math.floor(p2 / 4) * 4 + (p2 + rot) % 4
p2 = (p2 + rot) % 4
elseif p2 then
--
else
p2 = nil
end
local min_x, max_x, min_z, max_z, dy
if rot == 0 then
min_x, max_x = coords[1], coords[1] + coords[2] - 1
min_z, max_z = coords[5], coords[5] + coords[6] - 1
elseif rot == 1 then
min_x, max_x = coords[5], coords[5] + coords[6] - 1
min_z, max_z = csize.x - (coords[1] + coords[2]), csize.x - coords[1] - 1
elseif rot == 2 then
min_x, max_x = csize.x - (coords[1] + coords[2]), csize.x - coords[1] - 1
min_z, max_z = csize.z - (coords[5] + coords[6]), csize.z - coords[5] - 1
elseif rot == 3 then
min_x, max_x = csize.z - (coords[5] + coords[6]), csize.z - coords[5] - 1
min_z, max_z = coords[1], coords[1] + coords[2] - 1
end
if item.act == 'fill' or item.act == 'ladder' then
if item.line then
for dz = min_z - 1, max_z + 1 do
for dy = coords[3] - 1, coords[3] + coords[4] do
for dx = min_x - 1, max_x + 1 do
local ivm = area:index(minp.x + dx, minp.y + dy, minp.z + dz)
data[ivm] = node[item.line]
end
end
end
end
if item.floor then
for dz = min_z - 1, max_z + 1 do
for dx = min_x - 1, max_x + 1 do
local ivm = area:index(minp.x + dx, minp.y + coords[3] - 1, minp.z + dz)
data[ivm] = node[item.floor]
end
end
end
if geomoria_mod.cheap_lighting and item.node == 'air' then
if coords[4] < 13 and coords[2] > 10 and coords[6] > 10 then
for dz = min_z, max_z do
for dx = min_x, max_x do
if dx % 8 == 0 and dz % 8 == 0 then
local ivm = area:index(minp.x + dx, minp.y + coords[3] + coords[4], minp.z + dz)
if data[ivm] == node['default:stone'] or data[ivm] == node['default:stone_block'] then
data[ivm] = node['default:meselamp']
end
end
end
end
else
local alt = minp.y + coords[3] + math.floor(coords[4] / 2)
for dz = min_z - 1, max_z + 1, max_z - min_z + 2 do
for dx = min_x, max_x do
if dx % 10 == 0 then
local ivm = area:index(minp.x + dx, alt, minp.z + dz)
if data[ivm] == node['default:stone'] or data[ivm] == node['default:stone_block'] then
data[ivm] = node['default:meselamp']
end
end
end
end
for dz = min_z, max_z do
for dx = min_x - 1, max_x + 1, max_x - min_x + 2 do
if dz % 10 == 0 then
local ivm = area:index(minp.x + dx, alt, minp.z + dz)
if data[ivm] == node['default:stone'] or data[ivm] == node['default:stone_block'] then
data[ivm] = node['default:meselamp']
end
end
end
end
end
end
for dz = min_z, max_z do
for dx = min_x, max_x do
local height
if heightmap then
height = heightmap[dz * csize.x + dx + 1]
if item.node == 'air' then
height = height + 4
end
end
for dy = coords[3], coords[3] + coords[4] - 1 do
if not height or (minp.y + dy) < height then
if not item.random or math.random(item.random) == 1 then
local ivm = area:index(minp.x + dx, minp.y + dy, minp.z + dz)
data[ivm] = node[item.node]
p2data[ivm] = p2
end
end
end
end
end
if item.treasure and math.random(item.treasure) == 1 then
local x = minp.x + min_x + math.random(max_x - min_x + 1) - 1
local y = minp.y + coords[3]
local z = minp.z + min_z + math.random(max_z - min_z + 1) - 1
local n = geomoria_mod.treasure_chest_hook({x=x, y=y, z=z}, min_x, max_x, min_z, max_z, data, area, node)
if type(n) == 'number' then
local ivm = area:index(x, y, z)
data[ivm] = n
end
end
write = true
elseif item.act == 'stair' then
for dz = min_z, max_z do
for dx = min_x, max_x do
if p2 == 0 then
dy = coords[3] + dz - min_z
elseif p2 == 1 then
dy = coords[3] + dx - min_x
elseif p2 == 2 then
dy = coords[3] + max_z - dz
elseif p2 == 3 then
dy = coords[3] + max_x - dx
end
local height
if heightmap then
height = heightmap[dz * csize.x + dx + 1]
end
local y1 = item.depth and dy - item.depth or coords[3]
y1 = math.max(y1, coords[3])
for y = y1, dy - 1 do
if not height or (minp.y + y) <= height then
local ivm = area:index(minp.x + dx, minp.y + y, minp.z + dz)
data[ivm] = node['default:stone']
end
end
y1 = item.height and dy + item.height or coords[3] + coords[4] + 2
y1 = math.min(y1, coords[3] + coords[4] + 2)
for y = dy + 1, y1 do
if not height or (minp.y + y) <= (height + 4) then
local ivm = area:index(minp.x + dx, minp.y + y, minp.z + dz)
data[ivm] = node['air']
end
end
if not height or (minp.y + dy) <= height then
local ivm = area:index(minp.x + dx, minp.y + dy, minp.z + dz)
data[ivm] = node[item.node]
p2data[ivm] = p2
end
end
end
write = true
elseif item.act == 'cylinder' then
local ax = item.axis
if ax ~= 'y' and (rot == 1 or rot == 3) then
if ax == 'z' then
ax = 'x'
else
ax = 'z'
end
end
local r2 = (coords[4] / 2) ^ 2
if ax == 'y' then
r2 = (coords[2] / 2) ^ 2
end
local rx = (min_x + max_x) / 2
local ry = (coords[3] + coords[3] + coords[4] - 1) / 2
local rz = (min_z + max_z) / 2
min_x = math.max(min_x, 0)
local min_y = math.max(coords[3], 0)
min_z = math.max(min_z, 0)
max_x = math.min(max_x, csize.x - 1)
local max_y = math.min(coords[3] + coords[4] - 1, csize.y - 1)
max_z = math.min(max_z, csize.z - 1)
for dz = min_z, max_z do
for dy = min_y, max_y do
for dx = min_x, max_x do
if (ax == 'x' and (rz - dz) ^ 2 + (ry - dy) ^ 2 <= r2) or
(ax == 'y' and (rx - dx) ^ 2 + (rz - dz) ^ 2 <= r2) or
(ax == 'z' and (rx - dx) ^ 2 + (ry - dy) ^ 2 <= r2) then
if not item.random or math.random(item.random) == 1 then
local ivm = area:index(minp.x + dx, minp.y + dy, minp.z + dz)
data[ivm] = node[item.node]
p2data[ivm] = p2
end
end
end
end
end
write = true
elseif item.act == 'sphere' then
local r2 = (coords[2] / 2) ^ 2
local rx = (min_x + max_x) / 2
local ry = (coords[3] + coords[3] + coords[4] - 1) / 2
local rz = (min_z + max_z) / 2
min_x = math.max(min_x, 0)
local min_y = math.max(coords[3], 0)
min_z = math.max(min_z, 0)
max_x = math.min(max_x, csize.x - 1)
local max_y = math.min(coords[3] + coords[4] - 1, csize.y - 1)
max_z = math.min(max_z, csize.z - 1)
for dz = min_z, max_z do
for dy = min_y, max_y do
for dx = min_x, max_x do
if (rz - dz) ^ 2 + (ry - dy) ^ 2 + (rx - dx) ^ 2 <= r2 then
if not item.random or math.random(item.random) == 1 then
local ivm = area:index(minp.x + dx, minp.y + dy, minp.z + dz)
data[ivm] = node[item.node]
p2data[ivm] = p2
end
end
end
end
end
write = true
end
end
end
return write, wetness
end