]> git.lizzy.rs Git - worldedit.git/blob - worldedit_commands/init.lua
Add //hollowcube and //cube
[worldedit.git] / worldedit_commands / init.lua
1 minetest.register_privilege("worldedit", "Can use WorldEdit commands")\r
2 \r
3 worldedit.set_pos = {}\r
4 worldedit.inspect = {}\r
5 \r
6 worldedit.pos1 = {}\r
7 worldedit.pos2 = {}\r
8 if minetest.place_schematic then\r
9         worldedit.prob_pos = {}\r
10         worldedit.prob_list = {}\r
11 end\r
12 \r
13 dofile(minetest.get_modpath("worldedit_commands") .. "/cuboid.lua")\r
14 dofile(minetest.get_modpath("worldedit_commands") .. "/mark.lua")\r
15 dofile(minetest.get_modpath("worldedit_commands") .. "/wand.lua")\r
16 local safe_region, check_region, reset_pending = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua")\r
17 \r
18 local function get_position(name) --position 1 retrieval function for when not using `safe_region`\r
19         local pos1 = worldedit.pos1[name]\r
20         if pos1 == nil then\r
21                 worldedit.player_notify(name, "no position 1 selected")\r
22         end\r
23         return pos1\r
24 end\r
25 \r
26 local function get_node(name, nodename)\r
27         local node = worldedit.normalize_nodename(nodename)\r
28         if not node then\r
29                 worldedit.player_notify(name, "invalid node name: " .. nodename)\r
30                 return nil\r
31         end\r
32         return node\r
33 end\r
34 \r
35 function worldedit.player_notify(name, message)\r
36         minetest.chat_send_player(name, "WorldEdit -!- " .. message, false)\r
37 end\r
38 \r
39 --determines whether `nodename` is a valid node name, returning a boolean\r
40 worldedit.normalize_nodename = function(nodename)\r
41         nodename = nodename:gsub("^%s*(.-)%s*$", "%1")\r
42         if nodename == "" then return nil end\r
43         local fullname = ItemStack({name=nodename}):get_name() --resolve aliases of node names to full names\r
44         if minetest.registered_nodes[fullname] or fullname == "air" then --directly found node name or alias of nodename\r
45                 return fullname\r
46         end\r
47         for key, value in pairs(minetest.registered_nodes) do\r
48                 if key:find(":" .. nodename, 1, true) then --found in mod\r
49                         return key\r
50                 end\r
51         end\r
52         nodename = nodename:lower() --lowercase both for case insensitive comparison\r
53         for key, value in pairs(minetest.registered_nodes) do\r
54                 if value.description:lower() == nodename then --found in description\r
55                         return key\r
56                 end\r
57         end\r
58         return nil\r
59 end\r
60 \r
61 -- Determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)\r
62 function worldedit.player_axis(name)\r
63         local dir = minetest.get_player_by_name(name):get_look_dir()\r
64         local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)\r
65         if x > y then\r
66                 if x > z then\r
67                         return "x", dir.x > 0 and 1 or -1\r
68                 end\r
69         elseif y > z then\r
70                 return "y", dir.y > 0 and 1 or -1\r
71         end\r
72         return "z", dir.z > 0 and 1 or -1\r
73 end\r
74 \r
75 local function mkdir(path)\r
76         if minetest.mkdir then\r
77                 minetest.mkdir(path)\r
78         else\r
79                 os.execute('mkdir "' .. path .. '"')\r
80         end\r
81 end\r
82 \r
83 local function check_filename(name)\r
84         return name:find("^[%w%s%^&'@{}%[%],%$=!%-#%(%)%%%.%+~_]+$") ~= nil\r
85 end\r
86 \r
87 \r
88 minetest.register_chatcommand("/about", {\r
89         params = "",\r
90         description = "Get information about the mod",\r
91         func = function(name, param)\r
92                 worldedit.player_notify(name, "WorldEdit " .. worldedit.version_string .. " is available on this server. Type /help to get a list of commands, or get more information at https://github.com/Uberi/MineTest-WorldEdit/")\r
93         end,\r
94 })\r
95 \r
96 -- mostly copied from builtin/chatcommands.lua with minor modifications\r
97 minetest.register_chatcommand("/help", {\r
98         privs = {},\r
99         params = "[all/<cmd>]",\r
100         description = "Get help for WorldEdit commands",\r
101         func = function(name, param)\r
102                 local function is_we_command(cmd)\r
103                         return cmd:sub(0, 1) == "/"\r
104                 end\r
105                 local function format_help_line(cmd, def)\r
106                         local msg = minetest.colorize("#00ffff", "/"..cmd)\r
107                         if def.params and def.params ~= "" then\r
108                                 msg = msg .. " " .. def.params\r
109                         end\r
110                         if def.description and def.description ~= "" then\r
111                                 msg = msg .. ": " .. def.description\r
112                         end\r
113                         return msg\r
114                 end\r
115 \r
116                 if not minetest.check_player_privs(name, "worldedit") then\r
117                         return false, "You are not allowed to use any WorldEdit commands."\r
118                 end\r
119                 if param == "" then\r
120                         local msg = ""\r
121                         local cmds = {}\r
122                         for cmd, def in pairs(minetest.chatcommands) do\r
123                                 if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then\r
124                                         cmds[#cmds + 1] = cmd:sub(2) -- strip the /\r
125                                 end\r
126                         end\r
127                         table.sort(cmds)\r
128                         return true, "Available commands: " .. table.concat(cmds, " ") .. "\n"\r
129                                         .. "Use '//help <cmd>' to get more information,"\r
130                                         .. " or '//help all' to list everything."\r
131                 elseif param == "all" then\r
132                         local cmds = {}\r
133                         for cmd, def in pairs(minetest.chatcommands) do\r
134                                 if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then\r
135                                         cmds[#cmds + 1] = format_help_line(cmd, def)\r
136                                 end\r
137                         end\r
138                         table.sort(cmds)\r
139                         return true, "Available commands:\n"..table.concat(cmds, "\n")\r
140                 else\r
141                         return minetest.chatcommands["help"].func(name, "/" .. param)\r
142                 end\r
143         end,\r
144 })\r
145 \r
146 minetest.register_chatcommand("/inspect", {\r
147         params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>",\r
148         description = "Enable or disable node inspection",\r
149         privs = {worldedit=true},\r
150         func = function(name, param)\r
151                 if param == "on" or param == "1" or param == "true" or param == "yes" or param == "enable" or param == "" then\r
152                         worldedit.inspect[name] = true\r
153                         local axis, sign = worldedit.player_axis(name)\r
154                         worldedit.player_notify(name, string.format("inspector: inspection enabled for %s, currently facing the %s axis",\r
155                                 name, axis .. (sign > 0 and "+" or "-")))\r
156                 elseif param == "off" or param == "0" or param == "false" or param == "no" or param == "disable" then\r
157                         worldedit.inspect[name] = nil\r
158                         worldedit.player_notify(name, "inspector: inspection disabled")\r
159                 else\r
160                         worldedit.player_notify(name, "invalid usage: " .. param)\r
161                 end\r
162         end,\r
163 })\r
164 \r
165 local function get_node_rlight(pos)\r
166         local vecs = { -- neighboring nodes\r
167                 {x= 1, y= 0, z= 0},\r
168                 {x=-1, y= 0, z= 0},\r
169                 {x= 0, y= 1, z= 0},\r
170                 {x= 0, y=-1, z= 0},\r
171                 {x= 0, y= 0, z= 1},\r
172                 {x= 0, y= 0, z=-1},\r
173         }\r
174         local ret = 0\r
175         for _, v in ipairs(vecs) do\r
176                 ret = math.max(ret, minetest.get_node_light(vector.add(pos, v)))\r
177         end\r
178         return ret\r
179 end\r
180 \r
181 minetest.register_on_punchnode(function(pos, node, puncher)\r
182         local name = puncher:get_player_name()\r
183         if worldedit.inspect[name] then\r
184                 local axis, sign = worldedit.player_axis(name)\r
185                 message = string.format("inspector: %s at %s (param1=%d, param2=%d, received light=%d) punched facing the %s axis",\r
186                         node.name, minetest.pos_to_string(pos), node.param1, node.param2, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-"))\r
187                 worldedit.player_notify(name, message)\r
188         end\r
189 end)\r
190 \r
191 minetest.register_chatcommand("/reset", {\r
192         params = "",\r
193         description = "Reset the region so that it is empty",\r
194         privs = {worldedit=true},\r
195         func = function(name, param)\r
196                 worldedit.pos1[name] = nil\r
197                 worldedit.pos2[name] = nil\r
198                 worldedit.mark_pos1(name)\r
199                 worldedit.mark_pos2(name)\r
200                 worldedit.set_pos[name] = nil\r
201                 --make sure the user does not try to confirm an operation after resetting pos:\r
202                 reset_pending(name)\r
203                 worldedit.player_notify(name, "region reset")\r
204         end,\r
205 })\r
206 \r
207 minetest.register_chatcommand("/mark", {\r
208         params = "",\r
209         description = "Show markers at the region positions",\r
210         privs = {worldedit=true},\r
211         func = function(name, param)\r
212                 worldedit.mark_pos1(name)\r
213                 worldedit.mark_pos2(name)\r
214                 worldedit.player_notify(name, "region marked")\r
215         end,\r
216 })\r
217 \r
218 minetest.register_chatcommand("/unmark", {\r
219         params = "",\r
220         description = "Hide markers if currently shown",\r
221         privs = {worldedit=true},\r
222         func = function(name, param)\r
223                 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
224                 worldedit.pos1[name] = nil\r
225                 worldedit.pos2[name] = nil\r
226                 worldedit.mark_pos1(name)\r
227                 worldedit.mark_pos2(name)\r
228                 worldedit.pos1[name] = pos1\r
229                 worldedit.pos2[name] = pos2\r
230                 worldedit.player_notify(name, "region unmarked")\r
231         end,\r
232 })\r
233 \r
234 minetest.register_chatcommand("/pos1", {\r
235         params = "",\r
236         description = "Set WorldEdit region position 1 to the player's location",\r
237         privs = {worldedit=true},\r
238         func = function(name, param)\r
239                 local pos = minetest.get_player_by_name(name):getpos()\r
240                 pos.x, pos.y, pos.z = math.floor(pos.x + 0.5), math.floor(pos.y + 0.5), math.floor(pos.z + 0.5)\r
241                 worldedit.pos1[name] = pos\r
242                 worldedit.mark_pos1(name)\r
243                 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))\r
244         end,\r
245 })\r
246 \r
247 minetest.register_chatcommand("/pos2", {\r
248         params = "",\r
249         description = "Set WorldEdit region position 2 to the player's location",\r
250         privs = {worldedit=true},\r
251         func = function(name, param)\r
252                 local pos = minetest.get_player_by_name(name):getpos()\r
253                 pos.x, pos.y, pos.z = math.floor(pos.x + 0.5), math.floor(pos.y + 0.5), math.floor(pos.z + 0.5)\r
254                 worldedit.pos2[name] = pos\r
255                 worldedit.mark_pos2(name)\r
256                 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))\r
257         end,\r
258 })\r
259 \r
260 minetest.register_chatcommand("/p", {\r
261         params = "set/set1/set2/get",\r
262         description = "Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region",\r
263         privs = {worldedit=true},\r
264         func = function(name, param)\r
265                 if param == "set" then --set both WorldEdit positions\r
266                         worldedit.set_pos[name] = "pos1"\r
267                         worldedit.player_notify(name, "select positions by punching two nodes")\r
268                 elseif param == "set1" then --set WorldEdit position 1\r
269                         worldedit.set_pos[name] = "pos1only"\r
270                         worldedit.player_notify(name, "select position 1 by punching a node")\r
271                 elseif param == "set2" then --set WorldEdit position 2\r
272                         worldedit.set_pos[name] = "pos2"\r
273                         worldedit.player_notify(name, "select position 2 by punching a node")\r
274                 elseif param == "get" then --display current WorldEdit positions\r
275                         if worldedit.pos1[name] ~= nil then\r
276                                 worldedit.player_notify(name, "position 1: " .. minetest.pos_to_string(worldedit.pos1[name]))\r
277                         else\r
278                                 worldedit.player_notify(name, "position 1 not set")\r
279                         end\r
280                         if worldedit.pos2[name] ~= nil then\r
281                                 worldedit.player_notify(name, "position 2: " .. minetest.pos_to_string(worldedit.pos2[name]))\r
282                         else\r
283                                 worldedit.player_notify(name, "position 2 not set")\r
284                         end\r
285                 else\r
286                         worldedit.player_notify(name, "unknown subcommand: " .. param)\r
287                 end\r
288         end,\r
289 })\r
290 \r
291 minetest.register_chatcommand("/fixedpos", {\r
292         params = "set1/set2 x y z",\r
293         description = "Set a WorldEdit region position to the position at (<x>, <y>, <z>)",\r
294         privs = {worldedit=true},\r
295         func = function(name, param)\r
296                 local found, _, flag, x, y, z = param:find("^(set[12])%s+([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)$")\r
297                 if found == nil then\r
298                         worldedit.player_notify(name, "invalid usage: " .. param)\r
299                         return\r
300                 end\r
301                 local pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}\r
302                 if flag == "set1" then\r
303                         worldedit.pos1[name] = pos\r
304                         worldedit.mark_pos1(name)\r
305                         worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))\r
306                 else --flag == "set2"\r
307                         worldedit.pos2[name] = pos\r
308                         worldedit.mark_pos2(name)\r
309                         worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))\r
310                 end\r
311         end,\r
312 })\r
313 \r
314 minetest.register_on_punchnode(function(pos, node, puncher)\r
315         local name = puncher:get_player_name()\r
316         if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position\r
317                 if worldedit.set_pos[name] == "pos1" then --setting position 1\r
318                         worldedit.pos1[name] = pos\r
319                         worldedit.mark_pos1(name)\r
320                         worldedit.set_pos[name] = "pos2" --set position 2 on the next invocation\r
321                         worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))\r
322                 elseif worldedit.set_pos[name] == "pos1only" then --setting position 1 only\r
323                         worldedit.pos1[name] = pos\r
324                         worldedit.mark_pos1(name)\r
325                         worldedit.set_pos[name] = nil --finished setting positions\r
326                         worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))\r
327                 elseif worldedit.set_pos[name] == "pos2" then --setting position 2\r
328                         worldedit.pos2[name] = pos\r
329                         worldedit.mark_pos2(name)\r
330                         worldedit.set_pos[name] = nil --finished setting positions\r
331                         worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))\r
332                 elseif worldedit.set_pos[name] == "prob" then --setting Minetest schematic node probabilities\r
333                         worldedit.prob_pos[name] = pos\r
334                         minetest.show_formspec(puncher:get_player_name(), "prob_val_enter", "field[text;;]")\r
335                 end\r
336         end\r
337 end)\r
338 \r
339 minetest.register_chatcommand("/volume", {\r
340         params = "",\r
341         description = "Display the volume of the current WorldEdit region",\r
342         privs = {worldedit=true},\r
343         func = function(name, param)\r
344                 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
345                 if pos1 == nil or pos2 == nil then\r
346                         worldedit.player_notify(name, "no region selected")\r
347                         return nil\r
348                 end\r
349 \r
350                 local volume = worldedit.volume(pos1, pos2)\r
351                 local abs = math.abs\r
352                 worldedit.player_notify(name, "current region has a volume of " .. volume .. " nodes ("\r
353                         .. abs(pos2.x - pos1.x) + 1 .. "*"\r
354                         .. abs(pos2.y - pos1.y) + 1 .. "*"\r
355                         .. abs(pos2.z - pos1.z) + 1 .. ")")\r
356         end,\r
357 })\r
358 \r
359 minetest.register_chatcommand("/deleteblocks", {\r
360         params = "",\r
361         description = "remove all MapBlocks (16x16x16) containing the selected area from the map",\r
362         privs = {worldedit=true},\r
363         func = safe_region(function(name, param)\r
364                 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
365                 local success = minetest.delete_area(pos1, pos2)\r
366                 if success then\r
367                         worldedit.player_notify(name, "Area deleted.")\r
368                 else\r
369                         worldedit.player_notify(name, "There was an error during deletion of the area.")\r
370                 end\r
371         end),\r
372 })\r
373 \r
374 minetest.register_chatcommand("/set", {\r
375         params = "<node>",\r
376         description = "Set the current WorldEdit region to <node>",\r
377         privs = {worldedit=true},\r
378         func = safe_region(function(name, param)\r
379                 local node = get_node(name, param)\r
380                 if not node then\r
381                         worldedit.player_notify(name, "Could not identify node \"" .. param .. "\"")\r
382                         return\r
383                 end\r
384 \r
385                 local count = worldedit.set(worldedit.pos1[name], worldedit.pos2[name], node)\r
386                 worldedit.player_notify(name, count .. " nodes set")\r
387         end, check_region),\r
388 })\r
389 \r
390 minetest.register_chatcommand("/mix", {\r
391         params = "<node1> ...",\r
392         description = "Fill the current WorldEdit region with a random mix of <node1>, ...",\r
393         privs = {worldedit=true},\r
394         func = safe_region(function(name, param)\r
395                 local nodes = {}\r
396                 for nodename in param:gmatch("[^%s]+") do\r
397                         local node = get_node(name, nodename)\r
398                         if not node then\r
399                                 worldedit.player_notify(name, "Could not identify node \"" .. name .. "\"")\r
400                                 return\r
401                         end\r
402                         nodes[#nodes + 1] = node\r
403                 end\r
404 \r
405                 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
406                 local count = worldedit.set(pos1, pos2, nodes)\r
407                 worldedit.player_notify(name, count .. " nodes set")\r
408         end, check_region),\r
409 })\r
410 \r
411 local check_replace = function(name, param)\r
412         local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$")\r
413         if found == nil then\r
414                 worldedit.player_notify(name, "invalid usage: " .. param)\r
415                 return nil\r
416         end\r
417         local newsearchnode = worldedit.normalize_nodename(searchnode)\r
418         if not newsearchnode then\r
419                 worldedit.player_notify(name, "invalid search node name: " .. searchnode)\r
420                 return nil\r
421         end\r
422         local newreplacenode = worldedit.normalize_nodename(replacenode)\r
423         if not newreplacenode then\r
424                 worldedit.player_notify(name, "invalid replace node name: " .. replacenode)\r
425                 return nil\r
426         end\r
427         return check_region(name, param)\r
428 end\r
429 \r
430 minetest.register_chatcommand("/replace", {\r
431         params = "<search node> <replace node>",\r
432         description = "Replace all instances of <search node> with <replace node> in the current WorldEdit region",\r
433         privs = {worldedit=true},\r
434         func = safe_region(function(name, param)\r
435                 local found, _, search_node, replace_node = param:find("^([^%s]+)%s+(.+)$")\r
436                 local norm_search_node = worldedit.normalize_nodename(search_node)\r
437                 local norm_replace_node = worldedit.normalize_nodename(replace_node)\r
438                 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],\r
439                                 norm_search_node, norm_replace_node)\r
440                 worldedit.player_notify(name, count .. " nodes replaced")\r
441         end, check_replace),\r
442 })\r
443 \r
444 minetest.register_chatcommand("/replaceinverse", {\r
445         params = "<search node> <replace node>",\r
446         description = "Replace all nodes other than <search node> with <replace node> in the current WorldEdit region",\r
447         privs = {worldedit=true},\r
448         func = safe_region(function(name, param)\r
449                 local found, _, search_node, replace_node = param:find("^([^%s]+)%s+(.+)$")\r
450                 local norm_search_node = worldedit.normalize_nodename(search_node)\r
451                 local norm_replace_node = worldedit.normalize_nodename(replace_node)\r
452                 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],\r
453                                 norm_search_node, norm_replace_node, true)\r
454                 worldedit.player_notify(name, count .. " nodes replaced")\r
455         end, check_replace),\r
456 })\r
457 \r
458 local check_cube = function(name, param)\r
459         if worldedit.pos1[name] == nil then\r
460                 worldedit.player_notify(name, "no position 1 selected")\r
461                 return nil\r
462         end\r
463         local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
464         if found == nil then\r
465                 worldedit.player_notify(name, "invalid usage: " .. param)\r
466                 return nil\r
467         end\r
468         local node = get_node(name, nodename)\r
469         if not node then return nil end\r
470         return tonumber(w) * tonumber(h) * tonumber(l)\r
471 end\r
472 \r
473 minetest.register_chatcommand("/hollowcube", {\r
474         params = "<width> <height> <length> <node>",\r
475         description = "Add a hollow cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.",\r
476         privs = {worldedit=true},\r
477         func = safe_region(function(name, param)\r
478                 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
479                 local node = get_node(name, nodename)\r
480                 local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node, true)\r
481                 worldedit.player_notify(name, count .. " nodes added")\r
482         end, check_cube),\r
483 })\r
484 \r
485 minetest.register_chatcommand("/cube", {\r
486         params = "<width> <height> <length> <node>",\r
487         description = "Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.",\r
488         privs = {worldedit=true},\r
489         func = safe_region(function(name, param)\r
490                 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
491                 local node = get_node(name, nodename)\r
492                 local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node)\r
493                 worldedit.player_notify(name, count .. " nodes added")\r
494         end, check_cube),\r
495 })\r
496 \r
497 local check_sphere = function(name, param)\r
498         if worldedit.pos1[name] == nil then\r
499                 worldedit.player_notify(name, "no position 1 selected")\r
500                 return nil\r
501         end\r
502         local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")\r
503         if found == nil then\r
504                 worldedit.player_notify(name, "invalid usage: " .. param)\r
505                 return nil\r
506         end\r
507         local node = get_node(name, nodename)\r
508         if not node then return nil end\r
509         return math.ceil((4 * math.pi * (tonumber(radius) ^ 3)) / 3) --volume of sphere\r
510 end\r
511 \r
512 minetest.register_chatcommand("/hollowsphere", {\r
513         params = "<radius> <node>",\r
514         description = "Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",\r
515         privs = {worldedit=true},\r
516         func = safe_region(function(name, param)\r
517                 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")\r
518                 local node = get_node(name, nodename)\r
519                 local count = worldedit.sphere(worldedit.pos1[name], tonumber(radius), node, true)\r
520                 worldedit.player_notify(name, count .. " nodes added")\r
521         end, check_sphere),\r
522 })\r
523 \r
524 minetest.register_chatcommand("/sphere", {\r
525         params = "<radius> <node>",\r
526         description = "Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",\r
527         privs = {worldedit=true},\r
528         func = safe_region(function(name, param)\r
529                 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")\r
530                 local node = get_node(name, nodename)\r
531                 local count = worldedit.sphere(worldedit.pos1[name], tonumber(radius), node)\r
532                 worldedit.player_notify(name, count .. " nodes added")\r
533         end, check_sphere),\r
534 })\r
535 \r
536 local check_dome = function(name, param)\r
537         if worldedit.pos1[name] == nil then\r
538                 worldedit.player_notify(name, "no position 1 selected")\r
539                 return nil\r
540         end\r
541         local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")\r
542         if found == nil then\r
543                 worldedit.player_notify(name, "invalid usage: " .. param)\r
544                 return nil\r
545         end\r
546         local node = get_node(name, nodename)\r
547         if not node then return nil end\r
548         return math.ceil((2 * math.pi * (tonumber(radius) ^ 3)) / 3) --volume of dome\r
549 end\r
550 \r
551 minetest.register_chatcommand("/hollowdome", {\r
552         params = "<radius> <node>",\r
553         description = "Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",\r
554         privs = {worldedit=true},\r
555         func = safe_region(function(name, param)\r
556                 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")\r
557                 local node = get_node(name, nodename)\r
558                 local count = worldedit.dome(worldedit.pos1[name], tonumber(radius), node, true)\r
559                 worldedit.player_notify(name, count .. " nodes added")\r
560         end, check_dome),\r
561 })\r
562 \r
563 minetest.register_chatcommand("/dome", {\r
564         params = "<radius> <node>",\r
565         description = "Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",\r
566         privs = {worldedit=true},\r
567         func = safe_region(function(name, param)\r
568                 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")\r
569                 local node = get_node(name, nodename)\r
570                 local count = worldedit.dome(worldedit.pos1[name], tonumber(radius), node)\r
571                 worldedit.player_notify(name, count .. " nodes added")\r
572         end, check_dome),\r
573 })\r
574 \r
575 local check_cylinder = function(name, param)\r
576         if worldedit.pos1[name] == nil then\r
577                 worldedit.player_notify(name, "no position 1 selected")\r
578                 return nil\r
579         end\r
580         -- two radii\r
581         local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
582         if found == nil then\r
583                 -- single radius\r
584                 found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")\r
585                 radius2 = radius1\r
586         end\r
587         if found == nil then\r
588                 worldedit.player_notify(name, "invalid usage: " .. param)\r
589                 return nil\r
590         end\r
591         local node = get_node(name, nodename)\r
592         if not node then return nil end\r
593         local radius = math.max(tonumber(radius1), tonumber(radius2))\r
594         return math.ceil(math.pi * (radius ^ 2) * tonumber(length))\r
595 end\r
596 \r
597 minetest.register_chatcommand("/hollowcylinder", {\r
598         params = "x/y/z/? <length> <radius1> [radius2] <node>",\r
599         description = "Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>",\r
600         privs = {worldedit=true},\r
601         func = safe_region(function(name, param)\r
602                 -- two radii\r
603                 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
604                 if found == nil then\r
605                         -- single radius\r
606                         found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")\r
607                         radius2 = radius1\r
608                 end\r
609                 length = tonumber(length)\r
610                 if axis == "?" then\r
611                         axis, sign = worldedit.player_axis(name)\r
612                         length = length * sign\r
613                 end\r
614                 local node = get_node(name, nodename)\r
615                 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node, true)\r
616                 worldedit.player_notify(name, count .. " nodes added")\r
617         end, check_cylinder),\r
618 })\r
619 \r
620 minetest.register_chatcommand("/cylinder", {\r
621         params = "x/y/z/? <length> <radius1> [radius2] <node>",\r
622         description = "Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>",\r
623         privs = {worldedit=true},\r
624         func = safe_region(function(name, param)\r
625                 -- two radii\r
626                 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
627                 if found == nil then\r
628                         -- single radius\r
629                         found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")\r
630                         radius2 = radius1\r
631                 end\r
632                 length = tonumber(length)\r
633                 if axis == "?" then\r
634                         axis, sign = worldedit.player_axis(name)\r
635                         length = length * sign\r
636                 end\r
637                 local node = get_node(name, nodename)\r
638                 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node)\r
639                 worldedit.player_notify(name, count .. " nodes added")\r
640         end, check_cylinder),\r
641 })\r
642 \r
643 local check_pyramid = function(name, param)\r
644         if worldedit.pos1[name] == nil then\r
645                 worldedit.player_notify(name, "no position 1 selected")\r
646                 return nil\r
647         end\r
648         local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")\r
649         if found == nil then\r
650                 worldedit.player_notify(name, "invalid usage: " .. param)\r
651                 return nil\r
652         end\r
653         local node = get_node(name, nodename)\r
654         if not node then return nil end\r
655         height = tonumber(height)\r
656         return math.ceil(((height * 2 + 1) ^ 2) * height / 3)\r
657 end\r
658      \r
659 minetest.register_chatcommand("/hollowpyramid", {\r
660         params = "x/y/z/? <height> <node>",\r
661         description = "Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",\r
662         privs = {worldedit=true},\r
663         func = safe_region(function(name, param)\r
664                 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")\r
665                 height = tonumber(height)\r
666                 if axis == "?" then\r
667                         axis, sign = worldedit.player_axis(name)\r
668                         height = height * sign\r
669                 end\r
670                 local node = get_node(name, nodename)\r
671                 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true)\r
672                 worldedit.player_notify(name, count .. " nodes added")\r
673         end, check_pyramid),\r
674 })\r
675 \r
676 minetest.register_chatcommand("/pyramid", {\r
677         params = "x/y/z/? <height> <node>",\r
678         description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",\r
679         privs = {worldedit=true},\r
680         func = safe_region(function(name, param)\r
681                 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")\r
682                 height = tonumber(height)\r
683                 if axis == "?" then\r
684                         axis, sign = worldedit.player_axis(name)\r
685                         height = height * sign\r
686                 end\r
687                 local node = get_node(name, nodename)\r
688                 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)\r
689                 worldedit.player_notify(name, count .. " nodes added")\r
690         end, check_pyramid),\r
691 })\r
692 \r
693 minetest.register_chatcommand("/spiral", {\r
694         params = "<length> <height> <space> <node>",\r
695         description = "Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>",\r
696         privs = {worldedit=true},\r
697         func = safe_region(function(name, param)\r
698                 local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
699                 local node = get_node(name, nodename)\r
700                 local count = worldedit.spiral(worldedit.pos1[name], tonumber(length), tonumber(height), tonumber(space), node)\r
701                 worldedit.player_notify(name, count .. " nodes added")\r
702         end,\r
703         function(name, param)\r
704                 if worldedit.pos1[name] == nil then\r
705                         worldedit.player_notify(name, "no position 1 selected")\r
706                         return nil\r
707                 end\r
708                 local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
709                 if found == nil then\r
710                         worldedit.player_notify(name, "invalid usage: " .. param)\r
711                         return nil\r
712                 end\r
713                 local node = get_node(name, nodename)\r
714                 if not node then return nil end\r
715                 return 1 -- TODO: return an useful value\r
716         end),\r
717 })\r
718 \r
719 minetest.register_chatcommand("/copy", {\r
720         params = "x/y/z/? <amount>",\r
721         description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",\r
722         privs = {worldedit=true},\r
723         func = safe_region(function(name, param)\r
724                 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
725                 if found == nil then\r
726                         worldedit.player_notify(name, "invalid usage: " .. param)\r
727                         return\r
728                 end\r
729                 amount = tonumber(amount)\r
730                 if axis == "?" then\r
731                         axis, sign = worldedit.player_axis(name)\r
732                         amount = amount * sign\r
733                 end\r
734 \r
735                 local count = worldedit.copy(worldedit.pos1[name], worldedit.pos2[name], axis, amount)\r
736                 worldedit.player_notify(name, count .. " nodes copied")\r
737         end,\r
738         function(name, param)\r
739                 local volume = check_region(name, param)\r
740                 return volume and volume * 2 or volume\r
741         end),\r
742 })\r
743 \r
744 minetest.register_chatcommand("/move", {\r
745         params = "x/y/z/? <amount>",\r
746         description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",\r
747         privs = {worldedit=true},\r
748         func = safe_region(function(name, param)\r
749                 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
750                 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
751                 if found == nil then\r
752                         worldedit.player_notify(name, "invalid usage: " .. param)\r
753                         return\r
754                 end\r
755                 amount = tonumber(amount)\r
756                 if axis == "?" then\r
757                         axis, sign = worldedit.player_axis(name)\r
758                         amount = amount * sign\r
759                 end\r
760 \r
761                 local count = worldedit.move(pos1, pos2, axis, amount)\r
762 \r
763                 pos1[axis] = pos1[axis] + amount\r
764                 pos2[axis] = pos2[axis] + amount\r
765                 worldedit.mark_pos1(name)\r
766                 worldedit.mark_pos2(name)\r
767                 worldedit.player_notify(name, count .. " nodes moved")\r
768         end, check_region),\r
769 })\r
770 \r
771 minetest.register_chatcommand("/stack", {\r
772         params = "x/y/z/? <count>",\r
773         description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",\r
774         privs = {worldedit=true},\r
775         func = safe_region(function(name, param)\r
776                 local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
777                 repetitions = tonumber(repetitions)\r
778                 if axis == "?" then\r
779                         axis, sign = worldedit.player_axis(name)\r
780                         repetitions = repetitions * sign\r
781                 end\r
782                 local count = worldedit.stack(worldedit.pos1[name], worldedit.pos2[name], axis, repetitions)\r
783                 worldedit.player_notify(name, count .. " nodes stacked")\r
784         end,\r
785         function(name, param)\r
786                 local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
787                 if found == nil then\r
788                         worldedit.player_notify(name, "invalid usage: " .. param)\r
789                         return\r
790                 end\r
791                 local count = check_region(name, param)\r
792                 if count then return (tonumber(repetitions) + 1) * count end\r
793                 return nil\r
794         end),\r
795 })\r
796 \r
797 minetest.register_chatcommand("/stack2", {\r
798         params = "<count> <x> <y> <z>",\r
799         description = "Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>",\r
800         privs = {worldedit=true},\r
801         func = function(name, param)\r
802                 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
803                 if pos1 == nil or pos2 == nil then\r
804                         worldedit.player_notify(name, "Select a position first!")\r
805                         return\r
806                 end\r
807                 local repetitions, incs = param:match("(%d+)%s*(.+)")\r
808                 if repetitions == nil then\r
809                         worldedit.player_notify(name, "invalid count: " .. param)\r
810                         return\r
811                 end\r
812                 repetitions = tonumber(repetitions)\r
813 \r
814                 local x, y, z = incs:match("([+-]?%d+) ([+-]?%d+) ([+-]?%d+)")\r
815                 if x == nil then\r
816                         worldedit.player_notify(name, "invalid increments: " .. param)\r
817                         return\r
818                 end\r
819                 x, y, z = tonumber(x), tonumber(y), tonumber(z)\r
820 \r
821                 local count = worldedit.volume(pos1, pos2) * repetitions\r
822 \r
823                 return safe_region(function()\r
824                         worldedit.stack2(pos1, pos2, {x=x, y=y, z=z}, repetitions,\r
825                                 function() worldedit.player_notify(name, count .. " nodes stacked") end)\r
826                 end, function()\r
827                         return count\r
828                 end)(name,param) -- more hax --wip: clean this up a little bit\r
829         end\r
830 })\r
831 \r
832 \r
833 minetest.register_chatcommand("/stretch", {\r
834         params = "<stretchx> <stretchy> <stretchz>",\r
835         description = "Scale the current WorldEdit positions and region by a factor of <stretchx>, <stretchy>, <stretchz> along the X, Y, and Z axes, repectively, with position 1 as the origin",\r
836         privs = {worldedit=true},\r
837         func = safe_region(function(name, param)\r
838                 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
839                 local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")\r
840                 stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)\r
841                 local count, pos1, pos2 = worldedit.stretch(pos1, pos2, stretchx, stretchy, stretchz)\r
842 \r
843                 --reset markers to scaled positions\r
844                 worldedit.pos1[name] = pos1\r
845                 worldedit.pos2[name] = pos2\r
846                 worldedit.mark_pos1(name)\r
847                 worldedit.mark_pos2(name)\r
848 \r
849                 worldedit.player_notify(name, count .. " nodes stretched")\r
850         end,\r
851         function(name, param)\r
852                 local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")\r
853                 if found == nil then\r
854                         worldedit.player_notify(name, "invalid usage: " .. param)\r
855                         return nil\r
856                 end\r
857                 stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)\r
858                 if stretchx == 0 or stretchy == 0 or stretchz == 0 then\r
859                         worldedit.player_notify(name, "invalid scaling factors: " .. param)\r
860                 end\r
861                 local count = check_region(name, param)\r
862                 if count then return tonumber(stretchx) * tonumber(stretchy) * tonumber(stretchz) * count end\r
863                 return nil\r
864         end),\r
865 })\r
866 \r
867 minetest.register_chatcommand("/transpose", {\r
868         params = "x/y/z/? x/y/z/?",\r
869         description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",\r
870         privs = {worldedit=true},\r
871         func = safe_region(function(name, param)\r
872                 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
873                 local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")\r
874                 if axis1 == "?" then axis1 = worldedit.player_axis(name) end\r
875                 if axis2 == "?" then axis2 = worldedit.player_axis(name) end\r
876                 local count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)\r
877 \r
878                 --reset markers to transposed positions\r
879                 worldedit.pos1[name] = pos1\r
880                 worldedit.pos2[name] = pos2\r
881                 worldedit.mark_pos1(name)\r
882                 worldedit.mark_pos2(name)\r
883 \r
884                 worldedit.player_notify(name, count .. " nodes transposed")\r
885         end,\r
886         function(name, param)\r
887                 local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")\r
888                 if found == nil then\r
889                         worldedit.player_notify(name, "invalid usage: " .. param)\r
890                         return nil\r
891                 end\r
892                 if axis1 == axis2 then\r
893                         worldedit.player_notify(name, "invalid usage: axes must be different")\r
894                         return nil\r
895                 end\r
896                 return check_region(name, param)\r
897         end),\r
898 })\r
899 \r
900 minetest.register_chatcommand("/flip", {\r
901         params = "x/y/z/?",\r
902         description = "Flip the current WorldEdit region along the x/y/z/? axis",\r
903         privs = {worldedit=true},\r
904         func = safe_region(function(name, param)\r
905                 if param == "?" then param = worldedit.player_axis(name) end\r
906                 local count = worldedit.flip(worldedit.pos1[name], worldedit.pos2[name], param)\r
907                 worldedit.player_notify(name, count .. " nodes flipped")\r
908         end,\r
909         function(name, param)\r
910                 if param ~= "x" and param ~= "y" and param ~= "z" and param ~= "?" then\r
911                         worldedit.player_notify(name, "invalid usage: " .. param)\r
912                         return nil\r
913                 end\r
914                 return check_region(name, param)\r
915         end),\r
916 })\r
917 \r
918 minetest.register_chatcommand("/rotate", {\r
919         params = "<axis> <angle>",\r
920         description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",\r
921         privs = {worldedit=true},\r
922         func = safe_region(function(name, param)\r
923                 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]\r
924                 local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
925                 if axis == "?" then axis = worldedit.player_axis(name) end\r
926                 local count, pos1, pos2 = worldedit.rotate(pos1, pos2, axis, angle)\r
927 \r
928                 --reset markers to rotated positions\r
929                 worldedit.pos1[name] = pos1\r
930                 worldedit.pos2[name] = pos2\r
931                 worldedit.mark_pos1(name)\r
932                 worldedit.mark_pos2(name)\r
933 \r
934                 worldedit.player_notify(name, count .. " nodes rotated")\r
935         end,\r
936         function(name, param)\r
937                 local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")\r
938                 if found == nil then\r
939                         worldedit.player_notify(name, "invalid usage: " .. param)\r
940                         return nil\r
941                 end\r
942                 if angle % 90 ~= 0 then\r
943                         worldedit.player_notify(name, "invalid usage: angle must be multiple of 90")\r
944                         return nil\r
945                 end\r
946                 return check_region(name, param)\r
947         end),\r
948 })\r
949 \r
950 minetest.register_chatcommand("/orient", {\r
951         params = "<angle>",\r
952         description = "Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)",\r
953         privs = {worldedit=true},\r
954         func = safe_region(function(name, param)\r
955                 local found, _, angle = param:find("^([+-]?%d+)$")\r
956                 local count = worldedit.orient(worldedit.pos1[name], worldedit.pos2[name], angle)\r
957                 worldedit.player_notify(name, count .. " nodes oriented")\r
958         end,\r
959         function(name, param)\r
960                 local found, _, angle = param:find("^([+-]?%d+)$")\r
961                 if found == nil then\r
962                         worldedit.player_notify(name, "invalid usage: " .. param)\r
963                         return nil\r
964                 end\r
965                 if angle % 90 ~= 0 then\r
966                         worldedit.player_notify(name, "invalid usage: angle must be multiple of 90")\r
967                         return nil\r
968                 end\r
969                 return check_region(name, param)\r
970         end),\r
971 })\r
972 \r
973 minetest.register_chatcommand("/fixlight", {\r
974         params = "",\r
975         description = "Fix the lighting in the current WorldEdit region",\r
976         privs = {worldedit=true},\r
977         func = safe_region(function(name, param)\r
978                 local count = worldedit.fixlight(worldedit.pos1[name], worldedit.pos2[name])\r
979                 worldedit.player_notify(name, count .. " nodes updated")\r
980         end),\r
981 })\r
982 \r
983 minetest.register_chatcommand("/drain", {\r
984         params = "",\r
985         description = "Remove any fluid node within the current WorldEdit region",\r
986         privs = {worldedit=true},\r
987         func = safe_region(function(name, param)\r
988                 -- TODO: make an API function for this\r
989                 local count = 0\r
990                 local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])\r
991                 for x = pos1.x, pos2.x do\r
992                 for y = pos1.y, pos2.y do\r
993                 for z = pos1.z, pos2.z do\r
994                         local n = minetest.get_node({x=x, y=y, z=z}).name\r
995                         local d = minetest.registered_nodes[n]\r
996                         if d ~= nil and (d["drawtype"] == "liquid" or d["drawtype"] == "flowingliquid") then\r
997                                 minetest.remove_node({x=x, y=y, z=z})\r
998                                 count = count + 1\r
999                         end\r
1000                 end\r
1001                 end\r
1002                 end\r
1003                 worldedit.player_notify(name, count .. " nodes updated")\r
1004         end),\r
1005 })\r
1006 \r
1007 minetest.register_chatcommand("/hide", {\r
1008         params = "",\r
1009         description = "Hide all nodes in the current WorldEdit region non-destructively",\r
1010         privs = {worldedit=true},\r
1011         func = safe_region(function(name, param)\r
1012                 local count = worldedit.hide(worldedit.pos1[name], worldedit.pos2[name])\r
1013                 worldedit.player_notify(name, count .. " nodes hidden")\r
1014         end),\r
1015 })\r
1016 \r
1017 minetest.register_chatcommand("/suppress", {\r
1018         params = "<node>",\r
1019         description = "Suppress all <node> in the current WorldEdit region non-destructively",\r
1020         privs = {worldedit=true},\r
1021         func = safe_region(function(name, param)\r
1022                 local node = get_node(name, param)\r
1023                 local count = worldedit.suppress(worldedit.pos1[name], worldedit.pos2[name], node)\r
1024                 worldedit.player_notify(name, count .. " nodes suppressed")\r
1025         end, check_region),\r
1026 })\r
1027 \r
1028 minetest.register_chatcommand("/highlight", {\r
1029         params = "<node>",\r
1030         description = "Highlight <node> in the current WorldEdit region by hiding everything else non-destructively",\r
1031         privs = {worldedit=true},\r
1032         func = safe_region(function(name, param)\r
1033                 local node = get_node(name, param)\r
1034                 local count = worldedit.highlight(worldedit.pos1[name], worldedit.pos2[name], node)\r
1035                 worldedit.player_notify(name, count .. " nodes highlighted")\r
1036         end, check_region),\r
1037 })\r
1038 \r
1039 minetest.register_chatcommand("/restore", {\r
1040         params = "",\r
1041         description = "Restores nodes hidden with WorldEdit in the current WorldEdit region",\r
1042         privs = {worldedit=true},\r
1043         func = safe_region(function(name, param)\r
1044                 local count = worldedit.restore(worldedit.pos1[name], worldedit.pos2[name])\r
1045                 worldedit.player_notify(name, count .. " nodes restored")\r
1046         end),\r
1047 })\r
1048 \r
1049 minetest.register_chatcommand("/save", {\r
1050         params = "<file>",\r
1051         description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",\r
1052         privs = {worldedit=true},\r
1053         func = safe_region(function(name, param)\r
1054                 if param == "" then\r
1055                         worldedit.player_notify(name, "invalid usage: " .. param)\r
1056                         return\r
1057                 end\r
1058                 if not check_filename(param) then\r
1059                         worldedit.player_notify(name, "Disallowed file name: " .. param)\r
1060                         return\r
1061                 end\r
1062                 local result, count = worldedit.serialize(worldedit.pos1[name],\r
1063                                 worldedit.pos2[name])\r
1064 \r
1065                 local path = minetest.get_worldpath() .. "/schems"\r
1066                 -- Create directory if it does not already exist\r
1067                 mkdir(path)\r
1068 \r
1069                 local filename = path .. "/" .. param .. ".we"\r
1070                 local file, err = io.open(filename, "wb")\r
1071                 if err ~= nil then\r
1072                         worldedit.player_notify(name, "Could not save file to \"" .. filename .. "\"")\r
1073                         return\r
1074                 end\r
1075                 file:write(result)\r
1076                 file:flush()\r
1077                 file:close()\r
1078 \r
1079                 worldedit.player_notify(name, count .. " nodes saved")\r
1080         end),\r
1081 })\r
1082 \r
1083 minetest.register_chatcommand("/allocate", {\r
1084         params = "<file>",\r
1085         description = "Set the region defined by nodes from \"(world folder)/schems/<file>.we\" as the current WorldEdit region",\r
1086         privs = {worldedit=true},\r
1087         func = function(name, param)\r
1088                 local pos = get_position(name)\r
1089                 if pos == nil then return end\r
1090 \r
1091                 if param == "" then\r
1092                         worldedit.player_notify(name, "invalid usage: " .. param)\r
1093                         return\r
1094                 end\r
1095                 if not check_filename(param) then\r
1096                         worldedit.player_notify(name, "Disallowed file name: " .. param)\r
1097                         return\r
1098                 end\r
1099 \r
1100                 local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"\r
1101                 local file, err = io.open(filename, "rb")\r
1102                 if err ~= nil then\r
1103                         worldedit.player_notify(name, "could not open file \"" .. filename .. "\"")\r
1104                         return\r
1105                 end\r
1106                 local value = file:read("*a")\r
1107                 file:close()\r
1108 \r
1109                 local version = worldedit.read_header(value)\r
1110                 if version == 0 then\r
1111                         worldedit.player_notify(name, "File is invalid!")\r
1112                         return\r
1113                 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then\r
1114                         worldedit.player_notify(name, "File was created with newer version of WorldEdit!")\r
1115                 end\r
1116                 local nodepos1, nodepos2, count = worldedit.allocate(pos, value)\r
1117 \r
1118                 worldedit.pos1[name] = nodepos1\r
1119                 worldedit.mark_pos1(name)\r
1120                 worldedit.pos2[name] = nodepos2\r
1121                 worldedit.mark_pos2(name)\r
1122 \r
1123                 worldedit.player_notify(name, count .. " nodes allocated")\r
1124         end,\r
1125 })\r
1126 \r
1127 minetest.register_chatcommand("/load", {\r
1128         params = "<file>",\r
1129         description = "Load nodes from \"(world folder)/schems/<file>[.we[m]]\" with position 1 of the current WorldEdit region as the origin",\r
1130         privs = {worldedit=true},\r
1131         func = function(name, param)\r
1132                 local pos = get_position(name)\r
1133                 if pos == nil then return end\r
1134 \r
1135                 if param == "" then\r
1136                         worldedit.player_notify(name, "invalid usage: " .. param)\r
1137                         return\r
1138                 end\r
1139                 if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then\r
1140                         worldedit.player_notify(name, "invalid file name: " .. param)\r
1141                         return\r
1142                 end\r
1143 \r
1144                 --find the file in the world path\r
1145                 local testpaths = {\r
1146                         minetest.get_worldpath() .. "/schems/" .. param,\r
1147                         minetest.get_worldpath() .. "/schems/" .. param .. ".we",\r
1148                         minetest.get_worldpath() .. "/schems/" .. param .. ".wem",\r
1149                 }\r
1150                 local file, err\r
1151                 for index, path in ipairs(testpaths) do\r
1152                         file, err = io.open(path, "rb")\r
1153                         if not err then\r
1154                                 break\r
1155                         end\r
1156                 end\r
1157                 if err then\r
1158                         worldedit.player_notify(name, "could not open file \"" .. param .. "\"")\r
1159                         return\r
1160                 end\r
1161                 local value = file:read("*a")\r
1162                 file:close()\r
1163 \r
1164                 local version = worldedit.read_header(value)\r
1165                 if version == 0 then\r
1166                         worldedit.player_notify(name, "File is invalid!")\r
1167                         return\r
1168                 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then\r
1169                         worldedit.player_notify(name, "File was created with newer version of WorldEdit!")\r
1170                         return\r
1171                 end\r
1172 \r
1173                 local count = worldedit.deserialize(pos, value)\r
1174 \r
1175                 worldedit.player_notify(name, count .. " nodes loaded")\r
1176         end,\r
1177 })\r
1178 \r
1179 minetest.register_chatcommand("/lua", {\r
1180         params = "<code>",\r
1181         description = "Executes <code> as a Lua chunk in the global namespace",\r
1182         privs = {worldedit=true, server=true},\r
1183         func = function(name, param)\r
1184                 local err = worldedit.lua(param)\r
1185                 if err then\r
1186                         worldedit.player_notify(name, "code error: " .. err)\r
1187                         minetest.log("action", name.." tried to execute "..param)\r
1188                 else\r
1189                         worldedit.player_notify(name, "code successfully executed", false)\r
1190                         minetest.log("action", name.." executed "..param)\r
1191                 end\r
1192         end,\r
1193 })\r
1194 \r
1195 minetest.register_chatcommand("/luatransform", {\r
1196         params = "<code>",\r
1197         description = "Executes <code> as a Lua chunk in the global namespace with the variable pos available, for each node in the current WorldEdit region",\r
1198         privs = {worldedit=true, server=true},\r
1199         func = safe_region(function(name, param)\r
1200                 local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)\r
1201                 if err then\r
1202                         worldedit.player_notify(name, "code error: " .. err, false)\r
1203                         minetest.log("action", name.." tried to execute luatransform "..param)\r
1204                 else\r
1205                         worldedit.player_notify(name, "code successfully executed", false)\r
1206                         minetest.log("action", name.." executed luatransform "..param)\r
1207                 end\r
1208         end),\r
1209 })\r
1210 \r
1211 minetest.register_chatcommand("/mtschemcreate", {\r
1212         params = "<file>",\r
1213         description = "Save the current WorldEdit region using the Minetest "..\r
1214                 "Schematic format to \"(world folder)/schems/<filename>.mts\"",\r
1215         privs = {worldedit=true},\r
1216         func = safe_region(function(name, param)\r
1217                 if param == nil then\r
1218                         worldedit.player_notify(name, "No filename specified")\r
1219                         return\r
1220                 end\r
1221                 if not check_filename(param) then\r
1222                         worldedit.player_notify(name, "Disallowed file name: " .. param)\r
1223                         return\r
1224                 end\r
1225 \r
1226                 local path = minetest.get_worldpath() .. "/schems"\r
1227                 -- Create directory if it does not already exist\r
1228                 mkdir(path)\r
1229 \r
1230                 local filename = path .. "/" .. param .. ".mts"\r
1231                 local ret = minetest.create_schematic(worldedit.pos1[name],\r
1232                                 worldedit.pos2[name], worldedit.prob_list[name],\r
1233                                 filename)\r
1234                 if ret == nil then\r
1235                         worldedit.player_notify(name, "Failed to create Minetest schematic", false)\r
1236                 else\r
1237                         worldedit.player_notify(name, "Saved Minetest schematic to " .. param, false)\r
1238                 end\r
1239                 worldedit.prob_list[name] = {}\r
1240         end),\r
1241 })\r
1242 \r
1243 minetest.register_chatcommand("/mtschemplace", {\r
1244         params = "<file>",\r
1245         description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin",\r
1246         privs = {worldedit=true},\r
1247         func = function(name, param)\r
1248                 if param == "" then\r
1249                         worldedit.player_notify(name, "no filename specified")\r
1250                         return\r
1251                 end\r
1252                 if not check_filename(param) then\r
1253                         worldedit.player_notify(name, "Disallowed file name: " .. param)\r
1254                         return\r
1255                 end\r
1256 \r
1257                 local pos = get_position(name)\r
1258                 if pos == nil then return end\r
1259 \r
1260                 local path = minetest.get_worldpath() .. "/schems/" .. param .. ".mts"\r
1261                 if minetest.place_schematic(pos, path) == nil then\r
1262                         worldedit.player_notify(name, "failed to place Minetest schematic", false)\r
1263                 else\r
1264                         worldedit.player_notify(name, "placed Minetest schematic " .. param ..\r
1265                                 " at " .. minetest.pos_to_string(pos), false)\r
1266                 end\r
1267         end,\r
1268 })\r
1269 \r
1270 minetest.register_chatcommand("/mtschemprob", {\r
1271         params = "start/finish/get",\r
1272         description = "Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry",\r
1273         privs = {worldedit=true},\r
1274         func = function(name, param)\r
1275                 if param == "start" then --start probability setting\r
1276                         worldedit.set_pos[name] = "prob"\r
1277                         worldedit.prob_list[name] = {}\r
1278                         worldedit.player_notify(name, "select Minetest schematic probability values by punching nodes")\r
1279                 elseif param == "finish" then --finish probability setting\r
1280                         worldedit.set_pos[name] = nil\r
1281                         worldedit.player_notify(name, "finished Minetest schematic probability selection")\r
1282                 elseif param == "get" then --get all nodes that had probabilities set on them\r
1283                         local text = ""\r
1284                         local problist = worldedit.prob_list[name]\r
1285                         if problist == nil then\r
1286                                 return\r
1287                         end\r
1288                         for k,v in pairs(problist) do\r
1289                                 local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100\r
1290                                 text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "\r
1291                         end\r
1292                         worldedit.player_notify(name, "currently set node probabilities:")\r
1293                         worldedit.player_notify(name, text)\r
1294                 else\r
1295                         worldedit.player_notify(name, "unknown subcommand: " .. param)\r
1296                 end\r
1297         end,\r
1298 })\r
1299 \r
1300 minetest.register_on_player_receive_fields(function(player, formname, fields)\r
1301         if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then\r
1302                 local name = player:get_player_name()\r
1303                 local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}\r
1304                 local index = table.getn(worldedit.prob_list[name]) + 1\r
1305                 worldedit.prob_list[name][index] = prob_entry\r
1306         end\r
1307 end)\r
1308 \r
1309 minetest.register_chatcommand("/clearobjects", {\r
1310         params = "",\r
1311         description = "Clears all objects within the WorldEdit region",\r
1312         privs = {worldedit=true},\r
1313         func = safe_region(function(name, param)\r
1314                 local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name])\r
1315                 worldedit.player_notify(name, count .. " objects cleared")\r
1316         end),\r
1317 })\r