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 nodename = nodename:lower()
\r
166 for key, value in pairs(minetest.registered_nodes) do
\r
167 if string_endswith(key, ":" .. nodename) then -- matches name (w/o mod part)
\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 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 pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
943 local count = worldedit.move(pos1, pos2, axis, amount)
\r
945 pos1[axis] = pos1[axis] + amount
\r
946 pos2[axis] = pos2[axis] + amount
\r
947 worldedit.mark_pos1(name)
\r
948 worldedit.mark_pos2(name)
\r
949 worldedit.player_notify(name, count .. " nodes moved")
\r
953 worldedit.register_command("stack", {
\r
954 params = "x/y/z/? <count>",
\r
955 description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",
\r
956 privs = {worldedit=true},
\r
958 parse = function(param)
\r
959 local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
960 if found == nil then
\r
963 return true, axis, tonumber(repetitions)
\r
965 nodes_needed = function(name, axis, repetitions)
\r
966 return check_region(name) * math.abs(repetitions)
\r
968 func = function(name, axis, repetitions)
\r
969 if axis == "?" then
\r
971 axis, sign = worldedit.player_axis(name)
\r
972 repetitions = repetitions * sign
\r
975 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
976 local count = worldedit.volume(pos1, pos2) * math.abs(repetitions)
\r
977 worldedit.stack(pos1, pos2, axis, repetitions, function()
\r
978 worldedit.player_notify(name, count .. " nodes stacked")
\r
983 worldedit.register_command("stack2", {
\r
984 params = "<count> <x> <y> <z>",
\r
985 description = "Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>",
\r
986 privs = {worldedit=true},
\r
988 parse = function(param)
\r
989 local repetitions, incs = param:match("(%d+)%s*(.+)")
\r
990 if repetitions == nil then
\r
991 return false, "invalid count: " .. param
\r
993 local x, y, z = incs:match("([+-]?%d+) ([+-]?%d+) ([+-]?%d+)")
\r
995 return false, "invalid increments: " .. param
\r
998 return true, tonumber(repetitions), {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
\r
1000 nodes_needed = function(name, repetitions, offset)
\r
1001 return check_region(name) * repetitions
\r
1003 func = function(name, repetitions, offset)
\r
1004 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1005 local count = worldedit.volume(pos1, pos2) * repetitions
\r
1006 worldedit.stack2(pos1, pos2, offset, repetitions, function()
\r
1007 worldedit.player_notify(name, count .. " nodes stacked")
\r
1013 worldedit.register_command("stretch", {
\r
1014 params = "<stretchx> <stretchy> <stretchz>",
\r
1015 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
1016 privs = {worldedit=true},
\r
1018 parse = function(param)
\r
1019 local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
\r
1020 if found == nil then
\r
1023 stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)
\r
1024 if stretchx == 0 or stretchy == 0 or stretchz == 0 then
\r
1025 return false, "invalid scaling factors: " .. param
\r
1027 return true, stretchx, stretchy, stretchz
\r
1029 nodes_needed = function(name, stretchx, stretchy, stretchz)
\r
1030 return check_region(name) * stretchx * stretchy * stretchz
\r
1032 func = function(name, stretchx, stretchy, stretchz)
\r
1033 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1034 local count, pos1, pos2 = worldedit.stretch(pos1, pos2, stretchx, stretchy, stretchz)
\r
1036 --reset markers to scaled positions
\r
1037 worldedit.pos1[name] = pos1
\r
1038 worldedit.pos2[name] = pos2
\r
1039 worldedit.mark_pos1(name)
\r
1040 worldedit.mark_pos2(name)
\r
1042 worldedit.player_notify(name, count .. " nodes stretched")
\r
1046 worldedit.register_command("transpose", {
\r
1047 params = "x/y/z/? x/y/z/?",
\r
1048 description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",
\r
1049 privs = {worldedit=true},
\r
1051 parse = function(param)
\r
1052 local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
\r
1053 if found == nil then
\r
1055 elseif axis1 == axis2 then
\r
1056 return false, "invalid usage: axes must be different"
\r
1058 return true, axis1, axis2
\r
1060 nodes_needed = check_region,
\r
1061 func = function(name, axis1, axis2)
\r
1062 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1063 if axis1 == "?" then axis1 = worldedit.player_axis(name) end
\r
1064 if axis2 == "?" then axis2 = worldedit.player_axis(name) end
\r
1065 local count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
\r
1067 --reset markers to transposed positions
\r
1068 worldedit.pos1[name] = pos1
\r
1069 worldedit.pos2[name] = pos2
\r
1070 worldedit.mark_pos1(name)
\r
1071 worldedit.mark_pos2(name)
\r
1073 worldedit.player_notify(name, count .. " nodes transposed")
\r
1077 worldedit.register_command("flip", {
\r
1078 params = "x/y/z/?",
\r
1079 description = "Flip the current WorldEdit region along the x/y/z/? axis",
\r
1080 privs = {worldedit=true},
\r
1082 parse = function(param)
\r
1083 if param ~= "x" and param ~= "y" and param ~= "z" and param ~= "?" then
\r
1086 return true, param
\r
1088 nodes_needed = check_region,
\r
1089 func = function(name, param)
\r
1090 if param == "?" then param = worldedit.player_axis(name) end
\r
1091 local count = worldedit.flip(worldedit.pos1[name], worldedit.pos2[name], param)
\r
1092 worldedit.player_notify(name, count .. " nodes flipped")
\r
1096 worldedit.register_command("rotate", {
\r
1097 params = "<axis> <angle>",
\r
1098 description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",
\r
1099 privs = {worldedit=true},
\r
1101 parse = function(param)
\r
1102 local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
1103 if found == nil then
\r
1106 angle = tonumber(angle)
\r
1107 if angle % 90 ~= 0 or angle % 360 == 0 then
\r
1108 return false, "invalid usage: angle must be multiple of 90"
\r
1110 return true, axis, angle
\r
1112 nodes_needed = check_region,
\r
1113 func = function(name, axis, angle)
\r
1114 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1115 if axis == "?" then axis = worldedit.player_axis(name) end
\r
1116 local count, pos1, pos2 = worldedit.rotate(pos1, pos2, axis, angle)
\r
1118 --reset markers to rotated positions
\r
1119 worldedit.pos1[name] = pos1
\r
1120 worldedit.pos2[name] = pos2
\r
1121 worldedit.mark_pos1(name)
\r
1122 worldedit.mark_pos2(name)
\r
1124 worldedit.player_notify(name, count .. " nodes rotated")
\r
1128 worldedit.register_command("orient", {
\r
1129 params = "<angle>",
\r
1130 description = "Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)",
\r
1131 privs = {worldedit=true},
\r
1133 parse = function(param)
\r
1134 local found, _, angle = param:find("^([+-]?%d+)$")
\r
1135 if found == nil then
\r
1138 angle = tonumber(angle)
\r
1139 if angle % 90 ~= 0 then
\r
1140 return false, "invalid usage: angle must be multiple of 90"
\r
1142 return true, angle
\r
1144 nodes_needed = check_region,
\r
1145 func = function(name, angle)
\r
1146 local count = worldedit.orient(worldedit.pos1[name], worldedit.pos2[name], angle)
\r
1147 worldedit.player_notify(name, count .. " nodes oriented")
\r
1151 worldedit.register_command("fixlight", {
\r
1153 description = "Fix the lighting in the current WorldEdit region",
\r
1154 privs = {worldedit=true},
\r
1156 nodes_needed = check_region,
\r
1157 func = function(name)
\r
1158 local count = worldedit.fixlight(worldedit.pos1[name], worldedit.pos2[name])
\r
1159 worldedit.player_notify(name, count .. " nodes updated")
\r
1163 worldedit.register_command("drain", {
\r
1165 description = "Remove any fluid node within the current WorldEdit region",
\r
1166 privs = {worldedit=true},
\r
1168 nodes_needed = check_region,
\r
1169 func = function(name)
\r
1170 -- TODO: make an API function for this
\r
1172 local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
\r
1173 for x = pos1.x, pos2.x do
\r
1174 for y = pos1.y, pos2.y do
\r
1175 for z = pos1.z, pos2.z do
\r
1176 local n = minetest.get_node({x=x, y=y, z=z}).name
\r
1177 local d = minetest.registered_nodes[n]
\r
1178 if d ~= nil and (d["drawtype"] == "liquid" or d["drawtype"] == "flowingliquid") then
\r
1179 minetest.remove_node({x=x, y=y, z=z})
\r
1185 worldedit.player_notify(name, count .. " nodes updated")
\r
1189 worldedit.register_command("hide", {
\r
1191 description = "Hide all nodes in the current WorldEdit region non-destructively",
\r
1192 privs = {worldedit=true},
\r
1194 nodes_needed = check_region,
\r
1195 func = function(name)
\r
1196 local count = worldedit.hide(worldedit.pos1[name], worldedit.pos2[name])
\r
1197 worldedit.player_notify(name, count .. " nodes hidden")
\r
1201 worldedit.register_command("suppress", {
\r
1202 params = "<node>",
\r
1203 description = "Suppress all <node> in the current WorldEdit region non-destructively",
\r
1204 privs = {worldedit=true},
\r
1206 parse = function(param)
\r
1207 local node = worldedit.normalize_nodename(param)
\r
1209 return false, "invalid node name: " .. param
\r
1213 nodes_needed = check_region,
\r
1214 func = function(name, node)
\r
1215 local count = worldedit.suppress(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1216 worldedit.player_notify(name, count .. " nodes suppressed")
\r
1220 worldedit.register_command("highlight", {
\r
1221 params = "<node>",
\r
1222 description = "Highlight <node> in the current WorldEdit region by hiding everything else non-destructively",
\r
1223 privs = {worldedit=true},
\r
1225 parse = function(param)
\r
1226 local node = worldedit.normalize_nodename(param)
\r
1228 return false, "invalid node name: " .. param
\r
1232 nodes_needed = check_region,
\r
1233 func = function(name, node)
\r
1234 local count = worldedit.highlight(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1235 worldedit.player_notify(name, count .. " nodes highlighted")
\r
1239 worldedit.register_command("restore", {
\r
1241 description = "Restores nodes hidden with WorldEdit in the current WorldEdit region",
\r
1242 privs = {worldedit=true},
\r
1244 nodes_needed = check_region,
\r
1245 func = function(name)
\r
1246 local count = worldedit.restore(worldedit.pos1[name], worldedit.pos2[name])
\r
1247 worldedit.player_notify(name, count .. " nodes restored")
\r
1251 local function detect_misaligned_schematic(name, pos1, pos2)
\r
1252 pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
1253 -- Check that allocate/save can position the schematic correctly
\r
1254 -- The expected behaviour is that the (0,0,0) corner of the schematic stays
\r
1255 -- sat pos1, this only works when the minimum position is actually present
\r
1256 -- in the schematic.
\r
1257 local node = minetest.get_node(pos1)
\r
1258 local have_node_at_origin = node.name ~= "air" and node.name ~= "ignore"
\r
1259 if not have_node_at_origin then
\r
1260 worldedit.player_notify(name,
\r
1261 "Warning: The schematic contains excessive free space and WILL be "..
\r
1262 "misaligned when allocated or loaded. To avoid this, shrink your "..
\r
1263 "area to cover exactly the nodes to be saved."
\r
1268 worldedit.register_command("save", {
\r
1269 params = "<file>",
\r
1270 description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",
\r
1271 privs = {worldedit=true},
\r
1273 parse = function(param)
\r
1274 if param == "" then
\r
1277 if not check_filename(param) then
\r
1278 return false, "Disallowed file name: " .. param
\r
1280 return true, param
\r
1282 nodes_needed = check_region,
\r
1283 func = function(name, param)
\r
1284 local result, count = worldedit.serialize(worldedit.pos1[name],
\r
1285 worldedit.pos2[name])
\r
1286 detect_misaligned_schematic(name, worldedit.pos1[name], worldedit.pos2[name])
\r
1288 local path = minetest.get_worldpath() .. "/schems"
\r
1289 -- Create directory if it does not already exist
\r
1290 minetest.mkdir(path)
\r
1292 local filename = path .. "/" .. param .. ".we"
\r
1293 local file, err = io.open(filename, "wb")
\r
1294 if err ~= nil then
\r
1295 worldedit.player_notify(name, "Could not save file to \"" .. filename .. "\"")
\r
1298 file:write(result)
\r
1302 worldedit.player_notify(name, count .. " nodes saved")
\r
1306 worldedit.register_command("allocate", {
\r
1307 params = "<file>",
\r
1308 description = "Set the region defined by nodes from \"(world folder)/schems/<file>.we\" as the current WorldEdit region",
\r
1309 privs = {worldedit=true},
\r
1311 parse = function(param)
\r
1312 if param == "" then
\r
1315 if not check_filename(param) then
\r
1316 return false, "Disallowed file name: " .. param
\r
1318 return true, param
\r
1320 func = function(name, param)
\r
1321 local pos = worldedit.pos1[name]
\r
1323 local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"
\r
1324 local file, err = io.open(filename, "rb")
\r
1325 if err ~= nil then
\r
1326 worldedit.player_notify(name, "could not open file \"" .. filename .. "\"")
\r
1329 local value = file:read("*a")
\r
1332 local version = worldedit.read_header(value)
\r
1333 if version == nil or version == 0 then
\r
1334 worldedit.player_notify(name, "File is invalid!")
\r
1336 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1337 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1340 local nodepos1, nodepos2, count = worldedit.allocate(pos, value)
\r
1342 if not nodepos1 then
\r
1343 worldedit.player_notify(name, "Schematic empty, nothing allocated")
\r
1347 worldedit.pos1[name] = nodepos1
\r
1348 worldedit.mark_pos1(name)
\r
1349 worldedit.pos2[name] = nodepos2
\r
1350 worldedit.mark_pos2(name)
\r
1352 worldedit.player_notify(name, count .. " nodes allocated")
\r
1356 worldedit.register_command("load", {
\r
1357 params = "<file>",
\r
1358 description = "Load nodes from \"(world folder)/schems/<file>[.we[m]]\" with position 1 of the current WorldEdit region as the origin",
\r
1359 privs = {worldedit=true},
\r
1361 parse = function(param)
\r
1362 if param == "" then
\r
1365 if not check_filename(param) then
\r
1366 return false, "Disallowed file name: " .. param
\r
1368 return true, param
\r
1370 func = function(name, param)
\r
1371 local pos = worldedit.pos1[name]
\r
1373 if param == "" then
\r
1374 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1377 if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then
\r
1378 worldedit.player_notify(name, "invalid file name: " .. param)
\r
1382 --find the file in the world path
\r
1383 local testpaths = {
\r
1384 minetest.get_worldpath() .. "/schems/" .. param,
\r
1385 minetest.get_worldpath() .. "/schems/" .. param .. ".we",
\r
1386 minetest.get_worldpath() .. "/schems/" .. param .. ".wem",
\r
1389 for index, path in ipairs(testpaths) do
\r
1390 file, err = io.open(path, "rb")
\r
1396 worldedit.player_notify(name, "could not open file \"" .. param .. "\"")
\r
1399 local value = file:read("*a")
\r
1402 local version = worldedit.read_header(value)
\r
1403 if version == nil or version == 0 then
\r
1404 worldedit.player_notify(name, "File is invalid!")
\r
1406 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1407 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1411 local count = worldedit.deserialize(pos, value)
\r
1413 worldedit.player_notify(name, count .. " nodes loaded")
\r
1417 worldedit.register_command("lua", {
\r
1418 params = "<code>",
\r
1419 description = "Executes <code> as a Lua chunk in the global namespace",
\r
1420 privs = {worldedit=true, server=true},
\r
1421 parse = function(param)
\r
1422 return true, param
\r
1424 func = function(name, param)
\r
1425 local err = worldedit.lua(param)
\r
1427 worldedit.player_notify(name, "code error: " .. err)
\r
1428 minetest.log("action", name.." tried to execute "..param)
\r
1430 worldedit.player_notify(name, "code successfully executed", false)
\r
1431 minetest.log("action", name.." executed "..param)
\r
1436 worldedit.register_command("luatransform", {
\r
1437 params = "<code>",
\r
1438 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
1439 privs = {worldedit=true, server=true},
\r
1441 parse = function(param)
\r
1442 return true, param
\r
1444 nodes_needed = check_region,
\r
1445 func = function(name, param)
\r
1446 local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)
\r
1448 worldedit.player_notify(name, "code error: " .. err, false)
\r
1449 minetest.log("action", name.." tried to execute luatransform "..param)
\r
1451 worldedit.player_notify(name, "code successfully executed", false)
\r
1452 minetest.log("action", name.." executed luatransform "..param)
\r
1457 worldedit.register_command("mtschemcreate", {
\r
1458 params = "<file>",
\r
1459 description = "Save the current WorldEdit region using the Minetest "..
\r
1460 "Schematic format to \"(world folder)/schems/<filename>.mts\"",
\r
1461 privs = {worldedit=true},
\r
1463 parse = function(param)
\r
1464 if param == "" then
\r
1467 if not check_filename(param) then
\r
1468 return false, "Disallowed file name: " .. param
\r
1470 return true, param
\r
1472 nodes_needed = check_region,
\r
1473 func = function(name, param)
\r
1474 local path = minetest.get_worldpath() .. "/schems"
\r
1475 -- Create directory if it does not already exist
\r
1476 minetest.mkdir(path)
\r
1478 local filename = path .. "/" .. param .. ".mts"
\r
1479 local ret = minetest.create_schematic(worldedit.pos1[name],
\r
1480 worldedit.pos2[name], worldedit.prob_list[name],
\r
1482 if ret == nil then
\r
1483 worldedit.player_notify(name, "Failed to create Minetest schematic")
\r
1485 worldedit.player_notify(name, "Saved Minetest schematic to " .. param)
\r
1487 worldedit.prob_list[name] = {}
\r
1491 worldedit.register_command("mtschemplace", {
\r
1492 params = "<file>",
\r
1493 description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin",
\r
1494 privs = {worldedit=true},
\r
1496 parse = function(param)
\r
1497 if param == "" then
\r
1500 if not check_filename(param) then
\r
1501 return false, "Disallowed file name: " .. param
\r
1503 return true, param
\r
1505 func = function(name, param)
\r
1506 local pos = worldedit.pos1[name]
\r
1508 local path = minetest.get_worldpath() .. "/schems/" .. param .. ".mts"
\r
1509 if minetest.place_schematic(pos, path) == nil then
\r
1510 worldedit.player_notify(name, "failed to place Minetest schematic")
\r
1512 worldedit.player_notify(name, "placed Minetest schematic " .. param ..
\r
1513 " at " .. minetest.pos_to_string(pos))
\r
1518 worldedit.register_command("mtschemprob", {
\r
1519 params = "start/finish/get",
\r
1520 description = "Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry",
\r
1521 privs = {worldedit=true},
\r
1522 parse = function(param)
\r
1523 if param ~= "start" and param ~= "finish" and param ~= "get" then
\r
1524 return false, "unknown subcommand: " .. param
\r
1526 return true, param
\r
1528 func = function(name, param)
\r
1529 if param == "start" then --start probability setting
\r
1530 worldedit.set_pos[name] = "prob"
\r
1531 worldedit.prob_list[name] = {}
\r
1532 worldedit.player_notify(name, "select Minetest schematic probability values by punching nodes")
\r
1533 elseif param == "finish" then --finish probability setting
\r
1534 worldedit.set_pos[name] = nil
\r
1535 worldedit.player_notify(name, "finished Minetest schematic probability selection")
\r
1536 elseif param == "get" then --get all nodes that had probabilities set on them
\r
1538 local problist = worldedit.prob_list[name]
\r
1539 if problist == nil then
\r
1542 for k,v in pairs(problist) do
\r
1543 local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100
\r
1544 text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "
\r
1546 worldedit.player_notify(name, "currently set node probabilities:")
\r
1547 worldedit.player_notify(name, text)
\r
1552 minetest.register_on_player_receive_fields(function(player, formname, fields)
\r
1553 if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then
\r
1554 local name = player:get_player_name()
\r
1555 local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
\r
1556 local index = table.getn(worldedit.prob_list[name]) + 1
\r
1557 worldedit.prob_list[name][index] = prob_entry
\r
1561 worldedit.register_command("clearobjects", {
\r
1563 description = "Clears all objects within the WorldEdit region",
\r
1564 privs = {worldedit=true},
\r
1566 nodes_needed = check_region,
\r
1567 func = function(name)
\r
1568 local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name])
\r
1569 worldedit.player_notify(name, count .. " objects cleared")
\r