]> git.lizzy.rs Git - worldedit.git/commitdiff
Separate components into separate mods, add visualization API with hide(), suppress...
authorAnthony Zhang <azhang9@gmail.com>
Sun, 14 Oct 2012 01:45:50 +0000 (21:45 -0400)
committerAnthony Zhang <azhang9@gmail.com>
Sun, 14 Oct 2012 01:45:50 +0000 (21:45 -0400)
16 files changed:
Chat Commands.md
WorldEdit API.md
worldedit/functions.lua [deleted file]
worldedit/init.lua
worldedit/manipulations.lua [new file with mode: 0644]
worldedit/mark.lua [deleted file]
worldedit/primitives.lua [new file with mode: 0644]
worldedit/serialization.lua [new file with mode: 0644]
worldedit/textures/worldedit_pos1.png [deleted file]
worldedit/textures/worldedit_pos2.png [deleted file]
worldedit/visualization.lua [new file with mode: 0644]
worldedit_commands/depends.txt [new file with mode: 0644]
worldedit_commands/init.lua [new file with mode: 0644]
worldedit_commands/mark.lua [new file with mode: 0644]
worldedit_commands/textures/worldedit_pos1.png [new file with mode: 0644]
worldedit_commands/textures/worldedit_pos2.png [new file with mode: 0644]

index f4febf7ca20bcbc6753aa40445c4694d8e242b63..f140880fe1cde394e06fc13353c55d4f25896b94 100644 (file)
@@ -166,6 +166,34 @@ Dig the current WorldEdit region.
 \r
     //dig\r
 \r
+## //hide\r
+\r
+Hide all nodes in the current WorldEdit region non-destructively.\r
+\r
+    //hide\r
+\r
+### //suppress <node>\r
+\r
+Suppress all <node> in the current WorldEdit region non-destructively.\r
+\r
+    //suppress dirt\r
+    //suppress default:glass\r
+    //suppress mesecons:mesecon\r
+\r
+### //find <node>\r
+\r
+Find <node> in the current WorldEdit region by hiding everything else non-destructively.\r
+\r
+    //find dirt\r
+    //find default:glass\r
+    //find mesecons:mesecon\r
+\r
+### //restore\r
+\r
+Restores nodes hidden with WorldEdit in the current WorldEdit region.\r
+\r
+    //restore\r
+\r
 ### //save <file>\r
 \r
 Save the current WorldEdit region to "(world folder)/schems/<file>.we".\r
index 41fab6931ac22865555237ae471b38e662b12f66..30e4437f0e03908b6efa39c1d84cb8eb99588ca7 100644 (file)
@@ -1,7 +1,13 @@
 WorldEdit API\r
--------------\r
+=============\r
+The WorldEdit API is composed of multiple modules, each of which is independent and can be used without the other. Each module is contained within a single file.\r
+\r
 For more information, see the [README](README.md).\r
 \r
+Manipulations\r
+-------------\r
+Contained in manipulations.lua, this module allows several node operations to be applied over a region.\r
+\r
 ### worldedit.volume(pos1, pos2)\r
 \r
 Determines the volume of the region defined by positions `pos1` and `pos2`.\r
@@ -20,6 +26,54 @@ Replaces all instances of `searchnode` with `replacenode` in a region defined by
 \r
 Returns the number of nodes replaced.\r
 \r
+Returns the number of nodes added.\r
+\r
+### worldedit.copy(pos1, pos2, axis, amount)\r
+\r
+Copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes.\r
+\r
+Returns the number of nodes copied.\r
+\r
+### worldedit.move(pos1, pos2, axis, amount)\r
+\r
+Moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes.\r
+\r
+Returns the number of nodes moved.\r
+\r
+### worldedit.stack(pos1, pos2, axis, count)\r
+\r
+Duplicates the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") `count` times.\r
+\r
+Returns the number of nodes stacked.\r
+\r
+### worldedit.transpose(pos1, pos2, axis1, axis2)\r
+\r
+Transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes ("x" or "y" or "z").\r
+\r
+Returns the number of nodes transposed.\r
+\r
+### worldedit.flip(pos1, pos2, axis)\r
+\r
+Flips a region defined by the positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z").\r
+\r
+Returns the number of nodes flipped.\r
+\r
+### worldedit.rotate(pos1, pos2, angle)\r
+\r
+Rotates a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise around the y axis (supporting 90 degree increments only).\r
+\r
+Returns the number of nodes rotated.\r
+\r
+### worldedit.dig(pos1, pos2)\r
+\r
+Digs a region defined by positions `pos1` and `pos2`.\r
+\r
+Returns the number of nodes dug.\r
+\r
+Primitives\r
+----------\r
+Contained in primitives.lua, this module allows the creation of several geometric primitives.\r
+\r
 ### worldedit.hollow_sphere = function(pos, radius, nodename)\r
 \r
 Adds a hollow sphere at `pos` with radius `radius`, composed of `nodename`.\r
@@ -54,49 +108,37 @@ Returns the number of nodes added.
 \r
 Adds a spiral at `pos` with width `width`, height `height`, space between walls `spacer`, composed of `nodename`.\r
 \r
-Returns the number of nodes added.\r
-\r
-### worldedit.copy(pos1, pos2, axis, amount)\r
-\r
-Copies the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes.\r
-\r
-Returns the number of nodes copied.\r
-\r
-### worldedit.move(pos1, pos2, axis, amount)\r
-\r
-Moves the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") by `amount` nodes.\r
-\r
-Returns the number of nodes moved.\r
-\r
-### worldedit.stack(pos1, pos2, axis, count)\r
-\r
-Duplicates the region defined by positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z") `count` times.\r
+Visualization\r
+-------------\r
+Contained in visualization.lua, this module allows nodes to be visualized in different ways.\r
 \r
-Returns the number of nodes stacked.\r
+### worldedit.hide(pos1, pos2)\r
 \r
-### worldedit.transpose(pos1, pos2, axis1, axis2)\r
+Hides all nodes in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes.\r
 \r
-Transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes ("x" or "y" or "z").\r
+Returns the number of nodes hidden.\r
 \r
-Returns the number of nodes transposed.\r
+### worldedit.suppress(pos1, pos2, nodename)\r
 \r
-### worldedit.flip(pos1, pos2, axis)\r
+Suppresses all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively replacing them with invisible nodes.\r
 \r
-Flips a region defined by the positions `pos1` and `pos2` along the `axis` axis ("x" or "y" or "z").\r
+Returns the number of nodes suppressed.\r
 \r
-Returns the number of nodes flipped.\r
+### worldedit.find(pos1, pos2, nodename)\r
 \r
-### worldedit.rotate(pos1, pos2, angle)\r
+Finds all instances of `nodename` in a region defined by positions `pos1` and `pos2` by non-destructively hiding all other nodes.\r
 \r
-Rotates a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise around the y axis (supporting 90 degree increments only).\r
+Returns the number of nodes found.\r
 \r
-Returns the number of nodes rotated.\r
+### worldedit.restore(pos1, pos2)\r
 \r
-### worldedit.dig(pos1, pos2)\r
+Restores all nodes hidden with WorldEdit functions in a region defined by positions `pos1` and `pos2`.\r
 \r
-Digs a region defined by positions `pos1` and `pos2`.\r
+Returns the number of nodes restored.\r
 \r
-Returns the number of nodes dug.\r
+Serialization\r
+-------------\r
+Contained in serialization.lua, this module allows regions of nodes to be serialized and deserialized to formats suitable for use outside MineTest.\r
 \r
 ### worldedit.serialize(pos1, pos2)\r
 \r
diff --git a/worldedit/functions.lua b/worldedit/functions.lua
deleted file mode 100644 (file)
index 2d949c6..0000000
+++ /dev/null
@@ -1,707 +0,0 @@
---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\r
-worldedit.sort_pos = function(pos1, pos2)\r
-       pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}\r
-       pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}\r
-       if pos1.x > pos2.x then\r
-               pos2.x, pos1.x = pos1.x, pos2.x\r
-       end\r
-       if pos1.y > pos2.y then\r
-               pos2.y, pos1.y = pos1.y, pos2.y\r
-       end\r
-       if pos1.z > pos2.z then\r
-               pos2.z, pos1.z = pos1.z, pos2.z\r
-       end\r
-       return pos1, pos2\r
-end\r
-\r
---determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume\r
-worldedit.volume = function(pos1, pos2)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)\r
-end\r
-\r
---sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled\r
-worldedit.set = function(pos1, pos2, nodename)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
-\r
-       local node = {name=nodename}\r
-       local pos = {x=pos1.x, y=0, z=0}\r
-       while pos.x <= pos2.x do\r
-               pos.y = pos1.y\r
-               while pos.y <= pos2.y do\r
-                       pos.z = pos1.z\r
-                       while pos.z <= pos2.z do\r
-                               env:add_node(pos, node)\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.y = pos.y + 1\r
-               end\r
-               pos.x = pos.x + 1\r
-       end\r
-       return worldedit.volume(pos1, pos2)\r
-end\r
-\r
---replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced\r
-worldedit.replace = function(pos1, pos2, searchnode, replacenode)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
-\r
-       if minetest.registered_nodes[searchnode] == nil then\r
-               searchnode = "default:" .. searchnode\r
-       end\r
-       if minetest.registered_nodes[replacenode] == nil then\r
-               replacenode = "default:" .. replacenode\r
-       end\r
-\r
-       local pos = {x=pos1.x, y=0, z=0}\r
-       local node = {name=replacenode}\r
-       local count = 0\r
-       while pos.x <= pos2.x do\r
-               pos.y = pos1.y\r
-               while pos.y <= pos2.y do\r
-                       pos.z = pos1.z\r
-                       while pos.z <= pos2.z do\r
-                               if env:get_node(pos).name == searchnode then\r
-                                       env:add_node(pos, node)\r
-                                       count = count + 1\r
-                               end\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.y = pos.y + 1\r
-               end\r
-               pos.x = pos.x + 1\r
-       end\r
-       return count\r
-end\r
-\r
---adds a hollow sphere at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
-worldedit.hollow_sphere = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
-       local node = {name=nodename}\r
-       local pos1 = {x=0, y=0, z=0}\r
-       local full_radius = radius * radius + radius\r
-       local env = minetest.env\r
-       for x = -radius, radius do\r
-               pos1.x = pos.x + x\r
-               for y = -radius, radius do\r
-                       pos1.y = pos.y + y\r
-                       for z = -radius, radius do\r
-                               if x*x+y*y+z*z >= (radius-1) * (radius-1) + (radius-1) and x*x+y*y+z*z <= full_radius then\r
-                                       pos1.z = pos.z + z\r
-                                       env:add_node({x=pos.x+x,y=pos.y+y,z=pos.z+z}, node)\r
-                               end\r
-                       end\r
-               end\r
-       end\r
-end\r
-\r
---adds a sphere at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
-worldedit.sphere = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
-       local node = {name=nodename}\r
-       local pos1 = {x=0, y=0, z=0}\r
-       local full_radius = radius * radius + radius\r
-       local count = 0\r
-       local env = minetest.env\r
-       for x = -radius, radius do\r
-               pos1.x = pos.x + x\r
-               for y = -radius, radius do\r
-                       pos1.y = pos.y + y\r
-                       for z = -radius, radius do\r
-                               if x*x+y*y+z*z <= full_radius then\r
-                                       pos1.z = pos.z + z\r
-                                       env:add_node(pos1, node)\r
-                                       count = count + 1\r
-                               end\r
-                       end\r
-               end\r
-       end\r
-       return count\r
-end\r
-\r
---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\r
-worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename)\r
-       local other1, other2\r
-       if axis == "x" then\r
-               other1, other2 = "y", "z"\r
-       elseif axis == "y" then\r
-               other1, other2 = "x", "z"\r
-       else --axis == "z"\r
-               other1, other2 = "x", "y"\r
-       end\r
-\r
-       local env = minetest.env\r
-       local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
-       local node = {name=nodename}\r
-       local count = 0\r
-       local step = 1\r
-       if length < 0 then\r
-               length = -length\r
-               step = -1\r
-       end\r
-       for i = 1, length do\r
-               local offset1, offset2 = 0, radius\r
-               local delta = -radius\r
-               while offset1 <= offset2 do\r
-                       --add node at each octant\r
-                       local first1, first2 = pos[other1] + offset1, pos[other1] - offset1\r
-                       local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
-                       currentpos[other1], currentpos[other2] = first1, second1\r
-                       env:add_node(currentpos, node) --octant 1\r
-                       currentpos[other1] = first2\r
-                       env:add_node(currentpos, node) --octant 4\r
-                       currentpos[other2] = second2\r
-                       env:add_node(currentpos, node) --octant 5\r
-                       currentpos[other1] = first1\r
-                       env:add_node(currentpos, node) --octant 8\r
-                       local first1, first2 = pos[other1] + offset2, pos[other1] - offset2\r
-                       local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
-                       currentpos[other1], currentpos[other2] = first1, second1\r
-                       env:add_node(currentpos, node) --octant 2\r
-                       currentpos[other1] = first2\r
-                       env:add_node(currentpos, node) --octant 3\r
-                       currentpos[other2] = second2\r
-                       env:add_node(currentpos, node) --octant 6\r
-                       currentpos[other1] = first1\r
-                       env:add_node(currentpos, node) --octant 7\r
-\r
-                       count = count + 8 --wip: broken\r
-\r
-                       --move to next location\r
-                       delta = delta + (offset1 * 2) + 1\r
-                       if delta >= 0 then\r
-                               offset2 = offset2 - 1\r
-                               delta = delta - (offset2 * 2)\r
-                       end\r
-                       offset1 = offset1 + 1\r
-               end\r
-               currentpos[axis] = currentpos[axis] + step\r
-       end\r
-       return count\r
-end\r
-\r
---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\r
-worldedit.cylinder = function(pos, axis, length, radius, nodename)\r
-       local other1, other2\r
-       if axis == "x" then\r
-               other1, other2 = "y", "z"\r
-       elseif axis == "y" then\r
-               other1, other2 = "x", "z"\r
-       else --axis == "z"\r
-               other1, other2 = "x", "y"\r
-       end\r
-\r
-       local env = minetest.env\r
-       local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
-       local node = {name=nodename}\r
-       local count = 0\r
-       local step = 1\r
-       if length < 0 then\r
-               length = -length\r
-               step = -1\r
-       end\r
-       for i = 1, length do\r
-               local offset1, offset2 = 0, radius\r
-               local delta = -radius\r
-               while offset1 <= offset2 do\r
-                       --connect each pair of octants\r
-                       currentpos[other1] = pos[other1] - offset1\r
-                       local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
-                       for i = 0, offset1 * 2 do\r
-                               currentpos[other2] = second1\r
-                               env:add_node(currentpos, node) --octant 1 to 4\r
-                               currentpos[other2] = second2\r
-                               env:add_node(currentpos, node) --octant 5 to 8\r
-                               currentpos[other1] = currentpos[other1] + 1\r
-                       end\r
-                       currentpos[other1] = pos[other1] - offset2\r
-                       local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
-                       for i = 0, offset2 * 2 do\r
-                               currentpos[other2] = second1\r
-                               env:add_node(currentpos, node) --octant 2 to 3\r
-                               currentpos[other2] = second2\r
-                               env:add_node(currentpos, node) --octant 6 to 7\r
-                               currentpos[other1] = currentpos[other1] + 1\r
-                       end\r
-\r
-                       count = count + (offset1 * 4) + (offset2 * 4) + 4 --wip: broken\r
-\r
-                       --move to next location\r
-                       delta = delta + (offset1 * 2) + 1\r
-                       offset1 = offset1 + 1\r
-                       if delta >= 0 then\r
-                               offset2 = offset2 - 1\r
-                               delta = delta - (offset2 * 2)\r
-                       end\r
-               end\r
-               currentpos[axis] = currentpos[axis] + step\r
-       end\r
-       return count\r
-end\r
-\r
---adds a pyramid at `pos` with height `height`, composed of `nodename`, returning the number of nodes added\r
-worldedit.pyramid = function(pos, height, nodename)\r
-       local pos1x, pos1y, pos1z = pos.x - height, pos.y, pos.z - height\r
-       local pos2x, pos2y, pos2z = pos.x + height, pos.y + height, pos.z + height\r
-       local pos = {x=0, y=pos1y, z=0}\r
-\r
-       local count = 0\r
-       local node = {name=nodename}\r
-       local env = minetest.env\r
-       while pos.y <= pos2y do --each vertical level of the pyramid\r
-               pos.x = pos1x\r
-               while pos.x <= pos2x do\r
-                       pos.z = pos1z\r
-                       while pos.z <= pos2z do\r
-                               env:add_node(pos, node)\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.x = pos.x + 1\r
-               end\r
-               count = count + ((pos2y - pos.y) * 2 + 1) ^ 2\r
-               pos.y = pos.y + 1\r
-\r
-               pos1x, pos2x = pos1x + 1, pos2x - 1\r
-               pos1z, pos2z = pos1z + 1, pos2z - 1\r
-\r
-       end\r
-       return count\r
-end\r
-\r
---adds a spiral at `pos` with width `width`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added\r
-worldedit.spiral = function(pos, width, height, spacer, nodename) --wip: clean this up\r
-       -- spiral matrix - http://rosettacode.org/wiki/Spiral_matrix#Lua\r
-       av, sn = math.abs, function(s) return s~=0 and s/av(s) or 0 end\r
-       local function sindex(z, x) -- returns the value at (x, z) in a spiral that starts at 1 and goes outwards\r
-               if z == -x and z >= x then return (2*z+1)^2 end\r
-               local l = math.max(av(z), av(x))\r
-               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\r
-       end\r
-       local function spiralt(side)\r
-               local ret, id, start, stop = {}, 0, math.floor((-side+1)/2), math.floor((side-1)/2)\r
-               for i = 1, side do\r
-                       for j = 1, side do\r
-                               local id = side^2 - sindex(stop - i + 1,start + j - 1)\r
-                               ret[id] = {x=i,z=j}\r
-                       end\r
-               end\r
-               return ret\r
-       end\r
-       -- connect the joined parts\r
-       local spiral = spiralt(width)\r
-       height = tonumber(height)\r
-       if height < 1 then height = 1 end\r
-       spacer = tonumber(spacer)-1\r
-       if spacer < 1 then spacer = 1 end\r
-       local count = 0\r
-       local node = {name=nodename}\r
-       local np,lp\r
-       for y=0,height do\r
-               lp = nil\r
-               for _,v in ipairs(spiral) do\r
-                       np = {x=pos.x+v.x*spacer, y=pos.y+y, z=pos.z+v.z*spacer}\r
-                       if lp~=nil then\r
-                               if lp.x~=np.x then \r
-                                       if lp.x<np.x then \r
-                                               for i=lp.x+1,np.x do\r
-                                                       minetest.env:add_node({x=i, y=np.y, z=np.z}, node)\r
-                                                       count = count + 1\r
-                                               end\r
-                                       else\r
-                                               for i=np.x,lp.x-1 do\r
-                                                       minetest.env:add_node({x=i, y=np.y, z=np.z}, node)\r
-                                                       count = count + 1\r
-                                               end\r
-                                       end\r
-                               end\r
-                               if lp.z~=np.z then \r
-                                       if lp.z<np.z then \r
-                                               for i=lp.z+1,np.z do\r
-                                                       minetest.env:add_node({x=np.x, y=np.y, z=i}, node)\r
-                                                       count = count + 1\r
-                                               end\r
-                                       else\r
-                                               for i=np.z,lp.z-1 do\r
-                                                       minetest.env:add_node({x=np.x, y=np.y, z=i}, node)\r
-                                                       count = count + 1\r
-                                               end\r
-                                       end\r
-                               end\r
-                       end\r
-                       lp = np\r
-               end\r
-       end\r
-       return count\r
-end\r
-\r
---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\r
-worldedit.copy = function(pos1, pos2, axis, amount)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
-\r
-       if amount < 0 then\r
-               local pos = {x=pos1.x, y=0, z=0}\r
-               while pos.x <= pos2.x do\r
-                       pos.y = pos1.y\r
-                       while pos.y <= pos2.y do\r
-                               pos.z = pos1.z\r
-                               while pos.z <= pos2.z do\r
-                                       local node = env:get_node(pos) --obtain current node\r
-                                       local meta = env:get_meta(pos):to_table() --get meta of current node\r
-                                       local value = pos[axis] --store current position\r
-                                       pos[axis] = value + amount --move along axis\r
-                                       env:add_node(pos, node) --copy node to new position\r
-                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
-                                       pos[axis] = value --restore old position\r
-                                       pos.z = pos.z + 1\r
-                               end\r
-                               pos.y = pos.y + 1\r
-                       end\r
-                       pos.x = pos.x + 1\r
-               end\r
-       else\r
-               local pos = {x=pos2.x, y=0, z=0}\r
-               while pos.x >= pos1.x do\r
-                       pos.y = pos2.y\r
-                       while pos.y >= pos1.y do\r
-                               pos.z = pos2.z\r
-                               while pos.z >= pos1.z do\r
-                                       local node = minetest.env:get_node(pos) --obtain current node\r
-                                       local meta = env:get_meta(pos):to_table() --get meta of current node\r
-                                       local value = pos[axis] --store current position\r
-                                       pos[axis] = value + amount --move along axis\r
-                                       minetest.env:add_node(pos, node) --copy node to new position\r
-                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
-                                       pos[axis] = value --restore old position\r
-                                       pos.z = pos.z - 1\r
-                               end\r
-                               pos.y = pos.y - 1\r
-                       end\r
-                       pos.x = pos.x - 1\r
-               end\r
-       end\r
-       return worldedit.volume(pos1, pos2)\r
-end\r
-\r
---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\r
-worldedit.move = function(pos1, pos2, axis, amount)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
-\r
-       if amount < 0 then\r
-               local pos = {x=pos1.x, y=0, z=0}\r
-               while pos.x <= pos2.x do\r
-                       pos.y = pos1.y\r
-                       while pos.y <= pos2.y do\r
-                               pos.z = pos1.z\r
-                               while pos.z <= pos2.z do\r
-                                       local node = env:get_node(pos) --obtain current node\r
-                                       local meta = env:get_meta(pos):to_table() --get metadata of current node\r
-                                       env:remove_node(pos)\r
-                                       local value = pos[axis] --store current position\r
-                                       pos[axis] = value + amount --move along axis\r
-                                       env:add_node(pos, node) --move node to new position\r
-                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
-                                       pos[axis] = value --restore old position\r
-                                       pos.z = pos.z + 1\r
-                               end\r
-                               pos.y = pos.y + 1\r
-                       end\r
-                       pos.x = pos.x + 1\r
-               end\r
-       else\r
-               local pos = {x=pos2.x, y=0, z=0}\r
-               while pos.x >= pos1.x do\r
-                       pos.y = pos2.y\r
-                       while pos.y >= pos1.y do\r
-                               pos.z = pos2.z\r
-                               while pos.z >= pos1.z do\r
-                                       local node = env:get_node(pos) --obtain current node\r
-                                       local meta = env:get_meta(pos):to_table() --get metadata of current node\r
-                                       env:remove_node(pos)\r
-                                       local value = pos[axis] --store current position\r
-                                       pos[axis] = value + amount --move along axis\r
-                                       env:add_node(pos, node) --move node to new position\r
-                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
-                                       pos[axis] = value --restore old position\r
-                                       pos.z = pos.z - 1\r
-                               end\r
-                               pos.y = pos.y - 1\r
-                       end\r
-                       pos.x = pos.x - 1\r
-               end\r
-       end\r
-       return worldedit.volume(pos1, pos2)\r
-end\r
-\r
---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\r
-worldedit.stack = function(pos1, pos2, axis, count)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local length = pos2[axis] - pos1[axis] + 1\r
-       if count < 0 then\r
-               count = -count\r
-               length = -length\r
-       end\r
-       local amount = 0\r
-       local copy = worldedit.copy\r
-       for i = 1, count do\r
-               amount = amount + length\r
-               copy(pos1, pos2, axis, amount)\r
-       end\r
-       return worldedit.volume(pos1, pos2)\r
-end\r
-\r
---transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed\r
-worldedit.transpose = function(pos1, pos2, axis1, axis2)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-\r
-       local pos = {x=pos1.x, y=0, z=0}\r
-       local env = minetest.env\r
-       while pos.x <= pos2.x do\r
-               pos.y = pos1.y\r
-               while pos.y <= pos2.y do\r
-                       pos.z = pos1.z\r
-                       while pos.z <= pos2.z do\r
-                               local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]\r
-                               if extent1 < extent2 then\r
-                                       local node1 = env:get_node(pos)\r
-                                       local meta1 = env:get_meta(pos):to_table()\r
-                                       local value1, value2 = pos[axis1], pos[axis2]\r
-                                       pos[axis1], pos[axis2] = value1 + extent2, value2 + extent1\r
-                                       local node2 = env:get_node(pos)\r
-                                       local meta2 = env:get_meta(pos):to_table()\r
-                                       env:add_node(pos, node1)\r
-                                       env:get_meta(pos):from_table(meta1)\r
-                                       pos[axis1], pos[axis2] = value1, value2\r
-                                       env:add_node(pos, node2)\r
-                                       env:get_meta(pos):from_table(meta2)\r
-                               end\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.y = pos.y + 1\r
-               end\r
-               pos.x = pos.x + 1\r
-       end\r
-       return worldedit.volume(pos1, pos2)\r
-end\r
-\r
---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\r
-worldedit.flip = function(pos1, pos2, axis)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-\r
-       local pos = {x=pos1.x, y=0, z=0}\r
-       local start = pos1[axis] + pos2[axis]\r
-       pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)\r
-       local env = minetest.env\r
-       while pos.x <= pos2.x do\r
-               pos.y = pos1.y\r
-               while pos.y <= pos2.y do\r
-                       pos.z = pos1.z\r
-                       while pos.z <= pos2.z do\r
-                               local node1 = env:get_node(pos)\r
-                               local meta1 = env:get_meta(pos):to_table()\r
-                               local value = pos[axis]\r
-                               pos[axis] = start - value\r
-                               local node2 = env:get_node(pos)\r
-                               local meta2 = env:get_meta(pos):to_table()\r
-                               env:add_node(pos, node1)\r
-                               env:get_meta(pos):from_table(meta1)\r
-                               pos[axis] = value\r
-                               env:add_node(pos, node2)\r
-                               env:get_meta(pos):from_table(meta2)\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.y = pos.y + 1\r
-               end\r
-               pos.x = pos.x + 1\r
-       end\r
-       return worldedit.volume(pos1, pos2)\r
-end\r
-\r
---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\r
-worldedit.rotate = function(pos1, pos2, axis, angle)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-\r
-       local axis1, axis2\r
-       if axis == "x" then\r
-               axis1, axis2 = "z", "y"\r
-       elseif axis == "y" then\r
-               axis1, axis2 = "x", "z"\r
-       else --axis == "z"\r
-               axis1, axis2 = "y", "x"\r
-       end\r
-       angle = angle % 360\r
-\r
-       if angle == 90 then\r
-               worldedit.transpose(pos1, pos2, axis1, axis2)\r
-               worldedit.flip(pos1, pos2, axis2)\r
-       elseif angle == 180 then\r
-               worldedit.flip(pos1, pos2, axis1)\r
-               worldedit.flip(pos1, pos2, axis2)\r
-       elseif angle == 270 then\r
-               worldedit.transpose(pos1, pos2, axis1, axis2)\r
-               worldedit.flip(pos1, pos2, axis1)\r
-       end\r
-       return worldedit.volume(pos1, pos2)\r
-end\r
-\r
---digs a region defined by positions `pos1` and `pos2`, returning the number of nodes dug\r
-worldedit.dig = function(pos1, pos2)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local env = minetest.env\r
-\r
-       local pos = {x=pos1.x, y=0, z=0}\r
-       while pos.x <= pos2.x do\r
-               pos.y = pos1.y\r
-               while pos.y <= pos2.y do\r
-                       pos.z = pos1.z\r
-                       while pos.z <= pos2.z do\r
-                               env:dig_node(pos)\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.y = pos.y + 1\r
-               end\r
-               pos.x = pos.x + 1\r
-       end\r
-       return worldedit.volume(pos1, pos2)\r
-end\r
-\r
---converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized\r
-worldedit.serialize = function(pos1, pos2)\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local pos = {x=pos1.x, y=0, z=0}\r
-       local count = 0\r
-       local result = {}\r
-       local env = minetest.env\r
-       while pos.x <= pos2.x do\r
-               pos.y = pos1.y\r
-               while pos.y <= pos2.y do\r
-                       pos.z = pos1.z\r
-                       while pos.z <= pos2.z do\r
-                               local node = env:get_node(pos)\r
-                               if node.name ~= "air" and node.name ~= "ignore" then\r
-                                       count = count + 1\r
-                                       result[count] = pos.x - pos1.x .. " " .. pos.y - pos1.y .. " " .. pos.z - pos1.z .. " " .. node.name .. " " .. node.param1 .. " " .. node.param2\r
-                               end\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.y = pos.y + 1\r
-               end\r
-               pos.x = pos.x + 1\r
-       end\r
-       result = table.concat(result, "\n")\r
-       return result, count\r
-end\r
-\r
---loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized\r
-worldedit.deserialize = function(originpos, value)\r
-       local pos = {x=0, y=0, z=0}\r
-       local node = {name="", param1=0, param2=0}\r
-       local count = 0\r
-       local env = minetest.env\r
-       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\r
-               pos.x = originpos.x + tonumber(x)\r
-               pos.y = originpos.y + tonumber(y)\r
-               pos.z = originpos.z + tonumber(z)\r
-               node.name = name\r
-               node.param1 = param1\r
-               node.param2 = param2\r
-               env:add_node(pos, node)\r
-               count = count + 1\r
-       end\r
-       return count\r
-end\r
-\r
---loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized\r
---based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)\r
-worldedit.deserialize_old = function(originpos, value)\r
-       --obtain the node table\r
-       local count = 0\r
-       local get_tables = loadstring(value)\r
-       if get_tables == nil then --error loading value\r
-               return count\r
-       end\r
-       local tables = get_tables()\r
-\r
-       --transform the node table into an array of nodes\r
-       for i = 1, #tables do\r
-               for j, v in pairs(tables[i]) do\r
-                       if type(v) == "table" then\r
-                               tables[i][j] = tables[v[1]]\r
-                       end\r
-               end\r
-       end\r
-\r
-       --load the node array\r
-       local env = minetest.env\r
-       for i, v in ipairs(tables[1]) do\r
-               local pos = v[1]\r
-               pos.x, pos.y, pos.z = originpos.x + pos.x, originpos.y + pos.y, originpos.z + pos.z\r
-               env:add_node(pos, v[2])\r
-               count = count + 1\r
-       end\r
-       return count\r
-end\r
-\r
---saves the nodes and meta defined by positions `pos1` and `pos2` into a file, returning the number of nodes saved\r
-worldedit.metasave = function(pos1, pos2, file) --wip: simply work with strings instead of doing IO\r
-       local path = minetest.get_worldpath() .. "/schems"\r
-       local filename = path .. "/" .. file .. ".wem"\r
-       os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist\r
-       local rows = {}\r
-       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
-       local pos = {x=pos1.x, y=0, z=0}\r
-       local count = 0\r
-       local result = {}\r
-       local env = minetest.env\r
-       while pos.x <= pos2.x do\r
-               pos.y = pos1.y\r
-               while pos.y <= pos2.y do\r
-                       pos.z = pos1.z\r
-                       while pos.z <= pos2.z do\r
-                               local node = env:get_node(pos)\r
-                               if node.name ~= "air" and node.name ~= "ignore" then\r
-                                       count = count + 1\r
-                                       local row = {\r
-                                               x = pos.x-pos1.x,\r
-                                               y = pos.y-pos1.y,\r
-                                               z = pos.z-pos1.z,\r
-                                               name = node.name,\r
-                                               param1 = node.param1,\r
-                                               param2 = node.param2,\r
-                                               meta = env:get_meta(pos):to_table(),\r
-                                       }\r
-                                       table.insert(rows, row)\r
-                               end\r
-                               pos.z = pos.z + 1\r
-                       end\r
-                       pos.y = pos.y + 1\r
-               end\r
-               pos.x = pos.x + 1\r
-       end\r
-       local err = table.save(rows,filename)\r
-       if err then return _,err end\r
-       return count\r
-end\r
-\r
---loads the nodes and meta from `file` to position `pos1`, returning the number of nodes loaded\r
-worldedit.metaload = function(pos1, file) --wip: simply work with strings instead of doing IO\r
-       local filename = minetest.get_worldpath() .. "/schems/" .. file .. ".wem"\r
-       local rows, err = table.load(filename)\r
-       if err then return _,err end\r
-       local pos = {x=0, y=0, z=0}\r
-       local node = {name="", param1=0, param2=0}\r
-       local count = 0\r
-       local env = minetest.env\r
-       for i,row in pairs(rows) do\r
-               pos.x = pos1.x + tonumber(row.x)\r
-               pos.y = pos1.y + tonumber(row.y)\r
-               pos.z = pos1.z + tonumber(row.z)\r
-               node.name = row.name\r
-               node.param1 = row.param1\r
-               node.param2 = row.param2\r
-               env:add_node(pos, node)\r
-               env:get_meta(pos):from_table(row.meta)\r
-               count = count + 1\r
-       end\r
-       return count\r
-end
\ No newline at end of file
index 84cf4fe6880862994b9c4d571a198deb0b9301b2..6f841eb42fc52d13e75488f12e02ab15b369a70a 100644 (file)
@@ -1,655 +1,4 @@
-minetest.register_privilege("worldedit", "Can use WorldEdit commands")\r
-\r
-worldedit = {}\r
-\r
-worldedit.set_pos = {}\r
-\r
-worldedit.pos1 = {}\r
-worldedit.pos2 = {}\r
-\r
-dofile(minetest.get_modpath("worldedit") .. "/functions.lua")\r
-dofile(minetest.get_modpath("worldedit") .. "/mark.lua")\r
-\r
---determines whether `nodename` is a valid node name, returning a boolean\r
-worldedit.node_is_valid = function(temp_pos, nodename)\r
-       return minetest.registered_nodes[nodename] ~= nil\r
-       or minetest.registered_nodes["default:" .. nodename] ~= nil\r
-end\r
-\r
---determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)\r
-worldedit.player_axis = function(name)\r
-       local dir = minetest.env:get_player_by_name(name):get_look_dir()\r
-       local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)\r
-       if x > y then\r
-               if x > z then\r
-                       return "x", dir.x > 0 and 1 or -1\r
-               end\r
-       elseif y > z then\r
-               return "y", dir.y > 0 and 1 or -1\r
-       end\r
-       return "z", dir.z > 0 and 1 or -1\r
-end\r
-\r
-minetest.register_chatcommand("/reset", {\r
-       params = "",\r
-       description = "Reset the region so that it is empty",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               worldedit.pos1[name] = nil\r
-               worldedit.pos2[name] = nil\r
-               worldedit.mark_pos1(name)\r
-               worldedit.mark_pos2(name)\r
-               minetest.chat_send_player(name, "WorldEdit region reset")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/mark", {\r
-       params = "",\r
-       description = "Show markers at the region positions",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               worldedit.mark_pos1(name)\r
-               worldedit.mark_pos2(name)\r
-               minetest.chat_send_player(name, "WorldEdit region marked")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/pos1", {\r
-       params = "",\r
-       description = "Set WorldEdit region position 1 to the player's location",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos = minetest.env:get_player_by_name(name):getpos()\r
-               pos.x, pos.y, pos.z = math.floor(pos.x), math.floor(pos.y), math.floor(pos.z)\r
-               worldedit.pos1[name] = pos\r
-               worldedit.mark_pos1(name)\r
-               minetest.chat_send_player(name, "WorldEdit position 1 set to " .. minetest.pos_to_string(pos))\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/pos2", {\r
-       params = "",\r
-       description = "Set WorldEdit region position 2 to the player's location",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos = minetest.env:get_player_by_name(name):getpos()\r
-               pos.x, pos.y, pos.z = math.floor(pos.x), math.floor(pos.y), math.floor(pos.z)\r
-               worldedit.pos2[name] = pos\r
-               worldedit.mark_pos2(name)\r
-               minetest.chat_send_player(name, "WorldEdit position 2 set to " .. minetest.pos_to_string(pos))\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/p", {\r
-       params = "set/get",\r
-       description = "Set WorldEdit region by punching two nodes, or display the current WorldEdit region",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               if param == "set" then --set both WorldEdit positions\r
-                       worldedit.set_pos[name] = 1\r
-                       minetest.chat_send_player(name, "Select positions by punching two nodes")\r
-               elseif param == "get" then --display current WorldEdit positions\r
-                       if worldedit.pos1[name] ~= nil then\r
-                               minetest.chat_send_player(name, "WorldEdit position 1: " .. minetest.pos_to_string(worldedit.pos1[name]))\r
-                       else\r
-                               minetest.chat_send_player(name, "WorldEdit position 1 not set")\r
-                       end\r
-                       if worldedit.pos2[name] ~= nil then\r
-                               minetest.chat_send_player(name, "WorldEdit position 2: " .. minetest.pos_to_string(worldedit.pos2[name]))\r
-                       else\r
-                               minetest.chat_send_player(name, "WorldEdit position 2 not set")\r
-                       end\r
-               else\r
-                       minetest.chat_send_player(name, "Unknown subcommand: " .. param)\r
-               end\r
-       end,\r
-})\r
-\r
-minetest.register_on_punchnode(function(pos, node, puncher)\r
-       local name = puncher:get_player_name()\r
-       if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position\r
-               if worldedit.set_pos[name] == 1 then --setting position 1\r
-                       worldedit.set_pos[name] = 2 --set position 2 on the next invocation\r
-                       worldedit.pos1[name] = pos\r
-                       worldedit.mark_pos1(name)\r
-                       minetest.chat_send_player(name, "WorldEdit region position 1 set to " .. minetest.pos_to_string(pos))\r
-               else --setting position 2\r
-                       worldedit.set_pos[name] = nil --finished setting positions\r
-                       worldedit.pos2[name] = pos\r
-                       worldedit.mark_pos2(name)\r
-                       minetest.chat_send_player(name, "WorldEdit region position 2 set to " .. minetest.pos_to_string(pos))\r
-               end\r
-       end\r
-end)\r
-\r
-minetest.register_chatcommand("/volume", {\r
-       params = "",\r
-       description = "Display the volume of the current WorldEdit region",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local volume = worldedit.volume(pos1, pos2)\r
-               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 .. ")")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/set", {\r
-       params = "<node>",\r
-       description = "Set the current WorldEdit region to <node>",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               if param == "" or not worldedit.node_is_valid(pos1, param) then\r
-                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.set(pos1, pos2, param)\r
-               minetest.chat_send_player(name, count .. " nodes set")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/replace", {\r
-       params = "<search node> <replace node>",\r
-       description = "Replace all instances of <search node> with <place node> in the current WorldEdit region",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+([^%s]+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if not worldedit.node_is_valid(pos1, searchnode) then\r
-                       minetest.chat_send_player(name, "Invalid search node name: " .. searchnode)\r
-                       return\r
-               end\r
-               if not worldedit.node_is_valid(pos1, replacenode) then\r
-                       minetest.chat_send_player(name, "Invalid replace node name: " .. replacenode)\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.replace(pos1, pos2, searchnode, replacenode)\r
-               minetest.chat_send_player(name, count .. " nodes replaced")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/hollowsphere", {\r
-       params = "<radius> <node>",\r
-       description = "Add hollow sphere at WorldEdit position 1 with radius <radius>, composed of <node>",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos = worldedit.pos1[name]\r
-               if pos == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, radius, nodename = param:find("^(%d+)%s+([^%s]+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if not worldedit.node_is_valid(pos, nodename) then\r
-                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.hollow_sphere(pos, tonumber(radius), nodename)\r
-               minetest.chat_send_player(name, count .. " nodes added")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/sphere", {\r
-       params = "<radius> <node>",\r
-       description = "Add sphere at WorldEdit position 1 with radius <radius>, composed of <node>",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos = worldedit.pos1[name]\r
-               if pos == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, radius, nodename = param:find("^(%d+)%s+([^%s]+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if not worldedit.node_is_valid(pos, nodename) then\r
-                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.sphere(pos, tonumber(radius), nodename)\r
-               minetest.chat_send_player(name, count .. " nodes added")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/hollowcylinder", {\r
-       params = "x/y/z/? <length> <radius> <node>",\r
-       description = "Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos = worldedit.pos1[name]\r
-               if pos == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+([^%s]+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if axis == "?" then\r
-                       axis, sign = worldedit.player_axis(name)\r
-                       length = length * sign\r
-               end\r
-               if not worldedit.node_is_valid(pos, nodename) then\r
-                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.hollow_cylinder(pos, axis, tonumber(length), tonumber(radius), nodename)\r
-               minetest.chat_send_player(name, count .. " nodes added")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/cylinder", {\r
-       params = "x/y/z/? <length> <radius> <node>",\r
-       description = "Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos = worldedit.pos1[name]\r
-               if pos == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+([^%s]+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if axis == "?" then\r
-                       axis, sign = worldedit.player_axis(name)\r
-                       length = length * sign\r
-               end\r
-               if not worldedit.node_is_valid(pos, nodename) then\r
-                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.cylinder(pos, axis, tonumber(length), tonumber(radius), nodename)\r
-               minetest.chat_send_player(name, count .. " nodes added")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/pyramid", {\r
-       params = "<height> <node>",\r
-       description = "Add pyramid at WorldEdit position 1 with height <height>, composed of <node>",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos = worldedit.pos1[name]\r
-               if pos == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, size, nodename = param:find("(%d+)%s+([^%s]+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if not worldedit.node_is_valid(pos, nodename) then\r
-                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.pyramid(pos, tonumber(size), nodename)\r
-               minetest.chat_send_player(name, count .. " nodes added")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/spiral", {\r
-       params = "<width> <height> <space> <node>",\r
-       description = "Add spiral at WorldEdit position 1 with width <width>, height <height>, space between walls <space>, composed of <node>",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos = worldedit.pos1[name]\r
-               if pos == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, width, height, space, nodename = param:find("(%d+)%s+(%d+)%s+(%d+)%s+([^%s]+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if not worldedit.node_is_valid(pos, nodename) then\r
-                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.spiral(pos, tonumber(width), tonumber(height), tonumber(space), nodename)\r
-               minetest.chat_send_player(name, count .. " nodes changed")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/copy", {\r
-       params = "x/y/z/? <amount>",\r
-       description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if axis == "?" then\r
-                       axis, sign = worldedit.player_axis(name)\r
-                       amount = amount * sign\r
-               end\r
-\r
-               local count = worldedit.copy(pos1, pos2, axis, tonumber(amount))\r
-               minetest.chat_send_player(name, count .. " nodes copied")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/move", {\r
-       params = "x/y/z/? <amount>",\r
-       description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if axis == "?" then\r
-                       axis, sign = worldedit.player_axis(name)\r
-                       amount = amount * sign\r
-               end\r
-\r
-               local count = worldedit.move(pos1, pos2, axis, tonumber(amount))\r
-\r
-               pos1[axis] = pos1[axis] + amount\r
-               pos2[axis] = pos2[axis] + amount\r
-               worldedit.mark_pos1(name)\r
-               worldedit.mark_pos2(name)\r
-\r
-               minetest.chat_send_player(name, count .. " nodes moved")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/stack", {\r
-       params = "x/y/z/? <count>",\r
-       description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, axis, count = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if axis == "?" then\r
-                       axis, sign = worldedit.player_axis(name)\r
-                       count = count * sign\r
-               end\r
-\r
-               local count = worldedit.stack(pos1, pos2, axis, tonumber(count))\r
-               minetest.chat_send_player(name, count .. " nodes stacked")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/transpose", {\r
-       params = "x/y/z/? x/y/z/?",\r
-       description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if axis1 == "?" then\r
-                       axis1 = worldedit.player_axis(name)\r
-               end\r
-               if axis2 == "?" then\r
-                       axis2 = worldedit.player_axis(name)\r
-               end\r
-               if axis1 == axis2 then\r
-                       minetest.chat_send_player(name, "Invalid usage: axes are the same")\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.transpose(pos1, pos2, axis1, axis2)\r
-               minetest.chat_send_player(name, count .. " nodes transposed")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/flip", {\r
-       params = "x/y/z/?",\r
-       description = "Flip the current WorldEdit region along the x/y/z/? axis",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               if param == "?" then\r
-                       param = worldedit.player_axis(name)\r
-               end\r
-               if param ~= "x" and param ~= "y" and param ~= "z" then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.flip(pos1, pos2, param)\r
-               minetest.chat_send_player(name, count .. " nodes flipped")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/rotate", {\r
-       params = "<axis> <angle>",\r
-       description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
-               if found == nil then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               if axis == "?" then\r
-                       axis = worldedit.player_axis(name)\r
-               end\r
-               if angle % 90 ~= 0 then\r
-                       minetest.chat_send_player(name, "Invalid usage: angle must be multiple of 90")\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.rotate(pos1, pos2, axis, angle)\r
-               minetest.chat_send_player(name, count .. " nodes rotated")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/dig", {\r
-       params = "",\r
-       description = "Dig the current WorldEdit region",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               local count = worldedit.dig(pos1, pos2)\r
-               minetest.chat_send_player(name, count .. " nodes dug")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/save", {\r
-       params = "<file>",\r
-       description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               if param == "" then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-\r
-               local result, count = worldedit.serialize(pos1, pos2)\r
-\r
-               local path = minetest.get_worldpath() .. "/schems"\r
-               local filename = path .. "/" .. param .. ".we"\r
-               os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist\r
-               local file, err = io.open(filename, "wb")\r
-               if err ~= nil then\r
-                       minetest.chat_send_player(name, "Could not save file to \"" .. filename .. "\"")\r
-                       return\r
-               end\r
-               file:write(result)\r
-               file:flush()\r
-               file:close()\r
-\r
-               minetest.chat_send_player(name, count .. " nodes saved")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/load", {\r
-       params = "<file>",\r
-       description = "Load nodes from \"(world folder)/schems/<file>.we\" with position 1 of the current WorldEdit region as the origin",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1 = worldedit.pos1[name]\r
-               if pos1 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-\r
-               if param == "" then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-\r
-               local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"\r
-               local file, err = io.open(filename, "rb")\r
-               if err ~= nil then\r
-                       minetest.chat_send_player(name, "Could not open file \"" .. filename .. "\"")\r
-                       return\r
-               end\r
-               local value = file:read("*a")\r
-               file:close()\r
-\r
-               local count\r
-               if value:find("{") then --old WorldEdit format\r
-                       count = worldedit.deserialize_old(pos1, value)\r
-               else --new WorldEdit format\r
-                       count = worldedit.deserialize(pos1, value)\r
-               end\r
-\r
-               minetest.chat_send_player(name, count .. " nodes loaded")\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/metasave", {\r
-       params = "<file>",\r
-       description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.wem\"",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
-               if pos1 == nil or pos2 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-               if param == "" then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               local count, err = worldedit.metasave(pos1, pos2, param)\r
-               if err then\r
-                       minetest.chat_send_player(name, "error loading file: " .. err)\r
-               else\r
-                       minetest.chat_send_player(name, count .. " nodes saved")\r
-               end\r
-       end,\r
-})\r
-\r
-minetest.register_chatcommand("/metaload", {\r
-       params = "<file>",\r
-       description = "Load nodes from \"(world folder)/schems/<file>.wem\" with position 1 of the current WorldEdit region as the origin",\r
-       privs = {worldedit=true},\r
-       func = function(name, param)\r
-               local pos1 = worldedit.pos1[name]\r
-               if pos1 == nil then\r
-                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
-                       return\r
-               end\r
-               if param == "" then\r
-                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
-                       return\r
-               end\r
-               local count, err = worldedit.metaload(pos1, param)\r
-               if err then\r
-                       minetest.chat_send_player(name, "error loading file: " .. err)\r
-               else\r
-                       minetest.chat_send_player(name, count .. " nodes loaded")\r
-               end\r
-       end,\r
-})\r
+dofile(minetest.get_modpath("worldedit") .. "/manipulations.lua")\r
+dofile(minetest.get_modpath("worldedit") .. "/primitives.lua")\r
+dofile(minetest.get_modpath("worldedit") .. "/visualization.lua")\r
+dofile(minetest.get_modpath("worldedit") .. "/serialization.lua")
\ No newline at end of file
diff --git a/worldedit/manipulations.lua b/worldedit/manipulations.lua
new file mode 100644 (file)
index 0000000..ad64df6
--- /dev/null
@@ -0,0 +1,307 @@
+worldedit = worldedit or {}\r
+\r
+--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\r
+worldedit.sort_pos = function(pos1, pos2)\r
+       pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}\r
+       pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}\r
+       if pos1.x > pos2.x then\r
+               pos2.x, pos1.x = pos1.x, pos2.x\r
+       end\r
+       if pos1.y > pos2.y then\r
+               pos2.y, pos1.y = pos1.y, pos2.y\r
+       end\r
+       if pos1.z > pos2.z then\r
+               pos2.z, pos1.z = pos1.z, pos2.z\r
+       end\r
+       return pos1, pos2\r
+end\r
+\r
+--determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume\r
+worldedit.volume = function(pos1, pos2)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)\r
+end\r
+\r
+--sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled\r
+worldedit.set = function(pos1, pos2, nodename)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local env = minetest.env\r
+\r
+       local node = {name=nodename}\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               env:add_node(pos, node)\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
+--replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced\r
+worldedit.replace = function(pos1, pos2, searchnode, replacenode)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local env = minetest.env\r
+\r
+       if minetest.registered_nodes[searchnode] == nil then\r
+               searchnode = "default:" .. searchnode\r
+       end\r
+\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local node = {name=replacenode}\r
+       local count = 0\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               if env:get_node(pos).name == searchnode then\r
+                                       env:add_node(pos, node)\r
+                                       count = count + 1\r
+                               end\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       return count\r
+end\r
+\r
+--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\r
+worldedit.copy = function(pos1, pos2, axis, amount)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local env = minetest.env\r
+\r
+       if amount < 0 then\r
+               local pos = {x=pos1.x, y=0, z=0}\r
+               while pos.x <= pos2.x do\r
+                       pos.y = pos1.y\r
+                       while pos.y <= pos2.y do\r
+                               pos.z = pos1.z\r
+                               while pos.z <= pos2.z do\r
+                                       local node = env:get_node(pos) --obtain current node\r
+                                       local meta = env:get_meta(pos):to_table() --get meta of current node\r
+                                       local value = pos[axis] --store current position\r
+                                       pos[axis] = value + amount --move along axis\r
+                                       env:add_node(pos, node) --copy node to new position\r
+                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
+                                       pos[axis] = value --restore old position\r
+                                       pos.z = pos.z + 1\r
+                               end\r
+                               pos.y = pos.y + 1\r
+                       end\r
+                       pos.x = pos.x + 1\r
+               end\r
+       else\r
+               local pos = {x=pos2.x, y=0, z=0}\r
+               while pos.x >= pos1.x do\r
+                       pos.y = pos2.y\r
+                       while pos.y >= pos1.y do\r
+                               pos.z = pos2.z\r
+                               while pos.z >= pos1.z do\r
+                                       local node = minetest.env:get_node(pos) --obtain current node\r
+                                       local meta = env:get_meta(pos):to_table() --get meta of current node\r
+                                       local value = pos[axis] --store current position\r
+                                       pos[axis] = value + amount --move along axis\r
+                                       minetest.env:add_node(pos, node) --copy node to new position\r
+                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
+                                       pos[axis] = value --restore old position\r
+                                       pos.z = pos.z - 1\r
+                               end\r
+                               pos.y = pos.y - 1\r
+                       end\r
+                       pos.x = pos.x - 1\r
+               end\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
+--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\r
+worldedit.move = function(pos1, pos2, axis, amount)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local env = minetest.env\r
+\r
+       if amount < 0 then\r
+               local pos = {x=pos1.x, y=0, z=0}\r
+               while pos.x <= pos2.x do\r
+                       pos.y = pos1.y\r
+                       while pos.y <= pos2.y do\r
+                               pos.z = pos1.z\r
+                               while pos.z <= pos2.z do\r
+                                       local node = env:get_node(pos) --obtain current node\r
+                                       local meta = env:get_meta(pos):to_table() --get metadata of current node\r
+                                       env:remove_node(pos)\r
+                                       local value = pos[axis] --store current position\r
+                                       pos[axis] = value + amount --move along axis\r
+                                       env:add_node(pos, node) --move node to new position\r
+                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
+                                       pos[axis] = value --restore old position\r
+                                       pos.z = pos.z + 1\r
+                               end\r
+                               pos.y = pos.y + 1\r
+                       end\r
+                       pos.x = pos.x + 1\r
+               end\r
+       else\r
+               local pos = {x=pos2.x, y=0, z=0}\r
+               while pos.x >= pos1.x do\r
+                       pos.y = pos2.y\r
+                       while pos.y >= pos1.y do\r
+                               pos.z = pos2.z\r
+                               while pos.z >= pos1.z do\r
+                                       local node = env:get_node(pos) --obtain current node\r
+                                       local meta = env:get_meta(pos):to_table() --get metadata of current node\r
+                                       env:remove_node(pos)\r
+                                       local value = pos[axis] --store current position\r
+                                       pos[axis] = value + amount --move along axis\r
+                                       env:add_node(pos, node) --move node to new position\r
+                                       env:get_meta(pos):from_table(meta) --set metadata of new node\r
+                                       pos[axis] = value --restore old position\r
+                                       pos.z = pos.z - 1\r
+                               end\r
+                               pos.y = pos.y - 1\r
+                       end\r
+                       pos.x = pos.x - 1\r
+               end\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
+--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\r
+worldedit.stack = function(pos1, pos2, axis, count)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local length = pos2[axis] - pos1[axis] + 1\r
+       if count < 0 then\r
+               count = -count\r
+               length = -length\r
+       end\r
+       local amount = 0\r
+       local copy = worldedit.copy\r
+       for i = 1, count do\r
+               amount = amount + length\r
+               copy(pos1, pos2, axis, amount)\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
+--transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed\r
+worldedit.transpose = function(pos1, pos2, axis1, axis2)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local env = minetest.env\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]\r
+                               if extent1 < extent2 then\r
+                                       local node1 = env:get_node(pos)\r
+                                       local meta1 = env:get_meta(pos):to_table()\r
+                                       local value1, value2 = pos[axis1], pos[axis2]\r
+                                       pos[axis1], pos[axis2] = value1 + extent2, value2 + extent1\r
+                                       local node2 = env:get_node(pos)\r
+                                       local meta2 = env:get_meta(pos):to_table()\r
+                                       env:add_node(pos, node1)\r
+                                       env:get_meta(pos):from_table(meta1)\r
+                                       pos[axis1], pos[axis2] = value1, value2\r
+                                       env:add_node(pos, node2)\r
+                                       env:get_meta(pos):from_table(meta2)\r
+                               end\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
+--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\r
+worldedit.flip = function(pos1, pos2, axis)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local start = pos1[axis] + pos2[axis]\r
+       pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)\r
+       local env = minetest.env\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               local node1 = env:get_node(pos)\r
+                               local meta1 = env:get_meta(pos):to_table()\r
+                               local value = pos[axis]\r
+                               pos[axis] = start - value\r
+                               local node2 = env:get_node(pos)\r
+                               local meta2 = env:get_meta(pos):to_table()\r
+                               env:add_node(pos, node1)\r
+                               env:get_meta(pos):from_table(meta1)\r
+                               pos[axis] = value\r
+                               env:add_node(pos, node2)\r
+                               env:get_meta(pos):from_table(meta2)\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
+--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\r
+worldedit.rotate = function(pos1, pos2, axis, angle)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+\r
+       local axis1, axis2\r
+       if axis == "x" then\r
+               axis1, axis2 = "z", "y"\r
+       elseif axis == "y" then\r
+               axis1, axis2 = "x", "z"\r
+       else --axis == "z"\r
+               axis1, axis2 = "y", "x"\r
+       end\r
+       angle = angle % 360\r
+\r
+       if angle == 90 then\r
+               worldedit.transpose(pos1, pos2, axis1, axis2)\r
+               worldedit.flip(pos1, pos2, axis2)\r
+       elseif angle == 180 then\r
+               worldedit.flip(pos1, pos2, axis1)\r
+               worldedit.flip(pos1, pos2, axis2)\r
+       elseif angle == 270 then\r
+               worldedit.transpose(pos1, pos2, axis1, axis2)\r
+               worldedit.flip(pos1, pos2, axis1)\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
+--digs a region defined by positions `pos1` and `pos2`, returning the number of nodes dug\r
+worldedit.dig = function(pos1, pos2)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local env = minetest.env\r
+\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               env:dig_node(pos)\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end
\ No newline at end of file
diff --git a/worldedit/mark.lua b/worldedit/mark.lua
deleted file mode 100644 (file)
index 0f011d2..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-worldedit.marker1 = {}\r
-worldedit.marker2 = {}\r
-\r
---marks worldedit region position 1\r
-worldedit.mark_pos1 = function(name)\r
-       local pos = worldedit.pos1[name]\r
-       if worldedit.marker1[name] ~= nil then --marker already exists\r
-               worldedit.marker1[name]:remove() --remove marker\r
-               worldedit.marker1[name] = nil\r
-       end\r
-       if pos ~= nil then --add marker\r
-               worldedit.marker1[name] = minetest.env:add_entity(pos, "worldedit:pos1")\r
-               worldedit.marker1[name]:get_luaentity().active = true\r
-       end\r
-end\r
-\r
---marks worldedit region position 2\r
-worldedit.mark_pos2 = function(name)\r
-       local pos = worldedit.pos2[name]\r
-       if worldedit.marker2[name] ~= nil then --marker already exists\r
-               worldedit.marker2[name]:remove() --remove marker\r
-               worldedit.marker2[name] = nil\r
-       end\r
-       if pos ~= nil then --add marker\r
-               worldedit.marker2[name] = minetest.env:add_entity(pos, "worldedit:pos2")\r
-               worldedit.marker2[name]:get_luaentity().active = true\r
-       end\r
-end\r
-\r
-minetest.register_entity("worldedit:pos1", {\r
-       initial_properties = {\r
-               visual = "cube",\r
-               visual_size = {x=1.1, y=1.1},\r
-               textures = {"worldedit_pos1.png", "worldedit_pos1.png",\r
-                       "worldedit_pos1.png", "worldedit_pos1.png",\r
-                       "worldedit_pos1.png", "worldedit_pos1.png"},\r
-               collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},\r
-       },\r
-       on_step = function(self, dtime)\r
-               if self.active == nil then\r
-                       self.object:remove()\r
-               end\r
-       end,\r
-       on_punch = function(self, hitter)\r
-               self.object:remove()\r
-               local name = hitter:get_player_name()\r
-               worldedit.marker1[name] = nil\r
-       end,\r
-})\r
-\r
-minetest.register_entity("worldedit:pos2", {\r
-       initial_properties = {\r
-               visual = "cube",\r
-               visual_size = {x=1.1, y=1.1},\r
-               textures = {"worldedit_pos2.png", "worldedit_pos2.png",\r
-                       "worldedit_pos2.png", "worldedit_pos2.png",\r
-                       "worldedit_pos2.png", "worldedit_pos2.png"},\r
-               collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},\r
-       },\r
-       on_step = function(self, dtime)\r
-               if self.active == nil then\r
-                       self.object:remove()\r
-               end\r
-       end,\r
-       on_punch = function(self, hitter)\r
-               self.object:remove()\r
-               local name = hitter:get_player_name()\r
-               worldedit.marker2[name] = nil\r
-       end,\r
-})
\ No newline at end of file
diff --git a/worldedit/primitives.lua b/worldedit/primitives.lua
new file mode 100644 (file)
index 0000000..2685d2b
--- /dev/null
@@ -0,0 +1,259 @@
+worldedit = worldedit or {}\r
+\r
+--adds a hollow sphere at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
+worldedit.hollow_sphere = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
+       local node = {name=nodename}\r
+       local pos1 = {x=0, y=0, z=0}\r
+       local full_radius = radius * radius + radius\r
+       local env = minetest.env\r
+       for x = -radius, radius do\r
+               pos1.x = pos.x + x\r
+               for y = -radius, radius do\r
+                       pos1.y = pos.y + y\r
+                       for z = -radius, radius do\r
+                               if x*x+y*y+z*z >= (radius-1) * (radius-1) + (radius-1) and x*x+y*y+z*z <= full_radius then\r
+                                       pos1.z = pos.z + z\r
+                                       env:add_node({x=pos.x+x,y=pos.y+y,z=pos.z+z}, node)\r
+                               end\r
+                       end\r
+               end\r
+       end\r
+end\r
+\r
+--adds a sphere at `pos` with radius `radius`, composed of `nodename`, returning the number of nodes added\r
+worldedit.sphere = function(pos, radius, nodename) --wip: use bresenham sphere for maximum speed\r
+       local node = {name=nodename}\r
+       local pos1 = {x=0, y=0, z=0}\r
+       local full_radius = radius * radius + radius\r
+       local count = 0\r
+       local env = minetest.env\r
+       for x = -radius, radius do\r
+               pos1.x = pos.x + x\r
+               for y = -radius, radius do\r
+                       pos1.y = pos.y + y\r
+                       for z = -radius, radius do\r
+                               if x*x+y*y+z*z <= full_radius then\r
+                                       pos1.z = pos.z + z\r
+                                       env:add_node(pos1, node)\r
+                                       count = count + 1\r
+                               end\r
+                       end\r
+               end\r
+       end\r
+       return count\r
+end\r
+\r
+--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\r
+worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename)\r
+       local other1, other2\r
+       if axis == "x" then\r
+               other1, other2 = "y", "z"\r
+       elseif axis == "y" then\r
+               other1, other2 = "x", "z"\r
+       else --axis == "z"\r
+               other1, other2 = "x", "y"\r
+       end\r
+\r
+       local env = minetest.env\r
+       local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
+       local node = {name=nodename}\r
+       local count = 0\r
+       local step = 1\r
+       if length < 0 then\r
+               length = -length\r
+               step = -1\r
+       end\r
+       for i = 1, length do\r
+               local offset1, offset2 = 0, radius\r
+               local delta = -radius\r
+               while offset1 <= offset2 do\r
+                       --add node at each octant\r
+                       local first1, first2 = pos[other1] + offset1, pos[other1] - offset1\r
+                       local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
+                       currentpos[other1], currentpos[other2] = first1, second1\r
+                       env:add_node(currentpos, node) --octant 1\r
+                       currentpos[other1] = first2\r
+                       env:add_node(currentpos, node) --octant 4\r
+                       currentpos[other2] = second2\r
+                       env:add_node(currentpos, node) --octant 5\r
+                       currentpos[other1] = first1\r
+                       env:add_node(currentpos, node) --octant 8\r
+                       local first1, first2 = pos[other1] + offset2, pos[other1] - offset2\r
+                       local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
+                       currentpos[other1], currentpos[other2] = first1, second1\r
+                       env:add_node(currentpos, node) --octant 2\r
+                       currentpos[other1] = first2\r
+                       env:add_node(currentpos, node) --octant 3\r
+                       currentpos[other2] = second2\r
+                       env:add_node(currentpos, node) --octant 6\r
+                       currentpos[other1] = first1\r
+                       env:add_node(currentpos, node) --octant 7\r
+\r
+                       count = count + 8 --wip: broken\r
+\r
+                       --move to next location\r
+                       delta = delta + (offset1 * 2) + 1\r
+                       if delta >= 0 then\r
+                               offset2 = offset2 - 1\r
+                               delta = delta - (offset2 * 2)\r
+                       end\r
+                       offset1 = offset1 + 1\r
+               end\r
+               currentpos[axis] = currentpos[axis] + step\r
+       end\r
+       return count\r
+end\r
+\r
+--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\r
+worldedit.cylinder = function(pos, axis, length, radius, nodename)\r
+       local other1, other2\r
+       if axis == "x" then\r
+               other1, other2 = "y", "z"\r
+       elseif axis == "y" then\r
+               other1, other2 = "x", "z"\r
+       else --axis == "z"\r
+               other1, other2 = "x", "y"\r
+       end\r
+\r
+       local env = minetest.env\r
+       local currentpos = {x=pos.x, y=pos.y, z=pos.z}\r
+       local node = {name=nodename}\r
+       local count = 0\r
+       local step = 1\r
+       if length < 0 then\r
+               length = -length\r
+               step = -1\r
+       end\r
+       for i = 1, length do\r
+               local offset1, offset2 = 0, radius\r
+               local delta = -radius\r
+               while offset1 <= offset2 do\r
+                       --connect each pair of octants\r
+                       currentpos[other1] = pos[other1] - offset1\r
+                       local second1, second2 = pos[other2] + offset2, pos[other2] - offset2\r
+                       for i = 0, offset1 * 2 do\r
+                               currentpos[other2] = second1\r
+                               env:add_node(currentpos, node) --octant 1 to 4\r
+                               currentpos[other2] = second2\r
+                               env:add_node(currentpos, node) --octant 5 to 8\r
+                               currentpos[other1] = currentpos[other1] + 1\r
+                       end\r
+                       currentpos[other1] = pos[other1] - offset2\r
+                       local second1, second2 = pos[other2] + offset1, pos[other2] - offset1\r
+                       for i = 0, offset2 * 2 do\r
+                               currentpos[other2] = second1\r
+                               env:add_node(currentpos, node) --octant 2 to 3\r
+                               currentpos[other2] = second2\r
+                               env:add_node(currentpos, node) --octant 6 to 7\r
+                               currentpos[other1] = currentpos[other1] + 1\r
+                       end\r
+\r
+                       count = count + (offset1 * 4) + (offset2 * 4) + 4 --wip: broken\r
+\r
+                       --move to next location\r
+                       delta = delta + (offset1 * 2) + 1\r
+                       offset1 = offset1 + 1\r
+                       if delta >= 0 then\r
+                               offset2 = offset2 - 1\r
+                               delta = delta - (offset2 * 2)\r
+                       end\r
+               end\r
+               currentpos[axis] = currentpos[axis] + step\r
+       end\r
+       return count\r
+end\r
+\r
+--adds a pyramid at `pos` with height `height`, composed of `nodename`, returning the number of nodes added\r
+worldedit.pyramid = function(pos, height, nodename)\r
+       local pos1x, pos1y, pos1z = pos.x - height, pos.y, pos.z - height\r
+       local pos2x, pos2y, pos2z = pos.x + height, pos.y + height, pos.z + height\r
+       local pos = {x=0, y=pos1y, z=0}\r
+\r
+       local count = 0\r
+       local node = {name=nodename}\r
+       local env = minetest.env\r
+       while pos.y <= pos2y do --each vertical level of the pyramid\r
+               pos.x = pos1x\r
+               while pos.x <= pos2x do\r
+                       pos.z = pos1z\r
+                       while pos.z <= pos2z do\r
+                               env:add_node(pos, node)\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.x = pos.x + 1\r
+               end\r
+               count = count + ((pos2y - pos.y) * 2 + 1) ^ 2\r
+               pos.y = pos.y + 1\r
+\r
+               pos1x, pos2x = pos1x + 1, pos2x - 1\r
+               pos1z, pos2z = pos1z + 1, pos2z - 1\r
+\r
+       end\r
+       return count\r
+end\r
+\r
+--adds a spiral at `pos` with width `width`, height `height`, space between walls `spacer`, composed of `nodename`, returning the number of nodes added\r
+worldedit.spiral = function(pos, width, height, spacer, nodename) --wip: clean this up\r
+       -- spiral matrix - http://rosettacode.org/wiki/Spiral_matrix#Lua\r
+       av, sn = math.abs, function(s) return s~=0 and s/av(s) or 0 end\r
+       local function sindex(z, x) -- returns the value at (x, z) in a spiral that starts at 1 and goes outwards\r
+               if z == -x and z >= x then return (2*z+1)^2 end\r
+               local l = math.max(av(z), av(x))\r
+               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\r
+       end\r
+       local function spiralt(side)\r
+               local ret, id, start, stop = {}, 0, math.floor((-side+1)/2), math.floor((side-1)/2)\r
+               for i = 1, side do\r
+                       for j = 1, side do\r
+                               local id = side^2 - sindex(stop - i + 1,start + j - 1)\r
+                               ret[id] = {x=i,z=j}\r
+                       end\r
+               end\r
+               return ret\r
+       end\r
+       -- connect the joined parts\r
+       local spiral = spiralt(width)\r
+       height = tonumber(height)\r
+       if height < 1 then height = 1 end\r
+       spacer = tonumber(spacer)-1\r
+       if spacer < 1 then spacer = 1 end\r
+       local count = 0\r
+       local node = {name=nodename}\r
+       local np,lp\r
+       for y=0,height do\r
+               lp = nil\r
+               for _,v in ipairs(spiral) do\r
+                       np = {x=pos.x+v.x*spacer, y=pos.y+y, z=pos.z+v.z*spacer}\r
+                       if lp~=nil then\r
+                               if lp.x~=np.x then \r
+                                       if lp.x<np.x then \r
+                                               for i=lp.x+1,np.x do\r
+                                                       minetest.env:add_node({x=i, y=np.y, z=np.z}, node)\r
+                                                       count = count + 1\r
+                                               end\r
+                                       else\r
+                                               for i=np.x,lp.x-1 do\r
+                                                       minetest.env:add_node({x=i, y=np.y, z=np.z}, node)\r
+                                                       count = count + 1\r
+                                               end\r
+                                       end\r
+                               end\r
+                               if lp.z~=np.z then \r
+                                       if lp.z<np.z then \r
+                                               for i=lp.z+1,np.z do\r
+                                                       minetest.env:add_node({x=np.x, y=np.y, z=i}, node)\r
+                                                       count = count + 1\r
+                                               end\r
+                                       else\r
+                                               for i=np.z,lp.z-1 do\r
+                                                       minetest.env:add_node({x=np.x, y=np.y, z=i}, node)\r
+                                                       count = count + 1\r
+                                               end\r
+                                       end\r
+                               end\r
+                       end\r
+                       lp = np\r
+               end\r
+       end\r
+       return count\r
+end
\ No newline at end of file
diff --git a/worldedit/serialization.lua b/worldedit/serialization.lua
new file mode 100644 (file)
index 0000000..ae095eb
--- /dev/null
@@ -0,0 +1,160 @@
+worldedit = worldedit or {}\r
+\r
+dofile(minetest.get_modpath("worldedit") .. "/table_save.lua") --wip: remove dependency\r
+\r
+--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\r
+worldedit.sort_pos = function(pos1, pos2)\r
+       pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}\r
+       pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}\r
+       if pos1.x > pos2.x then\r
+               pos2.x, pos1.x = pos1.x, pos2.x\r
+       end\r
+       if pos1.y > pos2.y then\r
+               pos2.y, pos1.y = pos1.y, pos2.y\r
+       end\r
+       if pos1.z > pos2.z then\r
+               pos2.z, pos1.z = pos1.z, pos2.z\r
+       end\r
+       return pos1, pos2\r
+end\r
+\r
+--converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized\r
+worldedit.serialize = function(pos1, pos2)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local count = 0\r
+       local result = {}\r
+       local env = minetest.env\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               local node = env:get_node(pos)\r
+                               if node.name ~= "air" and node.name ~= "ignore" then\r
+                                       count = count + 1\r
+                                       result[count] = pos.x - pos1.x .. " " .. pos.y - pos1.y .. " " .. pos.z - pos1.z .. " " .. node.name .. " " .. node.param1 .. " " .. node.param2\r
+                               end\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       result = table.concat(result, "\n") --join all node entries into single string\r
+       return result, count\r
+end\r
+\r
+--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized\r
+worldedit.deserialize = function(originpos, value)\r
+       local pos = {x=0, y=0, z=0}\r
+       local node = {name="", param1=0, param2=0}\r
+       local count = 0\r
+       local env = minetest.env\r
+       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\r
+               pos.x = originpos.x + tonumber(x)\r
+               pos.y = originpos.y + tonumber(y)\r
+               pos.z = originpos.z + tonumber(z)\r
+               node.name = name\r
+               node.param1 = param1\r
+               node.param2 = param2\r
+               env:add_node(pos, node)\r
+               count = count + 1\r
+       end\r
+       return count\r
+end\r
+\r
+--loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized\r
+--based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)\r
+worldedit.deserialize_old = function(originpos, value)\r
+       --obtain the node table\r
+       local count = 0\r
+       local get_tables = loadstring(value)\r
+       if get_tables == nil then --error loading value\r
+               return count\r
+       end\r
+       local tables = get_tables()\r
+\r
+       --transform the node table into an array of nodes\r
+       for i = 1, #tables do\r
+               for j, v in pairs(tables[i]) do\r
+                       if type(v) == "table" then\r
+                               tables[i][j] = tables[v[1]]\r
+                       end\r
+               end\r
+       end\r
+\r
+       --load the node array\r
+       local env = minetest.env\r
+       for i, v in ipairs(tables[1]) do\r
+               local pos = v[1]\r
+               pos.x, pos.y, pos.z = originpos.x + pos.x, originpos.y + pos.y, originpos.z + pos.z\r
+               env:add_node(pos, v[2])\r
+               count = count + 1\r
+       end\r
+       return count\r
+end\r
+\r
+--saves the nodes and meta defined by positions `pos1` and `pos2` into a file, returning the number of nodes saved\r
+worldedit.metasave = function(pos1, pos2, file) --wip: simply work with strings instead of doing IO\r
+       local path = minetest.get_worldpath() .. "/schems"\r
+       local filename = path .. "/" .. file .. ".wem"\r
+       os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist\r
+       local rows = {}\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local count = 0\r
+       local result = {}\r
+       local env = minetest.env\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               local node = env:get_node(pos)\r
+                               if node.name ~= "air" and node.name ~= "ignore" then\r
+                                       count = count + 1\r
+                                       local row = {\r
+                                               x = pos.x-pos1.x,\r
+                                               y = pos.y-pos1.y,\r
+                                               z = pos.z-pos1.z,\r
+                                               name = node.name,\r
+                                               param1 = node.param1,\r
+                                               param2 = node.param2,\r
+                                               meta = env:get_meta(pos):to_table(),\r
+                                       }\r
+                                       table.insert(rows, row)\r
+                               end\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       local err = table.save(rows,filename)\r
+       if err then return _,err end\r
+       return count\r
+end\r
+\r
+--loads the nodes and meta from `file` to position `pos1`, returning the number of nodes loaded\r
+worldedit.metaload = function(pos1, file) --wip: simply work with strings instead of doing IO\r
+       local filename = minetest.get_worldpath() .. "/schems/" .. file .. ".wem"\r
+       local rows, err = table.load(filename)\r
+       if err then return _,err end\r
+       local pos = {x=0, y=0, z=0}\r
+       local node = {name="", param1=0, param2=0}\r
+       local count = 0\r
+       local env = minetest.env\r
+       for i,row in pairs(rows) do\r
+               pos.x = pos1.x + tonumber(row.x)\r
+               pos.y = pos1.y + tonumber(row.y)\r
+               pos.z = pos1.z + tonumber(row.z)\r
+               node.name = row.name\r
+               node.param1 = row.param1\r
+               node.param2 = row.param2\r
+               env:add_node(pos, node)\r
+               env:get_meta(pos):from_table(row.meta)\r
+               count = count + 1\r
+       end\r
+       return count\r
+end
\ No newline at end of file
diff --git a/worldedit/textures/worldedit_pos1.png b/worldedit/textures/worldedit_pos1.png
deleted file mode 100644 (file)
index 4c304aa..0000000
Binary files a/worldedit/textures/worldedit_pos1.png and /dev/null differ
diff --git a/worldedit/textures/worldedit_pos2.png b/worldedit/textures/worldedit_pos2.png
deleted file mode 100644 (file)
index 1502f16..0000000
Binary files a/worldedit/textures/worldedit_pos2.png and /dev/null differ
diff --git a/worldedit/visualization.lua b/worldedit/visualization.lua
new file mode 100644 (file)
index 0000000..f3d4a35
--- /dev/null
@@ -0,0 +1,164 @@
+worldedit = worldedit or {}\r
+\r
+--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\r
+worldedit.sort_pos = function(pos1, pos2)\r
+       pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}\r
+       pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}\r
+       if pos1.x > pos2.x then\r
+               pos2.x, pos1.x = pos1.x, pos2.x\r
+       end\r
+       if pos1.y > pos2.y then\r
+               pos2.y, pos1.y = pos1.y, pos2.y\r
+       end\r
+       if pos1.z > pos2.z then\r
+               pos2.z, pos1.z = pos1.z, pos2.z\r
+       end\r
+       return pos1, pos2\r
+end\r
+\r
+--determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume\r
+worldedit.volume = function(pos1, pos2)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)\r
+end\r
+\r
+minetest.register_node("worldedit:placeholder", {\r
+       drawtype = "airlike",\r
+       paramtype = "light",\r
+       sunlight_propagates = true,\r
+       diggable = false,\r
+       groups = {not_in_creative_inventory=1},\r
+})\r
+\r
+--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\r
+worldedit.hide = function(pos1, pos2)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local env = minetest.env\r
+\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local placeholder = {name="worldedit:placeholder", param1=0, param2=0}\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               local node = env:get_node(pos)\r
+                               placeholder.param1, placeholder.param2 = node.param1, node.param2 --copy node's param1 and param2\r
+                               local data = env:get_meta(pos):to_table() --obtain metadata of original node\r
+                               env:add_node(pos, placeholder) --add placeholder node\r
+                               local meta = env:get_meta(pos) --obtain placeholder meta\r
+                               meta:from_table(data) --set placeholder metadata to the original node's metadata\r
+                               meta:set_string("worldedit_placeholder", node.name)  --add the node's name\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       return worldedit.volume(pos1, pos2)\r
+end\r
+\r
+--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\r
+worldedit.suppress = function(pos1, pos2, nodename)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local env = minetest.env\r
+\r
+       if minetest.registered_nodes[nodename] == nil then\r
+               nodename = "default:" .. nodename\r
+       end\r
+\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local placeholder = {name="worldedit:placeholder", param1=0, param2=0}\r
+       local count = 0\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               local node = env:get_node(pos)\r
+                               if node.name == nodename then\r
+                                       placeholder.param1, placeholder.param2 = node.param1, node.param2 --copy node's param1 and param2\r
+                                       local data = env:get_meta(pos):to_table() --obtain metadata of original node\r
+                                       env:add_node(pos, placeholder) --add placeholder node\r
+                                       local meta = env:get_meta(pos) --obtain placeholder meta\r
+                                       meta:from_table(data) --set placeholder metadata to the original node's metadata\r
+                                       meta:set_string("worldedit_placeholder", nodename)  --add the node's name\r
+                                       count = count + 1\r
+                               end\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       return count\r
+end\r
+\r
+--finds 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\r
+worldedit.find = function(pos1, pos2, nodename)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local env = minetest.env\r
+\r
+       if minetest.registered_nodes[nodename] == nil then\r
+               nodename = "default:" .. nodename\r
+       end\r
+\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local placeholder = {name="worldedit:placeholder", param1=0, param2=0}\r
+       local count = 0\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               local node = env:get_node(pos)\r
+                               if node.name == nodename then --node found\r
+                                       count = count + 1\r
+                               else --hide other nodes\r
+                                       placeholder.param1, placeholder.param2 = node.param1, node.param2 --copy node's param1 and param2\r
+                                       local data = env:get_meta(pos):to_table() --obtain metadata of original node\r
+                                       env:add_node(pos, placeholder) --add placeholder node\r
+                                       local meta = env:get_meta(pos) --obtain placeholder meta\r
+                                       meta:from_table(data) --set placeholder metadata to the original node's metadata\r
+                                       meta:set_string("worldedit_placeholder", node.name)  --add the node's name\r
+                               end\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       return count\r
+end\r
+\r
+--restores all nodes hidden with WorldEdit functions in a region defined by positions `pos1` and `pos2`, returning the number of nodes restored\r
+worldedit.restore = function(pos1, pos2)\r
+       local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
+       local env = minetest.env\r
+\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local node = {name="", param1=0, param2=0}\r
+       local count = 0\r
+       while pos.x <= pos2.x do\r
+               pos.y = pos1.y\r
+               while pos.y <= pos2.y do\r
+                       pos.z = pos1.z\r
+                       while pos.z <= pos2.z do\r
+                               local currentnode = env:get_node(pos)\r
+                               if currentnode.name == "worldedit:placeholder" then\r
+                                       node.param1, node.param2 = currentnode.param1, currentnode.param2 --copy node's param1 and param2\r
+                                       local data = env:get_meta(pos):to_table() --obtain node metadata\r
+                                       node.name = data.fields.worldedit_placeholder --set node name\r
+                                       data.fields.worldedit_placeholder = nil --delete old nodename\r
+                                       env:add_node(pos, node) --add original node\r
+                                       env:get_meta(pos):from_table(data) --set original node metadata\r
+                                       count = count + 1\r
+                               end\r
+                               pos.z = pos.z + 1\r
+                       end\r
+                       pos.y = pos.y + 1\r
+               end\r
+               pos.x = pos.x + 1\r
+       end\r
+       return count\r
+end
\ No newline at end of file
diff --git a/worldedit_commands/depends.txt b/worldedit_commands/depends.txt
new file mode 100644 (file)
index 0000000..df8caff
--- /dev/null
@@ -0,0 +1 @@
+worldedit
\ No newline at end of file
diff --git a/worldedit_commands/init.lua b/worldedit_commands/init.lua
new file mode 100644 (file)
index 0000000..a1ba9ae
--- /dev/null
@@ -0,0 +1,726 @@
+minetest.register_privilege("worldedit", "Can use WorldEdit commands")\r
+\r
+worldedit.set_pos = {}\r
+\r
+worldedit.pos1 = {}\r
+worldedit.pos2 = {}\r
+\r
+dofile(minetest.get_modpath("worldedit_commands") .. "/mark.lua")\r
+\r
+--determines whether `nodename` is a valid node name, returning a boolean\r
+worldedit.node_is_valid = function(nodename)\r
+       return minetest.registered_nodes[nodename] ~= nil\r
+       or minetest.registered_nodes["default:" .. nodename] ~= nil\r
+end\r
+\r
+--determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)\r
+worldedit.player_axis = function(name)\r
+       local dir = minetest.env:get_player_by_name(name):get_look_dir()\r
+       local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)\r
+       if x > y then\r
+               if x > z then\r
+                       return "x", dir.x > 0 and 1 or -1\r
+               end\r
+       elseif y > z then\r
+               return "y", dir.y > 0 and 1 or -1\r
+       end\r
+       return "z", dir.z > 0 and 1 or -1\r
+end\r
+\r
+minetest.register_chatcommand("/reset", {\r
+       params = "",\r
+       description = "Reset the region so that it is empty",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               worldedit.pos1[name] = nil\r
+               worldedit.pos2[name] = nil\r
+               worldedit.mark_pos1(name)\r
+               worldedit.mark_pos2(name)\r
+               minetest.chat_send_player(name, "WorldEdit region reset")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/mark", {\r
+       params = "",\r
+       description = "Show markers at the region positions",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               worldedit.mark_pos1(name)\r
+               worldedit.mark_pos2(name)\r
+               minetest.chat_send_player(name, "WorldEdit region marked")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/pos1", {\r
+       params = "",\r
+       description = "Set WorldEdit region position 1 to the player's location",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos = minetest.env:get_player_by_name(name):getpos()\r
+               pos.x, pos.y, pos.z = math.floor(pos.x), math.floor(pos.y), math.floor(pos.z)\r
+               worldedit.pos1[name] = pos\r
+               worldedit.mark_pos1(name)\r
+               minetest.chat_send_player(name, "WorldEdit position 1 set to " .. minetest.pos_to_string(pos))\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/pos2", {\r
+       params = "",\r
+       description = "Set WorldEdit region position 2 to the player's location",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos = minetest.env:get_player_by_name(name):getpos()\r
+               pos.x, pos.y, pos.z = math.floor(pos.x), math.floor(pos.y), math.floor(pos.z)\r
+               worldedit.pos2[name] = pos\r
+               worldedit.mark_pos2(name)\r
+               minetest.chat_send_player(name, "WorldEdit position 2 set to " .. minetest.pos_to_string(pos))\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/p", {\r
+       params = "set/get",\r
+       description = "Set WorldEdit region by punching two nodes, or display the current WorldEdit region",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               if param == "set" then --set both WorldEdit positions\r
+                       worldedit.set_pos[name] = 1\r
+                       minetest.chat_send_player(name, "Select positions by punching two nodes")\r
+               elseif param == "get" then --display current WorldEdit positions\r
+                       if worldedit.pos1[name] ~= nil then\r
+                               minetest.chat_send_player(name, "WorldEdit position 1: " .. minetest.pos_to_string(worldedit.pos1[name]))\r
+                       else\r
+                               minetest.chat_send_player(name, "WorldEdit position 1 not set")\r
+                       end\r
+                       if worldedit.pos2[name] ~= nil then\r
+                               minetest.chat_send_player(name, "WorldEdit position 2: " .. minetest.pos_to_string(worldedit.pos2[name]))\r
+                       else\r
+                               minetest.chat_send_player(name, "WorldEdit position 2 not set")\r
+                       end\r
+               else\r
+                       minetest.chat_send_player(name, "Unknown subcommand: " .. param)\r
+               end\r
+       end,\r
+})\r
+\r
+minetest.register_on_punchnode(function(pos, node, puncher)\r
+       local name = puncher:get_player_name()\r
+       if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position\r
+               if worldedit.set_pos[name] == 1 then --setting position 1\r
+                       worldedit.set_pos[name] = 2 --set position 2 on the next invocation\r
+                       worldedit.pos1[name] = pos\r
+                       worldedit.mark_pos1(name)\r
+                       minetest.chat_send_player(name, "WorldEdit region position 1 set to " .. minetest.pos_to_string(pos))\r
+               else --setting position 2\r
+                       worldedit.set_pos[name] = nil --finished setting positions\r
+                       worldedit.pos2[name] = pos\r
+                       worldedit.mark_pos2(name)\r
+                       minetest.chat_send_player(name, "WorldEdit region position 2 set to " .. minetest.pos_to_string(pos))\r
+               end\r
+       end\r
+end)\r
+\r
+minetest.register_chatcommand("/volume", {\r
+       params = "",\r
+       description = "Display the volume of the current WorldEdit region",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local volume = worldedit.volume(pos1, pos2)\r
+               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 .. ")")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/set", {\r
+       params = "<node>",\r
+       description = "Set the current WorldEdit region to <node>",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               if param == "" or not worldedit.node_is_valid(param) then\r
+                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.set(pos1, pos2, param)\r
+               minetest.chat_send_player(name, count .. " nodes set")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/replace", {\r
+       params = "<search node> <replace node>",\r
+       description = "Replace all instances of <search node> with <place node> in the current WorldEdit region",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+([^%s]+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if not worldedit.node_is_valid(searchnode) then\r
+                       minetest.chat_send_player(name, "Invalid search node name: " .. searchnode)\r
+                       return\r
+               end\r
+               if not worldedit.node_is_valid(replacenode) then\r
+                       minetest.chat_send_player(name, "Invalid replace node name: " .. replacenode)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.replace(pos1, pos2, searchnode, replacenode)\r
+               minetest.chat_send_player(name, count .. " nodes replaced")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/hollowsphere", {\r
+       params = "<radius> <node>",\r
+       description = "Add hollow sphere at WorldEdit position 1 with radius <radius>, composed of <node>",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos = worldedit.pos1[name]\r
+               if pos == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, radius, nodename = param:find("^(%d+)%s+([^%s]+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if not worldedit.node_is_valid(nodename) then\r
+                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.hollow_sphere(pos, tonumber(radius), nodename)\r
+               minetest.chat_send_player(name, count .. " nodes added")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/sphere", {\r
+       params = "<radius> <node>",\r
+       description = "Add sphere at WorldEdit position 1 with radius <radius>, composed of <node>",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos = worldedit.pos1[name]\r
+               if pos == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, radius, nodename = param:find("^(%d+)%s+([^%s]+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if not worldedit.node_is_valid(nodename) then\r
+                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.sphere(pos, tonumber(radius), nodename)\r
+               minetest.chat_send_player(name, count .. " nodes added")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/hollowcylinder", {\r
+       params = "x/y/z/? <length> <radius> <node>",\r
+       description = "Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos = worldedit.pos1[name]\r
+               if pos == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+([^%s]+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if axis == "?" then\r
+                       axis, sign = worldedit.player_axis(name)\r
+                       length = length * sign\r
+               end\r
+               if not worldedit.node_is_valid(nodename) then\r
+                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.hollow_cylinder(pos, axis, tonumber(length), tonumber(radius), nodename)\r
+               minetest.chat_send_player(name, count .. " nodes added")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/cylinder", {\r
+       params = "x/y/z/? <length> <radius> <node>",\r
+       description = "Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos = worldedit.pos1[name]\r
+               if pos == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+([^%s]+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if axis == "?" then\r
+                       axis, sign = worldedit.player_axis(name)\r
+                       length = length * sign\r
+               end\r
+               if not worldedit.node_is_valid(nodename) then\r
+                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.cylinder(pos, axis, tonumber(length), tonumber(radius), nodename)\r
+               minetest.chat_send_player(name, count .. " nodes added")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/pyramid", {\r
+       params = "<height> <node>",\r
+       description = "Add pyramid at WorldEdit position 1 with height <height>, composed of <node>",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos = worldedit.pos1[name]\r
+               if pos == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, size, nodename = param:find("(%d+)%s+([^%s]+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if not worldedit.node_is_valid(nodename) then\r
+                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.pyramid(pos, tonumber(size), nodename)\r
+               minetest.chat_send_player(name, count .. " nodes added")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/spiral", {\r
+       params = "<width> <height> <space> <node>",\r
+       description = "Add spiral at WorldEdit position 1 with width <width>, height <height>, space between walls <space>, composed of <node>",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos = worldedit.pos1[name]\r
+               if pos == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, width, height, space, nodename = param:find("(%d+)%s+(%d+)%s+(%d+)%s+([^%s]+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if not worldedit.node_is_valid(nodename) then\r
+                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.spiral(pos, tonumber(width), tonumber(height), tonumber(space), nodename)\r
+               minetest.chat_send_player(name, count .. " nodes changed")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/copy", {\r
+       params = "x/y/z/? <amount>",\r
+       description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if axis == "?" then\r
+                       axis, sign = worldedit.player_axis(name)\r
+                       amount = amount * sign\r
+               end\r
+\r
+               local count = worldedit.copy(pos1, pos2, axis, tonumber(amount))\r
+               minetest.chat_send_player(name, count .. " nodes copied")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/move", {\r
+       params = "x/y/z/? <amount>",\r
+       description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if axis == "?" then\r
+                       axis, sign = worldedit.player_axis(name)\r
+                       amount = amount * sign\r
+               end\r
+\r
+               local count = worldedit.move(pos1, pos2, axis, tonumber(amount))\r
+\r
+               pos1[axis] = pos1[axis] + amount\r
+               pos2[axis] = pos2[axis] + amount\r
+               worldedit.mark_pos1(name)\r
+               worldedit.mark_pos2(name)\r
+\r
+               minetest.chat_send_player(name, count .. " nodes moved")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/stack", {\r
+       params = "x/y/z/? <count>",\r
+       description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, axis, count = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if axis == "?" then\r
+                       axis, sign = worldedit.player_axis(name)\r
+                       count = count * sign\r
+               end\r
+\r
+               local count = worldedit.stack(pos1, pos2, axis, tonumber(count))\r
+               minetest.chat_send_player(name, count .. " nodes stacked")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/transpose", {\r
+       params = "x/y/z/? x/y/z/?",\r
+       description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if axis1 == "?" then\r
+                       axis1 = worldedit.player_axis(name)\r
+               end\r
+               if axis2 == "?" then\r
+                       axis2 = worldedit.player_axis(name)\r
+               end\r
+               if axis1 == axis2 then\r
+                       minetest.chat_send_player(name, "Invalid usage: axes are the same")\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.transpose(pos1, pos2, axis1, axis2)\r
+               minetest.chat_send_player(name, count .. " nodes transposed")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/flip", {\r
+       params = "x/y/z/?",\r
+       description = "Flip the current WorldEdit region along the x/y/z/? axis",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               if param == "?" then\r
+                       param = worldedit.player_axis(name)\r
+               end\r
+               if param ~= "x" and param ~= "y" and param ~= "z" then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.flip(pos1, pos2, param)\r
+               minetest.chat_send_player(name, count .. " nodes flipped")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/rotate", {\r
+       params = "<axis> <angle>",\r
+       description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
+               if found == nil then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               if axis == "?" then\r
+                       axis = worldedit.player_axis(name)\r
+               end\r
+               if angle % 90 ~= 0 then\r
+                       minetest.chat_send_player(name, "Invalid usage: angle must be multiple of 90")\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.rotate(pos1, pos2, axis, angle)\r
+               minetest.chat_send_player(name, count .. " nodes rotated")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/dig", {\r
+       params = "",\r
+       description = "Dig the current WorldEdit region",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.dig(pos1, pos2)\r
+               minetest.chat_send_player(name, count .. " nodes dug")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/hide", {\r
+       params = "<node>",\r
+       description = "Hide all nodes in the current WorldEdit region non-destructively",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.hide(pos1, pos2)\r
+               minetest.chat_send_player(name, count .. " nodes hidden")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/suppress", {\r
+       params = "<node>",\r
+       description = "Suppress all <node> in the current WorldEdit region non-destructively",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               if param == "" or not worldedit.node_is_valid(param) then\r
+                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.suppress(pos1, pos2, param)\r
+               minetest.chat_send_player(name, count .. " nodes suppressed")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/find", {\r
+       params = "<node>",\r
+       description = "Find <node> in the current WorldEdit region by hiding everything else non-destructively",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               if param == "" or not worldedit.node_is_valid(param) then\r
+                       minetest.chat_send_player(name, "Invalid node name: " .. param)\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.find(pos1, pos2, param)\r
+               minetest.chat_send_player(name, count .. " nodes found")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/restore", {\r
+       params = "",\r
+       description = "Restores nodes hidden with WorldEdit in the current WorldEdit region",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               local count = worldedit.restore(pos1, pos2)\r
+               minetest.chat_send_player(name, count .. " nodes restored")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/save", {\r
+       params = "<file>",\r
+       description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               if param == "" then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+\r
+               local result, count = worldedit.serialize(pos1, pos2)\r
+\r
+               local path = minetest.get_worldpath() .. "/schems"\r
+               local filename = path .. "/" .. param .. ".we"\r
+               os.execute("mkdir \"" .. path .. "\"") --create directory if it does not already exist\r
+               local file, err = io.open(filename, "wb")\r
+               if err ~= nil then\r
+                       minetest.chat_send_player(name, "Could not save file to \"" .. filename .. "\"")\r
+                       return\r
+               end\r
+               file:write(result)\r
+               file:flush()\r
+               file:close()\r
+\r
+               minetest.chat_send_player(name, count .. " nodes saved")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/load", {\r
+       params = "<file>",\r
+       description = "Load nodes from \"(world folder)/schems/<file>.we\" with position 1 of the current WorldEdit region as the origin",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1 = worldedit.pos1[name]\r
+               if pos1 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+\r
+               if param == "" then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+\r
+               local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"\r
+               local file, err = io.open(filename, "rb")\r
+               if err ~= nil then\r
+                       minetest.chat_send_player(name, "Could not open file \"" .. filename .. "\"")\r
+                       return\r
+               end\r
+               local value = file:read("*a")\r
+               file:close()\r
+\r
+               local count\r
+               if value:find("{") then --old WorldEdit format\r
+                       count = worldedit.deserialize_old(pos1, value)\r
+               else --new WorldEdit format\r
+                       count = worldedit.deserialize(pos1, value)\r
+               end\r
+\r
+               minetest.chat_send_player(name, count .. " nodes loaded")\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/metasave", {\r
+       params = "<file>",\r
+       description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.wem\"",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
+               if pos1 == nil or pos2 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+               if param == "" then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               local count, err = worldedit.metasave(pos1, pos2, param)\r
+               if err then\r
+                       minetest.chat_send_player(name, "error loading file: " .. err)\r
+               else\r
+                       minetest.chat_send_player(name, count .. " nodes saved")\r
+               end\r
+       end,\r
+})\r
+\r
+minetest.register_chatcommand("/metaload", {\r
+       params = "<file>",\r
+       description = "Load nodes from \"(world folder)/schems/<file>.wem\" with position 1 of the current WorldEdit region as the origin",\r
+       privs = {worldedit=true},\r
+       func = function(name, param)\r
+               local pos1 = worldedit.pos1[name]\r
+               if pos1 == nil then\r
+                       minetest.chat_send_player(name, "No WorldEdit region selected")\r
+                       return\r
+               end\r
+               if param == "" then\r
+                       minetest.chat_send_player(name, "Invalid usage: " .. param)\r
+                       return\r
+               end\r
+               local count, err = worldedit.metaload(pos1, param)\r
+               if err then\r
+                       minetest.chat_send_player(name, "error loading file: " .. err)\r
+               else\r
+                       minetest.chat_send_player(name, count .. " nodes loaded")\r
+               end\r
+       end,\r
+})\r
diff --git a/worldedit_commands/mark.lua b/worldedit_commands/mark.lua
new file mode 100644 (file)
index 0000000..d2568ed
--- /dev/null
@@ -0,0 +1,70 @@
+worldedit.marker1 = {}\r
+worldedit.marker2 = {}\r
+\r
+--marks worldedit region position 1\r
+worldedit.mark_pos1 = function(name)\r
+       local pos = worldedit.pos1[name]\r
+       if worldedit.marker1[name] ~= nil then --marker already exists\r
+               worldedit.marker1[name]:remove() --remove marker\r
+               worldedit.marker1[name] = nil\r
+       end\r
+       if pos ~= nil then --add marker\r
+               worldedit.marker1[name] = minetest.env:add_entity(pos, "worldedit:pos1")\r
+               worldedit.marker1[name]:get_luaentity().active = true\r
+       end\r
+end\r
+\r
+--marks worldedit region position 2\r
+worldedit.mark_pos2 = function(name)\r
+       local pos = worldedit.pos2[name]\r
+       if worldedit.marker2[name] ~= nil then --marker already exists\r
+               worldedit.marker2[name]:remove() --remove marker\r
+               worldedit.marker2[name] = nil\r
+       end\r
+       if pos ~= nil then --add marker\r
+               worldedit.marker2[name] = minetest.env:add_entity(pos, "worldedit:pos2")\r
+               worldedit.marker2[name]:get_luaentity().active = true\r
+       end\r
+end\r
+\r
+minetest.register_entity(":worldedit:pos1", {\r
+       initial_properties = {\r
+               visual = "cube",\r
+               visual_size = {x=1.1, y=1.1},\r
+               textures = {"worldedit_pos1.png", "worldedit_pos1.png",\r
+                       "worldedit_pos1.png", "worldedit_pos1.png",\r
+                       "worldedit_pos1.png", "worldedit_pos1.png"},\r
+               collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},\r
+       },\r
+       on_step = function(self, dtime)\r
+               if self.active == nil then\r
+                       self.object:remove()\r
+               end\r
+       end,\r
+       on_punch = function(self, hitter)\r
+               self.object:remove()\r
+               local name = hitter:get_player_name()\r
+               worldedit.marker1[name] = nil\r
+       end,\r
+})\r
+\r
+minetest.register_entity(":worldedit:pos2", {\r
+       initial_properties = {\r
+               visual = "cube",\r
+               visual_size = {x=1.1, y=1.1},\r
+               textures = {"worldedit_pos2.png", "worldedit_pos2.png",\r
+                       "worldedit_pos2.png", "worldedit_pos2.png",\r
+                       "worldedit_pos2.png", "worldedit_pos2.png"},\r
+               collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},\r
+       },\r
+       on_step = function(self, dtime)\r
+               if self.active == nil then\r
+                       self.object:remove()\r
+               end\r
+       end,\r
+       on_punch = function(self, hitter)\r
+               self.object:remove()\r
+               local name = hitter:get_player_name()\r
+               worldedit.marker2[name] = nil\r
+       end,\r
+})
\ No newline at end of file
diff --git a/worldedit_commands/textures/worldedit_pos1.png b/worldedit_commands/textures/worldedit_pos1.png
new file mode 100644 (file)
index 0000000..4c304aa
Binary files /dev/null and b/worldedit_commands/textures/worldedit_pos1.png differ
diff --git a/worldedit_commands/textures/worldedit_pos2.png b/worldedit_commands/textures/worldedit_pos2.png
new file mode 100644 (file)
index 0000000..1502f16
Binary files /dev/null and b/worldedit_commands/textures/worldedit_pos2.png differ