1 --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
2 worldedit.sort_pos = function(pos1, pos2)
\r
3 pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}
\r
4 pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}
\r
5 if pos1.x > pos2.x then
\r
6 pos2.x, pos1.x = pos1.x, pos2.x
\r
8 if pos1.y > pos2.y then
\r
9 pos2.y, pos1.y = pos1.y, pos2.y
\r
11 if pos1.z > pos2.z then
\r
12 pos2.z, pos1.z = pos1.z, pos2.z
\r
17 --determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume
\r
18 worldedit.volume = function(pos1, pos2)
\r
19 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
20 return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)
\r
23 --sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled
\r
24 worldedit.set = function(pos1, pos2, nodename)
\r
25 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
26 local env = minetest.env
\r
28 local node = {name=nodename}
\r
29 local pos = {x=pos1.x, y=0, z=0}
\r
30 while pos.x <= pos2.x do
\r
32 while pos.y <= pos2.y do
\r
34 while pos.z <= pos2.z do
\r
35 env:add_node(pos, node)
\r
42 return worldedit.volume(pos1, pos2)
\r
45 --replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced
\r
46 worldedit.replace = function(pos1, pos2, searchnode, replacenode)
\r
47 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
48 local env = minetest.env
\r
50 if minetest.registered_nodes[searchnode] == nil then
\r
51 searchnode = "default:" .. searchnode
\r
53 if minetest.registered_nodes[replacenode] == nil then
\r
54 replacenode = "default:" .. replacenode
\r
57 local pos = {x=pos1.x, y=0, z=0}
\r
58 local node = {name=replacenode}
\r
60 while pos.x <= pos2.x do
\r
62 while pos.y <= pos2.y do
\r
64 while pos.z <= pos2.z do
\r
65 if env:get_node(pos).name == searchnode then
\r
66 env:add_node(pos, node)
\r
78 --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
79 worldedit.hollow_cylinder = function(pos, axis, length, radius, nodename)
\r
80 local other1, other2
\r
82 other1, other2 = "y", "z"
\r
83 elseif axis == "y" then
\r
84 other1, other2 = "x", "z"
\r
86 other1, other2 = "x", "y"
\r
89 local env = minetest.env
\r
90 local currentpos = {x=pos.x, y=pos.y, z=pos.z}
\r
91 local node = {name=nodename}
\r
93 for i = 1, length do
\r
94 local offset1, offset2 = 0, radius
\r
95 local delta = -radius
\r
96 while offset1 <= offset2 do
\r
97 --add node at each octant
\r
98 local first1, first2 = pos[other1] + offset1, pos[other1] - offset1
\r
99 local second1, second2 = pos[other2] + offset2, pos[other2] - offset2
\r
100 currentpos[other1], currentpos[other2] = first1, second1
\r
101 env:add_node(currentpos, node) --octant 1
\r
102 currentpos[other1] = first2
\r
103 env:add_node(currentpos, node) --octant 4
\r
104 currentpos[other2] = second2
\r
105 env:add_node(currentpos, node) --octant 5
\r
106 currentpos[other1] = first1
\r
107 env:add_node(currentpos, node) --octant 8
\r
108 local first1, first2 = pos[other1] + offset2, pos[other1] - offset2
\r
109 local second1, second2 = pos[other2] + offset1, pos[other2] - offset1
\r
110 currentpos[other1], currentpos[other2] = first1, second1
\r
111 env:add_node(currentpos, node) --octant 2
\r
112 currentpos[other1] = first2
\r
113 env:add_node(currentpos, node) --octant 3
\r
114 currentpos[other2] = second2
\r
115 env:add_node(currentpos, node) --octant 6
\r
116 currentpos[other1] = first1
\r
117 env:add_node(currentpos, node) --octant 7
\r
119 count = count + 8 --wip: broken
\r
121 --move to next location
\r
122 delta = delta + (offset1 * 2) + 1
\r
124 offset2 = offset2 - 1
\r
125 delta = delta - (offset2 * 2)
\r
127 offset1 = offset1 + 1
\r
129 currentpos[axis] = currentpos[axis] + 1
\r
134 --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
135 worldedit.cylinder = function(pos, axis, length, radius, nodename)
\r
136 local other1, other2
\r
137 if axis == "x" then
\r
138 other1, other2 = "y", "z"
\r
139 elseif axis == "y" then
\r
140 other1, other2 = "x", "z"
\r
142 other1, other2 = "x", "y"
\r
145 local env = minetest.env
\r
146 local currentpos = {x=pos.x, y=pos.y, z=pos.z}
\r
147 local node = {name=nodename}
\r
149 for i = 1, length do
\r
150 local offset1, offset2 = 0, radius
\r
151 local delta = -radius
\r
152 while offset1 <= offset2 do
\r
153 --connect each pair of octants
\r
154 currentpos[other1] = pos[other1] - offset1
\r
155 local second1, second2 = pos[other2] + offset2, pos[other2] - offset2
\r
156 for i = 0, offset1 * 2 do
\r
157 currentpos[other2] = second1
\r
158 env:add_node(currentpos, node) --octant 1 to 4
\r
159 currentpos[other2] = second2
\r
160 env:add_node(currentpos, node) --octant 5 to 8
\r
161 currentpos[other1] = currentpos[other1] + 1
\r
163 currentpos[other1] = pos[other1] - offset2
\r
164 local second1, second2 = pos[other2] + offset1, pos[other2] - offset1
\r
165 for i = 0, offset2 * 2 do
\r
166 currentpos[other2] = second1
\r
167 env:add_node(currentpos, node) --octant 2 to 3
\r
168 currentpos[other2] = second2
\r
169 env:add_node(currentpos, node) --octant 6 to 7
\r
170 currentpos[other1] = currentpos[other1] + 1
\r
173 count = count + (offset1 * 4) + (offset2 * 4) + 4 --wip: broken
\r
175 --move to next location
\r
176 delta = delta + (offset1 * 2) + 1
\r
177 offset1 = offset1 + 1
\r
179 offset2 = offset2 - 1
\r
180 delta = delta - (offset2 * 2)
\r
183 currentpos[axis] = currentpos[axis] + 1
\r
188 --adds a spiral at `pos` with size `size`, returning the number of nodes changed
\r
189 worldedit.spiral = function(pos, size, nodename)
\r
190 local shift_x, shift_y
\r
192 shift_y = #sa -- "Height" of the Array
\r
194 shift_x = #fe -- "Width" of the Array
\r
198 local node = {name=nodename}
\r
199 for x, v in ipairs(sa) do
\r
200 for y, z in ipairs(v) do
\r
201 minetest.env:add_node({x=pos.x - shift_x + x,y=pos.y - shift_y + y,z=pos.z + z}, node)
\r
219 --wip: needs to be faster
\r
220 function spiral_index(y, x) -- returns the value at (x, y) in a spiral that starts at 1 and goes outwards
\r
221 if y == -x and y >= x then
\r
222 return (2 * y + 1) ^ 2
\r
224 local l = math.max(math.abs(y), math.abs(x))
\r
226 if math.abs(y) == l then
\r
245 return ((2 * l - 1) ^ 2) + (l * 4) + t1 + (t2 * (l - value))
\r
248 --wip: needs to be faster
\r
249 function spiralt(side)
\r
251 local start, stop = math.floor((-side+1)/2), math.floor((side-1)/2)
\r
255 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
261 --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
262 worldedit.copy = function(pos1, pos2, axis, amount)
\r
263 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
264 local env = minetest.env
\r
267 local pos = {x=pos1.x, y=0, z=0}
\r
268 while pos.x <= pos2.x do
\r
270 while pos.y <= pos2.y do
\r
272 while pos.z <= pos2.z do
\r
273 local node = env:get_node(pos)
\r
274 local meta1 = env:get_meta(pos):to_table()
\r
275 local value = pos[axis]
\r
276 pos[axis] = value + amount
\r
277 env:add_node(pos, node)
\r
278 local meta2 = env:get_meta(pos)
\r
279 meta2:from_table(meta1)
\r
288 local pos = {x=pos2.x, y=0, z=0}
\r
289 while pos.x >= pos1.x do
\r
291 while pos.y >= pos1.y do
\r
293 while pos.z >= pos1.z do
\r
294 local node = minetest.env:get_node(pos)
\r
295 local meta1 = env:get_meta(pos):to_table()
\r
296 local value = pos[axis]
\r
297 pos[axis] = value + amount
\r
298 minetest.env:add_node(pos, node)
\r
299 local meta2 = env:get_meta(pos)
\r
300 meta2:from_table(meta1)
\r
309 return worldedit.volume(pos1, pos2)
\r
312 --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
313 worldedit.move = function(pos1, pos2, axis, amount)
\r
314 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
315 local env = minetest.env
\r
318 local pos = {x=pos1.x, y=0, z=0}
\r
319 while pos.x <= pos2.x do
\r
321 while pos.y <= pos2.y do
\r
323 while pos.z <= pos2.z do
\r
324 local node = env:get_node(pos)
\r
325 local meta1 = env:get_meta(pos):to_table()
\r
326 env:remove_node(pos)
\r
327 local value = pos[axis]
\r
328 pos[axis] = value + amount
\r
329 env:add_node(pos, node)
\r
330 local meta2 = env:get_meta(pos)
\r
331 meta2:from_table(meta1)
\r
340 local pos = {x=pos2.x, y=0, z=0}
\r
341 while pos.x >= pos1.x do
\r
343 while pos.y >= pos1.y do
\r
345 while pos.z >= pos1.z do
\r
346 local node = env:get_node(pos)
\r
347 local meta1 = env:get_meta(pos):to_table()
\r
348 env:remove_node(pos)
\r
349 local value = pos[axis]
\r
350 pos[axis] = value + amount
\r
351 env:add_node(pos, node)
\r
352 local meta2 = env:get_meta(pos)
\r
353 meta2:from_table(meta1)
\r
362 return worldedit.volume(pos1, pos2)
\r
365 --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
366 worldedit.stack = function(pos1, pos2, axis, count)
\r
367 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
368 local length = pos2[axis] - pos1[axis] + 1
\r
370 local copy = worldedit.copy
\r
375 for i = 1, count do
\r
376 amount = amount + length
\r
377 copy(pos1, pos2, axis, amount)
\r
379 return worldedit.volume(pos1, pos2)
\r
382 --transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed
\r
383 worldedit.transpose = function(pos1, pos2, axis1, axis2)
\r
384 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
386 local pos = {x=pos1.x, y=0, z=0}
\r
387 local env = minetest.env
\r
388 while pos.x <= pos2.x do
\r
390 while pos.y <= pos2.y do
\r
392 while pos.z <= pos2.z do
\r
393 local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]
\r
394 if extent1 < extent2 then
\r
395 local node1 = env:get_node(pos)
\r
396 local meta1a = env:get_meta(pos):to_table()
\r
397 local value1, value2 = pos[axis1], pos[axis2]
\r
398 pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1
\r
399 local node2 = env:get_node(pos)
\r
400 local meta2a = env:get_meta(pos):to_table()
\r
401 env:add_node(pos, node1)
\r
402 local meta1b = env:get_meta(pos)
\r
403 meta1b:from_table(meta1a)
\r
404 pos[axis1], pos[axis2] = pos1[axis1] + extent1, pos1[axis2] + extent2
\r
405 env:add_node(pos, node2)
\r
406 local meta2b = env:get_meta(pos)
\r
407 meta2b:from_table(meta2a)
\r
415 return worldedit.volume(pos1, pos2)
\r
418 --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
419 worldedit.flip = function(pos1, pos2, axis)
\r
420 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
422 local pos = {x=pos1.x, y=0, z=0}
\r
423 local start = pos1[axis] + pos2[axis]
\r
424 pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)
\r
425 local env = minetest.env
\r
426 while pos.x <= pos2.x do
\r
428 while pos.y <= pos2.y do
\r
430 while pos.z <= pos2.z do
\r
431 local node1 = env:get_node(pos)
\r
432 local meta1a = env:get_meta(pos):to_table()
\r
433 local value = pos[axis]
\r
434 pos[axis] = start - value
\r
435 local node2 = env:get_node(pos)
\r
436 local meta2a = env:get_meta(pos):to_table()
\r
437 env:add_node(pos, node1)
\r
438 local meta1b = env:get_meta(pos)
\r
439 meta1b:from_table(meta1a)
\r
441 env:add_node(pos, node2)
\r
442 local meta2b = env:get_meta(pos)
\r
443 meta2b:from_table(meta2a)
\r
450 return worldedit.volume(pos1, pos2)
\r
453 --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
454 worldedit.rotate = function(pos1, pos2, axis, angle)
\r
455 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
457 if axis == 'x' then
\r
459 elseif axis == 'y' then
\r
461 else--if axis == 'z' then
\r
464 angle = angle % 360
\r
466 local pos = {x=pos1.x, y=0, z=0}
\r
467 local newpos = {x=0, y=0, z=0}
\r
468 local offsetx, offsetz
\r
469 local env = minetest.env
\r
471 if angle == 90 then
\r
472 worldedit.transpose(pos1, pos2, axes[1], axes[2])
\r
473 worldedit.flip(pos1, pos2, axes[2])
\r
474 elseif angle == 180 then
\r
475 worldedit.flip(pos1, pos2, axes[1])
\r
476 worldedit.flip(pos1, pos2, axes[2])
\r
477 elseif angle == 270 then
\r
478 worldedit.transpose(pos1, pos2, axes[1], axes[2])
\r
479 worldedit.flip(pos1, pos2, axes[1])
\r
483 return worldedit.volume(pos1, pos2)
\r
486 --digs a region defined by positions `pos1` and `pos2`, returning the number of nodes dug
\r
487 worldedit.dig = function(pos1, pos2)
\r
488 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
489 local env = minetest.env
\r
491 local pos = {x=pos1.x, y=0, z=0}
\r
492 while pos.x <= pos2.x do
\r
494 while pos.y <= pos2.y do
\r
496 while pos.z <= pos2.z do
\r
504 return worldedit.volume(pos1, pos2)
\r
507 --converts the region defined by positions `pos1` and `pos2` into a single string, returning the serialized data and the number of nodes serialized
\r
508 worldedit.serialize = function(pos1, pos2)
\r
509 local pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
510 local pos = {x=pos1.x, y=0, z=0}
\r
513 local env = minetest.env
\r
514 while pos.x <= pos2.x do
\r
516 while pos.y <= pos2.y do
\r
518 while pos.z <= pos2.z do
\r
519 local node = env:get_node(pos)
\r
520 if node.name ~= "air" and node.name ~= "ignore" then
\r
522 result[count] = pos.x - pos1.x .. " " .. pos.y - pos1.y .. " " .. pos.z - pos1.z .. " " .. node.name .. " " .. node.param1 .. " " .. node.param2
\r
530 result = table.concat(result, "\n")
\r
531 return result, count
\r
534 --loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
\r
535 worldedit.deserialize = function(originpos, value)
\r
536 local pos = {x=0, y=0, z=0}
\r
537 local node = {name="", param1=0, param2=0}
\r
539 local env = minetest.env
\r
540 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
541 pos.x = originpos.x + tonumber(x)
\r
542 pos.y = originpos.y + tonumber(y)
\r
543 pos.z = originpos.z + tonumber(z)
\r
545 node.param1 = param1
\r
546 node.param2 = param2
\r
547 env:add_node(pos, node)
\r
553 --loads the nodes represented by string `value` at position `originpos`, returning the number of nodes deserialized
\r
554 --based on [table.save/table.load](http://lua-users.org/wiki/SaveTableToFile) by ChillCode, available under the MIT license (GPL compatible)
\r
555 worldedit.deserialize_old = function(originpos, value)
\r
556 --obtain the node table
\r
558 local get_tables = loadstring(value)
\r
559 if get_tables == nil then --error loading value
\r
562 local tables = get_tables()
\r
564 --transform the node table into an array of nodes
\r
565 for i = 1, #tables do
\r
566 for j, v in pairs(tables[i]) do
\r
567 if type(v) == "table" then
\r
568 tables[i][j] = tables[v[1]]
\r
573 --load the node array
\r
574 local env = minetest.env
\r
575 for i, v in ipairs(tables[1]) do
\r
577 pos.x, pos.y, pos.z = originpos.x + pos.x, originpos.y + pos.y, originpos.z + pos.z
\r
578 env:add_node(pos, v[2])
\r