1 minetest.register_privilege("worldedit", "Can use WorldEdit commands")
\r
6 worldedit.set_pos = {}
\r
7 worldedit.inspect = {}
\r
8 worldedit.prob_pos = {}
\r
9 worldedit.prob_list = {}
\r
13 local safe_region, reset_pending = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua")
\r
15 function worldedit.player_notify(name, message)
\r
16 minetest.chat_send_player(name, "WorldEdit -!- " .. message, false)
\r
19 worldedit.registered_commands = {}
\r
21 local function chatcommand_handler(cmd_name, name, param)
\r
22 local def = assert(worldedit.registered_commands[cmd_name])
\r
24 if def.require_pos == 2 then
\r
25 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
26 if pos1 == nil or pos2 == nil then
\r
27 worldedit.player_notify(name, "no region selected")
\r
30 elseif def.require_pos == 1 then
\r
31 local pos1 = worldedit.pos1[name]
\r
33 worldedit.player_notify(name, "no position 1 selected")
\r
38 local parsed = {def.parse(param)}
\r
39 local success = table.remove(parsed, 1)
\r
41 worldedit.player_notify(name, parsed[1] or "invalid usage")
\r
45 if def.nodes_needed then
\r
46 local count = def.nodes_needed(name, unpack(parsed))
\r
47 safe_region(name, count, function()
\r
48 local success, msg = def.func(name, unpack(parsed))
\r
50 minetest.chat_send_player(name, msg)
\r
54 -- no "safe region" check
\r
55 local success, msg = def.func(name, unpack(parsed))
\r
57 minetest.chat_send_player(name, msg)
\r
62 -- Registers a chatcommand for WorldEdit
\r
63 -- name = "about" -- Name of the chat command (without any /)
\r
65 -- privs = {}, -- Privileges needed
\r
66 -- params = "", -- Human readable parameter list (optional)
\r
67 -- -- setting params = "" will automatically provide a parse() if not given
\r
68 -- description = "", -- Description
\r
69 -- require_pos = 0, -- Number of positions required to be set (optional)
\r
70 -- parse = function(param)
\r
71 -- return true, foo, bar, ...
\r
75 -- return false, "error message"
\r
77 -- nodes_needed = function(name, foo, bar, ...), -- (optional)
\r
80 -- func = function(name, foo, bar, ...)
\r
81 -- return success, "message"
\r
84 function worldedit.register_command(name, def)
\r
85 local def = table.copy(def)
\r
86 assert(name and #name > 0)
\r
88 def.require_pos = def.require_pos or 0
\r
89 assert(def.require_pos >= 0 and def.require_pos < 3)
\r
90 if def.params == "" and not def.parse then
\r
91 def.parse = function(param) return true end
\r
95 assert(def.nodes_needed == nil or type(def.nodes_needed) == "function")
\r
99 --[[if def.require_pos == 2 and not def.nodes_needed then
\r
100 minetest.log("warning", "//" .. name .. " might be missing nodes_needed")
\r
103 minetest.register_chatcommand("/" .. name, {
\r
105 params = def.params,
\r
106 description = def.description,
\r
107 func = function(player_name, param)
\r
108 return chatcommand_handler(name, player_name, param)
\r
111 worldedit.registered_commands[name] = def
\r
116 dofile(minetest.get_modpath("worldedit_commands") .. "/cuboid.lua")
\r
117 dofile(minetest.get_modpath("worldedit_commands") .. "/mark.lua")
\r
118 dofile(minetest.get_modpath("worldedit_commands") .. "/wand.lua")
\r
121 local function check_region(name)
\r
122 return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
\r
125 -- https://github.com/minetest/minetest/blob/53dd7819277c53954d1298dfffa5287c306db8d0/src/util/string.cpp#L777
\r
126 local function strip_translation_escapes(input)
\r
127 local s = function(idx) return input:sub(idx, idx) end
\r
130 while i <= #input do
\r
131 if s(i) == "\027" then -- escape sequence
\r
133 if s(i) == "(" then -- enclosed
\r
135 while i <= #input and s(i) ~= ")" do
\r
136 if s(i) == "\\" then
\r
148 --print(("%q -> %q"):format(input, out))
\r
152 local function string_endswith(full, part)
\r
153 return full:find(part, 1, true) == #full - #part + 1
\r
156 -- normalizes node "description" `nodename`, returning a string (or nil)
\r
157 worldedit.normalize_nodename = function(nodename)
\r
158 nodename = nodename:gsub("^%s*(.-)%s*$", "%1") -- strip spaces
\r
159 if nodename == "" then return nil end
\r
161 local fullname = ItemStack({name=nodename}):get_name() -- resolve aliases
\r
162 if minetest.registered_nodes[fullname] or fullname == "air" then -- full name
\r
165 for key, value in pairs(minetest.registered_nodes) do
\r
166 if string_endswith(key, ":" .. nodename) then -- matches name (w/o mod part)
\r
170 nodename = nodename:lower() -- lowercase both for case insensitive comparison
\r
171 for key, value in pairs(minetest.registered_nodes) do
\r
172 local desc = strip_translation_escapes(value.description):lower()
\r
173 if desc == nodename then -- matches description
\r
176 if string_endswith(desc, " block") and desc == nodename.." block" then
\r
177 -- fuzzy description match (e.g. "Steel" == "Steel Block")
\r
183 for key, value in pairs(minetest.registered_nodes) do
\r
184 if value.description:lower():find(nodename, 1, true) ~= nil then
\r
185 if match ~= nil then
\r
188 match = key -- substring description match (only if no ambiguities)
\r
194 -- Determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)
\r
195 function worldedit.player_axis(name)
\r
196 local dir = minetest.get_player_by_name(name):get_look_dir()
\r
197 local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)
\r
200 return "x", dir.x > 0 and 1 or -1
\r
203 return "y", dir.y > 0 and 1 or -1
\r
205 return "z", dir.z > 0 and 1 or -1
\r
208 local function check_filename(name)
\r
209 return name:find("^[%w%s%^&'@{}%[%],%$=!%-#%(%)%%%.%+~_]+$") ~= nil
\r
213 worldedit.register_command("about", {
\r
216 description = "Get information about the WorldEdit mod",
\r
217 func = function(name)
\r
218 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
222 -- mostly copied from builtin/chatcommands.lua with minor modifications
\r
223 worldedit.register_command("help", {
\r
225 params = "[all/<cmd>]",
\r
226 description = "Get help for WorldEdit commands",
\r
227 parse = function(param)
\r
230 func = function(name, param)
\r
231 local function format_help_line(cmd, def)
\r
232 local msg = minetest.colorize("#00ffff", "//"..cmd)
\r
233 if def.params and def.params ~= "" then
\r
234 msg = msg .. " " .. def.params
\r
236 if def.description and def.description ~= "" then
\r
237 msg = msg .. ": " .. def.description
\r
242 if not minetest.check_player_privs(name, "worldedit") then
\r
243 return false, "You are not allowed to use any WorldEdit commands."
\r
245 if param == "" then
\r
248 for cmd, def in pairs(worldedit.registered_commands) do
\r
249 if minetest.check_player_privs(name, def.privs) then
\r
250 cmds[#cmds + 1] = cmd
\r
254 return true, "Available commands: " .. table.concat(cmds, " ") .. "\n"
\r
255 .. "Use '//help <cmd>' to get more information,"
\r
256 .. " or '//help all' to list everything."
\r
257 elseif param == "all" then
\r
259 for cmd, def in pairs(worldedit.registered_commands) do
\r
260 if minetest.check_player_privs(name, def.privs) then
\r
261 cmds[#cmds + 1] = format_help_line(cmd, def)
\r
265 return true, "Available commands:\n"..table.concat(cmds, "\n")
\r
267 local def = worldedit.registered_commands[param]
\r
269 return false, "Command not available: " .. param
\r
271 return true, format_help_line(param, def)
\r
277 worldedit.register_command("inspect", {
\r
278 params = "[on/off/1/0/true/false/yes/no/enable/disable]",
\r
279 description = "Enable or disable node inspection",
\r
280 privs = {worldedit=true},
\r
281 parse = function(param)
\r
282 if param == "on" or param == "1" or param == "true" or param == "yes" or param == "enable" or param == "" then
\r
284 elseif param == "off" or param == "0" or param == "false" or param == "no" or param == "disable" then
\r
289 func = function(name, enable)
\r
291 worldedit.inspect[name] = true
\r
292 local axis, sign = worldedit.player_axis(name)
\r
293 worldedit.player_notify(name, string.format("inspector: inspection enabled for %s, currently facing the %s axis",
\r
294 name, axis .. (sign > 0 and "+" or "-")))
\r
296 worldedit.inspect[name] = nil
\r
297 worldedit.player_notify(name, "inspector: inspection disabled")
\r
302 local function get_node_rlight(pos)
\r
303 local vecs = { -- neighboring nodes
\r
304 {x= 1, y= 0, z= 0},
\r
305 {x=-1, y= 0, z= 0},
\r
306 {x= 0, y= 1, z= 0},
\r
307 {x= 0, y=-1, z= 0},
\r
308 {x= 0, y= 0, z= 1},
\r
309 {x= 0, y= 0, z=-1},
\r
312 for _, v in ipairs(vecs) do
\r
313 ret = math.max(ret, minetest.get_node_light(vector.add(pos, v)))
\r
318 minetest.register_on_punchnode(function(pos, node, puncher)
\r
319 local name = puncher:get_player_name()
\r
320 if worldedit.inspect[name] then
\r
321 local axis, sign = worldedit.player_axis(name)
\r
322 local message = string.format("inspector: %s at %s (param1=%d, param2=%d, received light=%d) punched facing the %s axis",
\r
323 node.name, minetest.pos_to_string(pos), node.param1, node.param2, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-"))
\r
324 worldedit.player_notify(name, message)
\r
328 worldedit.register_command("reset", {
\r
330 description = "Reset the region so that it is empty",
\r
331 privs = {worldedit=true},
\r
332 func = function(name)
\r
333 worldedit.pos1[name] = nil
\r
334 worldedit.pos2[name] = nil
\r
335 worldedit.mark_pos1(name)
\r
336 worldedit.mark_pos2(name)
\r
337 worldedit.set_pos[name] = nil
\r
338 --make sure the user does not try to confirm an operation after resetting pos:
\r
339 reset_pending(name)
\r
340 worldedit.player_notify(name, "region reset")
\r
344 worldedit.register_command("mark", {
\r
346 description = "Show markers at the region positions",
\r
347 privs = {worldedit=true},
\r
348 func = function(name)
\r
349 worldedit.mark_pos1(name)
\r
350 worldedit.mark_pos2(name)
\r
351 worldedit.player_notify(name, "region marked")
\r
355 worldedit.register_command("unmark", {
\r
357 description = "Hide markers if currently shown",
\r
358 privs = {worldedit=true},
\r
359 func = function(name)
\r
360 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
361 worldedit.pos1[name] = nil
\r
362 worldedit.pos2[name] = nil
\r
363 worldedit.mark_pos1(name)
\r
364 worldedit.mark_pos2(name)
\r
365 worldedit.pos1[name] = pos1
\r
366 worldedit.pos2[name] = pos2
\r
367 worldedit.player_notify(name, "region unmarked")
\r
371 worldedit.register_command("pos1", {
\r
373 description = "Set WorldEdit region position 1 to the player's location",
\r
374 privs = {worldedit=true},
\r
375 func = function(name)
\r
376 local pos = minetest.get_player_by_name(name):get_pos()
\r
377 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
378 worldedit.pos1[name] = pos
\r
379 worldedit.mark_pos1(name)
\r
380 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
384 worldedit.register_command("pos2", {
\r
386 description = "Set WorldEdit region position 2 to the player's location",
\r
387 privs = {worldedit=true},
\r
388 func = function(name)
\r
389 local pos = minetest.get_player_by_name(name):get_pos()
\r
390 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
391 worldedit.pos2[name] = pos
\r
392 worldedit.mark_pos2(name)
\r
393 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
397 worldedit.register_command("p", {
\r
398 params = "set/set1/set2/get",
\r
399 description = "Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region",
\r
400 privs = {worldedit=true},
\r
401 parse = function(param)
\r
402 if param == "set" or param == "set1" or param == "set2" or param == "get" then
\r
405 return false, "unknown subcommand: " .. param
\r
407 func = function(name, param)
\r
408 if param == "set" then --set both WorldEdit positions
\r
409 worldedit.set_pos[name] = "pos1"
\r
410 worldedit.player_notify(name, "select positions by punching two nodes")
\r
411 elseif param == "set1" then --set WorldEdit position 1
\r
412 worldedit.set_pos[name] = "pos1only"
\r
413 worldedit.player_notify(name, "select position 1 by punching a node")
\r
414 elseif param == "set2" then --set WorldEdit position 2
\r
415 worldedit.set_pos[name] = "pos2"
\r
416 worldedit.player_notify(name, "select position 2 by punching a node")
\r
417 elseif param == "get" then --display current WorldEdit positions
\r
418 if worldedit.pos1[name] ~= nil then
\r
419 worldedit.player_notify(name, "position 1: " .. minetest.pos_to_string(worldedit.pos1[name]))
\r
421 worldedit.player_notify(name, "position 1 not set")
\r
423 if worldedit.pos2[name] ~= nil then
\r
424 worldedit.player_notify(name, "position 2: " .. minetest.pos_to_string(worldedit.pos2[name]))
\r
426 worldedit.player_notify(name, "position 2 not set")
\r
432 worldedit.register_command("fixedpos", {
\r
433 params = "set1/set2 x y z",
\r
434 description = "Set a WorldEdit region position to the position at (<x>, <y>, <z>)",
\r
435 privs = {worldedit=true},
\r
436 parse = function(param)
\r
437 local found, _, flag, x, y, z = param:find("^(set[12])%s+([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)$")
\r
438 if found == nil then
\r
441 return true, flag, {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
\r
443 func = function(name, flag, pos)
\r
444 if flag == "set1" then
\r
445 worldedit.pos1[name] = pos
\r
446 worldedit.mark_pos1(name)
\r
447 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
448 else --flag == "set2"
\r
449 worldedit.pos2[name] = pos
\r
450 worldedit.mark_pos2(name)
\r
451 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
456 minetest.register_on_punchnode(function(pos, node, puncher)
\r
457 local name = puncher:get_player_name()
\r
458 if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position
\r
459 if worldedit.set_pos[name] == "pos1" then --setting position 1
\r
460 worldedit.pos1[name] = pos
\r
461 worldedit.mark_pos1(name)
\r
462 worldedit.set_pos[name] = "pos2" --set position 2 on the next invocation
\r
463 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
464 elseif worldedit.set_pos[name] == "pos1only" then --setting position 1 only
\r
465 worldedit.pos1[name] = pos
\r
466 worldedit.mark_pos1(name)
\r
467 worldedit.set_pos[name] = nil --finished setting positions
\r
468 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
469 elseif worldedit.set_pos[name] == "pos2" then --setting position 2
\r
470 worldedit.pos2[name] = pos
\r
471 worldedit.mark_pos2(name)
\r
472 worldedit.set_pos[name] = nil --finished setting positions
\r
473 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
474 elseif worldedit.set_pos[name] == "prob" then --setting Minetest schematic node probabilities
\r
475 worldedit.prob_pos[name] = pos
\r
476 minetest.show_formspec(puncher:get_player_name(), "prob_val_enter", "field[text;;]")
\r
481 worldedit.register_command("volume", {
\r
483 description = "Display the volume of the current WorldEdit region",
\r
484 privs = {worldedit=true},
\r
486 func = function(name)
\r
487 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
489 local volume = worldedit.volume(pos1, pos2)
\r
490 local abs = math.abs
\r
491 worldedit.player_notify(name, "current region has a volume of " .. volume .. " nodes ("
\r
492 .. abs(pos2.x - pos1.x) + 1 .. "*"
\r
493 .. abs(pos2.y - pos1.y) + 1 .. "*"
\r
494 .. abs(pos2.z - pos1.z) + 1 .. ")")
\r
498 worldedit.register_command("deleteblocks", {
\r
500 description = "remove all MapBlocks (16x16x16) containing the selected area from the map",
\r
501 privs = {worldedit=true},
\r
503 nodes_needed = check_region,
\r
504 func = function(name)
\r
505 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
506 local success = minetest.delete_area(pos1, pos2)
\r
508 worldedit.player_notify(name, "Area deleted.")
\r
510 worldedit.player_notify(name, "There was an error during deletion of the area.")
\r
515 worldedit.register_command("set", {
\r
517 description = "Set the current WorldEdit region to <node>",
\r
518 privs = {worldedit=true},
\r
520 parse = function(param)
\r
521 local node = worldedit.normalize_nodename(param)
\r
523 return false, "invalid node name: " .. param
\r
527 nodes_needed = check_region,
\r
528 func = function(name, node)
\r
529 local count = worldedit.set(worldedit.pos1[name], worldedit.pos2[name], node)
\r
530 worldedit.player_notify(name, count .. " nodes set")
\r
534 worldedit.register_command("param2", {
\r
535 params = "<param2>",
\r
536 description = "Set param2 of all nodes in the current WorldEdit region to <param2>",
\r
537 privs = {worldedit=true},
\r
539 parse = function(param)
\r
540 local param2 = tonumber(param)
\r
542 return false, "Invalid or missing param2 argument"
\r
543 elseif param2 < 0 or param2 > 255 then
\r
544 return false, "Param2 is out of range (must be between 0 and 255 inclusive!)"
\r
546 return true, param2
\r
548 nodes_needed = check_region,
\r
549 func = function(name, param2)
\r
550 local count = worldedit.set_param2(worldedit.pos1[name], worldedit.pos2[name], param2)
\r
551 worldedit.player_notify(name, count .. " nodes altered")
\r
555 worldedit.register_command("mix", {
\r
556 params = "<node1> [<weighting1>] [<node2> [<weighting2>]] ...",
\r
557 description = "Fill the current WorldEdit region with a random mix of <node1>, ...",
\r
558 privs = {worldedit=true},
\r
560 parse = function(param)
\r
562 for nodename in param:gmatch("[^%s]+") do
\r
563 if tonumber(nodename) ~= nil and #nodes > 0 then
\r
564 local last_node = nodes[#nodes]
\r
565 for i = 1, tonumber(nodename) do
\r
566 nodes[#nodes + 1] = last_node
\r
569 local node = worldedit.normalize_nodename(nodename)
\r
571 return false, "invalid node name: " .. nodename
\r
573 nodes[#nodes + 1] = node
\r
578 nodes_needed = check_region,
\r
579 func = function(name, nodes)
\r
580 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
581 local count = worldedit.set(pos1, pos2, nodes)
\r
582 worldedit.player_notify(name, count .. " nodes set")
\r
586 local check_replace = function(param)
\r
587 local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$")
\r
588 if found == nil then
\r
591 local newsearchnode = worldedit.normalize_nodename(searchnode)
\r
592 if not newsearchnode then
\r
593 return false, "invalid search node name: " .. searchnode
\r
595 local newreplacenode = worldedit.normalize_nodename(replacenode)
\r
596 if not newreplacenode then
\r
597 return false, "invalid replace node name: " .. replacenode
\r
599 return true, newsearchnode, newreplacenode
\r
602 worldedit.register_command("replace", {
\r
603 params = "<search node> <replace node>",
\r
604 description = "Replace all instances of <search node> with <replace node> in the current WorldEdit region",
\r
605 privs = {worldedit=true},
\r
607 parse = check_replace,
\r
608 nodes_needed = check_region,
\r
609 func = function(name, search_node, replace_node)
\r
610 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
\r
611 search_node, replace_node)
\r
612 worldedit.player_notify(name, count .. " nodes replaced")
\r
616 worldedit.register_command("replaceinverse", {
\r
617 params = "<search node> <replace node>",
\r
618 description = "Replace all nodes other than <search node> with <replace node> in the current WorldEdit region",
\r
619 privs = {worldedit=true},
\r
621 parse = check_replace,
\r
622 nodes_needed = check_region,
\r
623 func = function(name, search_node, replace_node)
\r
624 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
\r
625 search_node, replace_node, true)
\r
626 worldedit.player_notify(name, count .. " nodes replaced")
\r
630 local check_cube = function(param)
\r
631 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
632 if found == nil then
\r
635 local node = worldedit.normalize_nodename(nodename)
\r
637 return false, "invalid node name: " .. nodename
\r
639 return true, tonumber(w), tonumber(h), tonumber(l), node
\r
642 worldedit.register_command("hollowcube", {
\r
643 params = "<width> <height> <length> <node>",
\r
644 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
645 privs = {worldedit=true},
\r
647 parse = check_cube,
\r
648 nodes_needed = function(name, w, h, l, node)
\r
651 func = function(name, w, h, l, node)
\r
652 local count = worldedit.cube(worldedit.pos1[name], w, h, l, node, true)
\r
653 worldedit.player_notify(name, count .. " nodes added")
\r
657 worldedit.register_command("cube", {
\r
658 params = "<width> <height> <length> <node>",
\r
659 description = "Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.",
\r
660 privs = {worldedit=true},
\r
662 parse = check_cube,
\r
663 nodes_needed = function(name, w, h, l, node)
\r
666 func = function(name, w, h, l, node)
\r
667 local count = worldedit.cube(worldedit.pos1[name], w, h, l, node)
\r
668 worldedit.player_notify(name, count .. " nodes added")
\r
672 local check_sphere = function(param)
\r
673 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
674 if found == nil then
\r
677 local node = worldedit.normalize_nodename(nodename)
\r
679 return false, "invalid node name: " .. nodename
\r
681 return true, tonumber(radius), node
\r
684 worldedit.register_command("hollowsphere", {
\r
685 params = "<radius> <node>",
\r
686 description = "Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
687 privs = {worldedit=true},
\r
689 parse = check_sphere,
\r
690 nodes_needed = function(name, radius, node)
\r
691 return math.ceil((4 * math.pi * (radius ^ 3)) / 3) --volume of sphere
\r
693 func = function(name, radius, node)
\r
694 local count = worldedit.sphere(worldedit.pos1[name], radius, node, true)
\r
695 worldedit.player_notify(name, count .. " nodes added")
\r
699 worldedit.register_command("sphere", {
\r
700 params = "<radius> <node>",
\r
701 description = "Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
702 privs = {worldedit=true},
\r
704 parse = check_sphere,
\r
705 nodes_needed = function(name, radius, node)
\r
706 return math.ceil((4 * math.pi * (radius ^ 3)) / 3) --volume of sphere
\r
708 func = function(name, radius, node)
\r
709 local count = worldedit.sphere(worldedit.pos1[name], radius, node)
\r
710 worldedit.player_notify(name, count .. " nodes added")
\r
714 local check_dome = function(param)
\r
715 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
716 if found == nil then
\r
719 local node = worldedit.normalize_nodename(nodename)
\r
721 return false, "invalid node name: " .. nodename
\r
723 return true, tonumber(radius), node
\r
726 worldedit.register_command("hollowdome", {
\r
727 params = "<radius> <node>",
\r
728 description = "Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
729 privs = {worldedit=true},
\r
731 parse = check_dome,
\r
732 nodes_needed = function(name, radius, node)
\r
733 return math.ceil((2 * math.pi * (radius ^ 3)) / 3) --volume of dome
\r
735 func = function(name, radius, node)
\r
736 local count = worldedit.dome(worldedit.pos1[name], radius, node, true)
\r
737 worldedit.player_notify(name, count .. " nodes added")
\r
741 worldedit.register_command("dome", {
\r
742 params = "<radius> <node>",
\r
743 description = "Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
744 privs = {worldedit=true},
\r
746 parse = check_dome,
\r
747 nodes_needed = function(name, radius, node)
\r
748 return math.ceil((2 * math.pi * (radius ^ 3)) / 3) --volume of dome
\r
750 func = function(name, radius, node)
\r
751 local count = worldedit.dome(worldedit.pos1[name], radius, node)
\r
752 worldedit.player_notify(name, count .. " nodes added")
\r
756 local check_cylinder = function(param)
\r
758 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
759 if found == nil then
\r
761 found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
\r
764 if found == nil then
\r
767 local node = worldedit.normalize_nodename(nodename)
\r
769 return false, "invalid node name: " .. nodename
\r
771 return true, axis, tonumber(length), tonumber(radius1), tonumber(radius2), node
\r
774 worldedit.register_command("hollowcylinder", {
\r
775 params = "x/y/z/? <length> <radius1> [radius2] <node>",
\r
776 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
777 privs = {worldedit=true},
\r
779 parse = check_cylinder,
\r
780 nodes_needed = function(name, axis, length, radius1, radius2, node)
\r
781 local radius = math.max(radius1, radius2)
\r
782 return math.ceil(math.pi * (radius ^ 2) * length)
\r
784 func = function(name, axis, length, radius1, radius2, node)
\r
785 if axis == "?" then
\r
787 axis, sign = worldedit.player_axis(name)
\r
788 length = length * sign
\r
790 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, radius1, radius2, node, true)
\r
791 worldedit.player_notify(name, count .. " nodes added")
\r
795 worldedit.register_command("cylinder", {
\r
796 params = "x/y/z/? <length> <radius1> [radius2] <node>",
\r
797 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
798 privs = {worldedit=true},
\r
800 parse = check_cylinder,
\r
801 nodes_needed = function(name, axis, length, radius1, radius2, node)
\r
802 local radius = math.max(radius1, radius2)
\r
803 return math.ceil(math.pi * (radius ^ 2) * length)
\r
805 func = function(name, axis, length, radius1, radius2, node)
\r
806 if axis == "?" then
\r
808 axis, sign = worldedit.player_axis(name)
\r
809 length = length * sign
\r
811 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, radius1, radius2, node)
\r
812 worldedit.player_notify(name, count .. " nodes added")
\r
816 local check_pyramid = function(param)
\r
817 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
\r
818 if found == nil then
\r
821 local node = worldedit.normalize_nodename(nodename)
\r
823 return false, "invalid node name: " .. nodename
\r
825 return true, axis, tonumber(height), node
\r
828 worldedit.register_command("hollowpyramid", {
\r
829 params = "x/y/z/? <height> <node>",
\r
830 description = "Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
\r
831 privs = {worldedit=true},
\r
833 parse = check_pyramid,
\r
834 nodes_needed = function(name, axis, height, node)
\r
835 return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
\r
837 func = function(name, axis, height, node)
\r
838 if axis == "?" then
\r
840 axis, sign = worldedit.player_axis(name)
\r
841 height = height * sign
\r
843 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true)
\r
844 worldedit.player_notify(name, count .. " nodes added")
\r
848 worldedit.register_command("pyramid", {
\r
849 params = "x/y/z/? <height> <node>",
\r
850 description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
\r
851 privs = {worldedit=true},
\r
853 parse = check_pyramid,
\r
854 nodes_needed = function(name, axis, height, node)
\r
855 return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
\r
857 func = function(name, axis, height, node)
\r
858 if axis == "?" then
\r
860 axis, sign = worldedit.player_axis(name)
\r
861 height = height * sign
\r
863 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)
\r
864 worldedit.player_notify(name, count .. " nodes added")
\r
868 worldedit.register_command("spiral", {
\r
869 params = "<length> <height> <space> <node>",
\r
870 description = "Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>",
\r
871 privs = {worldedit=true},
\r
873 parse = function(param)
\r
874 local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
875 if found == nil then
\r
878 local node = worldedit.normalize_nodename(nodename)
\r
880 return false, "invalid node name: " .. nodename
\r
882 return true, tonumber(length), tonumber(height), tonumber(space), node
\r
884 nodes_needed = function(name, length, height, space, node)
\r
885 return (length + space) * height -- TODO: this is not the upper bound
\r
887 func = function(name, length, height, space, node)
\r
888 local count = worldedit.spiral(worldedit.pos1[name], length, height, space, node)
\r
889 worldedit.player_notify(name, count .. " nodes added")
\r
893 worldedit.register_command("copy", {
\r
894 params = "x/y/z/? <amount>",
\r
895 description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
\r
896 privs = {worldedit=true},
\r
898 parse = function(param)
\r
899 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
900 if found == nil then
\r
903 return true, axis, tonumber(amount)
\r
905 nodes_needed = function(name, axis, amount)
\r
906 return check_region(name) * 2
\r
908 func = function(name, axis, amount)
\r
909 if axis == "?" then
\r
911 axis, sign = worldedit.player_axis(name)
\r
912 amount = amount * sign
\r
915 local count = worldedit.copy(worldedit.pos1[name], worldedit.pos2[name], axis, amount)
\r
916 worldedit.player_notify(name, count .. " nodes copied")
\r
920 worldedit.register_command("move", {
\r
921 params = "x/y/z/? <amount>",
\r
922 description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
\r
923 privs = {worldedit=true},
\r
925 parse = function(param)
\r
926 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
927 if found == nil then
\r
930 return true, axis, tonumber(amount)
\r
932 nodes_needed = function(name, axis, amount)
\r
933 return check_region(name) * 2
\r
935 func = function(name, axis, amount)
\r
936 if axis == "?" then
\r
938 axis, sign = worldedit.player_axis(name)
\r
939 amount = amount * sign
\r
942 local count = worldedit.move(pos1, pos2, axis, amount)
\r
944 pos1[axis] = pos1[axis] + amount
\r
945 pos2[axis] = pos2[axis] + amount
\r
946 worldedit.mark_pos1(name)
\r
947 worldedit.mark_pos2(name)
\r
948 worldedit.player_notify(name, count .. " nodes moved")
\r
952 worldedit.register_command("stack", {
\r
953 params = "x/y/z/? <count>",
\r
954 description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",
\r
955 privs = {worldedit=true},
\r
957 parse = function(param)
\r
958 local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
959 if found == nil then
\r
962 return true, axis, tonumber(repetitions)
\r
964 nodes_needed = function(name, axis, repetitions)
\r
965 return check_region(name) * math.abs(repetitions)
\r
967 func = function(name, axis, repetitions)
\r
968 if axis == "?" then
\r
970 axis, sign = worldedit.player_axis(name)
\r
971 repetitions = repetitions * sign
\r
974 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
975 local count = worldedit.volume(pos1, pos2) * math.abs(repetitions)
\r
976 worldedit.stack(pos1, pos2, axis, repetitions, function()
\r
977 worldedit.player_notify(name, count .. " nodes stacked")
\r
982 worldedit.register_command("stack2", {
\r
983 params = "<count> <x> <y> <z>",
\r
984 description = "Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>",
\r
985 privs = {worldedit=true},
\r
987 parse = function(param)
\r
988 local repetitions, incs = param:match("(%d+)%s*(.+)")
\r
989 if repetitions == nil then
\r
990 return false, "invalid count: " .. param
\r
992 local x, y, z = incs:match("([+-]?%d+) ([+-]?%d+) ([+-]?%d+)")
\r
994 return false, "invalid increments: " .. param
\r
997 return true, tonumber(repetitions), {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
\r
999 nodes_needed = function(name, repetitions, offset)
\r
1000 return check_region(name) * repetitions
\r
1002 func = function(name, repetitions, offset)
\r
1003 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1004 local count = worldedit.volume(pos1, pos2) * repetitions
\r
1005 worldedit.stack2(pos1, pos2, offset, repetitions, function()
\r
1006 worldedit.player_notify(name, count .. " nodes stacked")
\r
1012 worldedit.register_command("stretch", {
\r
1013 params = "<stretchx> <stretchy> <stretchz>",
\r
1014 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
1015 privs = {worldedit=true},
\r
1017 parse = function(param)
\r
1018 local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
\r
1019 if found == nil then
\r
1022 stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)
\r
1023 if stretchx == 0 or stretchy == 0 or stretchz == 0 then
\r
1024 return false, "invalid scaling factors: " .. param
\r
1026 return true, stretchx, stretchy, stretchz
\r
1028 nodes_needed = function(name, stretchx, stretchy, stretchz)
\r
1029 return check_region(name) * stretchx * stretchy * stretchz
\r
1031 func = function(name, stretchx, stretchy, stretchz)
\r
1032 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1033 local count, pos1, pos2 = worldedit.stretch(pos1, pos2, stretchx, stretchy, stretchz)
\r
1035 --reset markers to scaled positions
\r
1036 worldedit.pos1[name] = pos1
\r
1037 worldedit.pos2[name] = pos2
\r
1038 worldedit.mark_pos1(name)
\r
1039 worldedit.mark_pos2(name)
\r
1041 worldedit.player_notify(name, count .. " nodes stretched")
\r
1045 worldedit.register_command("transpose", {
\r
1046 params = "x/y/z/? x/y/z/?",
\r
1047 description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",
\r
1048 privs = {worldedit=true},
\r
1050 parse = function(param)
\r
1051 local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
\r
1052 if found == nil then
\r
1054 elseif axis1 == axis2 then
\r
1055 return false, "invalid usage: axes must be different"
\r
1057 return true, axis1, axis2
\r
1059 nodes_needed = check_region,
\r
1060 func = function(name, axis1, axis2)
\r
1061 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1062 if axis1 == "?" then axis1 = worldedit.player_axis(name) end
\r
1063 if axis2 == "?" then axis2 = worldedit.player_axis(name) end
\r
1064 local count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
\r
1066 --reset markers to transposed positions
\r
1067 worldedit.pos1[name] = pos1
\r
1068 worldedit.pos2[name] = pos2
\r
1069 worldedit.mark_pos1(name)
\r
1070 worldedit.mark_pos2(name)
\r
1072 worldedit.player_notify(name, count .. " nodes transposed")
\r
1076 worldedit.register_command("flip", {
\r
1077 params = "x/y/z/?",
\r
1078 description = "Flip the current WorldEdit region along the x/y/z/? axis",
\r
1079 privs = {worldedit=true},
\r
1081 parse = function(param)
\r
1082 if param ~= "x" and param ~= "y" and param ~= "z" and param ~= "?" then
\r
1085 return true, param
\r
1087 nodes_needed = check_region,
\r
1088 func = function(name, param)
\r
1089 if param == "?" then param = worldedit.player_axis(name) end
\r
1090 local count = worldedit.flip(worldedit.pos1[name], worldedit.pos2[name], param)
\r
1091 worldedit.player_notify(name, count .. " nodes flipped")
\r
1095 worldedit.register_command("rotate", {
\r
1096 params = "<axis> <angle>",
\r
1097 description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",
\r
1098 privs = {worldedit=true},
\r
1100 parse = function(param)
\r
1101 local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
1102 if found == nil then
\r
1105 angle = tonumber(angle)
\r
1106 if angle % 90 ~= 0 or angle % 360 == 0 then
\r
1107 return false, "invalid usage: angle must be multiple of 90"
\r
1109 return true, axis, angle
\r
1111 nodes_needed = check_region,
\r
1112 func = function(name, axis, angle)
\r
1113 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1114 if axis == "?" then axis = worldedit.player_axis(name) end
\r
1115 local count, pos1, pos2 = worldedit.rotate(pos1, pos2, axis, angle)
\r
1117 --reset markers to rotated positions
\r
1118 worldedit.pos1[name] = pos1
\r
1119 worldedit.pos2[name] = pos2
\r
1120 worldedit.mark_pos1(name)
\r
1121 worldedit.mark_pos2(name)
\r
1123 worldedit.player_notify(name, count .. " nodes rotated")
\r
1127 worldedit.register_command("orient", {
\r
1128 params = "<angle>",
\r
1129 description = "Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)",
\r
1130 privs = {worldedit=true},
\r
1132 parse = function(param)
\r
1133 local found, _, angle = param:find("^([+-]?%d+)$")
\r
1134 if found == nil then
\r
1137 angle = tonumber(angle)
\r
1138 if angle % 90 ~= 0 then
\r
1139 return false, "invalid usage: angle must be multiple of 90"
\r
1141 return true, angle
\r
1143 nodes_needed = check_region,
\r
1144 func = function(name, angle)
\r
1145 local count = worldedit.orient(worldedit.pos1[name], worldedit.pos2[name], angle)
\r
1146 worldedit.player_notify(name, count .. " nodes oriented")
\r
1150 worldedit.register_command("fixlight", {
\r
1152 description = "Fix the lighting in the current WorldEdit region",
\r
1153 privs = {worldedit=true},
\r
1155 nodes_needed = check_region,
\r
1156 func = function(name)
\r
1157 local count = worldedit.fixlight(worldedit.pos1[name], worldedit.pos2[name])
\r
1158 worldedit.player_notify(name, count .. " nodes updated")
\r
1162 worldedit.register_command("drain", {
\r
1164 description = "Remove any fluid node within the current WorldEdit region",
\r
1165 privs = {worldedit=true},
\r
1167 nodes_needed = check_region,
\r
1168 func = function(name)
\r
1169 -- TODO: make an API function for this
\r
1171 local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
\r
1172 for x = pos1.x, pos2.x do
\r
1173 for y = pos1.y, pos2.y do
\r
1174 for z = pos1.z, pos2.z do
\r
1175 local n = minetest.get_node({x=x, y=y, z=z}).name
\r
1176 local d = minetest.registered_nodes[n]
\r
1177 if d ~= nil and (d["drawtype"] == "liquid" or d["drawtype"] == "flowingliquid") then
\r
1178 minetest.remove_node({x=x, y=y, z=z})
\r
1184 worldedit.player_notify(name, count .. " nodes updated")
\r
1188 worldedit.register_command("hide", {
\r
1190 description = "Hide all nodes in the current WorldEdit region non-destructively",
\r
1191 privs = {worldedit=true},
\r
1193 nodes_needed = check_region,
\r
1194 func = function(name)
\r
1195 local count = worldedit.hide(worldedit.pos1[name], worldedit.pos2[name])
\r
1196 worldedit.player_notify(name, count .. " nodes hidden")
\r
1200 worldedit.register_command("suppress", {
\r
1201 params = "<node>",
\r
1202 description = "Suppress all <node> in the current WorldEdit region non-destructively",
\r
1203 privs = {worldedit=true},
\r
1205 parse = function(param)
\r
1206 local node = worldedit.normalize_nodename(param)
\r
1208 return false, "invalid node name: " .. param
\r
1212 nodes_needed = check_region,
\r
1213 func = function(name, node)
\r
1214 local count = worldedit.suppress(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1215 worldedit.player_notify(name, count .. " nodes suppressed")
\r
1219 worldedit.register_command("highlight", {
\r
1220 params = "<node>",
\r
1221 description = "Highlight <node> in the current WorldEdit region by hiding everything else non-destructively",
\r
1222 privs = {worldedit=true},
\r
1224 parse = function(param)
\r
1225 local node = worldedit.normalize_nodename(param)
\r
1227 return false, "invalid node name: " .. param
\r
1231 nodes_needed = check_region,
\r
1232 func = function(name, node)
\r
1233 local count = worldedit.highlight(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1234 worldedit.player_notify(name, count .. " nodes highlighted")
\r
1238 worldedit.register_command("restore", {
\r
1240 description = "Restores nodes hidden with WorldEdit in the current WorldEdit region",
\r
1241 privs = {worldedit=true},
\r
1243 nodes_needed = check_region,
\r
1244 func = function(name)
\r
1245 local count = worldedit.restore(worldedit.pos1[name], worldedit.pos2[name])
\r
1246 worldedit.player_notify(name, count .. " nodes restored")
\r
1250 local function detect_misaligned_schematic(name, pos1, pos2)
\r
1251 pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
1252 -- Check that allocate/save can position the schematic correctly
\r
1253 -- The expected behaviour is that the (0,0,0) corner of the schematic stays
\r
1254 -- sat pos1, this only works when the minimum position is actually present
\r
1255 -- in the schematic.
\r
1256 local node = minetest.get_node(pos1)
\r
1257 local have_node_at_origin = node.name ~= "air" and node.name ~= "ignore"
\r
1258 if not have_node_at_origin then
\r
1259 worldedit.player_notify(name,
\r
1260 "Warning: The schematic contains excessive free space and WILL be "..
\r
1261 "misaligned when allocated or loaded. To avoid this, shrink your "..
\r
1262 "area to cover exactly the nodes to be saved."
\r
1267 worldedit.register_command("save", {
\r
1268 params = "<file>",
\r
1269 description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",
\r
1270 privs = {worldedit=true},
\r
1272 parse = function(param)
\r
1273 if param == "" then
\r
1276 if not check_filename(param) then
\r
1277 return false, "Disallowed file name: " .. param
\r
1279 return true, param
\r
1281 nodes_needed = check_region,
\r
1282 func = function(name, param)
\r
1283 local result, count = worldedit.serialize(worldedit.pos1[name],
\r
1284 worldedit.pos2[name])
\r
1285 detect_misaligned_schematic(name, worldedit.pos1[name], worldedit.pos2[name])
\r
1287 local path = minetest.get_worldpath() .. "/schems"
\r
1288 -- Create directory if it does not already exist
\r
1289 minetest.mkdir(path)
\r
1291 local filename = path .. "/" .. param .. ".we"
\r
1292 local file, err = io.open(filename, "wb")
\r
1293 if err ~= nil then
\r
1294 worldedit.player_notify(name, "Could not save file to \"" .. filename .. "\"")
\r
1297 file:write(result)
\r
1301 worldedit.player_notify(name, count .. " nodes saved")
\r
1305 worldedit.register_command("allocate", {
\r
1306 params = "<file>",
\r
1307 description = "Set the region defined by nodes from \"(world folder)/schems/<file>.we\" as the current WorldEdit region",
\r
1308 privs = {worldedit=true},
\r
1310 parse = function(param)
\r
1311 if param == "" then
\r
1314 if not check_filename(param) then
\r
1315 return false, "Disallowed file name: " .. param
\r
1317 return true, param
\r
1319 func = function(name, param)
\r
1320 local pos = worldedit.pos1[name]
\r
1322 local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"
\r
1323 local file, err = io.open(filename, "rb")
\r
1324 if err ~= nil then
\r
1325 worldedit.player_notify(name, "could not open file \"" .. filename .. "\"")
\r
1328 local value = file:read("*a")
\r
1331 local version = worldedit.read_header(value)
\r
1332 if version == nil or version == 0 then
\r
1333 worldedit.player_notify(name, "File is invalid!")
\r
1335 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1336 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1339 local nodepos1, nodepos2, count = worldedit.allocate(pos, value)
\r
1341 if not nodepos1 then
\r
1342 worldedit.player_notify(name, "Schematic empty, nothing allocated")
\r
1346 worldedit.pos1[name] = nodepos1
\r
1347 worldedit.mark_pos1(name)
\r
1348 worldedit.pos2[name] = nodepos2
\r
1349 worldedit.mark_pos2(name)
\r
1351 worldedit.player_notify(name, count .. " nodes allocated")
\r
1355 worldedit.register_command("load", {
\r
1356 params = "<file>",
\r
1357 description = "Load nodes from \"(world folder)/schems/<file>[.we[m]]\" with position 1 of the current WorldEdit region as the origin",
\r
1358 privs = {worldedit=true},
\r
1360 parse = function(param)
\r
1361 if param == "" then
\r
1364 if not check_filename(param) then
\r
1365 return false, "Disallowed file name: " .. param
\r
1367 return true, param
\r
1369 func = function(name, param)
\r
1370 local pos = worldedit.pos1[name]
\r
1372 if param == "" then
\r
1373 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1376 if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then
\r
1377 worldedit.player_notify(name, "invalid file name: " .. param)
\r
1381 --find the file in the world path
\r
1382 local testpaths = {
\r
1383 minetest.get_worldpath() .. "/schems/" .. param,
\r
1384 minetest.get_worldpath() .. "/schems/" .. param .. ".we",
\r
1385 minetest.get_worldpath() .. "/schems/" .. param .. ".wem",
\r
1388 for index, path in ipairs(testpaths) do
\r
1389 file, err = io.open(path, "rb")
\r
1395 worldedit.player_notify(name, "could not open file \"" .. param .. "\"")
\r
1398 local value = file:read("*a")
\r
1401 local version = worldedit.read_header(value)
\r
1402 if version == nil or version == 0 then
\r
1403 worldedit.player_notify(name, "File is invalid!")
\r
1405 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1406 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1410 local count = worldedit.deserialize(pos, value)
\r
1412 worldedit.player_notify(name, count .. " nodes loaded")
\r
1416 worldedit.register_command("lua", {
\r
1417 params = "<code>",
\r
1418 description = "Executes <code> as a Lua chunk in the global namespace",
\r
1419 privs = {worldedit=true, server=true},
\r
1420 parse = function(param)
\r
1421 return true, param
\r
1423 func = function(name, param)
\r
1424 local err = worldedit.lua(param)
\r
1426 worldedit.player_notify(name, "code error: " .. err)
\r
1427 minetest.log("action", name.." tried to execute "..param)
\r
1429 worldedit.player_notify(name, "code successfully executed", false)
\r
1430 minetest.log("action", name.." executed "..param)
\r
1435 worldedit.register_command("luatransform", {
\r
1436 params = "<code>",
\r
1437 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
1438 privs = {worldedit=true, server=true},
\r
1440 parse = function(param)
\r
1441 return true, param
\r
1443 nodes_needed = check_region,
\r
1444 func = function(name, param)
\r
1445 local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)
\r
1447 worldedit.player_notify(name, "code error: " .. err, false)
\r
1448 minetest.log("action", name.." tried to execute luatransform "..param)
\r
1450 worldedit.player_notify(name, "code successfully executed", false)
\r
1451 minetest.log("action", name.." executed luatransform "..param)
\r
1456 worldedit.register_command("mtschemcreate", {
\r
1457 params = "<file>",
\r
1458 description = "Save the current WorldEdit region using the Minetest "..
\r
1459 "Schematic format to \"(world folder)/schems/<filename>.mts\"",
\r
1460 privs = {worldedit=true},
\r
1462 parse = function(param)
\r
1463 if param == "" then
\r
1466 if not check_filename(param) then
\r
1467 return false, "Disallowed file name: " .. param
\r
1469 return true, param
\r
1471 nodes_needed = check_region,
\r
1472 func = function(name, param)
\r
1473 local path = minetest.get_worldpath() .. "/schems"
\r
1474 -- Create directory if it does not already exist
\r
1475 minetest.mkdir(path)
\r
1477 local filename = path .. "/" .. param .. ".mts"
\r
1478 local ret = minetest.create_schematic(worldedit.pos1[name],
\r
1479 worldedit.pos2[name], worldedit.prob_list[name],
\r
1481 if ret == nil then
\r
1482 worldedit.player_notify(name, "Failed to create Minetest schematic")
\r
1484 worldedit.player_notify(name, "Saved Minetest schematic to " .. param)
\r
1486 worldedit.prob_list[name] = {}
\r
1490 worldedit.register_command("mtschemplace", {
\r
1491 params = "<file>",
\r
1492 description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin",
\r
1493 privs = {worldedit=true},
\r
1495 parse = function(param)
\r
1496 if param == "" then
\r
1499 if not check_filename(param) then
\r
1500 return false, "Disallowed file name: " .. param
\r
1502 return true, param
\r
1504 func = function(name, param)
\r
1505 local pos = worldedit.pos1[name]
\r
1507 local path = minetest.get_worldpath() .. "/schems/" .. param .. ".mts"
\r
1508 if minetest.place_schematic(pos, path) == nil then
\r
1509 worldedit.player_notify(name, "failed to place Minetest schematic")
\r
1511 worldedit.player_notify(name, "placed Minetest schematic " .. param ..
\r
1512 " at " .. minetest.pos_to_string(pos))
\r
1517 worldedit.register_command("mtschemprob", {
\r
1518 params = "start/finish/get",
\r
1519 description = "Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry",
\r
1520 privs = {worldedit=true},
\r
1521 parse = function(param)
\r
1522 if param ~= "start" and param ~= "finish" and param ~= "get" then
\r
1523 return false, "unknown subcommand: " .. param
\r
1525 return true, param
\r
1527 func = function(name, param)
\r
1528 if param == "start" then --start probability setting
\r
1529 worldedit.set_pos[name] = "prob"
\r
1530 worldedit.prob_list[name] = {}
\r
1531 worldedit.player_notify(name, "select Minetest schematic probability values by punching nodes")
\r
1532 elseif param == "finish" then --finish probability setting
\r
1533 worldedit.set_pos[name] = nil
\r
1534 worldedit.player_notify(name, "finished Minetest schematic probability selection")
\r
1535 elseif param == "get" then --get all nodes that had probabilities set on them
\r
1537 local problist = worldedit.prob_list[name]
\r
1538 if problist == nil then
\r
1541 for k,v in pairs(problist) do
\r
1542 local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100
\r
1543 text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "
\r
1545 worldedit.player_notify(name, "currently set node probabilities:")
\r
1546 worldedit.player_notify(name, text)
\r
1551 minetest.register_on_player_receive_fields(function(player, formname, fields)
\r
1552 if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then
\r
1553 local name = player:get_player_name()
\r
1554 local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
\r
1555 local index = table.getn(worldedit.prob_list[name]) + 1
\r
1556 worldedit.prob_list[name][index] = prob_entry
\r
1560 worldedit.register_command("clearobjects", {
\r
1562 description = "Clears all objects within the WorldEdit region",
\r
1563 privs = {worldedit=true},
\r
1565 nodes_needed = check_region,
\r
1566 func = function(name)
\r
1567 local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name])
\r
1568 worldedit.player_notify(name, count .. " objects cleared")
\r