]> git.lizzy.rs Git - worldedit.git/blobdiff - functions.lua
Merge pull request #5 from khonkhortisan/master
[worldedit.git] / functions.lua
index b752b06de9f6127dffb781bc1ec877b71683bc7b..b2f644387814d9305bc21d5ccdf285198c6fdaec 100644 (file)
@@ -72,6 +72,189 @@ worldedit.replace = function(pos1, pos2, searchnode, replacenode)
        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`, 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
+       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] + 1\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`, 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
+       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] + 1\r
+       end\r
+       return count\r
+end\r
+\r
+--adds a spiral at `pos` with size `size`, returning the number of nodes changed\r
+worldedit.spiral = function(pos, size, nodename)\r
+       local shift_x, shift_y\r
+       sa = spiralt(size)\r
+       shift_y = #sa -- "Height" of the Array\r
+       local fe = sa[1]\r
+       shift_x = #fe -- "Width" of the Array\r
+       fe = nil\r
+\r
+       local count = 0\r
+       local node = {name=nodename}\r
+       for x, v in ipairs(sa) do\r
+               for y, z in ipairs(v) do\r
+                       minetest.env:add_node({x=pos.x - shift_x + x,y=pos.y - shift_y + y,z=pos.z + z}, node)\r
+                       count = count + 1\r
+               end\r
+       end\r
+       return count\r
+end\r
+\r
+--wip: \r
+sign = function(s)\r
+       if s > 0 then\r
+               return 1\r
+       end\r
+       if s < 0 then\r
+               return -1\r
+       end\r
+       return 0\r
+end\r
+\r
+--wip: needs to be faster\r
+function spiral_index(y, x) -- returns the value at (x, y) in a spiral that starts at 1 and goes outwards\r
+       if y == -x and y >= x then\r
+               return (2 * y + 1) ^ 2\r
+       end\r
+       local l = math.max(math.abs(y), math.abs(x))\r
+       local value\r
+       if math.abs(y) == l then\r
+               value = x\r
+               if y < 0 then\r
+                       value = -value\r
+               end\r
+       else\r
+               value = y\r
+               if x < 0 then\r
+                       value = -value\r
+               end\r
+       end\r
+       t1 = l * 2\r
+       if x + y < 0 then\r
+               t1 = -t1\r
+       end\r
+       t2 = y ^ 2 - x ^ 2\r
+       if t2 < 0 then\r
+               t2 = -t2\r
+       end\r
+       return ((2 * l - 1) ^ 2) + (l * 4) + t1 + (t2 * (l - value))\r
+end\r
+\r
+--wip: needs to be faster\r
+function spiralt(side)\r
+       local spiral = {}\r
+       local start, stop = math.floor((-side+1)/2), math.floor((side-1)/2)\r
+       for i = 1, side do\r
+               spiral[i] = {}\r
+               for j = 1, side do\r
+                       spiral[i][j] = side ^ 2 - spiral_index(stop - i + 1,start + j - 1) --moves the coordinates so (0,0) is at the center of the spiral\r
+               end\r
+       end\r
+       return spiral\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
@@ -84,10 +267,13 @@ worldedit.copy = function(pos1, pos2, axis, amount)
                        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, node)\r
+                                       local node = env:get_node(pos)\r
+                                       local meta1 = env:get_meta(pos):to_table()\r
                                        local value = pos[axis]\r
-                                       pos[axis] = value - amount\r
+                                       pos[axis] = value + amount\r
                                        env:add_node(pos, node)\r
+                                       local meta2 = env:get_meta(pos)\r
+                                       meta2:from_table(meta1)\r
                                        pos[axis] = value\r
                                        pos.z = pos.z + 1\r
                                end\r
@@ -102,10 +288,13 @@ worldedit.copy = function(pos1, pos2, axis, amount)
                        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, node)\r
+                                       local node = minetest.env:get_node(pos)\r
+                                       local meta1 = env:get_meta(pos):to_table()\r
                                        local value = pos[axis]\r
                                        pos[axis] = value + amount\r
                                        minetest.env:add_node(pos, node)\r
+                                       local meta2 = env:get_meta(pos)\r
+                                       meta2:from_table(meta1)\r
                                        pos[axis] = value\r
                                        pos.z = pos.z - 1\r
                                end\r
@@ -129,11 +318,14 @@ worldedit.move = function(pos1, pos2, axis, amount)
                        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, node)\r
+                                       local node = env:get_node(pos)\r
+                                       local meta1 = env:get_meta(pos):to_table()\r
                                        env:remove_node(pos)\r
                                        local value = pos[axis]\r
-                                       pos[axis] = value - amount\r
+                                       pos[axis] = value + amount\r
                                        env:add_node(pos, node)\r
+                                       local meta2 = env:get_meta(pos)\r
+                                       meta2:from_table(meta1)\r
                                        pos[axis] = value\r
                                        pos.z = pos.z + 1\r
                                end\r
@@ -148,11 +340,14 @@ worldedit.move = function(pos1, pos2, axis, amount)
                        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, node)\r
+                                       local node = env:get_node(pos)\r
+                                       local meta1 = env:get_meta(pos):to_table()\r
                                        env:remove_node(pos)\r
                                        local value = pos[axis]\r
                                        pos[axis] = value + amount\r
-                                       minetest.env:add_node(pos, node)\r
+                                       env:add_node(pos, node)\r
+                                       local meta2 = env:get_meta(pos)\r
+                                       meta2:from_table(meta1)\r
                                        pos[axis] = value\r
                                        pos.z = pos.z - 1\r
                                end\r
@@ -181,6 +376,110 @@ worldedit.stack = function(pos1, pos2, axis, count)
        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 meta1a = env:get_meta(pos):to_table()\r
+                                       local value1, value2 = pos[axis1], pos[axis2]\r
+                                       pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1\r
+                                       local node2 = env:get_node(pos)\r
+                                       local meta2a = env:get_meta(pos):to_table()\r
+                                       env:add_node(pos, node1)\r
+                                       local meta1b = env:get_meta(pos)\r
+                                       meta1b:from_table(meta1a)\r
+                                       pos[axis1], pos[axis2] = pos1[axis1] + extent1, pos1[axis2] + extent2\r
+                                       env:add_node(pos, node2)\r
+                                       local meta2b = env:get_meta(pos)\r
+                                       meta2b:from_table(meta2a)\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 meta1a = 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 meta2a = env:get_meta(pos):to_table()\r
+                               env:add_node(pos, node1)\r
+                               local meta1b = env:get_meta(pos)\r
+                               meta1b:from_table(meta1a)\r
+                               pos[axis] = value\r
+                               env:add_node(pos, node2)\r
+                               local meta2b = env:get_meta(pos)\r
+                               meta2b:from_table(meta2a)\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 (if you are looking in the negative direction) around the `axis` (supporting 90 degree increments only), 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
+       if axis == 'x' then\r
+               axes = {'z', 'y'}\r
+       elseif axis == 'y' then\r
+               axes = {'x', 'z'}\r
+       else--if axis == 'z' then\r
+               axes = {'y', 'x'}\r
+       end\r
+       angle = angle % 360\r
+\r
+       local pos = {x=pos1.x, y=0, z=0}\r
+       local newpos = {x=0, y=0, z=0}\r
+       local offsetx, offsetz\r
+       local env = minetest.env\r
+\r
+       if angle == 90 then\r
+               worldedit.transpose(pos1, pos2, axes[1], axes[2])\r
+               worldedit.flip(pos1, pos2, axes[2])\r
+       elseif angle == 180 then\r
+               worldedit.flip(pos1, pos2, axes[1])\r
+               worldedit.flip(pos1, pos2, axes[2])\r
+       elseif angle == 270 then\r
+               worldedit.transpose(pos1, pos2, axes[1], axes[2])\r
+               worldedit.flip(pos1, pos2, axes[1])\r
+       else\r
+               return 0\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
@@ -246,4 +545,35 @@ worldedit.deserialize = function(originpos, value)
                count = count + 1\r
        end\r
        return count\r
-end
\ No newline at end of file
+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