summaryrefslogtreecommitdiff
path: root/worldedit
diff options
context:
space:
mode:
Diffstat (limited to 'worldedit')
-rw-r--r--worldedit/functions.lua707
-rw-r--r--worldedit/init.lua655
-rw-r--r--worldedit/mark.lua70
-rw-r--r--worldedit/table_save.lua133
-rw-r--r--worldedit/textures/worldedit_pos1.pngbin0 -> 142 bytes
-rw-r--r--worldedit/textures/worldedit_pos2.pngbin0 -> 157 bytes
6 files changed, 1565 insertions, 0 deletions
diff --git a/worldedit/functions.lua b/worldedit/functions.lua
new file mode 100644
index 0000000..2d949c6
--- /dev/null
+++ b/worldedit/functions.lua
@@ -0,0 +1,707 @@
+--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
+
+--sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled
+worldedit.set = function(pos1, pos2, nodename)
+ local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
+ local env = minetest.env
+
+ local node = {name=nodename}
+ 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
+ env:add_node(pos, node)
+ pos.z = pos.z + 1
+ end
+ pos.y = pos.y + 1
+ end
+ pos.x = pos.x + 1
+ end
+ 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)
+ local env = minetest.env
+
+ if minetest.registered_nodes[searchnode] == nil then
+ searchnode = "default:" .. searchnode
+ end
+ if minetest.registered_nodes[replacenode] == nil then
+ replacenode = "default:" .. replacenode
+ end
+
+ local pos = {x=pos1.x, y=0, z=0}
+ local node = {name=replacenode}
+ 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
+ if env:get_node(pos).name == searchnode then
+ env:add_node(pos, node)
+ count = count + 1
+ end
+ pos.z = pos.z + 1
+ end
+ pos.y = pos.y + 1
+ end
+ pos.x = pos.x + 1
+ end
+ return count
+end
+
+--adds a hollow sphere at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
+worldedit.hollow_sphere = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed
+ local node = {name=nodename}
+ local pos1 = {x=0, y=0, z=0}
+ local full_radius = radius * radius + radius
+ local env = minetest.env
+ for x = -radius, radius do
+ pos1.x = pos.x + x
+ for y = -radius, radius do
+ pos1.y = pos.y + y
+ for z = -radius, radius do
+ if x*x+y*y+z*z >= (radius-1) * (radius-1) + (radius-1) and x*x+y*y+z*z <= full_radius then
+ pos1.z = pos.z + z
+ env:add_node({x=pos.x+x,y=pos.y+y,z=pos.z+z}, node)
+ end
+ end
+ end
+ end
+end
+
+--adds a sphere at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added
+worldedit.sphere = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed
+ local node = {name=nodename}
+ local pos1 = {x=0, y=0, z=0}
+ local full_radius = radius * radius + radius
+ local count = 0
+ local env = minetest.env
+ for x = -radius, radius do
+ pos1.x = pos.x + x
+ for y = -radius, radius do
+ pos1.y = pos.y + y
+ for z = -radius, radius do
+ if x*x+y*y+z*z <= full_radius then
+ pos1.z = pos.z + z
+ env:add_node(pos1, node)
+ count = count + 1
+ end
+ end
+ end
+ end
+ 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
+
+ local env = minetest.env
+ local currentpos = {x=pos.x, y=pos.y, z=pos.z}
+ local node = {name=nodename}
+ local count = 0
+ local step = 1
+ if length < 0 then
+ length = -length
+ step = -1
+ end
+ for i = 1, length do
+ local offset1, offset2 = 0, radius
+ local delta = -radius
+ while offset1 <= offset2 do
+ --add node at each octant
+ local first1, first2 = pos[other1] + offset1, pos[other1] - offset1
+ local second1, second2 = pos[other2] + offset2, pos[other2] - offset2
+ currentpos[other1], currentpos[other2] = first1, second1
+ env:add_node(currentpos, node) --octant 1
+ currentpos[other1] = first2
+ env:add_node(currentpos, node) --octant 4
+ currentpos[other2] = second2
+ env:add_node(currentpos, node) --octant 5
+ currentpos[other1] = first1
+ env:add_node(currentpos, node) --octant 8
+ local first1, first2 = pos[other1] + offset2, pos[other1] - offset2
+ local second1, second2 = pos[other2] + offset1, pos[other2] - offset1
+ currentpos[other1], currentpos[other2] = first1, second1
+ env:add_node(currentpos, node) --octant 2
+ currentpos[other1] = first2
+ env:add_node(currentpos, node) --octant 3
+ currentpos[other2] = second2
+ env:add_node(currentpos, node) --octant 6
+ currentpos[other1] = first1
+ env:add_node(currentpos, node) --octant 7
+
+ count = count + 8 --wip: broken
+
+ --move to next location
+ delta = delta + (offset1 * 2) + 1
+ if delta >= 0 then
+ offset2 = offset2 - 1
+ delta = delta - (offset2 * 2)
+ end
+ offset1 = offset1 + 1
+ end
+ currentpos[axis] = currentpos[axis] + step
+ end
+ 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
+
+ local env = minetest.env
+ local currentpos = {x=pos.x, y=pos.y, z=pos.z}
+ local node = {name=nodename}
+ local count = 0
+ local step = 1
+ if length < 0 then
+ length = -length
+ step = -1
+ end
+ for i = 1, length do
+ local offset1, offset2 = 0, radius
+ local delta = -radius
+ while offset1 <= offset2 do
+ --connect each pair of octants
+ currentpos[other1] = pos[other1] - offset1
+ local second1, second2 = pos[other2] + offset2, pos[other2] - offset2
+ for i = 0, offset1 * 2 do
+ currentpos[other2] = second1
+ env:add_node(currentpos, node) --octant 1 to 4
+ currentpos[other2] = second2
+ env:add_node(currentpos, node) --octant 5 to 8
+ currentpos[other1] = currentpos[other1] + 1
+ end
+ currentpos[other1] = pos[other1] - offset2
+ local second1, second2 = pos[other2] + offset1, pos[other2] - offset1
+ for i = 0, offset2 * 2 do
+ currentpos[other2] = second1
+ env:add_node(currentpos, node) --octant 2 to 3
+ currentpos[other2] = second2
+ env:add_node(currentpos, node) --octant 6 to 7
+ currentpos[other1] = currentpos[other1] + 1
+ end
+
+ count = count + (offset1 * 4) + (offset2 * 4) + 4 --wip: broken
+
+ --move to next location
+ delta = delta + (offset1 * 2) + 1
+ offset1 = offset1 + 1
+ if delta >= 0 then
+ offset2 = offset2 - 1
+ delta = delta - (offset2 * 2)
+ end
+ end
+ currentpos[axis] = currentpos[axis] + step
+ end
+ return count
+end
+
+--adds a pyramid at `pos` with height `height`, composed of `nodename`, returning the number of nodes added
+worldedit.pyramid = function(pos, height, nodename)
+ local pos1x, pos1y, pos1z = pos.x - height, pos.y, pos.z - height
+ local pos2x, pos2y, pos2z = pos.x + height, pos.y + height, pos.z + height
+ local pos = {x=0, y=pos1y, z=0}
+
+ local count = 0
+ local node = {name=nodename}
+ local env = minetest.env
+ while pos.y <= pos2y do --each vertical level of the pyramid
+ pos.x = pos1x
+ while pos.x <= pos2x do
+ pos.z = pos1z
+ while pos.z <= pos2z do
+ env:add_node(pos, node)
+ pos.z = pos.z + 1
+ end
+ pos.x = pos.x + 1
+ end
+ count = count + ((pos2y - pos.y) * 2 + 1) ^ 2
+ pos.y = pos.y + 1
+
+ pos1x, pos2x = pos1x + 1, pos2x - 1
+ pos1z, pos2z = pos1z + 1, pos2z - 1
+
+ end
+ return count
+end
+
+--adds a spiral at `pos` with width `width`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added
+worldedit.spiral = function(pos, width, height, spacer, nodename) --wip: clean this up
+ -- spiral matrix - http://rosettacode.org/wiki/Spiral_matrix#Lua
+ av, sn = math.abs, function(s) return s~=0 and s/av(s) or 0 end
+ local function sindex(z, x) -- returns the value at (x, z) in a spiral that starts at 1 and goes outwards
+ if z == -x and z >= x then return (2*z+1)^2 end
+ local l = math.max(av(z), av(x))
+ return (2*l-1)^2+4*l+2*l*sn(x+z)+sn(z^2-x^2)*(l-(av(z)==l and sn(z)*x or sn(x)*z)) -- OH GOD WHAT
+ end
+ local function spiralt(side)
+ local ret, id, start, stop = {}, 0, math.floor((-side+1)/2), math.floor((side-1)/2)
+ for i = 1, side do
+ for j = 1, side do
+ local id = side^2 - sindex(stop - i + 1,start + j - 1)
+ ret[id] = {x=i,z=j}
+ end
+ end
+ return ret
+ end
+ -- connect the joined parts
+ local spiral = spiralt(width)
+ height = tonumber(height)
+ if height < 1 then height = 1 end
+ spacer = tonumber(spacer)-1
+ if spacer < 1 then spacer = 1 end
+ local count = 0
+ local node = {name=nodename}
+ local np,lp
+ for y=0,height do
+ lp = nil
+ for _,v in ipairs(spiral) do
+ np = {x=pos.x+v.x*spacer, y=pos.y+y, z=pos.z+v.z*spacer}
+ if lp~=nil then
+ if lp.x~=np.x then
+ if lp.x<np.x then
+ for i=lp.x+1,np.x do
+ minetest.env:add_node({x=i, y=np.y, z=np.z}, node)
+ count = count + 1
+ end
+ else
+ for i=np.x,lp.x-1 do
+ minetest.env:add_node({x=i, y=np.y, z=np.z}, node)
+ count = count + 1
+ end
+ end
+ end
+ if lp.z~=np.z then
+ if lp.z<np.z then
+ for i=lp.z+1,np.z do
+ minetest.env:add_node({x=np.x, y=np.y, z=i}, node)
+ count = count + 1
+ end
+ else
+ for i=np.z,lp.z-1 do
+ minetest.env:add_node({x=np.x, y=np.y, z=i}, node)
+ count = count + 1
+ end
+ end
+ end
+ end
+ lp = np
+ end
+ end
+ 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)
+ local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
+ local env = minetest.env
+
+ 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 = env:get_node(pos) --obtain current node
+ local meta = env:get_meta(pos):to_table() --get meta of current node
+ local value = pos[axis] --store current position
+ pos[axis] = value + amount --move along axis
+ env:add_node(pos, node) --copy node to new position
+ env: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 = minetest.env:get_node(pos) --obtain current node
+ local meta = env:get_meta(pos):to_table() --get meta of current node
+ local value = pos[axis] --store current position
+ pos[axis] = value + amount --move along axis
+ minetest.env:add_node(pos, node) --copy node to new position
+ env: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)
+ local env = minetest.env
+
+ 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 = env:get_node(pos) --obtain current node
+ local meta = env:get_meta(pos):to_table() --get metadata of current node
+ env:remove_node(pos)
+ local value = pos[axis] --store current position
+ pos[axis] = value + amount --move along axis
+ env:add_node(pos, node) --move node to new position
+ env: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 = env:get_node(pos) --obtain current node
+ local meta = env:get_meta(pos):to_table() --get metadata of current node
+ env:remove_node(pos)
+ local value = pos[axis] --store current position
+ pos[axis] = value + amount --move along axis
+ env:add_node(pos, node) --move node to new position
+ env: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
+ for i = 1, count do
+ amount = amount + length
+ copy(pos1, pos2, axis, amount)
+ end
+ return worldedit.volume(pos1, pos2)
+end
+
+--transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed
+worldedit.transpose = function(pos1, pos2, axis1, axis2)
+ local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
+
+ local pos = {x=pos1.x, y=0, z=0}
+ local env = minetest.env
+ 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 extent1 < extent2 then
+ local node1 = env:get_node(pos)
+ local meta1 = env:get_meta(pos):to_table()
+ local value1, value2 = pos[axis1], pos[axis2]
+ pos[axis1], pos[axis2] = value1 + extent2, value2 + extent1
+ local node2 = env:get_node(pos)
+ local meta2 = env:get_meta(pos):to_table()
+ env:add_node(pos, node1)
+ env:get_meta(pos):from_table(meta1)
+ pos[axis1], pos[axis2] = value1, value2
+ env:add_node(pos, node2)
+ env: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)
+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)
+
+ 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 env = minetest.env
+ 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 = env:get_node(pos)
+ local meta1 = env:get_meta(pos):to_table()
+ local value = pos[axis]
+ pos[axis] = start - value
+ local node2 = env:get_node(pos)
+ local meta2 = env:get_meta(pos):to_table()
+ env:add_node(pos, node1)
+ env:get_meta(pos):from_table(meta1)
+ pos[axis] = value
+ env:add_node(pos, node2)
+ env: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
+
+ if angle == 90 then
+ worldedit.transpose(pos1, pos2, axis1, axis2)
+ worldedit.flip(pos1, pos2, axis2)
+ elseif angle == 180 then
+ worldedit.flip(pos1, pos2, axis1)
+ worldedit.flip(pos1, pos2, axis2)
+ elseif angle == 270 then
+ worldedit.transpose(pos1, pos2, axis1, axis2)
+ worldedit.flip(pos1, pos2, axis1)
+ end
+ return worldedit.volume(pos1, pos2)
+end
+
+--digs a region defined by positions `pos1` and `pos2`, returning the number of nodes dug
+worldedit.dig = function(pos1, pos2)
+ local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
+ local env = minetest.env
+
+ 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
+ env:dig_node(pos)
+ pos.z = pos.z + 1
+ end
+ pos.y = pos.y + 1
+ end
+ pos.x = pos.x + 1
+ end
+ return worldedit.volume(pos1, pos2)
+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)
+ local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
+ local pos = {x=pos1.x, y=0, z=0}
+ local count = 0
+ local result = {}
+ local env = minetest.env
+ 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 = env:get_node(pos)
+ if node.name ~= "air" and node.name ~= "ignore" then
+ count = count + 1
+ result[count] = pos.x - pos1.x .. " " .. pos.y - pos1.y .. " " .. pos.z - pos1.z .. " " .. node.name .. " " .. node.param1 .. " " .. node.param2
+ end
+ pos.z = pos.z + 1
+ end
+ pos.y = pos.y + 1
+ end
+ pos.x = pos.x + 1
+ end
+ result = table.concat(result, "\n")
+ return result, count
+end
+
+--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
+worldedit.deserialize = function(originpos, value)
+ local pos = {x=0, y=0, z=0}
+ local node = {name="", param1=0, param2=0}
+ local count = 0
+ local env = minetest.env
+ 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
+ pos.x = originpos.x + tonumber(x)
+ pos.y = originpos.y + tonumber(y)
+ pos.z = originpos.z + tonumber(z)
+ node.name = name
+ node.param1 = param1
+ node.param2 = param2
+ env:add_node(pos, node)
+ count = count + 1
+ end
+ return count
+end
+
+--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
+--based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
+worldedit.deserialize_old = function(originpos, value)
+ --obtain the node table
+ local count = 0
+ local get_tables = loadstring(value)
+ if get_tables == nil 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
+
+ --load the node array
+ local env = minetest.env
+ for i, v in ipairs(tables[1]) do
+ local pos = v[1]
+ pos.x, pos.y, pos.z = originpos.x + pos.x, originpos.y + pos.y, originpos.z + pos.z
+ env:add_node(pos, v[2])
+ count = count + 1
+ end
+ return count
+end
+
+--saves the nodes and meta defined by positions `pos1` and `pos2` into a file, returning the number of nodes saved
+worldedit.metasave = function(pos1, pos2, file) --wip: simply work with strings instead of doing IO
+ local path = minetest.get_worldpath() .. "/schems"
+ local filename = path .. "/" .. file .. ".wem"
+ os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist
+ local rows = {}
+ local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
+ local pos = {x=pos1.x, y=0, z=0}
+ local count = 0
+ local result = {}
+ local env = minetest.env
+ 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 = env:get_node(pos)
+ if node.name ~= "air" and node.name ~= "ignore" then
+ count = count + 1
+ local row = {
+ 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 = env:get_meta(pos):to_table(),
+ }
+ table.insert(rows, row)
+ end
+ pos.z = pos.z + 1
+ end
+ pos.y = pos.y + 1
+ end
+ pos.x = pos.x + 1
+ end
+ local err = table.save(rows,filename)
+ if err then return _,err end
+ return count
+end
+
+--loads the nodes and meta from `file` to position `pos1`, returning the number of nodes loaded
+worldedit.metaload = function(pos1, file) --wip: simply work with strings instead of doing IO
+ local filename = minetest.get_worldpath() .. "/schems/" .. file .. ".wem"
+ local rows, err = table.load(filename)
+ if err then return _,err end
+ local pos = {x=0, y=0, z=0}
+ local node = {name="", param1=0, param2=0}
+ local count = 0
+ local env = minetest.env
+ for i,row in pairs(rows) do
+ pos.x = pos1.x + tonumber(row.x)
+ pos.y = pos1.y + tonumber(row.y)
+ pos.z = pos1.z + tonumber(row.z)
+ node.name = row.name
+ node.param1 = row.param1
+ node.param2 = row.param2
+ env:add_node(pos, node)
+ env:get_meta(pos):from_table(row.meta)
+ count = count + 1
+ end
+ return count
+end \ No newline at end of file
diff --git a/worldedit/init.lua b/worldedit/init.lua
new file mode 100644
index 0000000..84cf4fe
--- /dev/null
+++ b/worldedit/init.lua
@@ -0,0 +1,655 @@
+minetest.register_privilege("worldedit", "Can use WorldEdit commands")
+
+worldedit = {}
+
+worldedit.set_pos = {}
+
+worldedit.pos1 = {}
+worldedit.pos2 = {}
+
+dofile(minetest.get_modpath("worldedit") .. "/functions.lua")
+dofile(minetest.get_modpath("worldedit") .. "/mark.lua")
+
+--determines whether `nodename` is a valid node name, returning a boolean
+worldedit.node_is_valid = function(temp_pos, nodename)
+ return minetest.registered_nodes[nodename] ~= nil
+ or minetest.registered_nodes["default:" .. nodename] ~= nil
+end
+
+--determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)
+worldedit.player_axis = function(name)
+ local dir = minetest.env:get_player_by_name(name):get_look_dir()
+ local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)
+ if x > y then
+ if x > z then
+ return "x", dir.x > 0 and 1 or -1
+ end
+ elseif y > z then
+ return "y", dir.y > 0 and 1 or -1
+ end
+ return "z", dir.z > 0 and 1 or -1
+end
+
+minetest.register_chatcommand("/reset", {
+ params = "",
+ description = "Reset the region so that it is empty",
+ privs = {worldedit=true},
+ func = function(name, param)
+ worldedit.pos1[name] = nil
+ worldedit.pos2[name] = nil
+ worldedit.mark_pos1(name)
+ worldedit.mark_pos2(name)
+ minetest.chat_send_player(name, "WorldEdit region reset")
+ end,
+})
+
+minetest.register_chatcommand("/mark", {
+ params = "",
+ description = "Show markers at the region positions",
+ privs = {worldedit=true},
+ func = function(name, param)
+ worldedit.mark_pos1(name)
+ worldedit.mark_pos2(name)
+ minetest.chat_send_player(name, "WorldEdit region marked")
+ end,
+})
+
+minetest.register_chatcommand("/pos1", {
+ params = "",
+ description = "Set WorldEdit region position 1 to the player's location",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos = minetest.env:get_player_by_name(name):getpos()
+ pos.x, pos.y, pos.z = math.floor(pos.x), math.floor(pos.y), math.floor(pos.z)
+ worldedit.pos1[name] = pos
+ worldedit.mark_pos1(name)
+ minetest.chat_send_player(name, "WorldEdit position 1 set to " .. minetest.pos_to_string(pos))
+ end,
+})
+
+minetest.register_chatcommand("/pos2", {
+ params = "",
+ description = "Set WorldEdit region position 2 to the player's location",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos = minetest.env:get_player_by_name(name):getpos()
+ pos.x, pos.y, pos.z = math.floor(pos.x), math.floor(pos.y), math.floor(pos.z)
+ worldedit.pos2[name] = pos
+ worldedit.mark_pos2(name)
+ minetest.chat_send_player(name, "WorldEdit position 2 set to " .. minetest.pos_to_string(pos))
+ end,
+})
+
+minetest.register_chatcommand("/p", {
+ params = "set/get",
+ description = "Set WorldEdit region by punching two nodes, or display the current WorldEdit region",
+ privs = {worldedit=true},
+ func = function(name, param)
+ if param == "set" then --set both WorldEdit positions
+ worldedit.set_pos[name] = 1
+ minetest.chat_send_player(name, "Select positions by punching two nodes")
+ elseif param == "get" then --display current WorldEdit positions
+ if worldedit.pos1[name] ~= nil then
+ minetest.chat_send_player(name, "WorldEdit position 1: " .. minetest.pos_to_string(worldedit.pos1[name]))
+ else
+ minetest.chat_send_player(name, "WorldEdit position 1 not set")
+ end
+ if worldedit.pos2[name] ~= nil then
+ minetest.chat_send_player(name, "WorldEdit position 2: " .. minetest.pos_to_string(worldedit.pos2[name]))
+ else
+ minetest.chat_send_player(name, "WorldEdit position 2 not set")
+ end
+ else
+ minetest.chat_send_player(name, "Unknown subcommand: " .. param)
+ end
+ end,
+})
+
+minetest.register_on_punchnode(function(pos, node, puncher)
+ local name = puncher:get_player_name()
+ if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position
+ if worldedit.set_pos[name] == 1 then --setting position 1
+ worldedit.set_pos[name] = 2 --set position 2 on the next invocation
+ worldedit.pos1[name] = pos
+ worldedit.mark_pos1(name)
+ minetest.chat_send_player(name, "WorldEdit region position 1 set to " .. minetest.pos_to_string(pos))
+ else --setting position 2
+ worldedit.set_pos[name] = nil --finished setting positions
+ worldedit.pos2[name] = pos
+ worldedit.mark_pos2(name)
+ minetest.chat_send_player(name, "WorldEdit region position 2 set to " .. minetest.pos_to_string(pos))
+ end
+ end
+end)
+
+minetest.register_chatcommand("/volume", {
+ params = "",
+ description = "Display the volume of the current WorldEdit region",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local volume = worldedit.volume(pos1, pos2)
+ minetest.chat_send_player(name, "Current WorldEdit region has a volume of " .. volume .. " nodes (" .. pos2.x - pos1.x .. "*" .. pos2.y - pos1.y .. "*" .. pos2.z - pos1.z .. ")")
+ end,
+})
+
+minetest.register_chatcommand("/set", {
+ params = "<node>",
+ description = "Set the current WorldEdit region to <node>",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ if param == "" or not worldedit.node_is_valid(pos1, param) then
+ minetest.chat_send_player(name, "Invalid node name: " .. param)
+ return
+ end
+
+ local count = worldedit.set(pos1, pos2, param)
+ minetest.chat_send_player(name, count .. " nodes set")
+ end,
+})
+
+minetest.register_chatcommand("/replace", {
+ params = "<search node> <replace node>",
+ description = "Replace all instances of <search node> with <place node> in the current WorldEdit region",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+([^%s]+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if not worldedit.node_is_valid(pos1, searchnode) then
+ minetest.chat_send_player(name, "Invalid search node name: " .. searchnode)
+ return
+ end
+ if not worldedit.node_is_valid(pos1, replacenode) then
+ minetest.chat_send_player(name, "Invalid replace node name: " .. replacenode)
+ return
+ end
+
+ local count = worldedit.replace(pos1, pos2, searchnode, replacenode)
+ minetest.chat_send_player(name, count .. " nodes replaced")
+ end,
+})
+
+minetest.register_chatcommand("/hollowsphere", {
+ params = "<radius> <node>",
+ description = "Add hollow sphere at WorldEdit position 1 with radius <radius>, composed of <node>",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos = worldedit.pos1[name]
+ if pos == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, radius, nodename = param:find("^(%d+)%s+([^%s]+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if not worldedit.node_is_valid(pos, nodename) then
+ minetest.chat_send_player(name, "Invalid node name: " .. param)
+ return
+ end
+
+ local count = worldedit.hollow_sphere(pos, tonumber(radius), nodename)
+ minetest.chat_send_player(name, count .. " nodes added")
+ end,
+})
+
+minetest.register_chatcommand("/sphere", {
+ params = "<radius> <node>",
+ description = "Add sphere at WorldEdit position 1 with radius <radius>, composed of <node>",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos = worldedit.pos1[name]
+ if pos == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, radius, nodename = param:find("^(%d+)%s+([^%s]+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if not worldedit.node_is_valid(pos, nodename) then
+ minetest.chat_send_player(name, "Invalid node name: " .. param)
+ return
+ end
+
+ local count = worldedit.sphere(pos, tonumber(radius), nodename)
+ minetest.chat_send_player(name, count .. " nodes added")
+ end,
+})
+
+minetest.register_chatcommand("/hollowcylinder", {
+ params = "x/y/z/? <length> <radius> <node>",
+ description = "Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos = worldedit.pos1[name]
+ if pos == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+([^%s]+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if axis == "?" then
+ axis, sign = worldedit.player_axis(name)
+ length = length * sign
+ end
+ if not worldedit.node_is_valid(pos, nodename) then
+ minetest.chat_send_player(name, "Invalid node name: " .. param)
+ return
+ end
+
+ local count = worldedit.hollow_cylinder(pos, axis, tonumber(length), tonumber(radius), nodename)
+ minetest.chat_send_player(name, count .. " nodes added")
+ end,
+})
+
+minetest.register_chatcommand("/cylinder", {
+ params = "x/y/z/? <length> <radius> <node>",
+ description = "Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos = worldedit.pos1[name]
+ if pos == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+([^%s]+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if axis == "?" then
+ axis, sign = worldedit.player_axis(name)
+ length = length * sign
+ end
+ if not worldedit.node_is_valid(pos, nodename) then
+ minetest.chat_send_player(name, "Invalid node name: " .. param)
+ return
+ end
+
+ local count = worldedit.cylinder(pos, axis, tonumber(length), tonumber(radius), nodename)
+ minetest.chat_send_player(name, count .. " nodes added")
+ end,
+})
+
+minetest.register_chatcommand("/pyramid", {
+ params = "<height> <node>",
+ description = "Add pyramid at WorldEdit position 1 with height <height>, composed of <node>",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos = worldedit.pos1[name]
+ if pos == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, size, nodename = param:find("(%d+)%s+([^%s]+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if not worldedit.node_is_valid(pos, nodename) then
+ minetest.chat_send_player(name, "Invalid node name: " .. param)
+ return
+ end
+
+ local count = worldedit.pyramid(pos, tonumber(size), nodename)
+ minetest.chat_send_player(name, count .. " nodes added")
+ end,
+})
+
+minetest.register_chatcommand("/spiral", {
+ params = "<width> <height> <space> <node>",
+ description = "Add spiral at WorldEdit position 1 with width <width>, height <height>, space between walls <space>, composed of <node>",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos = worldedit.pos1[name]
+ if pos == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, width, height, space, nodename = param:find("(%d+)%s+(%d+)%s+(%d+)%s+([^%s]+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if not worldedit.node_is_valid(pos, nodename) then
+ minetest.chat_send_player(name, "Invalid node name: " .. param)
+ return
+ end
+
+ local count = worldedit.spiral(pos, tonumber(width), tonumber(height), tonumber(space), nodename)
+ minetest.chat_send_player(name, count .. " nodes changed")
+ end,
+})
+
+minetest.register_chatcommand("/copy", {
+ params = "x/y/z/? <amount>",
+ description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if axis == "?" then
+ axis, sign = worldedit.player_axis(name)
+ amount = amount * sign
+ end
+
+ local count = worldedit.copy(pos1, pos2, axis, tonumber(amount))
+ minetest.chat_send_player(name, count .. " nodes copied")
+ end,
+})
+
+minetest.register_chatcommand("/move", {
+ params = "x/y/z/? <amount>",
+ description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if axis == "?" then
+ axis, sign = worldedit.player_axis(name)
+ amount = amount * sign
+ end
+
+ local count = worldedit.move(pos1, pos2, axis, tonumber(amount))
+
+ pos1[axis] = pos1[axis] + amount
+ pos2[axis] = pos2[axis] + amount
+ worldedit.mark_pos1(name)
+ worldedit.mark_pos2(name)
+
+ minetest.chat_send_player(name, count .. " nodes moved")
+ end,
+})
+
+minetest.register_chatcommand("/stack", {
+ params = "x/y/z/? <count>",
+ description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, axis, count = param:find("^([xyz%?])%s+([+-]?%d+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if axis == "?" then
+ axis, sign = worldedit.player_axis(name)
+ count = count * sign
+ end
+
+ local count = worldedit.stack(pos1, pos2, axis, tonumber(count))
+ minetest.chat_send_player(name, count .. " nodes stacked")
+ end,
+})
+
+minetest.register_chatcommand("/transpose", {
+ params = "x/y/z/? x/y/z/?",
+ description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if axis1 == "?" then
+ axis1 = worldedit.player_axis(name)
+ end
+ if axis2 == "?" then
+ axis2 = worldedit.player_axis(name)
+ end
+ if axis1 == axis2 then
+ minetest.chat_send_player(name, "Invalid usage: axes are the same")
+ return
+ end
+
+ local count = worldedit.transpose(pos1, pos2, axis1, axis2)
+ minetest.chat_send_player(name, count .. " nodes transposed")
+ end,
+})
+
+minetest.register_chatcommand("/flip", {
+ params = "x/y/z/?",
+ description = "Flip the current WorldEdit region along the x/y/z/? axis",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ if param == "?" then
+ param = worldedit.player_axis(name)
+ end
+ if param ~= "x" and param ~= "y" and param ~= "z" then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+
+ local count = worldedit.flip(pos1, pos2, param)
+ minetest.chat_send_player(name, count .. " nodes flipped")
+ end,
+})
+
+minetest.register_chatcommand("/rotate", {
+ params = "<axis> <angle>",
+ description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
+ if found == nil then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ if axis == "?" then
+ axis = worldedit.player_axis(name)
+ end
+ if angle % 90 ~= 0 then
+ minetest.chat_send_player(name, "Invalid usage: angle must be multiple of 90")
+ return
+ end
+
+ local count = worldedit.rotate(pos1, pos2, axis, angle)
+ minetest.chat_send_player(name, count .. " nodes rotated")
+ end,
+})
+
+minetest.register_chatcommand("/dig", {
+ params = "",
+ description = "Dig the current WorldEdit region",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ local count = worldedit.dig(pos1, pos2)
+ minetest.chat_send_player(name, count .. " nodes dug")
+ end,
+})
+
+minetest.register_chatcommand("/save", {
+ params = "<file>",
+ description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ if param == "" then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+
+ local result, count = worldedit.serialize(pos1, pos2)
+
+ local path = minetest.get_worldpath() .. "/schems"
+ local filename = path .. "/" .. param .. ".we"
+ os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist
+ local file, err = io.open(filename, "wb")
+ if err ~= nil then
+ minetest.chat_send_player(name, "Could not save file to \"" .. filename .. "\"")
+ return
+ end
+ file:write(result)
+ file:flush()
+ file:close()
+
+ minetest.chat_send_player(name, count .. " nodes saved")
+ end,
+})
+
+minetest.register_chatcommand("/load", {
+ params = "<file>",
+ description = "Load nodes from \"(world folder)/schems/<file>.we\" with position 1 of the current WorldEdit region as the origin",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1 = worldedit.pos1[name]
+ if pos1 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+
+ if param == "" then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+
+ local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"
+ local file, err = io.open(filename, "rb")
+ if err ~= nil then
+ minetest.chat_send_player(name, "Could not open file \"" .. filename .. "\"")
+ return
+ end
+ local value = file:read("*a")
+ file:close()
+
+ local count
+ if value:find("{") then --old WorldEdit format
+ count = worldedit.deserialize_old(pos1, value)
+ else --new WorldEdit format
+ count = worldedit.deserialize(pos1, value)
+ end
+
+ minetest.chat_send_player(name, count .. " nodes loaded")
+ end,
+})
+
+minetest.register_chatcommand("/metasave", {
+ params = "<file>",
+ description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.wem\"",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
+ if pos1 == nil or pos2 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+ if param == "" then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ local count, err = worldedit.metasave(pos1, pos2, param)
+ if err then
+ minetest.chat_send_player(name, "error loading file: " .. err)
+ else
+ minetest.chat_send_player(name, count .. " nodes saved")
+ end
+ end,
+})
+
+minetest.register_chatcommand("/metaload", {
+ params = "<file>",
+ description = "Load nodes from \"(world folder)/schems/<file>.wem\" with position 1 of the current WorldEdit region as the origin",
+ privs = {worldedit=true},
+ func = function(name, param)
+ local pos1 = worldedit.pos1[name]
+ if pos1 == nil then
+ minetest.chat_send_player(name, "No WorldEdit region selected")
+ return
+ end
+ if param == "" then
+ minetest.chat_send_player(name, "Invalid usage: " .. param)
+ return
+ end
+ local count, err = worldedit.metaload(pos1, param)
+ if err then
+ minetest.chat_send_player(name, "error loading file: " .. err)
+ else
+ minetest.chat_send_player(name, count .. " nodes loaded")
+ end
+ end,
+})
diff --git a/worldedit/mark.lua b/worldedit/mark.lua
new file mode 100644
index 0000000..0f011d2
--- /dev/null
+++ b/worldedit/mark.lua
@@ -0,0 +1,70 @@
+worldedit.marker1 = {}
+worldedit.marker2 = {}
+
+--marks worldedit region position 1
+worldedit.mark_pos1 = function(name)
+ local pos = worldedit.pos1[name]
+ if worldedit.marker1[name] ~= nil then --marker already exists
+ worldedit.marker1[name]:remove() --remove marker
+ worldedit.marker1[name] = nil
+ end
+ if pos ~= nil then --add marker
+ worldedit.marker1[name] = minetest.env:add_entity(pos, "worldedit:pos1")
+ worldedit.marker1[name]:get_luaentity().active = true
+ end
+end
+
+--marks worldedit region position 2
+worldedit.mark_pos2 = function(name)
+ local pos = worldedit.pos2[name]
+ if worldedit.marker2[name] ~= nil then --marker already exists
+ worldedit.marker2[name]:remove() --remove marker
+ worldedit.marker2[name] = nil
+ end
+ if pos ~= nil then --add marker
+ worldedit.marker2[name] = minetest.env:add_entity(pos, "worldedit:pos2")
+ worldedit.marker2[name]:get_luaentity().active = true
+ end
+end
+
+minetest.register_entity("worldedit:pos1", {
+ initial_properties = {
+ visual = "cube",
+ visual_size = {x=1.1, y=1.1},
+ textures = {"worldedit_pos1.png", "worldedit_pos1.png",
+ "worldedit_pos1.png", "worldedit_pos1.png",
+ "worldedit_pos1.png", "worldedit_pos1.png"},
+ collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
+ },
+ on_step = function(self, dtime)
+ if self.active == nil then
+ self.object:remove()
+ end
+ end,
+ on_punch = function(self, hitter)
+ self.object:remove()
+ local name = hitter:get_player_name()
+ worldedit.marker1[name] = nil
+ end,
+})
+
+minetest.register_entity("worldedit:pos2", {
+ initial_properties = {
+ visual = "cube",
+ visual_size = {x=1.1, y=1.1},
+ textures = {"worldedit_pos2.png", "worldedit_pos2.png",
+ "worldedit_pos2.png", "worldedit_pos2.png",
+ "worldedit_pos2.png", "worldedit_pos2.png"},
+ collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
+ },
+ on_step = function(self, dtime)
+ if self.active == nil then
+ self.object:remove()
+ end
+ end,
+ on_punch = function(self, hitter)
+ self.object:remove()
+ local name = hitter:get_player_name()
+ worldedit.marker2[name] = nil
+ end,
+}) \ No newline at end of file
diff --git a/worldedit/table_save.lua b/worldedit/table_save.lua
new file mode 100644
index 0000000..cbc18ae
--- /dev/null
+++ b/worldedit/table_save.lua
@@ -0,0 +1,133 @@
+--[[
+ Save Table to File
+ Load Table from File
+ v 1.0
+
+ Lua 5.2 compatible
+
+ Only Saves Tables, Numbers and Strings
+ Insides Table References are saved
+ Does not save Userdata, Metatables, Functions and indices of these
+ ----------------------------------------------------
+ table.save( table , filename )
+
+ on failure: returns an error msg
+
+ ----------------------------------------------------
+ table.load( filename or stringtable )
+
+ Loads a table that has been saved via the table.save function
+
+ on success: returns a previously saved table
+ on failure: returns as second argument an error msg
+ ----------------------------------------------------
+
+ Licensed under the same terms as Lua itself.
+]]--
+do
+ -- declare local variables
+ --// exportstring( string )
+ --// returns a "Lua" portable version of the string
+ local function exportstring( s )
+ return string.format("%q", s)
+ end
+
+ --// The Save Function
+ function table.save( tbl,filename )
+ local charS,charE = " ","\n"
+ local file,err = io.open( filename, "wb" )
+ if err then return err end
+
+ -- initiate variables for save procedure
+ local tables,lookup = { tbl },{ [tbl] = 1 }
+ file:write( "return {"..charE )
+
+ for idx,t in ipairs( tables ) do
+ file:write( "-- Table: {"..idx.."}"..charE )
+ file:write( "{"..charE )
+ local thandled = {}
+
+ for i,v in ipairs( t ) do
+ thandled[i] = true
+ local stype = type( v )
+ -- only handle value
+ if stype == "table" then
+ if not lookup[v] then
+ table.insert( tables, v )
+ lookup[v] = #tables
+ end
+ file:write( charS.."{"..lookup[v].."},"..charE )
+ elseif stype == "string" then
+ file:write( charS..exportstring( v )..","..charE )
+ elseif stype == "number" then
+ file:write( charS..tostring( v )..","..charE )
+ end
+ end
+
+ for i,v in pairs( t ) do
+ -- escape handled values
+ if (not thandled[i]) then
+
+ local str = ""
+ local stype = type( i )
+ -- handle index
+ if stype == "table" then
+ if not lookup[i] then
+ table.insert( tables,i )
+ lookup[i] = #tables
+ end
+ str = charS.."[{"..lookup[i].."}]="
+ elseif stype == "string" then
+ str = charS.."["..exportstring( i ).."]="
+ elseif stype == "number" then
+ str = charS.."["..tostring( i ).."]="
+ end
+
+ if str ~= "" then
+ stype = type( v )
+ -- handle value
+ if stype == "table" then
+ if not lookup[v] then
+ table.insert( tables,v )
+ lookup[v] = #tables
+ end
+ file:write( str.."{"..lookup[v].."},"..charE )
+ elseif stype == "string" then
+ file:write( str..exportstring( v )..","..charE )
+ elseif stype == "number" then
+ file:write( str..tostring( v )..","..charE )
+ end
+ end
+ end
+ end
+ file:write( "},"..charE )
+ end
+ file:write( "}" )
+ file:close()
+ end
+
+ --// The Load Function
+ function table.load( sfile )
+ local ftables,err = loadfile( sfile )
+ if err then return _,err end
+ local tables = ftables()
+ for idx = 1,#tables do
+ local tolinki = {}
+ for i,v in pairs( tables[idx] ) do
+ if type( v ) == "table" then
+ tables[idx][i] = tables[v[1]]
+ end
+ if type( i ) == "table" and tables[i[1]] then
+ table.insert( tolinki,{ i,tables[i[1]] } )
+ end
+ end
+ -- link indices
+ for _,v in ipairs( tolinki ) do
+ tables[idx][v[2]],tables[idx][v[1]] = tables[idx][v[1]],nil
+ end
+ end
+ return tables[1]
+ end
+-- close do
+end
+-- ChillCode \ No newline at end of file
diff --git a/worldedit/textures/worldedit_pos1.png b/worldedit/textures/worldedit_pos1.png
new file mode 100644
index 0000000..4c304aa
--- /dev/null
+++ b/worldedit/textures/worldedit_pos1.png
Binary files differ
diff --git a/worldedit/textures/worldedit_pos2.png b/worldedit/textures/worldedit_pos2.png
new file mode 100644
index 0000000..1502f16
--- /dev/null
+++ b/worldedit/textures/worldedit_pos2.png
Binary files differ