]> git.lizzy.rs Git - worldedit.git/blob - worldedit/manipulations.lua
903a3b72e84f0599b8d742ccc92ef9852e646d5a
[worldedit.git] / worldedit / manipulations.lua
1 worldedit = worldedit or {}\r
2 \r
3 --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
4 worldedit.sort_pos = function(pos1, pos2)\r
5         pos1 = {x=pos1.x, y=pos1.y, z=pos1.z}\r
6         pos2 = {x=pos2.x, y=pos2.y, z=pos2.z}\r
7         if pos1.x > pos2.x then\r
8                 pos2.x, pos1.x = pos1.x, pos2.x\r
9         end\r
10         if pos1.y > pos2.y then\r
11                 pos2.y, pos1.y = pos1.y, pos2.y\r
12         end\r
13         if pos1.z > pos2.z then\r
14                 pos2.z, pos1.z = pos1.z, pos2.z\r
15         end\r
16         return pos1, pos2\r
17 end\r
18 \r
19 --determines the volume of the region defined by positions `pos1` and `pos2`, returning the volume\r
20 worldedit.volume = function(pos1, pos2)\r
21         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
22         return (pos2.x - pos1.x + 1) * (pos2.y - pos1.y + 1) * (pos2.z - pos1.z + 1)\r
23 end\r
24 \r
25 --sets a region defined by positions `pos1` and `pos2` to `nodename`, returning the number of nodes filled\r
26 worldedit.set = function(pos1, pos2, nodename)\r
27         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
28         local env = minetest.env\r
29 \r
30         local node = {name=nodename}\r
31         local pos = {x=pos1.x, y=0, z=0}\r
32         while pos.x <= pos2.x do\r
33                 pos.y = pos1.y\r
34                 while pos.y <= pos2.y do\r
35                         pos.z = pos1.z\r
36                         while pos.z <= pos2.z do\r
37                                 env:add_node(pos, node)\r
38                                 pos.z = pos.z + 1\r
39                         end\r
40                         pos.y = pos.y + 1\r
41                 end\r
42                 pos.x = pos.x + 1\r
43         end\r
44         return worldedit.volume(pos1, pos2)\r
45 end\r
46 \r
47 --replaces all instances of `searchnode` with `replacenode` in a region defined by positions `pos1` and `pos2`, returning the number of nodes replaced\r
48 worldedit.replace = function(pos1, pos2, searchnode, replacenode)\r
49         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
50         local env = minetest.env\r
51 \r
52         if minetest.registered_nodes[searchnode] == nil then\r
53                 searchnode = "default:" .. searchnode\r
54         end\r
55 \r
56         local pos = {x=pos1.x, y=0, z=0}\r
57         local node = {name=replacenode}\r
58         local count = 0\r
59         while pos.x <= pos2.x do\r
60                 pos.y = pos1.y\r
61                 while pos.y <= pos2.y do\r
62                         pos.z = pos1.z\r
63                         while pos.z <= pos2.z do\r
64                                 if env:get_node(pos).name == searchnode then\r
65                                         env:add_node(pos, node)\r
66                                         count = count + 1\r
67                                 end\r
68                                 pos.z = pos.z + 1\r
69                         end\r
70                         pos.y = pos.y + 1\r
71                 end\r
72                 pos.x = pos.x + 1\r
73         end\r
74         return count\r
75 end\r
76 \r
77 --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
78 worldedit.copy = function(pos1, pos2, axis, amount)\r
79         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
80         local env = minetest.env\r
81 \r
82         if amount < 0 then\r
83                 local pos = {x=pos1.x, y=0, z=0}\r
84                 while pos.x <= pos2.x do\r
85                         pos.y = pos1.y\r
86                         while pos.y <= pos2.y do\r
87                                 pos.z = pos1.z\r
88                                 while pos.z <= pos2.z do\r
89                                         local node = env:get_node(pos) --obtain current node\r
90                                         local meta = env:get_meta(pos):to_table() --get meta of current node\r
91                                         local value = pos[axis] --store current position\r
92                                         pos[axis] = value + amount --move along axis\r
93                                         env:add_node(pos, node) --copy node to new position\r
94                                         env:get_meta(pos):from_table(meta) --set metadata of new node\r
95                                         pos[axis] = value --restore old position\r
96                                         pos.z = pos.z + 1\r
97                                 end\r
98                                 pos.y = pos.y + 1\r
99                         end\r
100                         pos.x = pos.x + 1\r
101                 end\r
102         else\r
103                 local pos = {x=pos2.x, y=0, z=0}\r
104                 while pos.x >= pos1.x do\r
105                         pos.y = pos2.y\r
106                         while pos.y >= pos1.y do\r
107                                 pos.z = pos2.z\r
108                                 while pos.z >= pos1.z do\r
109                                         local node = minetest.env:get_node(pos) --obtain current node\r
110                                         local meta = env:get_meta(pos):to_table() --get meta of current node\r
111                                         local value = pos[axis] --store current position\r
112                                         pos[axis] = value + amount --move along axis\r
113                                         minetest.env:add_node(pos, node) --copy node to new position\r
114                                         env:get_meta(pos):from_table(meta) --set metadata of new node\r
115                                         pos[axis] = value --restore old position\r
116                                         pos.z = pos.z - 1\r
117                                 end\r
118                                 pos.y = pos.y - 1\r
119                         end\r
120                         pos.x = pos.x - 1\r
121                 end\r
122         end\r
123         return worldedit.volume(pos1, pos2)\r
124 end\r
125 \r
126 --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
127 worldedit.move = function(pos1, pos2, axis, amount)\r
128         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
129         local env = minetest.env\r
130 \r
131         if amount < 0 then\r
132                 local pos = {x=pos1.x, y=0, z=0}\r
133                 while pos.x <= pos2.x do\r
134                         pos.y = pos1.y\r
135                         while pos.y <= pos2.y do\r
136                                 pos.z = pos1.z\r
137                                 while pos.z <= pos2.z do\r
138                                         local node = env:get_node(pos) --obtain current node\r
139                                         local meta = env:get_meta(pos):to_table() --get metadata of current node\r
140                                         env:remove_node(pos)\r
141                                         local value = pos[axis] --store current position\r
142                                         pos[axis] = value + amount --move along axis\r
143                                         env:add_node(pos, node) --move node to new position\r
144                                         env:get_meta(pos):from_table(meta) --set metadata of new node\r
145                                         pos[axis] = value --restore old position\r
146                                         pos.z = pos.z + 1\r
147                                 end\r
148                                 pos.y = pos.y + 1\r
149                         end\r
150                         pos.x = pos.x + 1\r
151                 end\r
152         else\r
153                 local pos = {x=pos2.x, y=0, z=0}\r
154                 while pos.x >= pos1.x do\r
155                         pos.y = pos2.y\r
156                         while pos.y >= pos1.y do\r
157                                 pos.z = pos2.z\r
158                                 while pos.z >= pos1.z do\r
159                                         local node = env:get_node(pos) --obtain current node\r
160                                         local meta = env:get_meta(pos):to_table() --get metadata of current node\r
161                                         env:remove_node(pos)\r
162                                         local value = pos[axis] --store current position\r
163                                         pos[axis] = value + amount --move along axis\r
164                                         env:add_node(pos, node) --move node to new position\r
165                                         env:get_meta(pos):from_table(meta) --set metadata of new node\r
166                                         pos[axis] = value --restore old position\r
167                                         pos.z = pos.z - 1\r
168                                 end\r
169                                 pos.y = pos.y - 1\r
170                         end\r
171                         pos.x = pos.x - 1\r
172                 end\r
173         end\r
174         return worldedit.volume(pos1, pos2)\r
175 end\r
176 \r
177 --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
178 worldedit.stack = function(pos1, pos2, axis, count)\r
179         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
180         local length = pos2[axis] - pos1[axis] + 1\r
181         if count < 0 then\r
182                 count = -count\r
183                 length = -length\r
184         end\r
185         local amount = 0\r
186         local copy = worldedit.copy\r
187         for i = 1, count do\r
188                 amount = amount + length\r
189                 copy(pos1, pos2, axis, amount)\r
190         end\r
191         return worldedit.volume(pos1, pos2)\r
192 end\r
193 \r
194 --transposes a region defined by the positions `pos1` and `pos2` between the `axis1` and `axis2` axes, returning the number of nodes transposed, the new position 1, and the new position 2\r
195 worldedit.transpose = function(pos1, pos2, axis1, axis2)\r
196         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
197 \r
198         local compare\r
199         local extent1, extent2 = pos2[axis1] - pos1[axis1], pos2[axis2] - pos1[axis2]\r
200 \r
201         if extent1 > extent2 then\r
202                 compare = function(extent1, extent2)\r
203                         return extent1 > extent2\r
204                 end\r
205         else\r
206                 compare = function(extent1, extent2)\r
207                         return extent1 < extent2\r
208                 end\r
209         end\r
210 \r
211         --calculate the new position 2 after transposition\r
212         local newpos2 = {x=pos1.x, y=pos1.y, z=pos1.z}\r
213         newpos2[axis1] = pos1[axis1] + extent2\r
214         newpos2[axis2] = pos1[axis2] + extent1\r
215 \r
216         local pos = {x=pos1.x, y=0, z=0}\r
217         local env = minetest.env\r
218         while pos.x <= pos2.x do\r
219                 pos.y = pos1.y\r
220                 while pos.y <= pos2.y do\r
221                         pos.z = pos1.z\r
222                         while pos.z <= pos2.z do\r
223                                 local extent1, extent2 = pos[axis1] - pos1[axis1], pos[axis2] - pos1[axis2]\r
224                                 if compare(extent1, extent2) then --transpose only if below the diagonal\r
225                                         local node1 = env:get_node(pos)\r
226                                         local meta1 = env:get_meta(pos):to_table()\r
227                                         local value1, value2 = pos[axis1], pos[axis2] --save position values\r
228                                         pos[axis1], pos[axis2] = pos1[axis1] + extent2, pos1[axis2] + extent1 --swap axis extents\r
229                                         local node2 = env:get_node(pos)\r
230                                         local meta2 = env:get_meta(pos):to_table()\r
231                                         env:add_node(pos, node1)\r
232                                         env:get_meta(pos):from_table(meta1)\r
233                                         pos[axis1], pos[axis2] = value1, value2 --restore position values\r
234                                         env:add_node(pos, node2)\r
235                                         env:get_meta(pos):from_table(meta2)\r
236                                 end\r
237                                 pos.z = pos.z + 1\r
238                         end\r
239                         pos.y = pos.y + 1\r
240                 end\r
241                 pos.x = pos.x + 1\r
242         end\r
243         return worldedit.volume(pos1, pos2), pos1, newpos2\r
244 end\r
245 \r
246 --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
247 worldedit.flip = function(pos1, pos2, axis)\r
248         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
249 \r
250         local pos = {x=pos1.x, y=0, z=0}\r
251         local start = pos1[axis] + pos2[axis]\r
252         pos2[axis] = pos1[axis] + math.floor((pos2[axis] - pos1[axis]) / 2)\r
253         local env = minetest.env\r
254         while pos.x <= pos2.x do\r
255                 pos.y = pos1.y\r
256                 while pos.y <= pos2.y do\r
257                         pos.z = pos1.z\r
258                         while pos.z <= pos2.z do\r
259                                 local node1 = env:get_node(pos)\r
260                                 local meta1 = env:get_meta(pos):to_table()\r
261                                 local value = pos[axis]\r
262                                 pos[axis] = start - value\r
263                                 local node2 = env:get_node(pos)\r
264                                 local meta2 = env:get_meta(pos):to_table()\r
265                                 env:add_node(pos, node1)\r
266                                 env:get_meta(pos):from_table(meta1)\r
267                                 pos[axis] = value\r
268                                 env:add_node(pos, node2)\r
269                                 env:get_meta(pos):from_table(meta2)\r
270                                 pos.z = pos.z + 1\r
271                         end\r
272                         pos.y = pos.y + 1\r
273                 end\r
274                 pos.x = pos.x + 1\r
275         end\r
276         return worldedit.volume(pos1, pos2)\r
277 end\r
278 \r
279 --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
280 worldedit.rotate = function(pos1, pos2, axis, angle)\r
281         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
282 \r
283         local axis1, axis2\r
284         if axis == "x" then\r
285                 axis1, axis2 = "z", "y"\r
286         elseif axis == "y" then\r
287                 axis1, axis2 = "x", "z"\r
288         else --axis == "z"\r
289                 axis1, axis2 = "y", "x"\r
290         end\r
291         angle = angle % 360\r
292 \r
293         local count\r
294         if angle == 90 then\r
295                 worldedit.flip(pos1, pos2, axis1)\r
296                 count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)\r
297         elseif angle == 180 then\r
298                 worldedit.flip(pos1, pos2, axis1)\r
299                 count = worldedit.flip(pos1, pos2, axis2)\r
300         elseif angle == 270 then\r
301                 worldedit.flip(pos1, pos2, axis2)\r
302                 count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)\r
303         end\r
304         return count, pos1, pos2\r
305 end\r
306 \r
307 --digs a region defined by positions `pos1` and `pos2`, returning the number of nodes dug\r
308 worldedit.dig = function(pos1, pos2)\r
309         local pos1, pos2 = worldedit.sort_pos(pos1, pos2)\r
310         local env = minetest.env\r
311 \r
312         local pos = {x=pos1.x, y=0, z=0}\r
313         while pos.x <= pos2.x do\r
314                 pos.y = pos1.y\r
315                 while pos.y <= pos2.y do\r
316                         pos.z = pos1.z\r
317                         while pos.z <= pos2.z do#\r
318                                 local node = env:get_node(pos)\r
319                                 if node.name == "air":\r
320                                         env:dig_node(pos)\r
321                                 pos.z = pos.z + 1\r
322                         end\r
323                         pos.y = pos.y + 1\r
324                 end\r
325                 pos.x = pos.x + 1\r
326         end\r
327         return worldedit.volume(pos1, pos2)\r
328 end