]> git.lizzy.rs Git - worldedit.git/blob - worldedit/manipulations.lua
Change compatibility notices.
[worldedit.git] / worldedit / manipulations.lua
1 worldedit = worldedit or {}\r
2 local minetest = minetest --local copy of global\r
3 \r
4 --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
5 worldedit.sort_pos = function(pos1, pos2)\r
6         pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}\r
7         pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}\r
8         if pos1.x > pos2.x then\r
9                 pos2.x, pos1.x = pos1.x, pos2.x\r
10         end\r
11         if pos1.y > pos2.y then\r
12                 pos2.y, pos1.y = pos1.y, pos2.y\r
13         end\r
14         if pos1.z > pos2.z then\r
15                 pos2.z, pos1.z = pos1.z, pos2.z\r
16         end\r
17         return pos1, pos2\r
18 end\r
19 \r
20 --determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume\r
21 worldedit.volume = function(pos1, pos2)\r
22         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
23         return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)\r
24 end\r
25 \r
26 --sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled\r
27 worldedit.set = function(pos1, pos2, nodename)\r
28         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
29 \r
30         --set up voxel manipulator\r
31         local manip = minetest.get_voxel_manip()\r
32         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
33         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
34 \r
35         --fill emerged area with ignore\r
36         local nodes = {}\r
37         local ignore = minetest.get_content_id("ignore")\r
38         for i = 1, worldedit.volume(emerged_pos1, emerged_pos2) do\r
39                 nodes[i] = ignore\r
40         end\r
41 \r
42         --fill selected area with node\r
43         local node_id = minetest.get_content_id(nodename)\r
44         for i in area:iterp(pos1, pos2) do\r
45                 nodes[i] = node_id\r
46         end\r
47 \r
48         --update map nodes\r
49         manip:set_data(nodes)\r
50         manip:write_to_map()\r
51         manip:update_map()\r
52 \r
53         return worldedit.volume(pos1, pos2)\r
54 end\r
55 \r
56 --replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced\r
57 worldedit.replace = function(pos1, pos2, searchnode, replacenode)\r
58         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
59 \r
60         --set up voxel manipulator\r
61         local manip = minetest.get_voxel_manip()\r
62         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
63         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
64 \r
65         local nodes = manip:get_data()\r
66         local searchnode_id = minetest.get_content_id(searchnode)\r
67         local replacenode_id = minetest.get_content_id(replacenode)\r
68         local count = 0\r
69         for i in area:iterp(pos1, pos2) do --replace searchnode with replacenode\r
70                 if nodes[i] == searchnode_id then\r
71                         nodes[i] = replacenode_id\r
72                         count = count + 1\r
73                 end\r
74         end\r
75 \r
76         --update map nodes\r
77         manip:set_data(nodes)\r
78         manip:write_to_map()\r
79         manip:update_map()\r
80 \r
81         return count\r
82 end\r
83 \r
84 --replaces all nodes other than `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced\r
85 worldedit.replaceinverse = function(pos1, pos2, searchnode, replacenode)\r
86         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
87 \r
88         --set up voxel manipulator\r
89         local manip = minetest.get_voxel_manip()\r
90         local emerged_pos1, emerged_pos2 = manip:read_from_map(pos1, pos2)\r
91         local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})\r
92 \r
93         local nodes = manip:get_data()\r
94         local searchnode_id = minetest.get_content_id(searchnode)\r
95         local replacenode_id = minetest.get_content_id(replacenode)\r
96         local count = 0\r
97         for i in area:iterp(pos1, pos2) do --replace anything that is not searchnode with replacenode\r
98                 if nodes[i] ~= searchnode_id then\r
99                         nodes[i] = replacenode_id\r
100                         count = count + 1\r
101                 end\r
102         end\r
103 \r
104         --update map nodes\r
105         manip:set_data(nodes)\r
106         manip:write_to_map()\r
107         manip:update_map()\r
108 \r
109         return count\r
110 end\r
111 \r
112 worldedit.copy = function(pos1, pos2, axis, amount)\r
113         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
114 \r
115         if amount == 0 then\r
116                 return\r
117         end\r
118 \r
119         local other1, other2\r
120         if axis == "x" then\r
121                 other1, other2 = "y", "z"\r
122         elseif axis == "y" then\r
123                 other1, other2 = "x", "z"\r
124         else --axis == "z"\r
125                 other1, other2 = "x", "y"\r
126         end\r
127 \r
128         --make area stay loaded\r
129         local manip = minetest.get_voxel_manip()\r
130         manip:read_from_map(pos1, pos2)\r
131 \r
132         --prepare slice along axis\r
133         local extent = {\r
134                 [axis] = 1,\r
135                 [other1]=pos2[other1] - pos1[other1] + 1,\r
136                 [other2]=pos2[other2] - pos1[other2] + 1,\r
137         }\r
138         local nodes = {}\r
139         local schematic = {size=extent, data=nodes}\r
140 \r
141         local currentpos = {x=pos1.x, y=pos1.y, z=pos1.z}\r
142         local stride = {x=1, y=extent.x, z=extent.x * extent.y}\r
143         local get_node = minetest.get_node\r
144         for index1 = 1, extent[axis] do --go through each slice\r
145                 --copy slice into schematic\r
146                 local newindex1 = (index1 + offset[axis]) * stride[axis] + 1 --offset contributed by axis plus 1 to make it 1-indexed\r
147                 for index2 = 1, extent[other1] do\r
148                         local newindex2 = newindex1 + (index2 + offset[other1]) * stride[other1]\r
149                         for index3 = 1, extent[other2] do\r
150                                 local i = newindex2 + (index3 + offset[other2]) * stride[other2]\r
151                                 local node = get_node(pos)\r
152                                 node.param1 = 255 --node will always appear\r
153                                 nodes[i] = node\r
154                         end\r
155                 end\r
156 \r
157                 --copy schematic to target\r
158                 currentpos[axis] = currentpos[axis] + amount\r
159                 place_schematic(currentpos, schematic)\r
160 \r
161                 --wip: copy meta\r
162 \r
163                 currentpos[axis] = currentpos[axis] + 1\r
164         end\r
165         return worldedit.volume(pos1, pos2)\r
166 end\r
167 \r
168 --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
169 worldedit.copy = function(pos1, pos2, axis, amount)\r
170         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
171 \r
172         --make area stay loaded\r
173         local manip = minetest.get_voxel_manip()\r
174         manip:read_from_map(pos1, pos2)\r
175 \r
176         local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node\r
177         if amount < 0 then\r
178                 local pos = {x=pos1.x, y=0, z=0}\r
179                 while pos.x <= pos2.x do\r
180                         pos.y = pos1.y\r
181                         while pos.y <= pos2.y do\r
182                                 pos.z = pos1.z\r
183                                 while pos.z <= pos2.z do\r
184                                         local node = get_node(pos) --obtain current node\r
185                                         local meta = get_meta(pos):to_table() --get meta of current node\r
186                                         local value = pos[axis] --store current position\r
187                                         pos[axis] = value + amount --move along axis\r
188                                         add_node(pos, node) --copy node to new position\r
189                                         get_meta(pos):from_table(meta) --set metadata of new node\r
190                                         pos[axis] = value --restore old position\r
191                                         pos.z = pos.z + 1\r
192                                 end\r
193                                 pos.y = pos.y + 1\r
194                         end\r
195                         pos.x = pos.x + 1\r
196                 end\r
197         else\r
198                 local pos = {x=pos2.x, y=0, z=0}\r
199                 while pos.x >= pos1.x do\r
200                         pos.y = pos2.y\r
201                         while pos.y >= pos1.y do\r
202                                 pos.z = pos2.z\r
203                                 while pos.z >= pos1.z do\r
204                                         local node = get_node(pos) --obtain current node\r
205                                         local meta = get_meta(pos):to_table() --get meta of current node\r
206                                         local value = pos[axis] --store current position\r
207                                         pos[axis] = value + amount --move along axis\r
208                                         add_node(pos, node) --copy node to new position\r
209                                         get_meta(pos):from_table(meta) --set metadata of new node\r
210                                         pos[axis] = value --restore old position\r
211                                         pos.z = pos.z - 1\r
212                                 end\r
213                                 pos.y = pos.y - 1\r
214                         end\r
215                         pos.x = pos.x - 1\r
216                 end\r
217         end\r
218         return worldedit.volume(pos1, pos2)\r
219 end\r
220 \r
221 --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
222 worldedit.move = function(pos1, pos2, axis, amount)\r
223         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
224 \r
225         --make area stay loaded\r
226         local manip = minetest.get_voxel_manip()\r
227         manip:read_from_map(pos1, pos2)\r
228 \r
229         --wip: move slice by slice using schematic method in the move axis and transfer metadata in separate loop (and if the amount is greater than the length in the axis, copy whole thing at a time and erase original after, using schematic method)\r
230         local get_node, get_meta, add_node, remove_node = minetest.get_node, minetest.get_meta, minetest.add_node, minetest.remove_node\r
231         if amount < 0 then\r
232                 local pos = {x=pos1.x, y=0, z=0}\r
233                 while pos.x <= pos2.x do\r
234                         pos.y = pos1.y\r
235                         while pos.y <= pos2.y do\r
236                                 pos.z = pos1.z\r
237                                 while pos.z <= pos2.z do\r
238                                         local node = get_node(pos) --obtain current node\r
239                                         local meta = get_meta(pos):to_table() --get metadata of current node\r
240                                         remove_node(pos)\r
241                                         local value = pos[axis] --store current position\r
242                                         pos[axis] = value + amount --move along axis\r
243                                         add_node(pos, node) --move node to new position\r
244                                         get_meta(pos):from_table(meta) --set metadata of new node\r
245                                         pos[axis] = value --restore old position\r
246                                         pos.z = pos.z + 1\r
247                                 end\r
248                                 pos.y = pos.y + 1\r
249                         end\r
250                         pos.x = pos.x + 1\r
251                 end\r
252         else\r
253                 local pos = {x=pos2.x, y=0, z=0}\r
254                 while pos.x >= pos1.x do\r
255                         pos.y = pos2.y\r
256                         while pos.y >= pos1.y do\r
257                                 pos.z = pos2.z\r
258                                 while pos.z >= pos1.z do\r
259                                         local node = get_node(pos) --obtain current node\r
260                                         local meta = get_meta(pos):to_table() --get metadata of current node\r
261                                         remove_node(pos)\r
262                                         local value = pos[axis] --store current position\r
263                                         pos[axis] = value + amount --move along axis\r
264                                         add_node(pos, node) --move node to new position\r
265                                         get_meta(pos):from_table(meta) --set metadata of new node\r
266                                         pos[axis] = value --restore old position\r
267                                         pos.z = pos.z - 1\r
268                                 end\r
269                                 pos.y = pos.y - 1\r
270                         end\r
271                         pos.x = pos.x - 1\r
272                 end\r
273         end\r
274         return worldedit.volume(pos1, pos2)\r
275 end\r
276 \r
277 --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
278 worldedit.stack = function(pos1, pos2, axis, count)\r
279         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
280         local length = pos2[axis] - pos1[axis] + 1\r
281         if count < 0 then\r
282                 count = -count\r
283                 length = -length\r
284         end\r
285         local amount = 0\r
286         local copy = worldedit.copy\r
287         for i = 1, count do\r
288                 amount = amount + length\r
289                 copy(pos1, pos2, axis, amount)\r
290         end\r
291         return worldedit.volume(pos1, pos2) * count\r
292 end\r
293 \r
294 --scales the region defined by positions `pos1` and `pos2` by an factor of positive integer `factor` with `pos1` as the origin, returning the number of nodes scaled, the new scaled position 1, and the new scaled position 2\r
295 worldedit.scale = function(pos1, pos2, factor)\r
296         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
297 \r
298         --prepare schematic of large node\r
299         local get_node, get_meta, place_schematic = minetest.get_node, minetest.get_meta, minetest.place_schematic\r
300         local placeholder_node = {name="", param1=255, param2=0}\r
301         local nodes = {}\r
302         for i = 1, factor ^ 3 do\r
303                 nodes[i] = placeholder_node\r
304         end\r
305         local schematic = {size={x=factor, y=factor, z=factor}, data=nodes}\r
306 \r
307         local size = factor - 1\r
308 \r
309         --make area stay loaded\r
310         local manip = minetest.get_voxel_manip()\r
311         local new_pos2 = {x=pos1.x + (pos2.x - pos1.x) * factor + size, y=pos1.y + (pos2.y - pos1.y) * factor + size, z=pos1.z + (pos2.z - pos1.z) * factor + size}\r
312         manip:read_from_map(pos1, new_pos2)\r
313 \r
314         local pos = {x=pos2.x, y=0, z=0}\r
315         local bigpos = {x=0, y=0, z=0}\r
316         while pos.x >= pos1.x do\r
317                 pos.y = pos2.y\r
318                 while pos.y >= pos1.y do\r
319                         pos.z = pos2.z\r
320                         while pos.z >= pos1.z do\r
321                                 local node = get_node(pos) --obtain current node\r
322                                 local meta = get_meta(pos):to_table() --get meta of current node\r
323 \r
324                                 local value = pos[axis] --store current position\r
325                                 local posx, posy, posz = pos1.x + (pos.x - pos1.x) * factor, pos1.y + (pos.y - pos1.y) * factor, pos1.z + (pos.z - pos1.z) * factor\r
326 \r
327                                 --create large node\r
328                                 placeholder_node.name = node.name\r
329                                 placeholder_node.param2 = node.param2\r
330                                 bigpos.x, bigpos.y, bigpos.z = posx, posy, posz\r
331                                 place_schematic(bigpos, schematic)\r
332 \r
333                                 --fill in large node meta\r
334                                 if next(meta.fields) ~= nil and next(meta.inventory) ~= nil then --node has meta fields\r
335                                         for x = 0, size do\r
336                                                 for y = 0, size do\r
337                                                         for z = 0, size do\r
338                                                                 bigpos.x, bigpos.y, bigpos.z = posx + x, posy + y, posz + z\r
339                                                                 get_meta(bigpos):from_table(meta) --set metadata of new node\r
340                                                         end\r
341                                                 end\r
342                                         end\r
343                                 end\r
344                                 pos.z = pos.z - 1\r
345                         end\r
346                         pos.y = pos.y - 1\r
347                 end\r
348                 pos.x = pos.x - 1\r
349         end\r
350         return worldedit.volume(pos1, pos2) * (factor ^ 3), pos1, new_pos2\r
351 end\r
352 \r
353 --transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed, the new transposed position 1, and the new transposed position 2\r
354 worldedit.transpose = function(pos1, pos2, axis1, axis2)\r
355         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
356 \r
357         local compare\r
358         local extent1, extent2 = pos2[axis1] - pos1[axis1], pos2[axis2] - pos1[axis2]\r
359 \r
360         if extent1 > extent2 then\r
361                 compare = function(extent1, extent2)\r
362                         return extent1 > extent2\r
363                 end\r
364         else\r
365                 compare = function(extent1, extent2)\r
366                         return extent1 < extent2\r
367                 end\r
368         end\r
369 \r
370         --calculate the new position 2 after transposition\r
371         local new_pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}\r
372         new_pos2[axis1] = pos1[axis1] + extent2\r
373         new_pos2[axis2] = pos1[axis2] + extent1\r
374 \r
375         --make area stay loaded\r
376         local manip = minetest.get_voxel_manip()\r
377         local upperbound = {x=pos2.x, y=pos2.y, z=pos2.z}\r
378         if upperbound[axis1] < new_pos2[axis1] then upperbound[axis1] = new_pos2[axis1] end\r
379         if upperbound[axis2] < new_pos2[axis2] then upperbound[axis2] = new_pos2[axis2] end\r
380         manip:read_from_map(pos1, upperbound)\r
381 \r
382         local pos = {x=pos1.x, y=0, z=0}\r
383         local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node\r
384         while pos.x <= pos2.x do\r
385                 pos.y = pos1.y\r
386                 while pos.y <= pos2.y do\r
387                         pos.z = pos1.z\r
388                         while pos.z <= pos2.z do\r
389                                 local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]\r
390                                 if compare(extent1, extent2) then --transpose only if below the diagonal\r
391                                         local node1 = get_node(pos)\r
392                                         local meta1 = get_meta(pos):to_table()\r
393                                         local value1, value2 = pos[axis1], pos[axis2] --save position values\r
394                                         pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1 --swap axis extents\r
395                                         local node2 = get_node(pos)\r
396                                         local meta2 = get_meta(pos):to_table()\r
397                                         add_node(pos, node1)\r
398                                         get_meta(pos):from_table(meta1)\r
399                                         pos[axis1], pos[axis2] = value1, value2 --restore position values\r
400                                         add_node(pos, node2)\r
401                                         get_meta(pos):from_table(meta2)\r
402                                 end\r
403                                 pos.z = pos.z + 1\r
404                         end\r
405                         pos.y = pos.y + 1\r
406                 end\r
407                 pos.x = pos.x + 1\r
408         end\r
409         return worldedit.volume(pos1, pos2), pos1, new_pos2\r
410 end\r
411 \r
412 --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
413 worldedit.flip = function(pos1, pos2, axis)\r
414         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
415 \r
416         --make area stay loaded\r
417         local manip = minetest.get_voxel_manip()\r
418         manip:read_from_map(pos1, pos2)\r
419 \r
420         --wip: flip the region slice by slice along the flip axis using schematic method\r
421         local pos = {x=pos1.x, y=0, z=0}\r
422         local start = pos1[axis] + pos2[axis]\r
423         pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)\r
424         local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node\r
425         while pos.x <= pos2.x do\r
426                 pos.y = pos1.y\r
427                 while pos.y <= pos2.y do\r
428                         pos.z = pos1.z\r
429                         while pos.z <= pos2.z do\r
430                                 local node1 = get_node(pos)\r
431                                 local meta1 = get_meta(pos):to_table()\r
432                                 local value = pos[axis]\r
433                                 pos[axis] = start - value\r
434                                 local node2 = get_node(pos)\r
435                                 local meta2 = get_meta(pos):to_table()\r
436                                 add_node(pos, node1)\r
437                                 get_meta(pos):from_table(meta1)\r
438                                 pos[axis] = value\r
439                                 add_node(pos, node2)\r
440                                 get_meta(pos):from_table(meta2)\r
441                                 pos.z = pos.z + 1\r
442                         end\r
443                         pos.y = pos.y + 1\r
444                 end\r
445                 pos.x = pos.x + 1\r
446         end\r
447         return worldedit.volume(pos1, pos2)\r
448 end\r
449 \r
450 --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
451 worldedit.rotate = function(pos1, pos2, axis, angle)\r
452         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
453 \r
454         local axis1, axis2\r
455         if axis == "x" then\r
456                 axis1, axis2 = "z", "y"\r
457         elseif axis == "y" then\r
458                 axis1, axis2 = "x", "z"\r
459         else --axis == "z"\r
460                 axis1, axis2 = "y", "x"\r
461         end\r
462         angle = angle % 360\r
463 \r
464         local count\r
465         if angle == 90 then\r
466                 worldedit.flip(pos1, pos2, axis1)\r
467                 count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)\r
468         elseif angle == 180 then\r
469                 worldedit.flip(pos1, pos2, axis1)\r
470                 count = worldedit.flip(pos1, pos2, axis2)\r
471         elseif angle == 270 then\r
472                 worldedit.flip(pos1, pos2, axis2)\r
473                 count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)\r
474         end\r
475         return count, pos1, pos2\r
476 end\r
477 \r
478 --rotates all oriented nodes in a region defined by the positions `pos1` and `pos2` by `angle` degrees clockwise (90 degree increment) around the Y axis, returning the number of nodes oriented\r
479 worldedit.orient = function(pos1, pos2, angle) --wip: support 6D facedir rotation along arbitrary axis\r
480         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
481         local registered_nodes = minetest.registered_nodes\r
482 \r
483         local wallmounted = {\r
484                 [90]={[0]=0, [1]=1, [2]=5, [3]=4, [4]=2, [5]=3},\r
485                 [180]={[0]=0, [1]=1, [2]=3, [3]=2, [4]=5, [5]=4},\r
486                 [270]={[0]=0, [1]=1, [2]=4, [3]=5, [4]=3, [5]=2}\r
487         }\r
488         local facedir = {\r
489                 [90]={[0]=1, [1]=2, [2]=3, [3]=0},\r
490                 [180]={[0]=2, [1]=3, [2]=0, [3]=1},\r
491                 [270]={[0]=3, [1]=0, [2]=1, [3]=2}\r
492         }\r
493 \r
494         angle = angle % 360\r
495         if angle == 0 then\r
496                 return 0\r
497         end\r
498         local wallmounted_substitution = wallmounted[angle]\r
499         local facedir_substitution = facedir[angle]\r
500 \r
501         --make area stay loaded\r
502         local manip = minetest.get_voxel_manip()\r
503         manip:read_from_map(pos1, pos2)\r
504 \r
505         local count = 0\r
506         local get_node, get_meta, add_node = minetest.get_node, minetest.get_meta, minetest.add_node\r
507         local pos = {x=pos1.x, y=0, z=0}\r
508         while pos.x <= pos2.x do\r
509                 pos.y = pos1.y\r
510                 while pos.y <= pos2.y do\r
511                         pos.z = pos1.z\r
512                         while pos.z <= pos2.z do\r
513                                 local node = get_node(pos)\r
514                                 local def = registered_nodes[node.name]\r
515                                 if def then\r
516                                         if def.paramtype2 == "wallmounted" then\r
517                                                 node.param2 = wallmounted_substitution[node.param2]\r
518                                                 local meta = get_meta(pos):to_table()\r
519                                                 add_node(pos, node)\r
520                                                 get_meta(pos):from_table(meta)\r
521                                                 count = count + 1\r
522                                         elseif def.paramtype2 == "facedir" then\r
523                                                 node.param2 = facedir_substitution[node.param2]\r
524                                                 local meta = get_meta(pos):to_table()\r
525                                                 add_node(pos, node)\r
526                                                 get_meta(pos):from_table(meta)\r
527                                                 count = count + 1\r
528                                         end\r
529                                 end\r
530                                 pos.z = pos.z + 1\r
531                         end\r
532                         pos.y = pos.y + 1\r
533                 end\r
534                 pos.x = pos.x + 1\r
535         end\r
536         return count\r
537 end\r
538 \r
539 --fixes the lighting in a region defined by positions `pos1` and `pos2`, returning the number of nodes updated\r
540 worldedit.fixlight = function(pos1, pos2)\r
541         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
542 \r
543         --make area stay loaded\r
544         local manip = minetest.get_voxel_manip()\r
545         manip:read_from_map(pos1, pos2)\r
546 \r
547         local nodes = minetest.find_nodes_in_area(pos1, pos2, "air")\r
548         local dig_node = minetest.dig_node\r
549         for _, pos in ipairs(nodes) do\r
550                 dig_node(pos)\r
551         end\r
552         return #nodes\r
553 end\r
554 \r
555 --clears all objects in a region defined by the positions `pos1` and `pos2`, returning the number of objects cleared\r
556 worldedit.clearobjects = function(pos1, pos2)\r
557         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
558 \r
559         --make area stay loaded\r
560         local manip = minetest.get_voxel_manip()\r
561         manip:read_from_map(pos1, pos2)\r
562 \r
563         local pos1x, pos1y, pos1z = pos1.x, pos1.y, pos1.z\r
564         local pos2x, pos2y, pos2z = pos2.x + 1, pos2.y + 1, pos2.z + 1\r
565         local center = {x=(pos1x + pos2x) / 2, y=(pos1y + pos2y) / 2, z=(pos1z + pos2z) / 2} --center of region\r
566         local radius = ((center.x - pos1x + 0.5) + (center.y - pos1y + 0.5) + (center.z - pos1z + 0.5)) ^ 0.5 --bounding sphere radius\r
567         local count = 0\r
568         for _, obj in pairs(minetest.get_objects_inside_radius(center, radius)) do --all objects in bounding sphere\r
569                 local entity = obj:get_luaentity()\r
570                 if not (entity and entity.name:find("^worldedit:")) then --avoid WorldEdit entities\r
571                         local pos = obj:getpos()\r
572                         if pos.x >= pos1x and pos.x <= pos2x\r
573                         and pos.y >= pos1y and pos.y <= pos2y\r
574                         and pos.z >= pos1z and pos.z <= pos2z then --inside region\r
575                                 obj:remove()\r
576                                 count = count + 1\r
577                         end\r
578                 end\r
579         end\r
580         return count\r
581 end\r