-- 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