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