version 0.21

This commit is contained in:
davedevils 2015-06-29 19:55:56 +02:00
parent bd1233aaf1
commit c8d70b992c
1717 changed files with 68882 additions and 0 deletions

View 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

View 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

View 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!")

View 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

View 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

View 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

View 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