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 -- Strips any kind of escape codes (translation, colors) from a string
\r
126 -- https://github.com/minetest/minetest/blob/53dd7819277c53954d1298dfffa5287c306db8d0/src/util/string.cpp#L777
\r
127 local function strip_escapes(input)
\r
128 local s = function(idx) return input:sub(idx, idx) end
\r
131 while i <= #input do
\r
132 if s(i) == "\027" then -- escape sequence
\r
134 if s(i) == "(" then -- enclosed
\r
136 while i <= #input and s(i) ~= ")" do
\r
137 if s(i) == "\\" then
\r
149 --print(("%q -> %q"):format(input, out))
\r
153 local function string_endswith(full, part)
\r
154 return full:find(part, 1, true) == #full - #part + 1
\r
157 -- normalizes node "description" `nodename`, returning a string (or nil)
\r
158 worldedit.normalize_nodename = function(nodename)
\r
159 nodename = nodename:gsub("^%s*(.-)%s*$", "%1") -- strip spaces
\r
160 if nodename == "" then return nil end
\r
162 local fullname = ItemStack({name=nodename}):get_name() -- resolve aliases
\r
163 if minetest.registered_nodes[fullname] or fullname == "air" then -- full name
\r
166 nodename = nodename:lower()
\r
167 for key, value in pairs(minetest.registered_nodes) do
\r
168 if string_endswith(key, ":" .. nodename) then -- matches name (w/o mod part)
\r
172 for key, value in pairs(minetest.registered_nodes) do
\r
173 local desc = strip_escapes(value.description):gsub("\n.*", "", 1):lower()
\r
174 if desc == nodename then -- matches description
\r
177 if desc == nodename .. " block" then
\r
178 -- fuzzy description match (e.g. "Steel" == "Steel Block")
\r
184 for key, value in pairs(minetest.registered_nodes) do
\r
185 if value.description:lower():find(nodename, 1, true) ~= nil then
\r
186 if match ~= nil then
\r
189 match = key -- substring description match (only if no ambiguities)
\r
195 -- Determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)
\r
196 function worldedit.player_axis(name)
\r
197 local dir = minetest.get_player_by_name(name):get_look_dir()
\r
198 local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)
\r
201 return "x", dir.x > 0 and 1 or -1
\r
204 return "y", dir.y > 0 and 1 or -1
\r
206 return "z", dir.z > 0 and 1 or -1
\r
209 local function check_filename(name)
\r
210 return name:find("^[%w%s%^&'@{}%[%],%$=!%-#%(%)%%%.%+~_]+$") ~= nil
\r
214 worldedit.register_command("about", {
\r
217 description = "Get information about the WorldEdit mod",
\r
218 func = function(name)
\r
219 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
223 -- mostly copied from builtin/chatcommands.lua with minor modifications
\r
224 worldedit.register_command("help", {
\r
226 params = "[all/<cmd>]",
\r
227 description = "Get help for WorldEdit commands",
\r
228 parse = function(param)
\r
231 func = function(name, param)
\r
232 local function format_help_line(cmd, def)
\r
233 local msg = minetest.colorize("#00ffff", "//"..cmd)
\r
234 if def.params and def.params ~= "" then
\r
235 msg = msg .. " " .. def.params
\r
237 if def.description and def.description ~= "" then
\r
238 msg = msg .. ": " .. def.description
\r
243 if not minetest.check_player_privs(name, "worldedit") then
\r
244 return false, "You are not allowed to use any WorldEdit commands."
\r
246 if param == "" then
\r
249 for cmd, def in pairs(worldedit.registered_commands) do
\r
250 if minetest.check_player_privs(name, def.privs) then
\r
251 cmds[#cmds + 1] = cmd
\r
255 return true, "Available commands: " .. table.concat(cmds, " ") .. "\n"
\r
256 .. "Use '//help <cmd>' to get more information,"
\r
257 .. " or '//help all' to list everything."
\r
258 elseif param == "all" then
\r
260 for cmd, def in pairs(worldedit.registered_commands) do
\r
261 if minetest.check_player_privs(name, def.privs) then
\r
262 cmds[#cmds + 1] = format_help_line(cmd, def)
\r
266 return true, "Available commands:\n"..table.concat(cmds, "\n")
\r
268 local def = worldedit.registered_commands[param]
\r
270 return false, "Command not available: " .. param
\r
272 return true, format_help_line(param, def)
\r
278 worldedit.register_command("inspect", {
\r
279 params = "[on/off/1/0/true/false/yes/no/enable/disable]",
\r
280 description = "Enable or disable node inspection",
\r
281 privs = {worldedit=true},
\r
282 parse = function(param)
\r
283 if param == "on" or param == "1" or param == "true" or param == "yes" or param == "enable" or param == "" then
\r
285 elseif param == "off" or param == "0" or param == "false" or param == "no" or param == "disable" then
\r
290 func = function(name, enable)
\r
292 worldedit.inspect[name] = true
\r
293 local axis, sign = worldedit.player_axis(name)
\r
294 worldedit.player_notify(name, string.format("inspector: inspection enabled for %s, currently facing the %s axis",
\r
295 name, axis .. (sign > 0 and "+" or "-")))
\r
297 worldedit.inspect[name] = nil
\r
298 worldedit.player_notify(name, "inspector: inspection disabled")
\r
303 local function get_node_rlight(pos)
\r
304 local vecs = { -- neighboring nodes
\r
305 {x= 1, y= 0, z= 0},
\r
306 {x=-1, y= 0, z= 0},
\r
307 {x= 0, y= 1, z= 0},
\r
308 {x= 0, y=-1, z= 0},
\r
309 {x= 0, y= 0, z= 1},
\r
310 {x= 0, y= 0, z=-1},
\r
313 for _, v in ipairs(vecs) do
\r
314 ret = math.max(ret, minetest.get_node_light(vector.add(pos, v)))
\r
319 minetest.register_on_punchnode(function(pos, node, puncher)
\r
320 local name = puncher:get_player_name()
\r
321 if worldedit.inspect[name] then
\r
322 local axis, sign = worldedit.player_axis(name)
\r
323 local message = string.format("inspector: %s at %s (param1=%d, param2=%d, received light=%d) punched facing the %s axis",
\r
324 node.name, minetest.pos_to_string(pos), node.param1, node.param2, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-"))
\r
325 worldedit.player_notify(name, message)
\r
329 worldedit.register_command("reset", {
\r
331 description = "Reset the region so that it is empty",
\r
332 privs = {worldedit=true},
\r
333 func = function(name)
\r
334 worldedit.pos1[name] = nil
\r
335 worldedit.pos2[name] = nil
\r
336 worldedit.marker_update(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.marker_update(name)
\r
350 worldedit.player_notify(name, "region marked")
\r
354 worldedit.register_command("unmark", {
\r
356 description = "Hide markers if currently shown",
\r
357 privs = {worldedit=true},
\r
358 func = function(name)
\r
359 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
360 worldedit.pos1[name] = nil
\r
361 worldedit.pos2[name] = nil
\r
362 worldedit.marker_update(name)
\r
363 worldedit.pos1[name] = pos1
\r
364 worldedit.pos2[name] = pos2
\r
365 worldedit.player_notify(name, "region unmarked")
\r
369 worldedit.register_command("pos1", {
\r
371 description = "Set WorldEdit region position 1 to the player's location",
\r
372 privs = {worldedit=true},
\r
373 func = function(name)
\r
374 local pos = minetest.get_player_by_name(name):get_pos()
\r
375 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
376 worldedit.pos1[name] = pos
\r
377 worldedit.mark_pos1(name)
\r
378 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
382 worldedit.register_command("pos2", {
\r
384 description = "Set WorldEdit region position 2 to the player's location",
\r
385 privs = {worldedit=true},
\r
386 func = function(name)
\r
387 local pos = minetest.get_player_by_name(name):get_pos()
\r
388 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
389 worldedit.pos2[name] = pos
\r
390 worldedit.mark_pos2(name)
\r
391 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
395 worldedit.register_command("p", {
\r
396 params = "set/set1/set2/get",
\r
397 description = "Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region",
\r
398 privs = {worldedit=true},
\r
399 parse = function(param)
\r
400 if param == "set" or param == "set1" or param == "set2" or param == "get" then
\r
403 return false, "unknown subcommand: " .. param
\r
405 func = function(name, param)
\r
406 if param == "set" then --set both WorldEdit positions
\r
407 worldedit.set_pos[name] = "pos1"
\r
408 worldedit.player_notify(name, "select positions by punching two nodes")
\r
409 elseif param == "set1" then --set WorldEdit position 1
\r
410 worldedit.set_pos[name] = "pos1only"
\r
411 worldedit.player_notify(name, "select position 1 by punching a node")
\r
412 elseif param == "set2" then --set WorldEdit position 2
\r
413 worldedit.set_pos[name] = "pos2"
\r
414 worldedit.player_notify(name, "select position 2 by punching a node")
\r
415 elseif param == "get" then --display current WorldEdit positions
\r
416 if worldedit.pos1[name] ~= nil then
\r
417 worldedit.player_notify(name, "position 1: " .. minetest.pos_to_string(worldedit.pos1[name]))
\r
419 worldedit.player_notify(name, "position 1 not set")
\r
421 if worldedit.pos2[name] ~= nil then
\r
422 worldedit.player_notify(name, "position 2: " .. minetest.pos_to_string(worldedit.pos2[name]))
\r
424 worldedit.player_notify(name, "position 2 not set")
\r
430 worldedit.register_command("fixedpos", {
\r
431 params = "set1/set2 x y z",
\r
432 description = "Set a WorldEdit region position to the position at (<x>, <y>, <z>)",
\r
433 privs = {worldedit=true},
\r
434 parse = function(param)
\r
435 local found, _, flag, x, y, z = param:find("^(set[12])%s+([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)$")
\r
436 if found == nil then
\r
439 return true, flag, {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
\r
441 func = function(name, flag, pos)
\r
442 if flag == "set1" then
\r
443 worldedit.pos1[name] = pos
\r
444 worldedit.mark_pos1(name)
\r
445 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
446 else --flag == "set2"
\r
447 worldedit.pos2[name] = pos
\r
448 worldedit.mark_pos2(name)
\r
449 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
454 minetest.register_on_punchnode(function(pos, node, puncher)
\r
455 local name = puncher:get_player_name()
\r
456 if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position
\r
457 if worldedit.set_pos[name] == "pos1" then --setting position 1
\r
458 worldedit.pos1[name] = pos
\r
459 worldedit.mark_pos1(name)
\r
460 worldedit.set_pos[name] = "pos2" --set position 2 on the next invocation
\r
461 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
462 elseif worldedit.set_pos[name] == "pos1only" then --setting position 1 only
\r
463 worldedit.pos1[name] = pos
\r
464 worldedit.mark_pos1(name)
\r
465 worldedit.set_pos[name] = nil --finished setting positions
\r
466 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
467 elseif worldedit.set_pos[name] == "pos2" then --setting position 2
\r
468 worldedit.pos2[name] = pos
\r
469 worldedit.mark_pos2(name)
\r
470 worldedit.set_pos[name] = nil --finished setting positions
\r
471 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
472 elseif worldedit.set_pos[name] == "prob" then --setting Minetest schematic node probabilities
\r
473 worldedit.prob_pos[name] = pos
\r
474 minetest.show_formspec(puncher:get_player_name(), "prob_val_enter", "field[text;;]")
\r
479 worldedit.register_command("volume", {
\r
481 description = "Display the volume of the current WorldEdit region",
\r
482 privs = {worldedit=true},
\r
484 func = function(name)
\r
485 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
487 local volume = worldedit.volume(pos1, pos2)
\r
488 local abs = math.abs
\r
489 worldedit.player_notify(name, "current region has a volume of " .. volume .. " nodes ("
\r
490 .. abs(pos2.x - pos1.x) + 1 .. "*"
\r
491 .. abs(pos2.y - pos1.y) + 1 .. "*"
\r
492 .. abs(pos2.z - pos1.z) + 1 .. ")")
\r
496 worldedit.register_command("deleteblocks", {
\r
498 description = "remove all MapBlocks (16x16x16) containing the selected area from the map",
\r
499 privs = {worldedit=true},
\r
501 nodes_needed = check_region,
\r
502 func = function(name)
\r
503 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
504 local success = minetest.delete_area(pos1, pos2)
\r
506 worldedit.player_notify(name, "Area deleted.")
\r
508 worldedit.player_notify(name, "There was an error during deletion of the area.")
\r
513 worldedit.register_command("set", {
\r
515 description = "Set the current WorldEdit region to <node>",
\r
516 privs = {worldedit=true},
\r
518 parse = function(param)
\r
519 local node = worldedit.normalize_nodename(param)
\r
521 return false, "invalid node name: " .. param
\r
525 nodes_needed = check_region,
\r
526 func = function(name, node)
\r
527 local count = worldedit.set(worldedit.pos1[name], worldedit.pos2[name], node)
\r
528 worldedit.player_notify(name, count .. " nodes set")
\r
532 worldedit.register_command("param2", {
\r
533 params = "<param2>",
\r
534 description = "Set param2 of all nodes in the current WorldEdit region to <param2>",
\r
535 privs = {worldedit=true},
\r
537 parse = function(param)
\r
538 local param2 = tonumber(param)
\r
540 return false, "Invalid or missing param2 argument"
\r
541 elseif param2 < 0 or param2 > 255 then
\r
542 return false, "Param2 is out of range (must be between 0 and 255 inclusive!)"
\r
544 return true, param2
\r
546 nodes_needed = check_region,
\r
547 func = function(name, param2)
\r
548 local count = worldedit.set_param2(worldedit.pos1[name], worldedit.pos2[name], param2)
\r
549 worldedit.player_notify(name, count .. " nodes altered")
\r
553 worldedit.register_command("mix", {
\r
554 params = "<node1> [<weighting1>] [<node2> [<weighting2>]] ...",
\r
555 description = "Fill the current WorldEdit region with a random mix of <node1>, ...",
\r
556 privs = {worldedit=true},
\r
558 parse = function(param)
\r
560 for nodename in param:gmatch("[^%s]+") do
\r
561 if tonumber(nodename) ~= nil and #nodes > 0 then
\r
562 local last_node = nodes[#nodes]
\r
563 for i = 1, tonumber(nodename) do
\r
564 nodes[#nodes + 1] = last_node
\r
567 local node = worldedit.normalize_nodename(nodename)
\r
569 return false, "invalid node name: " .. nodename
\r
571 nodes[#nodes + 1] = node
\r
576 nodes_needed = check_region,
\r
577 func = function(name, nodes)
\r
578 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
579 local count = worldedit.set(pos1, pos2, nodes)
\r
580 worldedit.player_notify(name, count .. " nodes set")
\r
584 local check_replace = function(param)
\r
585 local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$")
\r
586 if found == nil then
\r
589 local newsearchnode = worldedit.normalize_nodename(searchnode)
\r
590 if not newsearchnode then
\r
591 return false, "invalid search node name: " .. searchnode
\r
593 local newreplacenode = worldedit.normalize_nodename(replacenode)
\r
594 if not newreplacenode then
\r
595 return false, "invalid replace node name: " .. replacenode
\r
597 return true, newsearchnode, newreplacenode
\r
600 worldedit.register_command("replace", {
\r
601 params = "<search node> <replace node>",
\r
602 description = "Replace all instances of <search node> with <replace node> in the current WorldEdit region",
\r
603 privs = {worldedit=true},
\r
605 parse = check_replace,
\r
606 nodes_needed = check_region,
\r
607 func = function(name, search_node, replace_node)
\r
608 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
\r
609 search_node, replace_node)
\r
610 worldedit.player_notify(name, count .. " nodes replaced")
\r
614 worldedit.register_command("replaceinverse", {
\r
615 params = "<search node> <replace node>",
\r
616 description = "Replace all nodes other than <search node> with <replace node> in the current WorldEdit region",
\r
617 privs = {worldedit=true},
\r
619 parse = check_replace,
\r
620 nodes_needed = check_region,
\r
621 func = function(name, search_node, replace_node)
\r
622 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
\r
623 search_node, replace_node, true)
\r
624 worldedit.player_notify(name, count .. " nodes replaced")
\r
628 local check_cube = function(param)
\r
629 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
630 if found == nil then
\r
633 local node = worldedit.normalize_nodename(nodename)
\r
635 return false, "invalid node name: " .. nodename
\r
637 return true, tonumber(w), tonumber(h), tonumber(l), node
\r
640 worldedit.register_command("hollowcube", {
\r
641 params = "<width> <height> <length> <node>",
\r
642 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
643 privs = {worldedit=true},
\r
645 parse = check_cube,
\r
646 nodes_needed = function(name, w, h, l, node)
\r
649 func = function(name, w, h, l, node)
\r
650 local count = worldedit.cube(worldedit.pos1[name], w, h, l, node, true)
\r
651 worldedit.player_notify(name, count .. " nodes added")
\r
655 worldedit.register_command("cube", {
\r
656 params = "<width> <height> <length> <node>",
\r
657 description = "Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.",
\r
658 privs = {worldedit=true},
\r
660 parse = check_cube,
\r
661 nodes_needed = function(name, w, h, l, node)
\r
664 func = function(name, w, h, l, node)
\r
665 local count = worldedit.cube(worldedit.pos1[name], w, h, l, node)
\r
666 worldedit.player_notify(name, count .. " nodes added")
\r
670 local check_sphere = function(param)
\r
671 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
672 if found == nil then
\r
675 local node = worldedit.normalize_nodename(nodename)
\r
677 return false, "invalid node name: " .. nodename
\r
679 return true, tonumber(radius), node
\r
682 worldedit.register_command("hollowsphere", {
\r
683 params = "<radius> <node>",
\r
684 description = "Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
685 privs = {worldedit=true},
\r
687 parse = check_sphere,
\r
688 nodes_needed = function(name, radius, node)
\r
689 return math.ceil((4 * math.pi * (radius ^ 3)) / 3) --volume of sphere
\r
691 func = function(name, radius, node)
\r
692 local count = worldedit.sphere(worldedit.pos1[name], radius, node, true)
\r
693 worldedit.player_notify(name, count .. " nodes added")
\r
697 worldedit.register_command("sphere", {
\r
698 params = "<radius> <node>",
\r
699 description = "Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
700 privs = {worldedit=true},
\r
702 parse = check_sphere,
\r
703 nodes_needed = function(name, radius, node)
\r
704 return math.ceil((4 * math.pi * (radius ^ 3)) / 3) --volume of sphere
\r
706 func = function(name, radius, node)
\r
707 local count = worldedit.sphere(worldedit.pos1[name], radius, node)
\r
708 worldedit.player_notify(name, count .. " nodes added")
\r
712 local check_dome = function(param)
\r
713 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
714 if found == nil then
\r
717 local node = worldedit.normalize_nodename(nodename)
\r
719 return false, "invalid node name: " .. nodename
\r
721 return true, tonumber(radius), node
\r
724 worldedit.register_command("hollowdome", {
\r
725 params = "<radius> <node>",
\r
726 description = "Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
727 privs = {worldedit=true},
\r
729 parse = check_dome,
\r
730 nodes_needed = function(name, radius, node)
\r
731 return math.ceil((2 * math.pi * (radius ^ 3)) / 3) --volume of dome
\r
733 func = function(name, radius, node)
\r
734 local count = worldedit.dome(worldedit.pos1[name], radius, node, true)
\r
735 worldedit.player_notify(name, count .. " nodes added")
\r
739 worldedit.register_command("dome", {
\r
740 params = "<radius> <node>",
\r
741 description = "Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
742 privs = {worldedit=true},
\r
744 parse = check_dome,
\r
745 nodes_needed = function(name, radius, node)
\r
746 return math.ceil((2 * math.pi * (radius ^ 3)) / 3) --volume of dome
\r
748 func = function(name, radius, node)
\r
749 local count = worldedit.dome(worldedit.pos1[name], radius, node)
\r
750 worldedit.player_notify(name, count .. " nodes added")
\r
754 local check_cylinder = function(param)
\r
756 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
757 if found == nil then
\r
759 found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
\r
762 if found == nil then
\r
765 local node = worldedit.normalize_nodename(nodename)
\r
767 return false, "invalid node name: " .. nodename
\r
769 return true, axis, tonumber(length), tonumber(radius1), tonumber(radius2), node
\r
772 worldedit.register_command("hollowcylinder", {
\r
773 params = "x/y/z/? <length> <radius1> [radius2] <node>",
\r
774 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
775 privs = {worldedit=true},
\r
777 parse = check_cylinder,
\r
778 nodes_needed = function(name, axis, length, radius1, radius2, node)
\r
779 local radius = math.max(radius1, radius2)
\r
780 return math.ceil(math.pi * (radius ^ 2) * length)
\r
782 func = function(name, axis, length, radius1, radius2, node)
\r
783 if axis == "?" then
\r
785 axis, sign = worldedit.player_axis(name)
\r
786 length = length * sign
\r
788 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, radius1, radius2, node, true)
\r
789 worldedit.player_notify(name, count .. " nodes added")
\r
793 worldedit.register_command("cylinder", {
\r
794 params = "x/y/z/? <length> <radius1> [radius2] <node>",
\r
795 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
796 privs = {worldedit=true},
\r
798 parse = check_cylinder,
\r
799 nodes_needed = function(name, axis, length, radius1, radius2, node)
\r
800 local radius = math.max(radius1, radius2)
\r
801 return math.ceil(math.pi * (radius ^ 2) * length)
\r
803 func = function(name, axis, length, radius1, radius2, node)
\r
804 if axis == "?" then
\r
806 axis, sign = worldedit.player_axis(name)
\r
807 length = length * sign
\r
809 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, radius1, radius2, node)
\r
810 worldedit.player_notify(name, count .. " nodes added")
\r
814 local check_pyramid = function(param)
\r
815 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
\r
816 if found == nil then
\r
819 local node = worldedit.normalize_nodename(nodename)
\r
821 return false, "invalid node name: " .. nodename
\r
823 return true, axis, tonumber(height), node
\r
826 worldedit.register_command("hollowpyramid", {
\r
827 params = "x/y/z/? <height> <node>",
\r
828 description = "Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
\r
829 privs = {worldedit=true},
\r
831 parse = check_pyramid,
\r
832 nodes_needed = function(name, axis, height, node)
\r
833 return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
\r
835 func = function(name, axis, height, node)
\r
836 if axis == "?" then
\r
838 axis, sign = worldedit.player_axis(name)
\r
839 height = height * sign
\r
841 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true)
\r
842 worldedit.player_notify(name, count .. " nodes added")
\r
846 worldedit.register_command("pyramid", {
\r
847 params = "x/y/z/? <height> <node>",
\r
848 description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
\r
849 privs = {worldedit=true},
\r
851 parse = check_pyramid,
\r
852 nodes_needed = function(name, axis, height, node)
\r
853 return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
\r
855 func = function(name, axis, height, node)
\r
856 if axis == "?" then
\r
858 axis, sign = worldedit.player_axis(name)
\r
859 height = height * sign
\r
861 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)
\r
862 worldedit.player_notify(name, count .. " nodes added")
\r
866 worldedit.register_command("spiral", {
\r
867 params = "<length> <height> <space> <node>",
\r
868 description = "Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>",
\r
869 privs = {worldedit=true},
\r
871 parse = function(param)
\r
872 local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
873 if found == nil then
\r
876 local node = worldedit.normalize_nodename(nodename)
\r
878 return false, "invalid node name: " .. nodename
\r
880 return true, tonumber(length), tonumber(height), tonumber(space), node
\r
882 nodes_needed = function(name, length, height, space, node)
\r
883 return (length + space) * height -- TODO: this is not the upper bound
\r
885 func = function(name, length, height, space, node)
\r
886 local count = worldedit.spiral(worldedit.pos1[name], length, height, space, node)
\r
887 worldedit.player_notify(name, count .. " nodes added")
\r
891 worldedit.register_command("copy", {
\r
892 params = "x/y/z/? <amount>",
\r
893 description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
\r
894 privs = {worldedit=true},
\r
896 parse = function(param)
\r
897 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
898 if found == nil then
\r
901 return true, axis, tonumber(amount)
\r
903 nodes_needed = function(name, axis, amount)
\r
904 return check_region(name) * 2
\r
906 func = function(name, axis, amount)
\r
907 if axis == "?" then
\r
909 axis, sign = worldedit.player_axis(name)
\r
910 amount = amount * sign
\r
913 local count = worldedit.copy(worldedit.pos1[name], worldedit.pos2[name], axis, amount)
\r
914 worldedit.player_notify(name, count .. " nodes copied")
\r
918 worldedit.register_command("move", {
\r
919 params = "x/y/z/? <amount>",
\r
920 description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
\r
921 privs = {worldedit=true},
\r
923 parse = function(param)
\r
924 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
925 if found == nil then
\r
928 return true, axis, tonumber(amount)
\r
930 nodes_needed = function(name, axis, amount)
\r
931 return check_region(name) * 2
\r
933 func = function(name, axis, amount)
\r
934 if axis == "?" then
\r
936 axis, sign = worldedit.player_axis(name)
\r
937 amount = amount * sign
\r
940 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
941 local count = worldedit.move(pos1, pos2, axis, amount)
\r
943 pos1[axis] = pos1[axis] + amount
\r
944 pos2[axis] = pos2[axis] + amount
\r
945 worldedit.marker_update(name)
\r
946 worldedit.player_notify(name, count .. " nodes moved")
\r
950 worldedit.register_command("stack", {
\r
951 params = "x/y/z/? <count>",
\r
952 description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",
\r
953 privs = {worldedit=true},
\r
955 parse = function(param)
\r
956 local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
957 if found == nil then
\r
960 return true, axis, tonumber(repetitions)
\r
962 nodes_needed = function(name, axis, repetitions)
\r
963 return check_region(name) * math.abs(repetitions)
\r
965 func = function(name, axis, repetitions)
\r
966 if axis == "?" then
\r
968 axis, sign = worldedit.player_axis(name)
\r
969 repetitions = repetitions * sign
\r
972 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
973 local count = worldedit.volume(pos1, pos2) * math.abs(repetitions)
\r
974 worldedit.stack(pos1, pos2, axis, repetitions, function()
\r
975 worldedit.player_notify(name, count .. " nodes stacked")
\r
980 worldedit.register_command("stack2", {
\r
981 params = "<count> <x> <y> <z>",
\r
982 description = "Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>",
\r
983 privs = {worldedit=true},
\r
985 parse = function(param)
\r
986 local repetitions, incs = param:match("(%d+)%s*(.+)")
\r
987 if repetitions == nil then
\r
988 return false, "invalid count: " .. param
\r
990 local x, y, z = incs:match("([+-]?%d+) ([+-]?%d+) ([+-]?%d+)")
\r
992 return false, "invalid increments: " .. param
\r
995 return true, tonumber(repetitions), {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
\r
997 nodes_needed = function(name, repetitions, offset)
\r
998 return check_region(name) * repetitions
\r
1000 func = function(name, repetitions, offset)
\r
1001 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1002 local count = worldedit.volume(pos1, pos2) * repetitions
\r
1003 worldedit.stack2(pos1, pos2, offset, repetitions, function()
\r
1004 worldedit.player_notify(name, count .. " nodes stacked")
\r
1010 worldedit.register_command("stretch", {
\r
1011 params = "<stretchx> <stretchy> <stretchz>",
\r
1012 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
1013 privs = {worldedit=true},
\r
1015 parse = function(param)
\r
1016 local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
\r
1017 if found == nil then
\r
1020 stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)
\r
1021 if stretchx == 0 or stretchy == 0 or stretchz == 0 then
\r
1022 return false, "invalid scaling factors: " .. param
\r
1024 return true, stretchx, stretchy, stretchz
\r
1026 nodes_needed = function(name, stretchx, stretchy, stretchz)
\r
1027 return check_region(name) * stretchx * stretchy * stretchz
\r
1029 func = function(name, stretchx, stretchy, stretchz)
\r
1030 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1031 local count, pos1, pos2 = worldedit.stretch(pos1, pos2, stretchx, stretchy, stretchz)
\r
1033 --reset markers to scaled positions
\r
1034 worldedit.pos1[name] = pos1
\r
1035 worldedit.pos2[name] = pos2
\r
1036 worldedit.marker_update(name)
\r
1038 worldedit.player_notify(name, count .. " nodes stretched")
\r
1042 worldedit.register_command("transpose", {
\r
1043 params = "x/y/z/? x/y/z/?",
\r
1044 description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",
\r
1045 privs = {worldedit=true},
\r
1047 parse = function(param)
\r
1048 local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
\r
1049 if found == nil then
\r
1051 elseif axis1 == axis2 then
\r
1052 return false, "invalid usage: axes must be different"
\r
1054 return true, axis1, axis2
\r
1056 nodes_needed = check_region,
\r
1057 func = function(name, axis1, axis2)
\r
1058 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1059 if axis1 == "?" then axis1 = worldedit.player_axis(name) end
\r
1060 if axis2 == "?" then axis2 = worldedit.player_axis(name) end
\r
1061 local count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
\r
1063 --reset markers to transposed positions
\r
1064 worldedit.pos1[name] = pos1
\r
1065 worldedit.pos2[name] = pos2
\r
1066 worldedit.marker_update(name)
\r
1068 worldedit.player_notify(name, count .. " nodes transposed")
\r
1072 worldedit.register_command("flip", {
\r
1073 params = "x/y/z/?",
\r
1074 description = "Flip the current WorldEdit region along the x/y/z/? axis",
\r
1075 privs = {worldedit=true},
\r
1077 parse = function(param)
\r
1078 if param ~= "x" and param ~= "y" and param ~= "z" and param ~= "?" then
\r
1081 return true, param
\r
1083 nodes_needed = check_region,
\r
1084 func = function(name, param)
\r
1085 if param == "?" then param = worldedit.player_axis(name) end
\r
1086 local count = worldedit.flip(worldedit.pos1[name], worldedit.pos2[name], param)
\r
1087 worldedit.player_notify(name, count .. " nodes flipped")
\r
1091 worldedit.register_command("rotate", {
\r
1092 params = "<axis> <angle>",
\r
1093 description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",
\r
1094 privs = {worldedit=true},
\r
1096 parse = function(param)
\r
1097 local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
1098 if found == nil then
\r
1101 angle = tonumber(angle)
\r
1102 if angle % 90 ~= 0 or angle % 360 == 0 then
\r
1103 return false, "invalid usage: angle must be multiple of 90"
\r
1105 return true, axis, angle
\r
1107 nodes_needed = check_region,
\r
1108 func = function(name, axis, angle)
\r
1109 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
1110 if axis == "?" then axis = worldedit.player_axis(name) end
\r
1111 local count, pos1, pos2 = worldedit.rotate(pos1, pos2, axis, angle)
\r
1113 --reset markers to rotated positions
\r
1114 worldedit.pos1[name] = pos1
\r
1115 worldedit.pos2[name] = pos2
\r
1116 worldedit.marker_update(name)
\r
1118 worldedit.player_notify(name, count .. " nodes rotated")
\r
1122 worldedit.register_command("orient", {
\r
1123 params = "<angle>",
\r
1124 description = "Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)",
\r
1125 privs = {worldedit=true},
\r
1127 parse = function(param)
\r
1128 local found, _, angle = param:find("^([+-]?%d+)$")
\r
1129 if found == nil then
\r
1132 angle = tonumber(angle)
\r
1133 if angle % 90 ~= 0 then
\r
1134 return false, "invalid usage: angle must be multiple of 90"
\r
1136 return true, angle
\r
1138 nodes_needed = check_region,
\r
1139 func = function(name, angle)
\r
1140 local count = worldedit.orient(worldedit.pos1[name], worldedit.pos2[name], angle)
\r
1141 worldedit.player_notify(name, count .. " nodes oriented")
\r
1145 worldedit.register_command("fixlight", {
\r
1147 description = "Fix the lighting in the current WorldEdit region",
\r
1148 privs = {worldedit=true},
\r
1150 nodes_needed = check_region,
\r
1151 func = function(name)
\r
1152 local count = worldedit.fixlight(worldedit.pos1[name], worldedit.pos2[name])
\r
1153 worldedit.player_notify(name, count .. " nodes updated")
\r
1157 worldedit.register_command("drain", {
\r
1159 description = "Remove any fluid node within the current WorldEdit region",
\r
1160 privs = {worldedit=true},
\r
1162 nodes_needed = check_region,
\r
1163 func = function(name)
\r
1164 -- TODO: make an API function for this
\r
1166 local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
\r
1167 for x = pos1.x, pos2.x do
\r
1168 for y = pos1.y, pos2.y do
\r
1169 for z = pos1.z, pos2.z do
\r
1170 local n = minetest.get_node({x=x, y=y, z=z}).name
\r
1171 local d = minetest.registered_nodes[n]
\r
1172 if d ~= nil and (d["drawtype"] == "liquid" or d["drawtype"] == "flowingliquid") then
\r
1173 minetest.remove_node({x=x, y=y, z=z})
\r
1179 worldedit.player_notify(name, count .. " nodes updated")
\r
1183 worldedit.register_command("hide", {
\r
1185 description = "Hide all nodes in the current WorldEdit region non-destructively",
\r
1186 privs = {worldedit=true},
\r
1188 nodes_needed = check_region,
\r
1189 func = function(name)
\r
1190 local count = worldedit.hide(worldedit.pos1[name], worldedit.pos2[name])
\r
1191 worldedit.player_notify(name, count .. " nodes hidden")
\r
1195 worldedit.register_command("suppress", {
\r
1196 params = "<node>",
\r
1197 description = "Suppress all <node> in the current WorldEdit region non-destructively",
\r
1198 privs = {worldedit=true},
\r
1200 parse = function(param)
\r
1201 local node = worldedit.normalize_nodename(param)
\r
1203 return false, "invalid node name: " .. param
\r
1207 nodes_needed = check_region,
\r
1208 func = function(name, node)
\r
1209 local count = worldedit.suppress(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1210 worldedit.player_notify(name, count .. " nodes suppressed")
\r
1214 worldedit.register_command("highlight", {
\r
1215 params = "<node>",
\r
1216 description = "Highlight <node> in the current WorldEdit region by hiding everything else non-destructively",
\r
1217 privs = {worldedit=true},
\r
1219 parse = function(param)
\r
1220 local node = worldedit.normalize_nodename(param)
\r
1222 return false, "invalid node name: " .. param
\r
1226 nodes_needed = check_region,
\r
1227 func = function(name, node)
\r
1228 local count = worldedit.highlight(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1229 worldedit.player_notify(name, count .. " nodes highlighted")
\r
1233 worldedit.register_command("restore", {
\r
1235 description = "Restores nodes hidden with WorldEdit in the current WorldEdit region",
\r
1236 privs = {worldedit=true},
\r
1238 nodes_needed = check_region,
\r
1239 func = function(name)
\r
1240 local count = worldedit.restore(worldedit.pos1[name], worldedit.pos2[name])
\r
1241 worldedit.player_notify(name, count .. " nodes restored")
\r
1245 local function detect_misaligned_schematic(name, pos1, pos2)
\r
1246 pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
1247 -- Check that allocate/save can position the schematic correctly
\r
1248 -- The expected behaviour is that the (0,0,0) corner of the schematic stays
\r
1249 -- sat pos1, this only works when the minimum position is actually present
\r
1250 -- in the schematic.
\r
1251 local node = minetest.get_node(pos1)
\r
1252 local have_node_at_origin = node.name ~= "air" and node.name ~= "ignore"
\r
1253 if not have_node_at_origin then
\r
1254 worldedit.player_notify(name,
\r
1255 "Warning: The schematic contains excessive free space and WILL be "..
\r
1256 "misaligned when allocated or loaded. To avoid this, shrink your "..
\r
1257 "area to cover exactly the nodes to be saved."
\r
1262 worldedit.register_command("save", {
\r
1263 params = "<file>",
\r
1264 description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",
\r
1265 privs = {worldedit=true},
\r
1267 parse = function(param)
\r
1268 if param == "" then
\r
1271 if not check_filename(param) then
\r
1272 return false, "Disallowed file name: " .. param
\r
1274 return true, param
\r
1276 nodes_needed = check_region,
\r
1277 func = function(name, param)
\r
1278 local result, count = worldedit.serialize(worldedit.pos1[name],
\r
1279 worldedit.pos2[name])
\r
1280 detect_misaligned_schematic(name, worldedit.pos1[name], worldedit.pos2[name])
\r
1282 local path = minetest.get_worldpath() .. "/schems"
\r
1283 -- Create directory if it does not already exist
\r
1284 minetest.mkdir(path)
\r
1286 local filename = path .. "/" .. param .. ".we"
\r
1287 local file, err = io.open(filename, "wb")
\r
1288 if err ~= nil then
\r
1289 worldedit.player_notify(name, "Could not save file to \"" .. filename .. "\"")
\r
1292 file:write(result)
\r
1296 worldedit.player_notify(name, count .. " nodes saved")
\r
1300 worldedit.register_command("allocate", {
\r
1301 params = "<file>",
\r
1302 description = "Set the region defined by nodes from \"(world folder)/schems/<file>.we\" as the current WorldEdit region",
\r
1303 privs = {worldedit=true},
\r
1305 parse = function(param)
\r
1306 if param == "" then
\r
1309 if not check_filename(param) then
\r
1310 return false, "Disallowed file name: " .. param
\r
1312 return true, param
\r
1314 func = function(name, param)
\r
1315 local pos = worldedit.pos1[name]
\r
1317 local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"
\r
1318 local file, err = io.open(filename, "rb")
\r
1319 if err ~= nil then
\r
1320 worldedit.player_notify(name, "could not open file \"" .. filename .. "\"")
\r
1323 local value = file:read("*a")
\r
1326 local version = worldedit.read_header(value)
\r
1327 if version == nil or version == 0 then
\r
1328 worldedit.player_notify(name, "File is invalid!")
\r
1330 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1331 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1334 local nodepos1, nodepos2, count = worldedit.allocate(pos, value)
\r
1336 if not nodepos1 then
\r
1337 worldedit.player_notify(name, "Schematic empty, nothing allocated")
\r
1341 worldedit.pos1[name] = nodepos1
\r
1342 worldedit.pos2[name] = nodepos2
\r
1343 worldedit.marker_update(name)
\r
1345 worldedit.player_notify(name, count .. " nodes allocated")
\r
1349 worldedit.register_command("load", {
\r
1350 params = "<file>",
\r
1351 description = "Load nodes from \"(world folder)/schems/<file>[.we[m]]\" with position 1 of the current WorldEdit region as the origin",
\r
1352 privs = {worldedit=true},
\r
1354 parse = function(param)
\r
1355 if param == "" then
\r
1358 if not check_filename(param) then
\r
1359 return false, "Disallowed file name: " .. param
\r
1361 return true, param
\r
1363 func = function(name, param)
\r
1364 local pos = worldedit.pos1[name]
\r
1366 if param == "" then
\r
1367 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1370 if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then
\r
1371 worldedit.player_notify(name, "invalid file name: " .. param)
\r
1375 --find the file in the world path
\r
1376 local testpaths = {
\r
1377 minetest.get_worldpath() .. "/schems/" .. param,
\r
1378 minetest.get_worldpath() .. "/schems/" .. param .. ".we",
\r
1379 minetest.get_worldpath() .. "/schems/" .. param .. ".wem",
\r
1382 for index, path in ipairs(testpaths) do
\r
1383 file, err = io.open(path, "rb")
\r
1389 worldedit.player_notify(name, "could not open file \"" .. param .. "\"")
\r
1392 local value = file:read("*a")
\r
1395 local version = worldedit.read_header(value)
\r
1396 if version == nil or version == 0 then
\r
1397 worldedit.player_notify(name, "File is invalid!")
\r
1399 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1400 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1404 local count = worldedit.deserialize(pos, value)
\r
1406 worldedit.player_notify(name, count .. " nodes loaded")
\r
1410 worldedit.register_command("lua", {
\r
1411 params = "<code>",
\r
1412 description = "Executes <code> as a Lua chunk in the global namespace",
\r
1413 privs = {worldedit=true, server=true},
\r
1414 parse = function(param)
\r
1415 return true, param
\r
1417 func = function(name, param)
\r
1418 local err = worldedit.lua(param)
\r
1420 worldedit.player_notify(name, "code error: " .. err)
\r
1421 minetest.log("action", name.." tried to execute "..param)
\r
1423 worldedit.player_notify(name, "code successfully executed", false)
\r
1424 minetest.log("action", name.." executed "..param)
\r
1429 worldedit.register_command("luatransform", {
\r
1430 params = "<code>",
\r
1431 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
1432 privs = {worldedit=true, server=true},
\r
1434 parse = function(param)
\r
1435 return true, param
\r
1437 nodes_needed = check_region,
\r
1438 func = function(name, param)
\r
1439 local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)
\r
1441 worldedit.player_notify(name, "code error: " .. err, false)
\r
1442 minetest.log("action", name.." tried to execute luatransform "..param)
\r
1444 worldedit.player_notify(name, "code successfully executed", false)
\r
1445 minetest.log("action", name.." executed luatransform "..param)
\r
1450 worldedit.register_command("mtschemcreate", {
\r
1451 params = "<file>",
\r
1452 description = "Save the current WorldEdit region using the Minetest "..
\r
1453 "Schematic format to \"(world folder)/schems/<filename>.mts\"",
\r
1454 privs = {worldedit=true},
\r
1456 parse = function(param)
\r
1457 if param == "" then
\r
1460 if not check_filename(param) then
\r
1461 return false, "Disallowed file name: " .. param
\r
1463 return true, param
\r
1465 nodes_needed = check_region,
\r
1466 func = function(name, param)
\r
1467 local path = minetest.get_worldpath() .. "/schems"
\r
1468 -- Create directory if it does not already exist
\r
1469 minetest.mkdir(path)
\r
1471 local filename = path .. "/" .. param .. ".mts"
\r
1472 local ret = minetest.create_schematic(worldedit.pos1[name],
\r
1473 worldedit.pos2[name], worldedit.prob_list[name],
\r
1475 if ret == nil then
\r
1476 worldedit.player_notify(name, "Failed to create Minetest schematic")
\r
1478 worldedit.player_notify(name, "Saved Minetest schematic to " .. param)
\r
1480 worldedit.prob_list[name] = {}
\r
1484 worldedit.register_command("mtschemplace", {
\r
1485 params = "<file>",
\r
1486 description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin",
\r
1487 privs = {worldedit=true},
\r
1489 parse = function(param)
\r
1490 if param == "" then
\r
1493 if not check_filename(param) then
\r
1494 return false, "Disallowed file name: " .. param
\r
1496 return true, param
\r
1498 func = function(name, param)
\r
1499 local pos = worldedit.pos1[name]
\r
1501 local path = minetest.get_worldpath() .. "/schems/" .. param .. ".mts"
\r
1502 if minetest.place_schematic(pos, path) == nil then
\r
1503 worldedit.player_notify(name, "failed to place Minetest schematic")
\r
1505 worldedit.player_notify(name, "placed Minetest schematic " .. param ..
\r
1506 " at " .. minetest.pos_to_string(pos))
\r
1511 worldedit.register_command("mtschemprob", {
\r
1512 params = "start/finish/get",
\r
1513 description = "Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry",
\r
1514 privs = {worldedit=true},
\r
1515 parse = function(param)
\r
1516 if param ~= "start" and param ~= "finish" and param ~= "get" then
\r
1517 return false, "unknown subcommand: " .. param
\r
1519 return true, param
\r
1521 func = function(name, param)
\r
1522 if param == "start" then --start probability setting
\r
1523 worldedit.set_pos[name] = "prob"
\r
1524 worldedit.prob_list[name] = {}
\r
1525 worldedit.player_notify(name, "select Minetest schematic probability values by punching nodes")
\r
1526 elseif param == "finish" then --finish probability setting
\r
1527 worldedit.set_pos[name] = nil
\r
1528 worldedit.player_notify(name, "finished Minetest schematic probability selection")
\r
1529 elseif param == "get" then --get all nodes that had probabilities set on them
\r
1531 local problist = worldedit.prob_list[name]
\r
1532 if problist == nil then
\r
1535 for k,v in pairs(problist) do
\r
1536 local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100
\r
1537 text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "
\r
1539 worldedit.player_notify(name, "currently set node probabilities:")
\r
1540 worldedit.player_notify(name, text)
\r
1545 minetest.register_on_player_receive_fields(function(player, formname, fields)
\r
1546 if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then
\r
1547 local name = player:get_player_name()
\r
1548 local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
\r
1549 local index = table.getn(worldedit.prob_list[name]) + 1
\r
1550 worldedit.prob_list[name][index] = prob_entry
\r
1554 worldedit.register_command("clearobjects", {
\r
1556 description = "Clears all objects within the WorldEdit region",
\r
1557 privs = {worldedit=true},
\r
1559 nodes_needed = check_region,
\r
1560 func = function(name)
\r
1561 local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name])
\r
1562 worldedit.player_notify(name, count .. " objects cleared")
\r