version 0.21
This commit is contained in:
parent
bd1233aaf1
commit
c8d70b992c
1717 changed files with 68882 additions and 0 deletions
70
mods/WorldEdit/worldedit/code.lua
Normal file
70
mods/WorldEdit/worldedit/code.lua
Normal file
|
@ -0,0 +1,70 @@
|
|||
worldedit = worldedit or {}
|
||||
local minetest = minetest -- local copy of global
|
||||
|
||||
-- Copies and modifies positions `pos1` and `pos2` so that each component of
|
||||
-- `pos1` is less than or equal to the corresponding component of `pos2`.
|
||||
-- Returns the new positions.
|
||||
worldedit.sort_pos = function(pos1, pos2)
|
||||
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
|
||||
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
|
||||
if pos1.x > pos2.x then
|
||||
pos2.x, pos1.x = pos1.x, pos2.x
|
||||
end
|
||||
if pos1.y > pos2.y then
|
||||
pos2.y, pos1.y = pos1.y, pos2.y
|
||||
end
|
||||
if pos1.z > pos2.z then
|
||||
pos2.z, pos1.z = pos1.z, pos2.z
|
||||
end
|
||||
return pos1, pos2
|
||||
end
|
||||
|
||||
-- Executes `code` as a Lua chunk in the global namespace,
|
||||
-- returning an error if the code fails, or nil otherwise.
|
||||
worldedit.lua = function(code)
|
||||
local func, err = loadstring(code)
|
||||
if not func then -- Syntax error
|
||||
return err
|
||||
end
|
||||
local good, err = pcall(operation)
|
||||
if not good then -- Runtime error
|
||||
return err
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
-- Executes `code` as a Lua chunk in the global namespace with the variable
|
||||
-- pos available, for each node in a region defined by positions `pos1` and
|
||||
-- `pos2`, returning an error if the code fails, or nil otherwise
|
||||
worldedit.luatransform = function(pos1, pos2, code)
|
||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
local factory, err = loadstring("return function(pos) " .. code .. " end")
|
||||
if not factory then -- Syntax error
|
||||
return err
|
||||
end
|
||||
local func = factory()
|
||||
|
||||
-- Keep area loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local pos = {x=pos1.x, y=0, z=0}
|
||||
while pos.x <= pos2.x do
|
||||
pos.y = pos1.y
|
||||
while pos.y <= pos2.y do
|
||||
pos.z = pos1.z
|
||||
while pos.z <= pos2.z do
|
||||
local good, err = pcall(func, pos)
|
||||
if not good then -- Runtime error
|
||||
return err
|
||||
end
|
||||
pos.z = pos.z + 1
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
pos.x = pos.x + 1
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
23
mods/WorldEdit/worldedit/compatibility.lua
Normal file
23
mods/WorldEdit/worldedit/compatibility.lua
Normal file
|
@ -0,0 +1,23 @@
|
|||
worldedit = worldedit or {}
|
||||
local minetest = minetest --local copy of global
|
||||
|
||||
worldedit.allocate_old = worldedit.allocate
|
||||
worldedit.deserialize_old = worldedit.deserialize
|
||||
worldedit.metasave = function(pos1, pos2, filename)
|
||||
local file, err = io.open(filename, "wb")
|
||||
if err then return 0 end
|
||||
local data, count = worldedit.serialize(pos1, pos2)
|
||||
file:write(data)
|
||||
file:close()
|
||||
return count
|
||||
end
|
||||
worldedit.metaload = function(originpos, filename)
|
||||
filename = minetest.get_worldpath() .. "/schems/" .. file .. ".wem"
|
||||
local file, err = io.open(filename, "wb")
|
||||
if err then return 0 end
|
||||
local data = file:read("*a")
|
||||
return worldedit.deserialize(originpos, data)
|
||||
end
|
||||
worldedit.scale = function(pos1, pos2, factor)
|
||||
return worldedit.stretch(pos1, pos2, factor, factor, factor)
|
||||
end
|
25
mods/WorldEdit/worldedit/init.lua
Normal file
25
mods/WorldEdit/worldedit/init.lua
Normal file
|
@ -0,0 +1,25 @@
|
|||
worldedit = worldedit or {}
|
||||
worldedit.version = {major=1, minor=0}
|
||||
worldedit.version_string = "1.0"
|
||||
|
||||
assert(minetest.get_voxel_manip, string.rep(">", 300) .. "HEY YOU! YES, YOU OVER THERE. THIS VERSION OF WORLDEDIT REQUIRES MINETEST 0.4.8 OR LATER! YOU HAVE AN OLD VERSION." .. string.rep("<", 300))
|
||||
|
||||
local path = minetest.get_modpath(minetest.get_current_modname())
|
||||
|
||||
local loadmodule = function(path)
|
||||
local file = io.open(path)
|
||||
if not file then
|
||||
return
|
||||
end
|
||||
file:close()
|
||||
return dofile(path)
|
||||
end
|
||||
|
||||
loadmodule(path .. "/manipulations.lua")
|
||||
loadmodule(path .. "/primitives.lua")
|
||||
loadmodule(path .. "/visualization.lua")
|
||||
loadmodule(path .. "/serialization.lua")
|
||||
loadmodule(path .. "/code.lua")
|
||||
loadmodule(path .. "/compatibility.lua")
|
||||
|
||||
print("[MOD] WorldEdit loaded!")
|
731
mods/WorldEdit/worldedit/manipulations.lua
Normal file
731
mods/WorldEdit/worldedit/manipulations.lua
Normal file
|
@ -0,0 +1,731 @@
|
|||
worldedit = worldedit or {}
|
||||
local minetest = minetest --local copy of global
|
||||
|
||||
-- Copies and modifies positions `pos1` and `pos2` so that each component of
|
||||
-- `pos1` is less than or equal to the corresponding component of `pos2`.
|
||||
-- Returns the new positions.
|
||||
worldedit.sort_pos = function(pos1, pos2)
|
||||
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
|
||||
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
|
||||
if pos1.x > pos2.x then
|
||||
pos2.x, pos1.x = pos1.x, pos2.x
|
||||
end
|
||||
if pos1.y > pos2.y then
|
||||
pos2.y, pos1.y = pos1.y, pos2.y
|
||||
end
|
||||
if pos1.z > pos2.z then
|
||||
pos2.z, pos1.z = pos1.z, pos2.z
|
||||
end
|
||||
return pos1, pos2
|
||||
end
|
||||
|
||||
--determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume
|
||||
worldedit.volume = function(pos1, pos2)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)
|
||||
end
|
||||
|
||||
--sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled
|
||||
worldedit.set = function(pos1, pos2, nodenames)
|
||||
if type(nodenames) == "string" then
|
||||
nodenames = {nodenames}
|
||||
end
|
||||
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
--fill emerged area with ignore
|
||||
local nodes = {}
|
||||
local ignore = minetest.get_content_id("ignore")
|
||||
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
|
||||
nodes[i] = ignore
|
||||
end
|
||||
|
||||
--fill selected area with node
|
||||
local node_ids = {}
|
||||
for i,v in ipairs(nodenames) do
|
||||
node_ids[i] = minetest.get_content_id(nodenames[i])
|
||||
end
|
||||
if #node_ids == 1 then --only one type of node
|
||||
local id = node_ids[1]
|
||||
for i in area:iterp(pos1, pos2) do nodes[i] = id end --fill area with node
|
||||
else --several types of nodes specified
|
||||
local id_count, rand = #node_ids, math.random
|
||||
for i in area:iterp(pos1, pos2) do nodes[i] = node_ids[rand(id_count)] end --fill randomly with all types of specified nodes
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return worldedit.volume(pos1, pos2)
|
||||
end
|
||||
|
||||
--replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced
|
||||
worldedit.replace = function(pos1, pos2, searchnode, replacenode)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
local nodes = manip:get_data()
|
||||
local searchnode_id = minetest.get_content_id(searchnode)
|
||||
local replacenode_id = minetest.get_content_id(replacenode)
|
||||
local count = 0
|
||||
for i in area:iterp(pos1, pos2) do --replace searchnode with replacenode
|
||||
if nodes[i] == searchnode_id then
|
||||
nodes[i] = replacenode_id
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
--replaces all nodes other than `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced
|
||||
worldedit.replaceinverse = function(pos1, pos2, searchnode, replacenode)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
local nodes = manip:get_data()
|
||||
local searchnode_id = minetest.get_content_id(searchnode)
|
||||
local replacenode_id = minetest.get_content_id(replacenode)
|
||||
local count = 0
|
||||
for i in area:iterp(pos1, pos2) do --replace anything that is not searchnode with replacenode
|
||||
if nodes[i] ~= searchnode_id then
|
||||
nodes[i] = replacenode_id
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
--copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes copied
|
||||
worldedit.copy = function(pos1, pos2, axis, amount) --wip: replace the old version below
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
if amount == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
local other1, other2
|
||||
if axis == "x" then
|
||||
other1, other2 = "y", "z"
|
||||
elseif axis == "y" then
|
||||
other1, other2 = "x", "z"
|
||||
else --axis == "z"
|
||||
other1, other2 = "x", "y"
|
||||
end
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
--prepare slice along axis
|
||||
local extent = {
|
||||
[axis] = 1,
|
||||
[other1]=pos2[other1] - pos1[other1] + 1,
|
||||
[other2]=pos2[other2] - pos1[other2] + 1,
|
||||
}
|
||||
local nodes = {}
|
||||
local schematic = {size=extent, data=nodes}
|
||||
|
||||
local currentpos = {x=pos1.x, y=pos1.y, z=pos1.z}
|
||||
local stride = {x=1, y=extent.x, z=extent.x * extent.y}
|
||||
local get_node = minetest.get_node
|
||||
for index1 = 1, extent[axis] do --go through each slice
|
||||
--copy slice into schematic
|
||||
local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed
|
||||
for index2 = 1, extent[other1] do
|
||||
local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]
|
||||
for index3 = 1, extent[other2] do
|
||||
local i = newindex2 + (index3 + offset[other2]) * stride[other2]
|
||||
local node = get_node(pos)
|
||||
node.param1 = 255 --node will always appear
|
||||
nodes[i] = node
|
||||
end
|
||||
end
|
||||
|
||||
--copy schematic to target
|
||||
currentpos[axis] = currentpos[axis] + amount
|
||||
place_schematic(currentpos, schematic)
|
||||
|
||||
--wip: copy meta
|
||||
|
||||
currentpos[axis] = currentpos[axis] + 1
|
||||
end
|
||||
return worldedit.volume(pos1, pos2)
|
||||
end
|
||||
|
||||
worldedit.copy2 = function(pos1, pos2, direction, volume)
|
||||
-- the overlap shouldn't matter as long as we
|
||||
-- 1) start at the furthest separated corner
|
||||
-- 2) complete an edge before moving inward, either edge works
|
||||
-- 3) complete a face before moving inward, similarly
|
||||
--
|
||||
-- to do this I
|
||||
-- 1) find the furthest destination in the direction, of each axis
|
||||
-- 2) call those the furthest separated corner
|
||||
-- 3) make sure to iterate inward from there
|
||||
-- 4) nested loop to make sure complete edge, complete face, then complete cube.
|
||||
|
||||
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
|
||||
local somemeta = get_meta(pos1) -- hax lol
|
||||
local to_table = somemeta.to_table
|
||||
local from_table = somemeta.from_table
|
||||
somemeta = nil
|
||||
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local sx, sy, sz -- direction sign
|
||||
local ix, iy, iz -- initial destination
|
||||
local ex, ey, ez -- final destination
|
||||
local originalx, originaly, originalz -- source
|
||||
-- vim -> :'<,'>s/\<\([ioes]\?\)x\>/\1y/g
|
||||
if direction.x > 0 then
|
||||
originalx = pos2.x
|
||||
ix = originalx + direction.x
|
||||
ex = pos1.x + direction.x
|
||||
sx = -1
|
||||
elseif direction.x < 0 then
|
||||
originalx = pos1.x
|
||||
ix = originalx + direction.x
|
||||
ex = pos2.x + direction.x
|
||||
sx = 1
|
||||
else
|
||||
originalx = pos1.x
|
||||
ix = originalx -- whatever
|
||||
ex = pos2.x
|
||||
sx = 1
|
||||
end
|
||||
|
||||
if direction.y > 0 then
|
||||
originaly = pos2.y
|
||||
iy = originaly + direction.y
|
||||
ey = pos1.y + direction.y
|
||||
sy = -1
|
||||
elseif direction.y < 0 then
|
||||
originaly = pos1.y
|
||||
iy = originaly + direction.y
|
||||
ey = pos2.y + direction.y
|
||||
sy = 1
|
||||
else
|
||||
originaly = pos1.y
|
||||
iy = originaly -- whatever
|
||||
ey = pos2.y
|
||||
sy = 1
|
||||
end
|
||||
|
||||
if direction.z > 0 then
|
||||
originalz = pos2.z
|
||||
iz = originalz + direction.z
|
||||
ez = pos1.z + direction.z
|
||||
sz = -1
|
||||
elseif direction.z < 0 then
|
||||
originalz = pos1.z
|
||||
iz = originalz + direction.z
|
||||
ez = pos2.z + direction.z
|
||||
sz = 1
|
||||
else
|
||||
originalz = pos1.z
|
||||
iz = originalz -- whatever
|
||||
ez = pos2.z
|
||||
sz = 1
|
||||
end
|
||||
-- print('copy',originalx,ix,ex,sx,originaly,iy,ey,sy,originalz,iz,ez,sz)
|
||||
|
||||
local ox,oy,oz
|
||||
|
||||
ox = originalx
|
||||
for x = ix, ex, sx do
|
||||
oy = originaly
|
||||
for y = iy, ey, sy do
|
||||
oz = originalz
|
||||
for z = iz, ez, sz do
|
||||
-- reusing pos1/pos2 as source/dest here
|
||||
pos1.x, pos1.y, pos1.z = ox, oy, oz
|
||||
pos2.x, pos2.y, pos2.z = x, y, z
|
||||
local node = get_node(pos1)
|
||||
local meta = to_table(get_meta(pos1)) --get meta of current node
|
||||
add_node(pos2,node)
|
||||
from_table(get_meta(pos2),meta)
|
||||
oz = oz + sz
|
||||
end
|
||||
oy = oy + sy
|
||||
end
|
||||
ox = ox + sx
|
||||
end
|
||||
end
|
||||
|
||||
--duplicates the region defined by positions `pos1` and `pos2` `amount` times with offset vector `direction`, returning the number of nodes stacked
|
||||
worldedit.stack2 = function(pos1, pos2, direction, amount, finished)
|
||||
local i = 0
|
||||
local translated = {x=0,y=0,z=0}
|
||||
local function nextone()
|
||||
if i <= amount then
|
||||
i = i + 1
|
||||
translated.x = translated.x + direction.x
|
||||
translated.y = translated.y + direction.y
|
||||
translated.z = translated.z + direction.z
|
||||
worldedit.copy2(pos1, pos2, translated, volume)
|
||||
minetest.after(0, nextone)
|
||||
else
|
||||
if finished then
|
||||
finished()
|
||||
end
|
||||
end
|
||||
end
|
||||
nextone()
|
||||
return worldedit.volume(pos1, pos2) * amount
|
||||
end
|
||||
|
||||
--copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes copied
|
||||
worldedit.copy = function(pos1, pos2, axis, amount)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
|
||||
if amount < 0 then
|
||||
local pos = {x=pos1.x, y=0, z=0}
|
||||
while pos.x <= pos2.x do
|
||||
pos.y = pos1.y
|
||||
while pos.y <= pos2.y do
|
||||
pos.z = pos1.z
|
||||
while pos.z <= pos2.z do
|
||||
local node = get_node(pos) --obtain current node
|
||||
local meta = get_meta(pos):to_table() --get meta of current node
|
||||
local value = pos[axis] --store current position
|
||||
pos[axis] = value + amount --move along axis
|
||||
add_node(pos, node) --copy node to new position
|
||||
get_meta(pos):from_table(meta) --set metadata of new node
|
||||
pos[axis] = value --restore old position
|
||||
pos.z = pos.z + 1
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
pos.x = pos.x + 1
|
||||
end
|
||||
else
|
||||
local pos = {x=pos2.x, y=0, z=0}
|
||||
while pos.x >= pos1.x do
|
||||
pos.y = pos2.y
|
||||
while pos.y >= pos1.y do
|
||||
pos.z = pos2.z
|
||||
while pos.z >= pos1.z do
|
||||
local node = get_node(pos) --obtain current node
|
||||
local meta = get_meta(pos):to_table() --get meta of current node
|
||||
local value = pos[axis] --store current position
|
||||
pos[axis] = value + amount --move along axis
|
||||
add_node(pos, node) --copy node to new position
|
||||
get_meta(pos):from_table(meta) --set metadata of new node
|
||||
pos[axis] = value --restore old position
|
||||
pos.z = pos.z - 1
|
||||
end
|
||||
pos.y = pos.y - 1
|
||||
end
|
||||
pos.x = pos.x - 1
|
||||
end
|
||||
end
|
||||
return worldedit.volume(pos1, pos2)
|
||||
end
|
||||
|
||||
--moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes, returning the number of nodes moved
|
||||
worldedit.move = function(pos1, pos2, axis, amount)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
--wip: move slice by slice using schematic method in the move axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time and erase original after, using schematic method)
|
||||
local get_node, get_meta, add_node, remove_node = minetest.get_node, minetest.get_meta, minetest.add_node, minetest.remove_node
|
||||
if amount < 0 then
|
||||
local pos = {x=pos1.x, y=0, z=0}
|
||||
while pos.x <= pos2.x do
|
||||
pos.y = pos1.y
|
||||
while pos.y <= pos2.y do
|
||||
pos.z = pos1.z
|
||||
while pos.z <= pos2.z do
|
||||
local node = get_node(pos) --obtain current node
|
||||
local meta = get_meta(pos):to_table() --get metadata of current node
|
||||
remove_node(pos)
|
||||
local value = pos[axis] --store current position
|
||||
pos[axis] = value + amount --move along axis
|
||||
add_node(pos, node) --move node to new position
|
||||
get_meta(pos):from_table(meta) --set metadata of new node
|
||||
pos[axis] = value --restore old position
|
||||
pos.z = pos.z + 1
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
pos.x = pos.x + 1
|
||||
end
|
||||
else
|
||||
local pos = {x=pos2.x, y=0, z=0}
|
||||
while pos.x >= pos1.x do
|
||||
pos.y = pos2.y
|
||||
while pos.y >= pos1.y do
|
||||
pos.z = pos2.z
|
||||
while pos.z >= pos1.z do
|
||||
local node = get_node(pos) --obtain current node
|
||||
local meta = get_meta(pos):to_table() --get metadata of current node
|
||||
remove_node(pos)
|
||||
local value = pos[axis] --store current position
|
||||
pos[axis] = value + amount --move along axis
|
||||
add_node(pos, node) --move node to new position
|
||||
get_meta(pos):from_table(meta) --set metadata of new node
|
||||
pos[axis] = value --restore old position
|
||||
pos.z = pos.z - 1
|
||||
end
|
||||
pos.y = pos.y - 1
|
||||
end
|
||||
pos.x = pos.x - 1
|
||||
end
|
||||
end
|
||||
return worldedit.volume(pos1, pos2)
|
||||
end
|
||||
|
||||
--duplicates the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") `count` times, returning the number of nodes stacked
|
||||
worldedit.stack = function(pos1, pos2, axis, count)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local length = pos2[axis] - pos1[axis] + 1
|
||||
if count < 0 then
|
||||
count = -count
|
||||
length = -length
|
||||
end
|
||||
local amount = 0
|
||||
local copy = worldedit.copy
|
||||
local i = 1
|
||||
function nextone()
|
||||
if i <= count then
|
||||
i = i + 1
|
||||
amount = amount + length
|
||||
copy(pos1, pos2, axis, amount)
|
||||
minetest.after(0, nextone)
|
||||
end
|
||||
end
|
||||
nextone()
|
||||
return worldedit.volume(pos1, pos2) * count
|
||||
end
|
||||
|
||||
--stretches the region defined by positions `pos1` and `pos2` by an factor of positive integers `stretchx`, `stretchy`. and `stretchz` along the X, Y, and Z axes, respectively, with `pos1` as the origin, returning the number of nodes scaled, the new scaled position 1, and the new scaled position 2
|
||||
worldedit.stretch = function(pos1, pos2, stretchx, stretchy, stretchz) --wip: test this
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
--prepare schematic of large node
|
||||
local get_node, get_meta, place_schematic = minetest.get_node, minetest.get_meta, minetest.place_schematic
|
||||
local placeholder_node = {name="", param1=255, param2=0}
|
||||
local nodes = {}
|
||||
for i = 1, stretchx * stretchy * stretchz do
|
||||
nodes[i] = placeholder_node
|
||||
end
|
||||
local schematic = {size={x=stretchx, y=stretchy, z=stretchz}, data=nodes}
|
||||
|
||||
local sizex, sizey, sizez = stretchx - 1, stretchy - 1, stretchz - 1
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local new_pos2 = {
|
||||
x=pos1.x + (pos2.x - pos1.x) * stretchx + sizex,
|
||||
y=pos1.y + (pos2.y - pos1.y) * stretchy + sizey,
|
||||
z=pos1.z + (pos2.z - pos1.z) * stretchz + sizez,
|
||||
}
|
||||
manip:read_from_map(pos1, new_pos2)
|
||||
|
||||
local pos = {x=pos2.x, y=0, z=0}
|
||||
local bigpos = {x=0, y=0, z=0}
|
||||
while pos.x >= pos1.x do
|
||||
pos.y = pos2.y
|
||||
while pos.y >= pos1.y do
|
||||
pos.z = pos2.z
|
||||
while pos.z >= pos1.z do
|
||||
local node = get_node(pos) --obtain current node
|
||||
local meta = get_meta(pos):to_table() --get meta of current node
|
||||
|
||||
--calculate far corner of the big node
|
||||
local posx = pos1.x + (pos.x - pos1.x) * stretchx
|
||||
local posy = pos1.y + (pos.y - pos1.y) * stretchy
|
||||
local posz = pos1.z + (pos.z - pos1.z) * stretchz
|
||||
|
||||
--create large node
|
||||
placeholder_node.name = node.name
|
||||
placeholder_node.param2 = node.param2
|
||||
bigpos.x, bigpos.y, bigpos.z = posx, posy, posz
|
||||
place_schematic(bigpos, schematic)
|
||||
|
||||
--fill in large node meta
|
||||
if next(meta.fields) ~= nil or next(meta.inventory) ~= nil then --node has meta fields
|
||||
for x = 0, sizex do
|
||||
for y = 0, sizey do
|
||||
for z = 0, sizez do
|
||||
bigpos.x, bigpos.y, bigpos.z = posx + x, posy + y, posz + z
|
||||
get_meta(bigpos):from_table(meta) --set metadata of new node
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
pos.z = pos.z - 1
|
||||
end
|
||||
pos.y = pos.y - 1
|
||||
end
|
||||
pos.x = pos.x - 1
|
||||
end
|
||||
return worldedit.volume(pos1, pos2) * stretchx * stretchy * stretchz, pos1, new_pos2
|
||||
end
|
||||
|
||||
--transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed, the new transposed position 1, and the new transposed position 2
|
||||
worldedit.transpose = function(pos1, pos2, axis1, axis2)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
local compare
|
||||
local extent1, extent2 = pos2[axis1] - pos1[axis1], pos2[axis2] - pos1[axis2]
|
||||
|
||||
if extent1 > extent2 then
|
||||
compare = function(extent1, extent2)
|
||||
return extent1 > extent2
|
||||
end
|
||||
else
|
||||
compare = function(extent1, extent2)
|
||||
return extent1 < extent2
|
||||
end
|
||||
end
|
||||
|
||||
--calculate the new position 2 after transposition
|
||||
local new_pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
|
||||
new_pos2[axis1] = pos1[axis1] + extent2
|
||||
new_pos2[axis2] = pos1[axis2] + extent1
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local upperbound = {x=pos2.x, y=pos2.y, z=pos2.z}
|
||||
if upperbound[axis1] < new_pos2[axis1] then upperbound[axis1] = new_pos2[axis1] end
|
||||
if upperbound[axis2] < new_pos2[axis2] then upperbound[axis2] = new_pos2[axis2] end
|
||||
manip:read_from_map(pos1, upperbound)
|
||||
|
||||
local pos = {x=pos1.x, y=0, z=0}
|
||||
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
|
||||
while pos.x <= pos2.x do
|
||||
pos.y = pos1.y
|
||||
while pos.y <= pos2.y do
|
||||
pos.z = pos1.z
|
||||
while pos.z <= pos2.z do
|
||||
local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]
|
||||
if compare(extent1, extent2) then --transpose only if below the diagonal
|
||||
local node1 = get_node(pos)
|
||||
local meta1 = get_meta(pos):to_table()
|
||||
local value1, value2 = pos[axis1], pos[axis2] --save position values
|
||||
pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1 --swap axis extents
|
||||
local node2 = get_node(pos)
|
||||
local meta2 = get_meta(pos):to_table()
|
||||
add_node(pos, node1)
|
||||
get_meta(pos):from_table(meta1)
|
||||
pos[axis1], pos[axis2] = value1, value2 --restore position values
|
||||
add_node(pos, node2)
|
||||
get_meta(pos):from_table(meta2)
|
||||
end
|
||||
pos.z = pos.z + 1
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
pos.x = pos.x + 1
|
||||
end
|
||||
return worldedit.volume(pos1, pos2), pos1, new_pos2
|
||||
end
|
||||
|
||||
--flips a region defined by the positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z"), returning the number of nodes flipped
|
||||
worldedit.flip = function(pos1, pos2, axis)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
--wip: flip the region slice by slice along the flip axis using schematic method
|
||||
local pos = {x=pos1.x, y=0, z=0}
|
||||
local start = pos1[axis] + pos2[axis]
|
||||
pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)
|
||||
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
|
||||
while pos.x <= pos2.x do
|
||||
pos.y = pos1.y
|
||||
while pos.y <= pos2.y do
|
||||
pos.z = pos1.z
|
||||
while pos.z <= pos2.z do
|
||||
local node1 = get_node(pos)
|
||||
local meta1 = get_meta(pos):to_table()
|
||||
local value = pos[axis]
|
||||
pos[axis] = start - value
|
||||
local node2 = get_node(pos)
|
||||
local meta2 = get_meta(pos):to_table()
|
||||
add_node(pos, node1)
|
||||
get_meta(pos):from_table(meta1)
|
||||
pos[axis] = value
|
||||
add_node(pos, node2)
|
||||
get_meta(pos):from_table(meta2)
|
||||
pos.z = pos.z + 1
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
pos.x = pos.x + 1
|
||||
end
|
||||
return worldedit.volume(pos1, pos2)
|
||||
end
|
||||
|
||||
--rotates a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise around axis `axis` (90 degree increment), returning the number of nodes rotated
|
||||
worldedit.rotate = function(pos1, pos2, axis, angle)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
local axis1, axis2
|
||||
if axis == "x" then
|
||||
axis1, axis2 = "z", "y"
|
||||
elseif axis == "y" then
|
||||
axis1, axis2 = "x", "z"
|
||||
else --axis == "z"
|
||||
axis1, axis2 = "y", "x"
|
||||
end
|
||||
angle = angle % 360
|
||||
|
||||
local count
|
||||
if angle == 90 then
|
||||
worldedit.flip(pos1, pos2, axis1)
|
||||
count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
|
||||
elseif angle == 180 then
|
||||
worldedit.flip(pos1, pos2, axis1)
|
||||
count = worldedit.flip(pos1, pos2, axis2)
|
||||
elseif angle == 270 then
|
||||
worldedit.flip(pos1, pos2, axis2)
|
||||
count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
|
||||
end
|
||||
return count, pos1, pos2
|
||||
end
|
||||
|
||||
--rotates all oriented nodes in a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise (90 degree increment) around the Y axis, returning the number of nodes oriented
|
||||
worldedit.orient = function(pos1, pos2, angle) --wip: support 6D facedir rotation along arbitrary axis
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local registered_nodes = minetest.registered_nodes
|
||||
|
||||
local wallmounted = {
|
||||
[90]={[0]=0, [1]=1, [2]=5, [3]=4, [4]=2, [5]=3},
|
||||
[180]={[0]=0, [1]=1, [2]=3, [3]=2, [4]=5, [5]=4},
|
||||
[270]={[0]=0, [1]=1, [2]=4, [3]=5, [4]=3, [5]=2}
|
||||
}
|
||||
local facedir = {
|
||||
[90]={[0]=1, [1]=2, [2]=3, [3]=0},
|
||||
[180]={[0]=2, [1]=3, [2]=0, [3]=1},
|
||||
[270]={[0]=3, [1]=0, [2]=1, [3]=2}
|
||||
}
|
||||
|
||||
angle = angle % 360
|
||||
if angle == 0 then
|
||||
return 0
|
||||
end
|
||||
local wallmounted_substitution = wallmounted[angle]
|
||||
local facedir_substitution = facedir[angle]
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local count = 0
|
||||
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
|
||||
local pos = {x=pos1.x, y=0, z=0}
|
||||
while pos.x <= pos2.x do
|
||||
pos.y = pos1.y
|
||||
while pos.y <= pos2.y do
|
||||
pos.z = pos1.z
|
||||
while pos.z <= pos2.z do
|
||||
local node = get_node(pos)
|
||||
local def = registered_nodes[node.name]
|
||||
if def then
|
||||
if def.paramtype2 == "wallmounted" then
|
||||
node.param2 = wallmounted_substitution[node.param2]
|
||||
local meta = get_meta(pos):to_table()
|
||||
add_node(pos, node)
|
||||
get_meta(pos):from_table(meta)
|
||||
count = count + 1
|
||||
elseif def.paramtype2 == "facedir" then
|
||||
node.param2 = facedir_substitution[node.param2]
|
||||
local meta = get_meta(pos):to_table()
|
||||
add_node(pos, node)
|
||||
get_meta(pos):from_table(meta)
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
pos.z = pos.z + 1
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
pos.x = pos.x + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
--fixes the lighting in a region defined by positions `pos1` and `pos2`, returning the number of nodes updated
|
||||
worldedit.fixlight = function(pos1, pos2)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local nodes = minetest.find_nodes_in_area(pos1, pos2, "air")
|
||||
local dig_node = minetest.dig_node
|
||||
for _, pos in ipairs(nodes) do
|
||||
dig_node(pos)
|
||||
end
|
||||
return #nodes
|
||||
end
|
||||
|
||||
--clears all objects in a region defined by the positions `pos1` and `pos2`, returning the number of objects cleared
|
||||
worldedit.clearobjects = function(pos1, pos2)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local pos1x, pos1y, pos1z = pos1.x, pos1.y, pos1.z
|
||||
local pos2x, pos2y, pos2z = pos2.x + 1, pos2.y + 1, pos2.z + 1
|
||||
local center = {x=(pos1x + pos2x) / 2, y=(pos1y + pos2y) / 2, z=(pos1z + pos2z) / 2} --center of region
|
||||
local radius = ((center.x - pos1x + 0.5) + (center.y - pos1y + 0.5) + (center.z - pos1z + 0.5)) ^ 0.5 --bounding sphere radius
|
||||
local count = 0
|
||||
for _, obj in pairs(minetest.get_objects_inside_radius(center, radius)) do --all objects in bounding sphere
|
||||
local entity = obj:get_luaentity()
|
||||
if not (entity and entity.name:find("^worldedit:")) then --avoid WorldEdit entities
|
||||
local pos = obj:getpos()
|
||||
if pos.x >= pos1x and pos.x <= pos2x
|
||||
and pos.y >= pos1y and pos.y <= pos2y
|
||||
and pos.z >= pos1z and pos.z <= pos2z then --inside region
|
||||
obj:remove()
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
470
mods/WorldEdit/worldedit/primitives.lua
Normal file
470
mods/WorldEdit/worldedit/primitives.lua
Normal file
|
@ -0,0 +1,470 @@
|
|||
worldedit = worldedit or {}
|
||||
local minetest = minetest --local copy of global
|
||||
|
||||
--adds a hollow sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
|
||||
worldedit.hollow_sphere = function(pos, radius, nodename)
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}
|
||||
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
--fill emerged area with ignore
|
||||
local nodes = {}
|
||||
local ignore = minetest.get_content_id("ignore")
|
||||
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
|
||||
nodes[i] = ignore
|
||||
end
|
||||
|
||||
--fill selected area with node
|
||||
local node_id = minetest.get_content_id(nodename)
|
||||
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
|
||||
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
|
||||
local zstride, ystride = area.zstride, area.ystride
|
||||
local count = 0
|
||||
for z = -radius, radius do
|
||||
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed
|
||||
for y = -radius, radius do
|
||||
local newy = newz + (y + offsety) * ystride
|
||||
for x = -radius, radius do
|
||||
local squared = x * x + y * y + z * z
|
||||
if squared >= min_radius and squared <= max_radius then --position is on surface of sphere
|
||||
local i = newy + (x + offsetx)
|
||||
nodes[i] = node_id
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
--adds a sphere centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
|
||||
worldedit.sphere = function(pos, radius, nodename)
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local pos1 = {x=pos.x - radius, y=pos.y - radius, z=pos.z - radius}
|
||||
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
--fill emerged area with ignore
|
||||
local nodes = {}
|
||||
local ignore = minetest.get_content_id("ignore")
|
||||
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
|
||||
nodes[i] = ignore
|
||||
end
|
||||
|
||||
--fill selected area with node
|
||||
local node_id = minetest.get_content_id(nodename)
|
||||
local max_radius = radius * (radius + 1)
|
||||
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
|
||||
local zstride, ystride = area.zstride, area.ystride
|
||||
local count = 0
|
||||
for z = -radius, radius do
|
||||
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed
|
||||
for y = -radius, radius do
|
||||
local newy = newz + (y + offsety) * ystride
|
||||
for x = -radius, radius do
|
||||
if x * x + y * y + z * z <= max_radius then --position is inside sphere
|
||||
local i = newy + (x + offsetx)
|
||||
nodes[i] = node_id
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
--adds a hollow dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
|
||||
worldedit.hollow_dome = function(pos, radius, nodename)
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius}
|
||||
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
--fill emerged area with ignore
|
||||
local nodes = {}
|
||||
local ignore = minetest.get_content_id("ignore")
|
||||
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
|
||||
nodes[i] = ignore
|
||||
end
|
||||
|
||||
local miny, maxy = 0, radius
|
||||
if radius < 0 then
|
||||
radius = -radius
|
||||
miny, maxy = -radius, 0
|
||||
end
|
||||
|
||||
--fill selected area with node
|
||||
local node_id = minetest.get_content_id(nodename)
|
||||
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
|
||||
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
|
||||
local zstride, ystride = area.zstride, area.ystride
|
||||
local count = 0
|
||||
for z = -radius, radius do
|
||||
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed
|
||||
for y = miny, maxy do
|
||||
local newy = newz + (y + offsety) * ystride
|
||||
for x = -radius, radius do
|
||||
local squared = x * x + y * y + z * z
|
||||
if squared >= min_radius and squared <= max_radius then --position is on surface of sphere
|
||||
local i = newy + (x + offsetx)
|
||||
nodes[i] = node_id
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
--adds a dome centered at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
|
||||
worldedit.dome = function(pos, radius, nodename)
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local pos1 = {x=pos.x - radius, y=pos.y, z=pos.z - radius}
|
||||
local pos2 = {x=pos.x + radius, y=pos.y + radius, z=pos.z + radius}
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
--fill emerged area with ignore
|
||||
local nodes = {}
|
||||
local ignore = minetest.get_content_id("ignore")
|
||||
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
|
||||
nodes[i] = ignore
|
||||
end
|
||||
|
||||
local miny, maxy = 0, radius
|
||||
if radius < 0 then
|
||||
radius = -radius
|
||||
miny, maxy = -radius, 0
|
||||
end
|
||||
|
||||
--fill selected area with node
|
||||
local node_id = minetest.get_content_id(nodename)
|
||||
local max_radius = radius * (radius + 1)
|
||||
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
|
||||
local zstride, ystride = area.zstride, area.ystride
|
||||
local count = 0
|
||||
for z = -radius, radius do
|
||||
local newz = (z + offsetz) * zstride + 1 --offset contributed by z plus 1 to make it 1-indexed
|
||||
for y = miny, maxy do
|
||||
local newy = newz + (y + offsety) * ystride
|
||||
for x = -radius, radius do
|
||||
if x * x + y * y + z * z <= max_radius then --position is inside sphere
|
||||
local i = newy + (x + offsetx)
|
||||
nodes[i] = node_id
|
||||
count = count + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
--adds a hollow cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added
|
||||
worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename)
|
||||
local other1, other2
|
||||
if axis == "x" then
|
||||
other1, other2 = "y", "z"
|
||||
elseif axis == "y" then
|
||||
other1, other2 = "x", "z"
|
||||
else --axis == "z"
|
||||
other1, other2 = "x", "y"
|
||||
end
|
||||
|
||||
--handle negative lengths
|
||||
local currentpos = {x=pos.x, y=pos.y, z=pos.z}
|
||||
if length < 0 then
|
||||
length = -length
|
||||
currentpos[axis] = currentpos[axis] - length
|
||||
end
|
||||
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local pos1 = {
|
||||
[axis]=currentpos[axis],
|
||||
[other1]=currentpos[other1] - radius,
|
||||
[other2]=currentpos[other2] - radius
|
||||
}
|
||||
local pos2 = {
|
||||
[axis]=currentpos[axis] + length - 1,
|
||||
[other1]=currentpos[other1] + radius,
|
||||
[other2]=currentpos[other2] + radius
|
||||
}
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
--fill emerged area with ignore
|
||||
local nodes = {}
|
||||
local ignore = minetest.get_content_id("ignore")
|
||||
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
|
||||
nodes[i] = ignore
|
||||
end
|
||||
|
||||
--fill selected area with node
|
||||
local node_id = minetest.get_content_id(nodename)
|
||||
local min_radius, max_radius = radius * (radius - 1), radius * (radius + 1)
|
||||
local stride = {x=1, y=area.ystride, z=area.zstride}
|
||||
local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z}
|
||||
local min_slice, max_slice = offset[axis], offset[axis] + length - 1
|
||||
local count = 0
|
||||
for index2 = -radius, radius do
|
||||
local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed
|
||||
for index3 = -radius, radius do
|
||||
local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2]
|
||||
local squared = index2 * index2 + index3 * index3
|
||||
if squared >= min_radius and squared <= max_radius then --position is on surface of cylinder
|
||||
for index1 = min_slice, max_slice do --add column along axis
|
||||
local i = newindex3 + index1 * stride[axis]
|
||||
nodes[i] = node_id
|
||||
end
|
||||
count = count + length
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
--adds a cylinder at `pos` along the `axis` axis ("x" or "y" or "z") with length `length` and radius `radius`, composed of `nodename`, returning the number of nodes added
|
||||
worldedit.cylinder = function(pos, axis, length, radius, nodename)
|
||||
local other1, other2
|
||||
if axis == "x" then
|
||||
other1, other2 = "y", "z"
|
||||
elseif axis == "y" then
|
||||
other1, other2 = "x", "z"
|
||||
else --axis == "z"
|
||||
other1, other2 = "x", "y"
|
||||
end
|
||||
|
||||
--handle negative lengths
|
||||
local currentpos = {x=pos.x, y=pos.y, z=pos.z}
|
||||
if length < 0 then
|
||||
length = -length
|
||||
currentpos[axis] = currentpos[axis] - length
|
||||
end
|
||||
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local pos1 = {
|
||||
[axis]=currentpos[axis],
|
||||
[other1]=currentpos[other1] - radius,
|
||||
[other2]=currentpos[other2] - radius
|
||||
}
|
||||
local pos2 = {
|
||||
[axis]=currentpos[axis] + length - 1,
|
||||
[other1]=currentpos[other1] + radius,
|
||||
[other2]=currentpos[other2] + radius
|
||||
}
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
--fill emerged area with ignore
|
||||
local nodes = {}
|
||||
local ignore = minetest.get_content_id("ignore")
|
||||
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
|
||||
nodes[i] = ignore
|
||||
end
|
||||
|
||||
--fill selected area with node
|
||||
local node_id = minetest.get_content_id(nodename)
|
||||
local max_radius = radius * (radius + 1)
|
||||
local stride = {x=1, y=area.ystride, z=area.zstride}
|
||||
local offset = {x=currentpos.x - emerged_pos1.x, y=currentpos.y - emerged_pos1.y, z=currentpos.z - emerged_pos1.z}
|
||||
local min_slice, max_slice = offset[axis], offset[axis] + length - 1
|
||||
local count = 0
|
||||
for index2 = -radius, radius do
|
||||
local newindex2 = (index2 + offset[other1]) * stride[other1] + 1 --offset contributed by other axis 1 plus 1 to make it 1-indexed
|
||||
for index3 = -radius, radius do
|
||||
local newindex3 = newindex2 + (index3 + offset[other2]) * stride[other2]
|
||||
if index2 * index2 + index3 * index3 <= max_radius then --position is within cylinder
|
||||
for index1 = min_slice, max_slice do --add column along axis
|
||||
local i = newindex3 + index1 * stride[axis]
|
||||
nodes[i] = node_id
|
||||
end
|
||||
count = count + length
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
--adds a pyramid centered at `pos` with height `height`, composed of `nodename`, returning the number of nodes added
|
||||
worldedit.pyramid = function(pos, axis, height, nodename)
|
||||
local other1, other2
|
||||
if axis == "x" then
|
||||
other1, other2 = "y", "z"
|
||||
elseif axis == "y" then
|
||||
other1, other2 = "x", "z"
|
||||
else --axis == "z"
|
||||
other1, other2 = "x", "y"
|
||||
end
|
||||
|
||||
local pos1 = {x=pos.x - height, y=pos.y - height, z=pos.z - height}
|
||||
local pos2 = {x=pos.x + height, y=pos.y + height, z=pos.z + height}
|
||||
|
||||
--handle inverted pyramids
|
||||
local startaxis, endaxis, step
|
||||
if height > 0 then
|
||||
height = height - 1
|
||||
step = 1
|
||||
pos1[axis] = pos[axis] --upper half of box
|
||||
else
|
||||
height = height + 1
|
||||
step = -1
|
||||
pos2[axis] = pos[axis] --lower half of box
|
||||
end
|
||||
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
--fill emerged area with ignore
|
||||
local nodes = {}
|
||||
local ignore = minetest.get_content_id("ignore")
|
||||
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
|
||||
nodes[i] = ignore
|
||||
end
|
||||
|
||||
--fill selected area with node
|
||||
local node_id = minetest.get_content_id(nodename)
|
||||
local stride = {x=1, y=area.ystride, z=area.zstride}
|
||||
local offset = {x=pos.x - emerged_pos1.x, y=pos.y - emerged_pos1.y, z=pos.z - emerged_pos1.z}
|
||||
local size = height * step
|
||||
local count = 0
|
||||
for index1 = 0, height, step do --go through each level of the pyramid
|
||||
local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed
|
||||
for index2 = -size, size do
|
||||
local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]
|
||||
for index3 = -size, size do
|
||||
local i = newindex2 + (index3 + offset[other2]) * stride[other2]
|
||||
nodes[i] = node_id
|
||||
end
|
||||
end
|
||||
count = count + (size * 2 + 1) ^ 2
|
||||
size = size - 1
|
||||
end
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
||||
|
||||
--adds a spiral centered at `pos` with side length `length`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added
|
||||
worldedit.spiral = function(pos, length, height, spacer, nodename)
|
||||
local extent = math.ceil(length / 2)
|
||||
local pos1 = {x=pos.x - extent, y=pos.y, z=pos.z - extent}
|
||||
local pos2 = {x=pos.x + extent, y=pos.y + height, z=pos.z + extent}
|
||||
|
||||
--set up voxel manipulator
|
||||
local manip = minetest.get_voxel_manip()
|
||||
local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
|
||||
--fill emerged area with ignore
|
||||
local nodes = {}
|
||||
local ignore = minetest.get_content_id("ignore")
|
||||
for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do
|
||||
nodes[i] = ignore
|
||||
end
|
||||
|
||||
--set up variables
|
||||
local node_id = minetest.get_content_id(nodename)
|
||||
local stride = {x=1, y=area.ystride, z=area.zstride}
|
||||
local offsetx, offsety, offsetz = pos.x - emerged_pos1.x, pos.y - emerged_pos1.y, pos.z - emerged_pos1.z
|
||||
local i = offsetz * stride.z + offsety * stride.y + offsetx + 1
|
||||
|
||||
--add first column
|
||||
local count = height
|
||||
local column = i
|
||||
for y = 1, height do
|
||||
nodes[column] = node_id
|
||||
column = column + stride.y
|
||||
end
|
||||
|
||||
--add spiral segments
|
||||
local strideaxis, strideother = stride.x, stride.z
|
||||
local sign = -1
|
||||
local segment_length = 0
|
||||
spacer = spacer + 1
|
||||
for segment = 1, math.floor(length / spacer) * 2 do --go through each segment except the last
|
||||
if segment % 2 == 1 then --change sign and length every other turn starting with the first
|
||||
sign = -sign
|
||||
segment_length = segment_length + spacer
|
||||
end
|
||||
for index = 1, segment_length do --fill segment
|
||||
i = i + strideaxis * sign --move along the direction of the segment
|
||||
local column = i
|
||||
for y = 1, height do --add column
|
||||
nodes[column] = node_id
|
||||
column = column + stride.y
|
||||
end
|
||||
end
|
||||
count = count + segment_length * height
|
||||
strideaxis, strideother = strideother, strideaxis --swap axes
|
||||
end
|
||||
|
||||
--add shorter final segment
|
||||
sign = -sign
|
||||
for index = 1, segment_length do
|
||||
i = i + strideaxis * sign
|
||||
local column = i
|
||||
for y = 1, height do --add column
|
||||
nodes[column] = node_id
|
||||
column = column + stride.y
|
||||
end
|
||||
end
|
||||
count = count + segment_length * height
|
||||
|
||||
--update map nodes
|
||||
manip:set_data(nodes)
|
||||
manip:write_to_map()
|
||||
manip:update_map()
|
||||
|
||||
return count
|
||||
end
|
273
mods/WorldEdit/worldedit/serialization.lua
Normal file
273
mods/WorldEdit/worldedit/serialization.lua
Normal file
|
@ -0,0 +1,273 @@
|
|||
worldedit = worldedit or {}
|
||||
local minetest = minetest --local copy of global
|
||||
|
||||
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions
|
||||
worldedit.sort_pos = function(pos1, pos2)
|
||||
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
|
||||
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
|
||||
if pos1.x > pos2.x then
|
||||
pos2.x, pos1.x = pos1.x, pos2.x
|
||||
end
|
||||
if pos1.y > pos2.y then
|
||||
pos2.y, pos1.y = pos1.y, pos2.y
|
||||
end
|
||||
if pos1.z > pos2.z then
|
||||
pos2.z, pos1.z = pos1.z, pos2.z
|
||||
end
|
||||
return pos1, pos2
|
||||
end
|
||||
|
||||
--determines the version of serialized data `value`, returning the version as a positive integer or 0 for unknown versions
|
||||
worldedit.valueversion = function(value)
|
||||
if value:find("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)") and not value:find("%{") then --previous list format
|
||||
return 3
|
||||
elseif value:find("^[^\"']+%{%d+%}") then
|
||||
if value:find("%[\"meta\"%]") then --previous meta flat table format
|
||||
return 2
|
||||
end
|
||||
return 1 --original flat table format
|
||||
elseif value:find("%{") then --current nested table format
|
||||
return 4
|
||||
end
|
||||
return 0 --unknown format
|
||||
end
|
||||
|
||||
--converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized
|
||||
worldedit.serialize = function(pos1, pos2)
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local pos = {x=pos1.x, y=0, z=0}
|
||||
local count = 0
|
||||
local result = {}
|
||||
local get_node, get_meta = minetest.get_node, minetest.get_meta
|
||||
while pos.x <= pos2.x do
|
||||
pos.y = pos1.y
|
||||
while pos.y <= pos2.y do
|
||||
pos.z = pos1.z
|
||||
while pos.z <= pos2.z do
|
||||
local node = get_node(pos)
|
||||
if node.name ~= "air" and node.name ~= "ignore" then
|
||||
count = count + 1
|
||||
local meta = get_meta(pos):to_table()
|
||||
|
||||
--convert metadata itemstacks to itemstrings
|
||||
for name, inventory in pairs(meta.inventory) do
|
||||
for index, stack in ipairs(inventory) do
|
||||
inventory[index] = stack.to_string and stack:to_string() or stack
|
||||
end
|
||||
end
|
||||
|
||||
result[count] = {
|
||||
x = pos.x - pos1.x,
|
||||
y = pos.y - pos1.y,
|
||||
z = pos.z - pos1.z,
|
||||
name = node.name,
|
||||
param1 = node.param1,
|
||||
param2 = node.param2,
|
||||
meta = meta,
|
||||
}
|
||||
end
|
||||
pos.z = pos.z + 1
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
pos.x = pos.x + 1
|
||||
end
|
||||
result = minetest.serialize(result) --convert entries to a string
|
||||
return result, count
|
||||
end
|
||||
|
||||
--determines the volume the nodes represented by string `value` would occupy if deserialized at `originpos`, returning the two corner positions and the number of nodes
|
||||
--contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
|
||||
worldedit.allocate = function(originpos, value)
|
||||
local huge = math.huge
|
||||
local pos1x, pos1y, pos1z = huge, huge, huge
|
||||
local pos2x, pos2y, pos2z = -huge, -huge, -huge
|
||||
local originx, originy, originz = originpos.x, originpos.y, originpos.z
|
||||
local count = 0
|
||||
local version = worldedit.valueversion(value)
|
||||
if version == 1 or version == 2 then --flat table format
|
||||
--obtain the node table
|
||||
local get_tables = loadstring(value)
|
||||
if get_tables then --error loading value
|
||||
return originpos, originpos, count
|
||||
end
|
||||
local tables = get_tables()
|
||||
|
||||
--transform the node table into an array of nodes
|
||||
for i = 1, #tables do
|
||||
for j, v in pairs(tables[i]) do
|
||||
if type(v) == "table" then
|
||||
tables[i][j] = tables[v[1]]
|
||||
end
|
||||
end
|
||||
end
|
||||
local nodes = tables[1]
|
||||
|
||||
--check the node array
|
||||
count = #nodes
|
||||
if version == 1 then --original flat table format
|
||||
for index = 1, count do
|
||||
local entry = nodes[index]
|
||||
local pos = entry[1]
|
||||
local x, y, z = originx - pos.x, originy - pos.y, originz - pos.z
|
||||
if x < pos1x then pos1x = x end
|
||||
if y < pos1y then pos1y = y end
|
||||
if z < pos1z then pos1z = z end
|
||||
if x > pos2x then pos2x = x end
|
||||
if y > pos2y then pos2y = y end
|
||||
if z > pos2z then pos2z = z end
|
||||
end
|
||||
else --previous meta flat table format
|
||||
for index = 1, count do
|
||||
local entry = nodes[index]
|
||||
local x, y, z = originx - entry.x, originy - entry.y, originz - entry.z
|
||||
if x < pos1x then pos1x = x end
|
||||
if y < pos1y then pos1y = y end
|
||||
if z < pos1z then pos1z = z end
|
||||
if x > pos2x then pos2x = x end
|
||||
if y > pos2y then pos2y = y end
|
||||
if z > pos2z then pos2z = z end
|
||||
end
|
||||
end
|
||||
elseif version == 3 then --previous list format
|
||||
for x, y, z, name, param1, param2 in value:gmatch("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do --match node entries
|
||||
x, y, z = originx + tonumber(x), originy + tonumber(y), originz + tonumber(z)
|
||||
if x < pos1x then pos1x = x end
|
||||
if y < pos1y then pos1y = y end
|
||||
if z < pos1z then pos1z = z end
|
||||
if x > pos2x then pos2x = x end
|
||||
if y > pos2y then pos2y = y end
|
||||
if z > pos2z then pos2z = z end
|
||||
count = count + 1
|
||||
end
|
||||
elseif version == 4 then --current nested table format
|
||||
--wip: this is a filthy hack that works surprisingly well
|
||||
value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1)
|
||||
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
|
||||
local startpos, startpos1, endpos = 1, 1
|
||||
local nodes = {}
|
||||
while true do
|
||||
startpos, endpos = escaped:find("},%s*{", startpos)
|
||||
if not startpos then
|
||||
break
|
||||
end
|
||||
local current = value:sub(startpos1, startpos)
|
||||
table.insert(nodes, minetest.deserialize("return " .. current))
|
||||
startpos, startpos1 = endpos, endpos
|
||||
end
|
||||
table.insert(nodes, minetest.deserialize("return " .. value:sub(startpos1)))
|
||||
|
||||
--local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT
|
||||
|
||||
count = #nodes
|
||||
for index = 1, count do
|
||||
local entry = nodes[index]
|
||||
x, y, z = originx + entry.x, originy + entry.y, originz + entry.z
|
||||
if x < pos1x then pos1x = x end
|
||||
if y < pos1y then pos1y = y end
|
||||
if z < pos1z then pos1z = z end
|
||||
if x > pos2x then pos2x = x end
|
||||
if y > pos2y then pos2y = y end
|
||||
if z > pos2z then pos2z = z end
|
||||
end
|
||||
end
|
||||
local pos1 = {x=pos1x, y=pos1y, z=pos1z}
|
||||
local pos2 = {x=pos2x, y=pos2y, z=pos2z}
|
||||
return pos1, pos2, count
|
||||
end
|
||||
|
||||
--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
|
||||
--contains code based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
|
||||
worldedit.deserialize = function(originpos, value)
|
||||
--make area stay loaded
|
||||
local pos1, pos2 = worldedit.allocate(originpos, value)
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local originx, originy, originz = originpos.x, originpos.y, originpos.z
|
||||
local count = 0
|
||||
local add_node, get_meta = minetest.add_node, minetest.get_meta
|
||||
local version = worldedit.valueversion(value)
|
||||
if version == 1 or version == 2 then --original flat table format
|
||||
--obtain the node table
|
||||
local get_tables = loadstring(value)
|
||||
if not get_tables then --error loading value
|
||||
return count
|
||||
end
|
||||
local tables = get_tables()
|
||||
|
||||
--transform the node table into an array of nodes
|
||||
for i = 1, #tables do
|
||||
for j, v in pairs(tables[i]) do
|
||||
if type(v) == "table" then
|
||||
tables[i][j] = tables[v[1]]
|
||||
end
|
||||
end
|
||||
end
|
||||
local nodes = tables[1]
|
||||
|
||||
--load the node array
|
||||
count = #nodes
|
||||
if version == 1 then --original flat table format
|
||||
for index = 1, count do
|
||||
local entry = nodes[index]
|
||||
local pos = entry[1]
|
||||
pos.x, pos.y, pos.z = originx - pos.x, originy - pos.y, originz - pos.z
|
||||
add_node(pos, entry[2])
|
||||
end
|
||||
else --previous meta flat table format
|
||||
for index = 1, #nodes do
|
||||
local entry = nodes[index]
|
||||
entry.x, entry.y, entry.z = originx + entry.x, originy + entry.y, originz + entry.z
|
||||
add_node(entry, entry) --entry acts both as position and as node
|
||||
get_meta(entry):from_table(entry.meta)
|
||||
end
|
||||
end
|
||||
elseif version == 3 then --previous list format
|
||||
local pos = {x=0, y=0, z=0}
|
||||
local node = {name="", param1=0, param2=0}
|
||||
for x, y, z, name, param1, param2 in value:gmatch("([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)%s+([^%s]+)%s+(%d+)%s+(%d+)[^\r\n]*[\r\n]*") do --match node entries
|
||||
pos.x, pos.y, pos.z = originx + tonumber(x), originy + tonumber(y), originz + tonumber(z)
|
||||
node.name, node.param1, node.param2 = name, param1, param2
|
||||
add_node(pos, node)
|
||||
count = count + 1
|
||||
end
|
||||
elseif version == 4 then --current nested table format
|
||||
--wip: this is a filthy hack that works surprisingly well
|
||||
value = value:gsub("return%s*{", "", 1):gsub("}%s*$", "", 1)
|
||||
local escaped = value:gsub("\\\\", "@@"):gsub("\\\"", "@@"):gsub("(\"[^\"]*\")", function(s) return string.rep("@", #s) end)
|
||||
local startpos, startpos1, endpos = 1, 1
|
||||
local nodes = {}
|
||||
while true do
|
||||
startpos, endpos = escaped:find("},%s*{", startpos)
|
||||
if not startpos then
|
||||
break
|
||||
end
|
||||
local current = value:sub(startpos1, startpos)
|
||||
table.insert(nodes, minetest.deserialize("return " .. current))
|
||||
startpos, startpos1 = endpos, endpos
|
||||
end
|
||||
table.insert(nodes, minetest.deserialize("return " .. value:sub(startpos1)))
|
||||
|
||||
--local nodes = minetest.deserialize(value) --wip: this is broken for larger tables in the current version of LuaJIT
|
||||
|
||||
--load the nodes
|
||||
count = #nodes
|
||||
for index = 1, count do
|
||||
local entry = nodes[index]
|
||||
entry.x, entry.y, entry.z = originx + entry.x, originy + entry.y, originz + entry.z
|
||||
add_node(entry, entry) --entry acts both as position and as node
|
||||
end
|
||||
|
||||
--load the metadata
|
||||
for index = 1, count do
|
||||
local entry = nodes[index]
|
||||
get_meta(entry):from_table(entry.meta)
|
||||
end
|
||||
end
|
||||
return count
|
||||
end
|
142
mods/WorldEdit/worldedit/visualization.lua
Normal file
142
mods/WorldEdit/worldedit/visualization.lua
Normal file
|
@ -0,0 +1,142 @@
|
|||
worldedit = worldedit or {}
|
||||
local minetest = minetest --local copy of global
|
||||
|
||||
--modifies positions `pos1` and `pos2` so that each component of `pos1` is less than or equal to its corresponding conent of `pos2`, returning two new positions
|
||||
worldedit.sort_pos = function(pos1, pos2)
|
||||
pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
|
||||
pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
|
||||
if pos1.x > pos2.x then
|
||||
pos2.x, pos1.x = pos1.x, pos2.x
|
||||
end
|
||||
if pos1.y > pos2.y then
|
||||
pos2.y, pos1.y = pos1.y, pos2.y
|
||||
end
|
||||
if pos1.z > pos2.z then
|
||||
pos2.z, pos1.z = pos1.z, pos2.z
|
||||
end
|
||||
return pos1, pos2
|
||||
end
|
||||
|
||||
--determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume
|
||||
worldedit.volume = function(pos1, pos2)
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)
|
||||
end
|
||||
|
||||
minetest.register_node("worldedit:placeholder", {
|
||||
drawtype = "airlike",
|
||||
paramtype = "light",
|
||||
sunlight_propagates = true,
|
||||
diggable = false,
|
||||
groups = {not_in_creative_inventory=1},
|
||||
})
|
||||
|
||||
--hides all nodes in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes hidden
|
||||
worldedit.hide = function(pos1, pos2)
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local pos = {x=pos1.x, y=0, z=0}
|
||||
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
|
||||
while pos.x <= pos2.x do
|
||||
pos.y = pos1.y
|
||||
while pos.y <= pos2.y do
|
||||
pos.z = pos1.z
|
||||
while pos.z <= pos2.z do
|
||||
local node = get_node(pos)
|
||||
if node.name ~= "worldedit:placeholder" then
|
||||
local data = get_meta(pos):to_table() --obtain metadata of original node
|
||||
data.fields.worldedit_placeholder = node.name --add the node's name
|
||||
node.name = "worldedit:placeholder" --set node name
|
||||
add_node(pos, node) --add placeholder node
|
||||
get_meta(pos):from_table(data) --set placeholder metadata to the original node's metadata
|
||||
end
|
||||
pos.z = pos.z + 1
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
pos.x = pos.x + 1
|
||||
end
|
||||
return worldedit.volume(pos1, pos2)
|
||||
end
|
||||
|
||||
--suppresses all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes, returning the number of nodes suppressed
|
||||
worldedit.suppress = function(pos1, pos2, nodename)
|
||||
--ignore placeholder supression
|
||||
if nodename == "worldedit:placeholder" then
|
||||
return 0
|
||||
end
|
||||
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local nodes = minetest.find_nodes_in_area(pos1, pos2, nodename)
|
||||
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
|
||||
for _, pos in ipairs(nodes) do
|
||||
local node = get_node(pos)
|
||||
local data = get_meta(pos):to_table() --obtain metadata of original node
|
||||
data.fields.worldedit_placeholder = node.name --add the node's name
|
||||
node.name = "worldedit:placeholder" --set node name
|
||||
add_node(pos, node) --add placeholder node
|
||||
get_meta(pos):from_table(data) --set placeholder metadata to the original node's metadata
|
||||
end
|
||||
return #nodes
|
||||
end
|
||||
|
||||
--highlights all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively hiding all other nodes, returning the number of nodes found
|
||||
worldedit.highlight = function(pos1, pos2, nodename)
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local pos = {x=pos1.x, y=0, z=0}
|
||||
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
|
||||
local count = 0
|
||||
while pos.x <= pos2.x do
|
||||
pos.y = pos1.y
|
||||
while pos.y <= pos2.y do
|
||||
pos.z = pos1.z
|
||||
while pos.z <= pos2.z do
|
||||
local node = get_node(pos)
|
||||
if node.name == nodename then --node found
|
||||
count = count + 1
|
||||
elseif node.name ~= "worldedit:placeholder" then --hide other nodes
|
||||
local data = get_meta(pos):to_table() --obtain metadata of original node
|
||||
data.fields.worldedit_placeholder = node.name --add the node's name
|
||||
node.name = "worldedit:placeholder" --set node name
|
||||
add_node(pos, node) --add placeholder node
|
||||
get_meta(pos):from_table(data) --set placeholder metadata to the original node's metadata
|
||||
end
|
||||
pos.z = pos.z + 1
|
||||
end
|
||||
pos.y = pos.y + 1
|
||||
end
|
||||
pos.x = pos.x + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
--restores all nodes hidden with WorldEdit functions in a region defined by positions `pos1` and `pos2`, returning the number of nodes restored
|
||||
worldedit.restore = function(pos1, pos2)
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos2)
|
||||
|
||||
local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
local nodes = minetest.find_nodes_in_area(pos1, pos2, "worldedit:placeholder")
|
||||
local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node
|
||||
for _, pos in ipairs(nodes) do
|
||||
local node = get_node(pos)
|
||||
local data = get_meta(pos):to_table() --obtain node metadata
|
||||
node.name = data.fields.worldedit_placeholder --set node name
|
||||
data.fields.worldedit_placeholder = nil --delete old nodename
|
||||
add_node(pos, node) --add original node
|
||||
get_meta(pos):from_table(data) --set original node metadata
|
||||
end
|
||||
return #nodes
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue