1 minetest.register_privilege("worldedit", "Can use WorldEdit commands")
\r
3 worldedit.set_pos = {}
\r
4 worldedit.inspect = {}
\r
8 if minetest.place_schematic then
\r
9 worldedit.prob_pos = {}
\r
10 worldedit.prob_list = {}
\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
18 local function get_position(name) --position 1 retrieval function for when not using `safe_region`
\r
19 local pos1 = worldedit.pos1[name]
\r
21 worldedit.player_notify(name, "no position 1 selected")
\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
30 worldedit.player_notify(name, "invalid node name: " .. nodename)
\r
36 function worldedit.player_notify(name, message)
\r
37 minetest.chat_send_player(name, "WorldEdit -!- " .. message, false)
\r
40 -- https://github.com/minetest/minetest/blob/53dd7819277c53954d1298dfffa5287c306db8d0/src/util/string.cpp#L777
\r
41 local function strip_translation_escapes(input)
\r
42 local s = function(idx) return input:sub(idx, idx) end
\r
45 while i <= #input do
\r
46 if s(i) == "\027" then -- escape sequence
\r
48 if s(i) == "(" then -- enclosed
\r
50 while i <= #input and s(i) ~= ")" do
\r
51 if s(i) == "\\" then
\r
63 --print(("%q -> %q"):format(input, out))
\r
67 local function string_endswith(full, part)
\r
68 return full:find(part, 1, true) == #full - #part + 1
\r
71 -- normalizes node "description" `nodename`, returning a string (or nil)
\r
72 worldedit.normalize_nodename = function(nodename)
\r
73 nodename = nodename:gsub("^%s*(.-)%s*$", "%1") -- strip spaces
\r
74 if nodename == "" then return nil end
\r
76 local fullname = ItemStack({name=nodename}):get_name() -- resolve aliases
\r
77 if minetest.registered_nodes[fullname] or fullname == "air" then -- full name
\r
80 for key, value in pairs(minetest.registered_nodes) do
\r
81 if string_endswith(key, ":" .. nodename) then -- matches name (w/o mod part)
\r
85 nodename = nodename:lower() -- lowercase both for case insensitive comparison
\r
86 for key, value in pairs(minetest.registered_nodes) do
\r
87 local desc = strip_translation_escapes(value.description):lower()
\r
88 if desc == nodename then -- matches description
\r
91 if string_endswith(desc, " block") and desc == nodename.." block" then
\r
92 -- fuzzy description match (e.g. "Steel" == "Steel Block")
\r
98 for key, value in pairs(minetest.registered_nodes) do
\r
99 if value.description:lower():find(nodename, 1, true) ~= nil then
\r
100 if match ~= nil then
\r
103 match = key -- substring description match (only if no ambiguities)
\r
109 -- Determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)
\r
110 function worldedit.player_axis(name)
\r
111 local dir = minetest.get_player_by_name(name):get_look_dir()
\r
112 local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)
\r
115 return "x", dir.x > 0 and 1 or -1
\r
118 return "y", dir.y > 0 and 1 or -1
\r
120 return "z", dir.z > 0 and 1 or -1
\r
123 local function check_filename(name)
\r
124 return name:find("^[%w%s%^&'@{}%[%],%$=!%-#%(%)%%%.%+~_]+$") ~= nil
\r
128 minetest.register_chatcommand("/about", {
\r
130 description = "Get information about the WorldEdit mod",
\r
131 func = function(name, param)
\r
132 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
136 -- mostly copied from builtin/chatcommands.lua with minor modifications
\r
137 minetest.register_chatcommand("/help", {
\r
139 params = "[all/<cmd>]",
\r
140 description = "Get help for WorldEdit commands",
\r
141 func = function(name, param)
\r
142 local function is_we_command(cmd)
\r
143 return cmd:sub(0, 1) == "/"
\r
145 local function format_help_line(cmd, def)
\r
146 local msg = minetest.colorize("#00ffff", "/"..cmd)
\r
147 if def.params and def.params ~= "" then
\r
148 msg = msg .. " " .. def.params
\r
150 if def.description and def.description ~= "" then
\r
151 msg = msg .. ": " .. def.description
\r
156 if not minetest.check_player_privs(name, "worldedit") then
\r
157 return false, "You are not allowed to use any WorldEdit commands."
\r
159 if param == "" then
\r
162 for cmd, def in pairs(minetest.chatcommands) do
\r
163 if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then
\r
164 cmds[#cmds + 1] = cmd:sub(2) -- strip the /
\r
168 return true, "Available commands: " .. table.concat(cmds, " ") .. "\n"
\r
169 .. "Use '//help <cmd>' to get more information,"
\r
170 .. " or '//help all' to list everything."
\r
171 elseif param == "all" then
\r
173 for cmd, def in pairs(minetest.chatcommands) do
\r
174 if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then
\r
175 cmds[#cmds + 1] = format_help_line(cmd, def)
\r
179 return true, "Available commands:\n"..table.concat(cmds, "\n")
\r
181 return minetest.chatcommands["help"].func(name, "/" .. param)
\r
186 minetest.register_chatcommand("/inspect", {
\r
187 params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>",
\r
188 description = "Enable or disable node inspection",
\r
189 privs = {worldedit=true},
\r
190 func = function(name, param)
\r
191 if param == "on" or param == "1" or param == "true" or param == "yes" or param == "enable" or param == "" then
\r
192 worldedit.inspect[name] = true
\r
193 local axis, sign = worldedit.player_axis(name)
\r
194 worldedit.player_notify(name, string.format("inspector: inspection enabled for %s, currently facing the %s axis",
\r
195 name, axis .. (sign > 0 and "+" or "-")))
\r
196 elseif param == "off" or param == "0" or param == "false" or param == "no" or param == "disable" then
\r
197 worldedit.inspect[name] = nil
\r
198 worldedit.player_notify(name, "inspector: inspection disabled")
\r
200 worldedit.player_notify(name, "invalid usage: " .. param)
\r
205 local function get_node_rlight(pos)
\r
206 local vecs = { -- neighboring nodes
\r
207 {x= 1, y= 0, z= 0},
\r
208 {x=-1, y= 0, z= 0},
\r
209 {x= 0, y= 1, z= 0},
\r
210 {x= 0, y=-1, z= 0},
\r
211 {x= 0, y= 0, z= 1},
\r
212 {x= 0, y= 0, z=-1},
\r
215 for _, v in ipairs(vecs) do
\r
216 ret = math.max(ret, minetest.get_node_light(vector.add(pos, v)))
\r
221 minetest.register_on_punchnode(function(pos, node, puncher)
\r
222 local name = puncher:get_player_name()
\r
223 if worldedit.inspect[name] then
\r
224 local axis, sign = worldedit.player_axis(name)
\r
225 local message = string.format("inspector: %s at %s (param1=%d, param2=%d, received light=%d) punched facing the %s axis",
\r
226 node.name, minetest.pos_to_string(pos), node.param1, node.param2, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-"))
\r
227 worldedit.player_notify(name, message)
\r
231 minetest.register_chatcommand("/reset", {
\r
233 description = "Reset the region so that it is empty",
\r
234 privs = {worldedit=true},
\r
235 func = function(name, param)
\r
236 worldedit.pos1[name] = nil
\r
237 worldedit.pos2[name] = nil
\r
238 worldedit.mark_pos1(name)
\r
239 worldedit.mark_pos2(name)
\r
240 worldedit.set_pos[name] = nil
\r
241 --make sure the user does not try to confirm an operation after resetting pos:
\r
242 reset_pending(name)
\r
243 worldedit.player_notify(name, "region reset")
\r
247 minetest.register_chatcommand("/mark", {
\r
249 description = "Show markers at the region positions",
\r
250 privs = {worldedit=true},
\r
251 func = function(name, param)
\r
252 worldedit.mark_pos1(name)
\r
253 worldedit.mark_pos2(name)
\r
254 worldedit.player_notify(name, "region marked")
\r
258 minetest.register_chatcommand("/unmark", {
\r
260 description = "Hide markers if currently shown",
\r
261 privs = {worldedit=true},
\r
262 func = function(name, param)
\r
263 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
264 worldedit.pos1[name] = nil
\r
265 worldedit.pos2[name] = nil
\r
266 worldedit.mark_pos1(name)
\r
267 worldedit.mark_pos2(name)
\r
268 worldedit.pos1[name] = pos1
\r
269 worldedit.pos2[name] = pos2
\r
270 worldedit.player_notify(name, "region unmarked")
\r
274 minetest.register_chatcommand("/pos1", {
\r
276 description = "Set WorldEdit region position 1 to the player's location",
\r
277 privs = {worldedit=true},
\r
278 func = function(name, param)
\r
279 local pos = minetest.get_player_by_name(name):get_pos()
\r
280 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
281 worldedit.pos1[name] = pos
\r
282 worldedit.mark_pos1(name)
\r
283 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
287 minetest.register_chatcommand("/pos2", {
\r
289 description = "Set WorldEdit region position 2 to the player's location",
\r
290 privs = {worldedit=true},
\r
291 func = function(name, param)
\r
292 local pos = minetest.get_player_by_name(name):get_pos()
\r
293 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
294 worldedit.pos2[name] = pos
\r
295 worldedit.mark_pos2(name)
\r
296 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
300 minetest.register_chatcommand("/p", {
\r
301 params = "set/set1/set2/get",
\r
302 description = "Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region",
\r
303 privs = {worldedit=true},
\r
304 func = function(name, param)
\r
305 if param == "set" then --set both WorldEdit positions
\r
306 worldedit.set_pos[name] = "pos1"
\r
307 worldedit.player_notify(name, "select positions by punching two nodes")
\r
308 elseif param == "set1" then --set WorldEdit position 1
\r
309 worldedit.set_pos[name] = "pos1only"
\r
310 worldedit.player_notify(name, "select position 1 by punching a node")
\r
311 elseif param == "set2" then --set WorldEdit position 2
\r
312 worldedit.set_pos[name] = "pos2"
\r
313 worldedit.player_notify(name, "select position 2 by punching a node")
\r
314 elseif param == "get" then --display current WorldEdit positions
\r
315 if worldedit.pos1[name] ~= nil then
\r
316 worldedit.player_notify(name, "position 1: " .. minetest.pos_to_string(worldedit.pos1[name]))
\r
318 worldedit.player_notify(name, "position 1 not set")
\r
320 if worldedit.pos2[name] ~= nil then
\r
321 worldedit.player_notify(name, "position 2: " .. minetest.pos_to_string(worldedit.pos2[name]))
\r
323 worldedit.player_notify(name, "position 2 not set")
\r
326 worldedit.player_notify(name, "unknown subcommand: " .. param)
\r
331 minetest.register_chatcommand("/fixedpos", {
\r
332 params = "set1/set2 x y z",
\r
333 description = "Set a WorldEdit region position to the position at (<x>, <y>, <z>)",
\r
334 privs = {worldedit=true},
\r
335 func = function(name, param)
\r
336 local found, _, flag, x, y, z = param:find("^(set[12])%s+([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)$")
\r
337 if found == nil then
\r
338 worldedit.player_notify(name, "invalid usage: " .. param)
\r
341 local pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
\r
342 if flag == "set1" then
\r
343 worldedit.pos1[name] = pos
\r
344 worldedit.mark_pos1(name)
\r
345 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
346 else --flag == "set2"
\r
347 worldedit.pos2[name] = pos
\r
348 worldedit.mark_pos2(name)
\r
349 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
354 minetest.register_on_punchnode(function(pos, node, puncher)
\r
355 local name = puncher:get_player_name()
\r
356 if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position
\r
357 if worldedit.set_pos[name] == "pos1" then --setting position 1
\r
358 worldedit.pos1[name] = pos
\r
359 worldedit.mark_pos1(name)
\r
360 worldedit.set_pos[name] = "pos2" --set position 2 on the next invocation
\r
361 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
362 elseif worldedit.set_pos[name] == "pos1only" then --setting position 1 only
\r
363 worldedit.pos1[name] = pos
\r
364 worldedit.mark_pos1(name)
\r
365 worldedit.set_pos[name] = nil --finished setting positions
\r
366 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
367 elseif worldedit.set_pos[name] == "pos2" then --setting position 2
\r
368 worldedit.pos2[name] = pos
\r
369 worldedit.mark_pos2(name)
\r
370 worldedit.set_pos[name] = nil --finished setting positions
\r
371 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
372 elseif worldedit.set_pos[name] == "prob" then --setting Minetest schematic node probabilities
\r
373 worldedit.prob_pos[name] = pos
\r
374 minetest.show_formspec(puncher:get_player_name(), "prob_val_enter", "field[text;;]")
\r
379 minetest.register_chatcommand("/volume", {
\r
381 description = "Display the volume of the current WorldEdit region",
\r
382 privs = {worldedit=true},
\r
383 func = function(name, param)
\r
384 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
385 if pos1 == nil or pos2 == nil then
\r
386 worldedit.player_notify(name, "no region selected")
\r
390 local volume = worldedit.volume(pos1, pos2)
\r
391 local abs = math.abs
\r
392 worldedit.player_notify(name, "current region has a volume of " .. volume .. " nodes ("
\r
393 .. abs(pos2.x - pos1.x) + 1 .. "*"
\r
394 .. abs(pos2.y - pos1.y) + 1 .. "*"
\r
395 .. abs(pos2.z - pos1.z) + 1 .. ")")
\r
399 minetest.register_chatcommand("/deleteblocks", {
\r
401 description = "remove all MapBlocks (16x16x16) containing the selected area from the map",
\r
402 privs = {worldedit=true},
\r
403 func = safe_region(function(name, param)
\r
404 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
405 local success = minetest.delete_area(pos1, pos2)
\r
407 worldedit.player_notify(name, "Area deleted.")
\r
409 worldedit.player_notify(name, "There was an error during deletion of the area.")
\r
414 minetest.register_chatcommand("/set", {
\r
416 description = "Set the current WorldEdit region to <node>",
\r
417 privs = {worldedit=true},
\r
418 func = safe_region(function(name, param)
\r
419 local node = get_node(name, param)
\r
420 if not node then return end
\r
422 local count = worldedit.set(worldedit.pos1[name], worldedit.pos2[name], node)
\r
423 worldedit.player_notify(name, count .. " nodes set")
\r
424 end, check_region),
\r
427 minetest.register_chatcommand("/param2", {
\r
428 params = "<param2>",
\r
429 description = "Set param2 of all nodes in the current WorldEdit region to <param2>",
\r
430 privs = {worldedit=true},
\r
431 func = safe_region(function(name, param)
\r
432 local param2 = tonumber(param)
\r
434 worldedit.player_notify(name, "Invalid or missing param2 argument")
\r
436 elseif param2 < 0 or param2 > 255 then
\r
437 worldedit.player_notify(name, "Param2 is out of range (must be between 0 and 255 inclusive)!")
\r
441 local count = worldedit.set_param2(worldedit.pos1[name], worldedit.pos2[name], param2)
\r
442 worldedit.player_notify(name, count .. " nodes altered")
\r
443 end, check_region),
\r
446 minetest.register_chatcommand("/mix", {
\r
447 params = "<node1> [<weighting1>] [<node2> [<weighting2>]] ...",
\r
448 description = "Fill the current WorldEdit region with a random mix of <node1>, ...",
\r
449 privs = {worldedit=true},
\r
450 func = safe_region(function(name, param)
\r
452 for nodename in param:gmatch("[^%s]+") do
\r
453 if tonumber(nodename) ~= nil and #nodes > 0 then
\r
454 local last_node = nodes[#nodes]
\r
455 for i = 1, tonumber(nodename) do
\r
456 nodes[#nodes + 1] = last_node
\r
459 local node = get_node(name, nodename)
\r
460 if not node then return end
\r
461 nodes[#nodes + 1] = node
\r
465 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
466 local count = worldedit.set(pos1, pos2, nodes)
\r
467 worldedit.player_notify(name, count .. " nodes set")
\r
468 end, check_region),
\r
471 local check_replace = function(name, param)
\r
472 local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$")
\r
473 if found == nil then
\r
474 worldedit.player_notify(name, "invalid usage: " .. param)
\r
477 local newsearchnode = worldedit.normalize_nodename(searchnode)
\r
478 if not newsearchnode then
\r
479 worldedit.player_notify(name, "invalid search node name: " .. searchnode)
\r
482 local newreplacenode = worldedit.normalize_nodename(replacenode)
\r
483 if not newreplacenode then
\r
484 worldedit.player_notify(name, "invalid replace node name: " .. replacenode)
\r
487 return check_region(name, param)
\r
490 minetest.register_chatcommand("/replace", {
\r
491 params = "<search node> <replace node>",
\r
492 description = "Replace all instances of <search node> with <replace node> in the current WorldEdit region",
\r
493 privs = {worldedit=true},
\r
494 func = safe_region(function(name, param)
\r
495 local found, _, search_node, replace_node = param:find("^([^%s]+)%s+(.+)$")
\r
496 local norm_search_node = worldedit.normalize_nodename(search_node)
\r
497 local norm_replace_node = worldedit.normalize_nodename(replace_node)
\r
498 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
\r
499 norm_search_node, norm_replace_node)
\r
500 worldedit.player_notify(name, count .. " nodes replaced")
\r
501 end, check_replace),
\r
504 minetest.register_chatcommand("/replaceinverse", {
\r
505 params = "<search node> <replace node>",
\r
506 description = "Replace all nodes other than <search node> with <replace node> in the current WorldEdit region",
\r
507 privs = {worldedit=true},
\r
508 func = safe_region(function(name, param)
\r
509 local found, _, search_node, replace_node = param:find("^([^%s]+)%s+(.+)$")
\r
510 local norm_search_node = worldedit.normalize_nodename(search_node)
\r
511 local norm_replace_node = worldedit.normalize_nodename(replace_node)
\r
512 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
\r
513 norm_search_node, norm_replace_node, true)
\r
514 worldedit.player_notify(name, count .. " nodes replaced")
\r
515 end, check_replace),
\r
518 local check_cube = function(name, param)
\r
519 if worldedit.pos1[name] == nil then
\r
520 worldedit.player_notify(name, "no position 1 selected")
\r
523 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
524 if found == nil then
\r
525 worldedit.player_notify(name, "invalid usage: " .. param)
\r
528 local node = get_node(name, nodename)
\r
529 if not node then return nil end
\r
530 return tonumber(w) * tonumber(h) * tonumber(l)
\r
533 minetest.register_chatcommand("/hollowcube", {
\r
534 params = "<width> <height> <length> <node>",
\r
535 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
536 privs = {worldedit=true},
\r
537 func = safe_region(function(name, param)
\r
538 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
539 local node = get_node(name, nodename)
\r
540 local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node, true)
\r
541 worldedit.player_notify(name, count .. " nodes added")
\r
545 minetest.register_chatcommand("/cube", {
\r
546 params = "<width> <height> <length> <node>",
\r
547 description = "Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.",
\r
548 privs = {worldedit=true},
\r
549 func = safe_region(function(name, param)
\r
550 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
551 local node = get_node(name, nodename)
\r
552 local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node)
\r
553 worldedit.player_notify(name, count .. " nodes added")
\r
557 local check_sphere = function(name, param)
\r
558 if worldedit.pos1[name] == nil then
\r
559 worldedit.player_notify(name, "no position 1 selected")
\r
562 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
563 if found == nil then
\r
564 worldedit.player_notify(name, "invalid usage: " .. param)
\r
567 local node = get_node(name, nodename)
\r
568 if not node then return nil end
\r
569 return math.ceil((4 * math.pi * (tonumber(radius) ^ 3)) / 3) --volume of sphere
\r
572 minetest.register_chatcommand("/hollowsphere", {
\r
573 params = "<radius> <node>",
\r
574 description = "Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
575 privs = {worldedit=true},
\r
576 func = safe_region(function(name, param)
\r
577 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
578 local node = get_node(name, nodename)
\r
579 local count = worldedit.sphere(worldedit.pos1[name], tonumber(radius), node, true)
\r
580 worldedit.player_notify(name, count .. " nodes added")
\r
581 end, check_sphere),
\r
584 minetest.register_chatcommand("/sphere", {
\r
585 params = "<radius> <node>",
\r
586 description = "Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
587 privs = {worldedit=true},
\r
588 func = safe_region(function(name, param)
\r
589 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
590 local node = get_node(name, nodename)
\r
591 local count = worldedit.sphere(worldedit.pos1[name], tonumber(radius), node)
\r
592 worldedit.player_notify(name, count .. " nodes added")
\r
593 end, check_sphere),
\r
596 local check_dome = function(name, param)
\r
597 if worldedit.pos1[name] == nil then
\r
598 worldedit.player_notify(name, "no position 1 selected")
\r
601 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
602 if found == nil then
\r
603 worldedit.player_notify(name, "invalid usage: " .. param)
\r
606 local node = get_node(name, nodename)
\r
607 if not node then return nil end
\r
608 return math.ceil((2 * math.pi * (tonumber(radius) ^ 3)) / 3) --volume of dome
\r
611 minetest.register_chatcommand("/hollowdome", {
\r
612 params = "<radius> <node>",
\r
613 description = "Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
614 privs = {worldedit=true},
\r
615 func = safe_region(function(name, param)
\r
616 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
617 local node = get_node(name, nodename)
\r
618 local count = worldedit.dome(worldedit.pos1[name], tonumber(radius), node, true)
\r
619 worldedit.player_notify(name, count .. " nodes added")
\r
623 minetest.register_chatcommand("/dome", {
\r
624 params = "<radius> <node>",
\r
625 description = "Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
626 privs = {worldedit=true},
\r
627 func = safe_region(function(name, param)
\r
628 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
629 local node = get_node(name, nodename)
\r
630 local count = worldedit.dome(worldedit.pos1[name], tonumber(radius), node)
\r
631 worldedit.player_notify(name, count .. " nodes added")
\r
635 local check_cylinder = function(name, param)
\r
636 if worldedit.pos1[name] == nil then
\r
637 worldedit.player_notify(name, "no position 1 selected")
\r
641 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
642 if found == nil then
\r
644 found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
\r
647 if found == nil then
\r
648 worldedit.player_notify(name, "invalid usage: " .. param)
\r
651 local node = get_node(name, nodename)
\r
652 if not node then return nil end
\r
653 local radius = math.max(tonumber(radius1), tonumber(radius2))
\r
654 return math.ceil(math.pi * (radius ^ 2) * tonumber(length))
\r
657 minetest.register_chatcommand("/hollowcylinder", {
\r
658 params = "x/y/z/? <length> <radius1> [radius2] <node>",
\r
659 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
660 privs = {worldedit=true},
\r
661 func = safe_region(function(name, param)
\r
663 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
664 if found == nil then
\r
666 found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
\r
669 length = tonumber(length)
\r
670 if axis == "?" then
\r
672 axis, sign = worldedit.player_axis(name)
\r
673 length = length * sign
\r
675 local node = get_node(name, nodename)
\r
676 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node, true)
\r
677 worldedit.player_notify(name, count .. " nodes added")
\r
678 end, check_cylinder),
\r
681 minetest.register_chatcommand("/cylinder", {
\r
682 params = "x/y/z/? <length> <radius1> [radius2] <node>",
\r
683 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
684 privs = {worldedit=true},
\r
685 func = safe_region(function(name, param)
\r
687 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
688 if found == nil then
\r
690 found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
\r
693 length = tonumber(length)
\r
694 if axis == "?" then
\r
696 axis, sign = worldedit.player_axis(name)
\r
697 length = length * sign
\r
699 local node = get_node(name, nodename)
\r
700 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node)
\r
701 worldedit.player_notify(name, count .. " nodes added")
\r
702 end, check_cylinder),
\r
705 local check_pyramid = function(name, param)
\r
706 if worldedit.pos1[name] == nil then
\r
707 worldedit.player_notify(name, "no position 1 selected")
\r
710 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
\r
711 if found == nil then
\r
712 worldedit.player_notify(name, "invalid usage: " .. param)
\r
715 local node = get_node(name, nodename)
\r
716 if not node then return nil end
\r
717 height = tonumber(height)
\r
718 return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
\r
721 minetest.register_chatcommand("/hollowpyramid", {
\r
722 params = "x/y/z/? <height> <node>",
\r
723 description = "Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
\r
724 privs = {worldedit=true},
\r
725 func = safe_region(function(name, param)
\r
726 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
\r
727 height = tonumber(height)
\r
728 if axis == "?" then
\r
730 axis, sign = worldedit.player_axis(name)
\r
731 height = height * sign
\r
733 local node = get_node(name, nodename)
\r
734 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true)
\r
735 worldedit.player_notify(name, count .. " nodes added")
\r
736 end, check_pyramid),
\r
739 minetest.register_chatcommand("/pyramid", {
\r
740 params = "x/y/z/? <height> <node>",
\r
741 description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
\r
742 privs = {worldedit=true},
\r
743 func = safe_region(function(name, param)
\r
744 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
\r
745 height = tonumber(height)
\r
746 if axis == "?" then
\r
748 axis, sign = worldedit.player_axis(name)
\r
749 height = height * sign
\r
751 local node = get_node(name, nodename)
\r
752 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)
\r
753 worldedit.player_notify(name, count .. " nodes added")
\r
754 end, check_pyramid),
\r
757 minetest.register_chatcommand("/spiral", {
\r
758 params = "<length> <height> <space> <node>",
\r
759 description = "Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>",
\r
760 privs = {worldedit=true},
\r
761 func = safe_region(function(name, param)
\r
762 local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
763 local node = get_node(name, nodename)
\r
764 local count = worldedit.spiral(worldedit.pos1[name], tonumber(length), tonumber(height), tonumber(space), node)
\r
765 worldedit.player_notify(name, count .. " nodes added")
\r
767 function(name, param)
\r
768 if worldedit.pos1[name] == nil then
\r
769 worldedit.player_notify(name, "no position 1 selected")
\r
772 local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
773 if found == nil then
\r
774 worldedit.player_notify(name, "invalid usage: " .. param)
\r
777 local node = get_node(name, nodename)
\r
778 if not node then return nil end
\r
779 return 1 -- TODO: return an useful value
\r
783 minetest.register_chatcommand("/copy", {
\r
784 params = "x/y/z/? <amount>",
\r
785 description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
\r
786 privs = {worldedit=true},
\r
787 func = safe_region(function(name, param)
\r
788 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
789 if found == nil then
\r
790 worldedit.player_notify(name, "invalid usage: " .. param)
\r
793 amount = tonumber(amount)
\r
794 if axis == "?" then
\r
796 axis, sign = worldedit.player_axis(name)
\r
797 amount = amount * sign
\r
800 local count = worldedit.copy(worldedit.pos1[name], worldedit.pos2[name], axis, amount)
\r
801 worldedit.player_notify(name, count .. " nodes copied")
\r
803 function(name, param)
\r
804 local volume = check_region(name, param)
\r
805 return volume and volume * 2 or volume
\r
809 minetest.register_chatcommand("/move", {
\r
810 params = "x/y/z/? <amount>",
\r
811 description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
\r
812 privs = {worldedit=true},
\r
813 func = safe_region(function(name, param)
\r
814 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
815 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
816 if found == nil then
\r
817 worldedit.player_notify(name, "invalid usage: " .. param)
\r
820 amount = tonumber(amount)
\r
821 if axis == "?" then
\r
823 axis, sign = worldedit.player_axis(name)
\r
824 amount = amount * sign
\r
827 local count = worldedit.move(pos1, pos2, axis, amount)
\r
829 pos1[axis] = pos1[axis] + amount
\r
830 pos2[axis] = pos2[axis] + amount
\r
831 worldedit.mark_pos1(name)
\r
832 worldedit.mark_pos2(name)
\r
833 worldedit.player_notify(name, count .. " nodes moved")
\r
834 end, check_region),
\r
837 minetest.register_chatcommand("/stack", {
\r
838 params = "x/y/z/? <count>",
\r
839 description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",
\r
840 privs = {worldedit=true},
\r
841 func = safe_region(function(name, param)
\r
842 local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
843 repetitions = tonumber(repetitions)
\r
844 if axis == "?" then
\r
846 axis, sign = worldedit.player_axis(name)
\r
847 repetitions = repetitions * sign
\r
850 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
851 local count = worldedit.volume(pos1, pos2) * math.abs(repetitions)
\r
852 worldedit.stack(pos1, pos2, axis, repetitions, function()
\r
853 worldedit.player_notify(name, count .. " nodes stacked")
\r
856 function(name, param)
\r
857 local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
858 if found == nil then
\r
859 worldedit.player_notify(name, "invalid usage: " .. param)
\r
863 local count = check_region(name, param)
\r
864 if count then return tonumber(repetitions) * count end
\r
869 minetest.register_chatcommand("/stack2", {
\r
870 params = "<count> <x> <y> <z>",
\r
871 description = "Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>",
\r
872 privs = {worldedit=true},
\r
873 func = safe_region(function(name, param)
\r
874 local repetitions, incs = param:match("(%d+)%s*(.+)")
\r
875 if repetitions == nil then
\r
878 repetitions = tonumber(repetitions)
\r
880 local x, y, z = incs:match("([+-]?%d+) ([+-]?%d+) ([+-]?%d+)")
\r
882 worldedit.player_notify(name, "invalid increments: " .. param)
\r
885 x, y, z = tonumber(x), tonumber(y), tonumber(z)
\r
887 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
888 local count = worldedit.volume(pos1, pos2) * repetitions
\r
889 worldedit.stack2(pos1, pos2, {x=x, y=y, z=z}, repetitions, function()
\r
890 worldedit.player_notify(name, count .. " nodes stacked")
\r
893 function(name, param)
\r
894 local repetitions, incs = param:match("(%d+)%s*(.+)")
\r
895 if repetitions == nil then
\r
896 worldedit.player_notify(name, "invalid count: " .. param)
\r
899 repetitions = tonumber(repetitions)
\r
901 local count = check_region(name, param)
\r
902 if count then return repetitions * count end
\r
908 minetest.register_chatcommand("/stretch", {
\r
909 params = "<stretchx> <stretchy> <stretchz>",
\r
910 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
911 privs = {worldedit=true},
\r
912 func = safe_region(function(name, param)
\r
913 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
914 local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
\r
915 stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)
\r
916 local count, pos1, pos2 = worldedit.stretch(pos1, pos2, stretchx, stretchy, stretchz)
\r
918 --reset markers to scaled positions
\r
919 worldedit.pos1[name] = pos1
\r
920 worldedit.pos2[name] = pos2
\r
921 worldedit.mark_pos1(name)
\r
922 worldedit.mark_pos2(name)
\r
924 worldedit.player_notify(name, count .. " nodes stretched")
\r
926 function(name, param)
\r
927 local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
\r
928 if found == nil then
\r
929 worldedit.player_notify(name, "invalid usage: " .. param)
\r
932 stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)
\r
933 if stretchx == 0 or stretchy == 0 or stretchz == 0 then
\r
934 worldedit.player_notify(name, "invalid scaling factors: " .. param)
\r
937 local count = check_region(name, param)
\r
939 return stretchx * stretchy * stretchz * count
\r
945 minetest.register_chatcommand("/transpose", {
\r
946 params = "x/y/z/? x/y/z/?",
\r
947 description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",
\r
948 privs = {worldedit=true},
\r
949 func = safe_region(function(name, param)
\r
950 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
951 local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
\r
952 if axis1 == "?" then axis1 = worldedit.player_axis(name) end
\r
953 if axis2 == "?" then axis2 = worldedit.player_axis(name) end
\r
954 local count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
\r
956 --reset markers to transposed positions
\r
957 worldedit.pos1[name] = pos1
\r
958 worldedit.pos2[name] = pos2
\r
959 worldedit.mark_pos1(name)
\r
960 worldedit.mark_pos2(name)
\r
962 worldedit.player_notify(name, count .. " nodes transposed")
\r
964 function(name, param)
\r
965 local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
\r
966 if found == nil then
\r
967 worldedit.player_notify(name, "invalid usage: " .. param)
\r
970 if axis1 == axis2 then
\r
971 worldedit.player_notify(name, "invalid usage: axes must be different")
\r
974 return check_region(name, param)
\r
978 minetest.register_chatcommand("/flip", {
\r
979 params = "x/y/z/?",
\r
980 description = "Flip the current WorldEdit region along the x/y/z/? axis",
\r
981 privs = {worldedit=true},
\r
982 func = safe_region(function(name, param)
\r
983 if param == "?" then param = worldedit.player_axis(name) end
\r
984 local count = worldedit.flip(worldedit.pos1[name], worldedit.pos2[name], param)
\r
985 worldedit.player_notify(name, count .. " nodes flipped")
\r
987 function(name, param)
\r
988 if param ~= "x" and param ~= "y" and param ~= "z" and param ~= "?" then
\r
989 worldedit.player_notify(name, "invalid usage: " .. param)
\r
992 return check_region(name, param)
\r
996 minetest.register_chatcommand("/rotate", {
\r
997 params = "<axis> <angle>",
\r
998 description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",
\r
999 privs = {worldedit=true},
\r
1000 func = safe_region(function(name, param)
\r
1001 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1002 local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
1003 if axis == "?" then axis = worldedit.player_axis(name) end
\r
1004 local count, pos1, pos2 = worldedit.rotate(pos1, pos2, axis, angle)
\r
1006 --reset markers to rotated positions
\r
1007 worldedit.pos1[name] = pos1
\r
1008 worldedit.pos2[name] = pos2
\r
1009 worldedit.mark_pos1(name)
\r
1010 worldedit.mark_pos2(name)
\r
1012 worldedit.player_notify(name, count .. " nodes rotated")
\r
1014 function(name, param)
\r
1015 local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
1016 if found == nil then
\r
1017 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1020 if angle % 90 ~= 0 or angle % 360 == 0 then
\r
1021 worldedit.player_notify(name, "invalid usage: angle must be multiple of 90")
\r
1024 return check_region(name, param)
\r
1028 minetest.register_chatcommand("/orient", {
\r
1029 params = "<angle>",
\r
1030 description = "Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)",
\r
1031 privs = {worldedit=true},
\r
1032 func = safe_region(function(name, param)
\r
1033 local found, _, angle = param:find("^([+-]?%d+)$")
\r
1034 local count = worldedit.orient(worldedit.pos1[name], worldedit.pos2[name], angle)
\r
1035 worldedit.player_notify(name, count .. " nodes oriented")
\r
1037 function(name, param)
\r
1038 local found, _, angle = param:find("^([+-]?%d+)$")
\r
1039 if found == nil then
\r
1040 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1043 if angle % 90 ~= 0 then
\r
1044 worldedit.player_notify(name, "invalid usage: angle must be multiple of 90")
\r
1047 return check_region(name, param)
\r
1051 minetest.register_chatcommand("/fixlight", {
\r
1053 description = "Fix the lighting in the current WorldEdit region",
\r
1054 privs = {worldedit=true},
\r
1055 func = safe_region(function(name, param)
\r
1056 local count = worldedit.fixlight(worldedit.pos1[name], worldedit.pos2[name])
\r
1057 worldedit.player_notify(name, count .. " nodes updated")
\r
1061 minetest.register_chatcommand("/drain", {
\r
1063 description = "Remove any fluid node within the current WorldEdit region",
\r
1064 privs = {worldedit=true},
\r
1065 func = safe_region(function(name, param)
\r
1066 -- TODO: make an API function for this
\r
1068 local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
\r
1069 for x = pos1.x, pos2.x do
\r
1070 for y = pos1.y, pos2.y do
\r
1071 for z = pos1.z, pos2.z do
\r
1072 local n = minetest.get_node({x=x, y=y, z=z}).name
\r
1073 local d = minetest.registered_nodes[n]
\r
1074 if d ~= nil and (d["drawtype"] == "liquid" or d["drawtype"] == "flowingliquid") then
\r
1075 minetest.remove_node({x=x, y=y, z=z})
\r
1081 worldedit.player_notify(name, count .. " nodes updated")
\r
1085 minetest.register_chatcommand("/hide", {
\r
1087 description = "Hide all nodes in the current WorldEdit region non-destructively",
\r
1088 privs = {worldedit=true},
\r
1089 func = safe_region(function(name, param)
\r
1090 local count = worldedit.hide(worldedit.pos1[name], worldedit.pos2[name])
\r
1091 worldedit.player_notify(name, count .. " nodes hidden")
\r
1095 minetest.register_chatcommand("/suppress", {
\r
1096 params = "<node>",
\r
1097 description = "Suppress all <node> in the current WorldEdit region non-destructively",
\r
1098 privs = {worldedit=true},
\r
1099 func = safe_region(function(name, param)
\r
1100 local node = get_node(name, param)
\r
1101 local count = worldedit.suppress(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1102 worldedit.player_notify(name, count .. " nodes suppressed")
\r
1103 end, check_region),
\r
1106 minetest.register_chatcommand("/highlight", {
\r
1107 params = "<node>",
\r
1108 description = "Highlight <node> in the current WorldEdit region by hiding everything else non-destructively",
\r
1109 privs = {worldedit=true},
\r
1110 func = safe_region(function(name, param)
\r
1111 local node = get_node(name, param)
\r
1112 local count = worldedit.highlight(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1113 worldedit.player_notify(name, count .. " nodes highlighted")
\r
1114 end, check_region),
\r
1117 minetest.register_chatcommand("/restore", {
\r
1119 description = "Restores nodes hidden with WorldEdit in the current WorldEdit region",
\r
1120 privs = {worldedit=true},
\r
1121 func = safe_region(function(name, param)
\r
1122 local count = worldedit.restore(worldedit.pos1[name], worldedit.pos2[name])
\r
1123 worldedit.player_notify(name, count .. " nodes restored")
\r
1127 local function detect_misaligned_schematic(name, pos1, pos2)
\r
1128 pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
1129 -- Check that allocate/save can position the schematic correctly
\r
1130 -- The expected behaviour is that the (0,0,0) corner of the schematic stays
\r
1131 -- sat pos1, this only works when the minimum position is actually present
\r
1132 -- in the schematic.
\r
1133 local node = minetest.get_node(pos1)
\r
1134 local have_node_at_origin = node.name ~= "air" and node.name ~= "ignore"
\r
1135 if not have_node_at_origin then
\r
1136 worldedit.player_notify(name,
\r
1137 "Warning: The schematic contains excessive free space and WILL be "..
\r
1138 "misaligned when allocated or loaded. To avoid this, shrink your "..
\r
1139 "area to cover exactly the nodes to be saved."
\r
1144 minetest.register_chatcommand("/save", {
\r
1145 params = "<file>",
\r
1146 description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",
\r
1147 privs = {worldedit=true},
\r
1148 func = safe_region(function(name, param)
\r
1149 if param == "" then
\r
1150 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1153 if not check_filename(param) then
\r
1154 worldedit.player_notify(name, "Disallowed file name: " .. param)
\r
1157 local result, count = worldedit.serialize(worldedit.pos1[name],
\r
1158 worldedit.pos2[name])
\r
1159 detect_misaligned_schematic(name, worldedit.pos1[name], worldedit.pos2[name])
\r
1161 local path = minetest.get_worldpath() .. "/schems"
\r
1162 -- Create directory if it does not already exist
\r
1163 minetest.mkdir(path)
\r
1165 local filename = path .. "/" .. param .. ".we"
\r
1166 local file, err = io.open(filename, "wb")
\r
1167 if err ~= nil then
\r
1168 worldedit.player_notify(name, "Could not save file to \"" .. filename .. "\"")
\r
1171 file:write(result)
\r
1175 worldedit.player_notify(name, count .. " nodes saved")
\r
1179 minetest.register_chatcommand("/allocate", {
\r
1180 params = "<file>",
\r
1181 description = "Set the region defined by nodes from \"(world folder)/schems/<file>.we\" as the current WorldEdit region",
\r
1182 privs = {worldedit=true},
\r
1183 func = function(name, param)
\r
1184 local pos = get_position(name)
\r
1185 if pos == nil then return end
\r
1187 if param == "" then
\r
1188 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1191 if not check_filename(param) then
\r
1192 worldedit.player_notify(name, "Disallowed file name: " .. param)
\r
1196 local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"
\r
1197 local file, err = io.open(filename, "rb")
\r
1198 if err ~= nil then
\r
1199 worldedit.player_notify(name, "could not open file \"" .. filename .. "\"")
\r
1202 local value = file:read("*a")
\r
1205 local version = worldedit.read_header(value)
\r
1206 if version == nil or version == 0 then
\r
1207 worldedit.player_notify(name, "File is invalid!")
\r
1209 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1210 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1213 local nodepos1, nodepos2, count = worldedit.allocate(pos, value)
\r
1215 if not nodepos1 then
\r
1216 worldedit.player_notify(name, "Schematic empty, nothing allocated")
\r
1220 worldedit.pos1[name] = nodepos1
\r
1221 worldedit.mark_pos1(name)
\r
1222 worldedit.pos2[name] = nodepos2
\r
1223 worldedit.mark_pos2(name)
\r
1225 worldedit.player_notify(name, count .. " nodes allocated")
\r
1229 minetest.register_chatcommand("/load", {
\r
1230 params = "<file>",
\r
1231 description = "Load nodes from \"(world folder)/schems/<file>[.we[m]]\" with position 1 of the current WorldEdit region as the origin",
\r
1232 privs = {worldedit=true},
\r
1233 func = function(name, param)
\r
1234 local pos = get_position(name)
\r
1235 if pos == nil then return end
\r
1237 if param == "" then
\r
1238 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1241 if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then
\r
1242 worldedit.player_notify(name, "invalid file name: " .. param)
\r
1246 --find the file in the world path
\r
1247 local testpaths = {
\r
1248 minetest.get_worldpath() .. "/schems/" .. param,
\r
1249 minetest.get_worldpath() .. "/schems/" .. param .. ".we",
\r
1250 minetest.get_worldpath() .. "/schems/" .. param .. ".wem",
\r
1253 for index, path in ipairs(testpaths) do
\r
1254 file, err = io.open(path, "rb")
\r
1260 worldedit.player_notify(name, "could not open file \"" .. param .. "\"")
\r
1263 local value = file:read("*a")
\r
1266 local version = worldedit.read_header(value)
\r
1267 if version == nil or version == 0 then
\r
1268 worldedit.player_notify(name, "File is invalid!")
\r
1270 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1271 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1275 local count = worldedit.deserialize(pos, value)
\r
1277 worldedit.player_notify(name, count .. " nodes loaded")
\r
1281 minetest.register_chatcommand("/lua", {
\r
1282 params = "<code>",
\r
1283 description = "Executes <code> as a Lua chunk in the global namespace",
\r
1284 privs = {worldedit=true, server=true},
\r
1285 func = function(name, param)
\r
1286 local err = worldedit.lua(param)
\r
1288 worldedit.player_notify(name, "code error: " .. err)
\r
1289 minetest.log("action", name.." tried to execute "..param)
\r
1291 worldedit.player_notify(name, "code successfully executed", false)
\r
1292 minetest.log("action", name.." executed "..param)
\r
1297 minetest.register_chatcommand("/luatransform", {
\r
1298 params = "<code>",
\r
1299 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
1300 privs = {worldedit=true, server=true},
\r
1301 func = safe_region(function(name, param)
\r
1302 local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)
\r
1304 worldedit.player_notify(name, "code error: " .. err, false)
\r
1305 minetest.log("action", name.." tried to execute luatransform "..param)
\r
1307 worldedit.player_notify(name, "code successfully executed", false)
\r
1308 minetest.log("action", name.." executed luatransform "..param)
\r
1313 minetest.register_chatcommand("/mtschemcreate", {
\r
1314 params = "<file>",
\r
1315 description = "Save the current WorldEdit region using the Minetest "..
\r
1316 "Schematic format to \"(world folder)/schems/<filename>.mts\"",
\r
1317 privs = {worldedit=true},
\r
1318 func = safe_region(function(name, param)
\r
1319 if param == nil then
\r
1320 worldedit.player_notify(name, "No filename specified")
\r
1323 if not check_filename(param) then
\r
1324 worldedit.player_notify(name, "Disallowed file name: " .. param)
\r
1328 local path = minetest.get_worldpath() .. "/schems"
\r
1329 -- Create directory if it does not already exist
\r
1330 minetest.mkdir(path)
\r
1332 local filename = path .. "/" .. param .. ".mts"
\r
1333 local ret = minetest.create_schematic(worldedit.pos1[name],
\r
1334 worldedit.pos2[name], worldedit.prob_list[name],
\r
1336 if ret == nil then
\r
1337 worldedit.player_notify(name, "Failed to create Minetest schematic", false)
\r
1339 worldedit.player_notify(name, "Saved Minetest schematic to " .. param, false)
\r
1341 worldedit.prob_list[name] = {}
\r
1345 minetest.register_chatcommand("/mtschemplace", {
\r
1346 params = "<file>",
\r
1347 description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin",
\r
1348 privs = {worldedit=true},
\r
1349 func = function(name, param)
\r
1350 if param == "" then
\r
1351 worldedit.player_notify(name, "no filename specified")
\r
1354 if not check_filename(param) then
\r
1355 worldedit.player_notify(name, "Disallowed file name: " .. param)
\r
1359 local pos = get_position(name)
\r
1360 if pos == nil then return end
\r
1362 local path = minetest.get_worldpath() .. "/schems/" .. param .. ".mts"
\r
1363 if minetest.place_schematic(pos, path) == nil then
\r
1364 worldedit.player_notify(name, "failed to place Minetest schematic", false)
\r
1366 worldedit.player_notify(name, "placed Minetest schematic " .. param ..
\r
1367 " at " .. minetest.pos_to_string(pos), false)
\r
1372 minetest.register_chatcommand("/mtschemprob", {
\r
1373 params = "start/finish/get",
\r
1374 description = "Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry",
\r
1375 privs = {worldedit=true},
\r
1376 func = function(name, param)
\r
1377 if param == "start" then --start probability setting
\r
1378 worldedit.set_pos[name] = "prob"
\r
1379 worldedit.prob_list[name] = {}
\r
1380 worldedit.player_notify(name, "select Minetest schematic probability values by punching nodes")
\r
1381 elseif param == "finish" then --finish probability setting
\r
1382 worldedit.set_pos[name] = nil
\r
1383 worldedit.player_notify(name, "finished Minetest schematic probability selection")
\r
1384 elseif param == "get" then --get all nodes that had probabilities set on them
\r
1386 local problist = worldedit.prob_list[name]
\r
1387 if problist == nil then
\r
1390 for k,v in pairs(problist) do
\r
1391 local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100
\r
1392 text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "
\r
1394 worldedit.player_notify(name, "currently set node probabilities:")
\r
1395 worldedit.player_notify(name, text)
\r
1397 worldedit.player_notify(name, "unknown subcommand: " .. param)
\r
1402 minetest.register_on_player_receive_fields(function(player, formname, fields)
\r
1403 if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then
\r
1404 local name = player:get_player_name()
\r
1405 local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
\r
1406 local index = table.getn(worldedit.prob_list[name]) + 1
\r
1407 worldedit.prob_list[name][index] = prob_entry
\r
1411 minetest.register_chatcommand("/clearobjects", {
\r
1413 description = "Clears all objects within the WorldEdit region",
\r
1414 privs = {worldedit=true},
\r
1415 func = safe_region(function(name, param)
\r
1416 local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name])
\r
1417 worldedit.player_notify(name, count .. " objects cleared")
\r