1 minetest.register_privilege("worldedit", "Can use WorldEdit commands")
\r
3 worldedit.set_pos = {}
\r
4 worldedit.inspect = {}
\r
8 if minetest.place_schematic then
\r
9 worldedit.prob_pos = {}
\r
10 worldedit.prob_list = {}
\r
13 dofile(minetest.get_modpath("worldedit_commands") .. "/cuboid.lua")
\r
14 dofile(minetest.get_modpath("worldedit_commands") .. "/mark.lua")
\r
15 dofile(minetest.get_modpath("worldedit_commands") .. "/wand.lua")
\r
16 local safe_region, check_region, reset_pending = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua")
\r
18 local function get_position(name) --position 1 retrieval function for when not using `safe_region`
\r
19 local pos1 = worldedit.pos1[name]
\r
21 worldedit.player_notify(name, "no position 1 selected")
\r
26 -- normalize_nodename wrapper for convenience purposes
\r
27 local function get_node(name, nodename)
\r
28 local node = worldedit.normalize_nodename(nodename)
\r
30 worldedit.player_notify(name, "invalid node name: " .. nodename)
\r
36 function worldedit.player_notify(name, message)
\r
37 minetest.chat_send_player(name, "WorldEdit -!- " .. message, false)
\r
40 local function string_endswith(full, part)
\r
41 return full:find(part, 1, true) == #full - #part + 1
\r
44 -- normalizes node "description" `nodename`, returning a string (or nil)
\r
45 worldedit.normalize_nodename = function(nodename)
\r
46 nodename = nodename:gsub("^%s*(.-)%s*$", "%1") -- strip spaces
\r
47 if nodename == "" then return nil end
\r
49 local fullname = ItemStack({name=nodename}):get_name() -- resolve aliases
\r
50 if minetest.registered_nodes[fullname] or fullname == "air" then -- full name
\r
53 for key, value in pairs(minetest.registered_nodes) do
\r
54 if string_endswith(key, ":" .. nodename) then -- matches name (w/o mod part)
\r
58 nodename = nodename:lower() -- lowercase both for case insensitive comparison
\r
59 for key, value in pairs(minetest.registered_nodes) do
\r
60 local desc = value.description:lower()
\r
61 if desc == nodename then -- matches description
\r
64 if string_endswith(desc, " block") and desc == nodename.." block" then
\r
65 -- fuzzy description match (e.g. "Steel" == "Steel Block")
\r
71 for key, value in pairs(minetest.registered_nodes) do
\r
72 if value.description:lower():find(nodename, 1, true) ~= nil then
\r
73 if match ~= nil then
\r
76 match = key -- substring description match (only if no ambiguities)
\r
82 -- Determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)
\r
83 function worldedit.player_axis(name)
\r
84 local dir = minetest.get_player_by_name(name):get_look_dir()
\r
85 local x, y, z = math.abs(dir.x), math.abs(dir.y), math.abs(dir.z)
\r
88 return "x", dir.x > 0 and 1 or -1
\r
91 return "y", dir.y > 0 and 1 or -1
\r
93 return "z", dir.z > 0 and 1 or -1
\r
96 local function check_filename(name)
\r
97 return name:find("^[%w%s%^&'@{}%[%],%$=!%-#%(%)%%%.%+~_]+$") ~= nil
\r
101 minetest.register_chatcommand("/about", {
\r
103 description = "Get information about the WorldEdit mod",
\r
104 func = function(name, param)
\r
105 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
109 -- mostly copied from builtin/chatcommands.lua with minor modifications
\r
110 minetest.register_chatcommand("/help", {
\r
112 params = "[all/<cmd>]",
\r
113 description = "Get help for WorldEdit commands",
\r
114 func = function(name, param)
\r
115 local function is_we_command(cmd)
\r
116 return cmd:sub(0, 1) == "/"
\r
118 local function format_help_line(cmd, def)
\r
119 local msg = minetest.colorize("#00ffff", "/"..cmd)
\r
120 if def.params and def.params ~= "" then
\r
121 msg = msg .. " " .. def.params
\r
123 if def.description and def.description ~= "" then
\r
124 msg = msg .. ": " .. def.description
\r
129 if not minetest.check_player_privs(name, "worldedit") then
\r
130 return false, "You are not allowed to use any WorldEdit commands."
\r
132 if param == "" then
\r
135 for cmd, def in pairs(minetest.chatcommands) do
\r
136 if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then
\r
137 cmds[#cmds + 1] = cmd:sub(2) -- strip the /
\r
141 return true, "Available commands: " .. table.concat(cmds, " ") .. "\n"
\r
142 .. "Use '//help <cmd>' to get more information,"
\r
143 .. " or '//help all' to list everything."
\r
144 elseif param == "all" then
\r
146 for cmd, def in pairs(minetest.chatcommands) do
\r
147 if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then
\r
148 cmds[#cmds + 1] = format_help_line(cmd, def)
\r
152 return true, "Available commands:\n"..table.concat(cmds, "\n")
\r
154 return minetest.chatcommands["help"].func(name, "/" .. param)
\r
159 minetest.register_chatcommand("/inspect", {
\r
160 params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>",
\r
161 description = "Enable or disable node inspection",
\r
162 privs = {worldedit=true},
\r
163 func = function(name, param)
\r
164 if param == "on" or param == "1" or param == "true" or param == "yes" or param == "enable" or param == "" then
\r
165 worldedit.inspect[name] = true
\r
166 local axis, sign = worldedit.player_axis(name)
\r
167 worldedit.player_notify(name, string.format("inspector: inspection enabled for %s, currently facing the %s axis",
\r
168 name, axis .. (sign > 0 and "+" or "-")))
\r
169 elseif param == "off" or param == "0" or param == "false" or param == "no" or param == "disable" then
\r
170 worldedit.inspect[name] = nil
\r
171 worldedit.player_notify(name, "inspector: inspection disabled")
\r
173 worldedit.player_notify(name, "invalid usage: " .. param)
\r
178 local function get_node_rlight(pos)
\r
179 local vecs = { -- neighboring nodes
\r
180 {x= 1, y= 0, z= 0},
\r
181 {x=-1, y= 0, z= 0},
\r
182 {x= 0, y= 1, z= 0},
\r
183 {x= 0, y=-1, z= 0},
\r
184 {x= 0, y= 0, z= 1},
\r
185 {x= 0, y= 0, z=-1},
\r
188 for _, v in ipairs(vecs) do
\r
189 ret = math.max(ret, minetest.get_node_light(vector.add(pos, v)))
\r
194 minetest.register_on_punchnode(function(pos, node, puncher)
\r
195 local name = puncher:get_player_name()
\r
196 if worldedit.inspect[name] then
\r
197 local axis, sign = worldedit.player_axis(name)
\r
198 local message = string.format("inspector: %s at %s (param1=%d, param2=%d, received light=%d) punched facing the %s axis",
\r
199 node.name, minetest.pos_to_string(pos), node.param1, node.param2, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-"))
\r
200 worldedit.player_notify(name, message)
\r
204 minetest.register_chatcommand("/reset", {
\r
206 description = "Reset the region so that it is empty",
\r
207 privs = {worldedit=true},
\r
208 func = function(name, param)
\r
209 worldedit.pos1[name] = nil
\r
210 worldedit.pos2[name] = nil
\r
211 worldedit.mark_pos1(name)
\r
212 worldedit.mark_pos2(name)
\r
213 worldedit.set_pos[name] = nil
\r
214 --make sure the user does not try to confirm an operation after resetting pos:
\r
215 reset_pending(name)
\r
216 worldedit.player_notify(name, "region reset")
\r
220 minetest.register_chatcommand("/mark", {
\r
222 description = "Show markers at the region positions",
\r
223 privs = {worldedit=true},
\r
224 func = function(name, param)
\r
225 worldedit.mark_pos1(name)
\r
226 worldedit.mark_pos2(name)
\r
227 worldedit.player_notify(name, "region marked")
\r
231 minetest.register_chatcommand("/unmark", {
\r
233 description = "Hide markers if currently shown",
\r
234 privs = {worldedit=true},
\r
235 func = function(name, param)
\r
236 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
237 worldedit.pos1[name] = nil
\r
238 worldedit.pos2[name] = nil
\r
239 worldedit.mark_pos1(name)
\r
240 worldedit.mark_pos2(name)
\r
241 worldedit.pos1[name] = pos1
\r
242 worldedit.pos2[name] = pos2
\r
243 worldedit.player_notify(name, "region unmarked")
\r
247 minetest.register_chatcommand("/pos1", {
\r
249 description = "Set WorldEdit region position 1 to the player's location",
\r
250 privs = {worldedit=true},
\r
251 func = function(name, param)
\r
252 local pos = minetest.get_player_by_name(name):get_pos()
\r
253 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
254 worldedit.pos1[name] = pos
\r
255 worldedit.mark_pos1(name)
\r
256 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
260 minetest.register_chatcommand("/pos2", {
\r
262 description = "Set WorldEdit region position 2 to the player's location",
\r
263 privs = {worldedit=true},
\r
264 func = function(name, param)
\r
265 local pos = minetest.get_player_by_name(name):get_pos()
\r
266 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
267 worldedit.pos2[name] = pos
\r
268 worldedit.mark_pos2(name)
\r
269 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
273 minetest.register_chatcommand("/p", {
\r
274 params = "set/set1/set2/get",
\r
275 description = "Set WorldEdit region, WorldEdit position 1, or WorldEdit position 2 by punching nodes, or display the current WorldEdit region",
\r
276 privs = {worldedit=true},
\r
277 func = function(name, param)
\r
278 if param == "set" then --set both WorldEdit positions
\r
279 worldedit.set_pos[name] = "pos1"
\r
280 worldedit.player_notify(name, "select positions by punching two nodes")
\r
281 elseif param == "set1" then --set WorldEdit position 1
\r
282 worldedit.set_pos[name] = "pos1only"
\r
283 worldedit.player_notify(name, "select position 1 by punching a node")
\r
284 elseif param == "set2" then --set WorldEdit position 2
\r
285 worldedit.set_pos[name] = "pos2"
\r
286 worldedit.player_notify(name, "select position 2 by punching a node")
\r
287 elseif param == "get" then --display current WorldEdit positions
\r
288 if worldedit.pos1[name] ~= nil then
\r
289 worldedit.player_notify(name, "position 1: " .. minetest.pos_to_string(worldedit.pos1[name]))
\r
291 worldedit.player_notify(name, "position 1 not set")
\r
293 if worldedit.pos2[name] ~= nil then
\r
294 worldedit.player_notify(name, "position 2: " .. minetest.pos_to_string(worldedit.pos2[name]))
\r
296 worldedit.player_notify(name, "position 2 not set")
\r
299 worldedit.player_notify(name, "unknown subcommand: " .. param)
\r
304 minetest.register_chatcommand("/fixedpos", {
\r
305 params = "set1/set2 x y z",
\r
306 description = "Set a WorldEdit region position to the position at (<x>, <y>, <z>)",
\r
307 privs = {worldedit=true},
\r
308 func = function(name, param)
\r
309 local found, _, flag, x, y, z = param:find("^(set[12])%s+([+-]?%d+)%s+([+-]?%d+)%s+([+-]?%d+)$")
\r
310 if found == nil then
\r
311 worldedit.player_notify(name, "invalid usage: " .. param)
\r
314 local pos = {x=tonumber(x), y=tonumber(y), z=tonumber(z)}
\r
315 if flag == "set1" then
\r
316 worldedit.pos1[name] = pos
\r
317 worldedit.mark_pos1(name)
\r
318 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
319 else --flag == "set2"
\r
320 worldedit.pos2[name] = pos
\r
321 worldedit.mark_pos2(name)
\r
322 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
327 minetest.register_on_punchnode(function(pos, node, puncher)
\r
328 local name = puncher:get_player_name()
\r
329 if name ~= "" and worldedit.set_pos[name] ~= nil then --currently setting position
\r
330 if worldedit.set_pos[name] == "pos1" then --setting position 1
\r
331 worldedit.pos1[name] = pos
\r
332 worldedit.mark_pos1(name)
\r
333 worldedit.set_pos[name] = "pos2" --set position 2 on the next invocation
\r
334 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
335 elseif worldedit.set_pos[name] == "pos1only" then --setting position 1 only
\r
336 worldedit.pos1[name] = pos
\r
337 worldedit.mark_pos1(name)
\r
338 worldedit.set_pos[name] = nil --finished setting positions
\r
339 worldedit.player_notify(name, "position 1 set to " .. minetest.pos_to_string(pos))
\r
340 elseif worldedit.set_pos[name] == "pos2" then --setting position 2
\r
341 worldedit.pos2[name] = pos
\r
342 worldedit.mark_pos2(name)
\r
343 worldedit.set_pos[name] = nil --finished setting positions
\r
344 worldedit.player_notify(name, "position 2 set to " .. minetest.pos_to_string(pos))
\r
345 elseif worldedit.set_pos[name] == "prob" then --setting Minetest schematic node probabilities
\r
346 worldedit.prob_pos[name] = pos
\r
347 minetest.show_formspec(puncher:get_player_name(), "prob_val_enter", "field[text;;]")
\r
352 minetest.register_chatcommand("/volume", {
\r
354 description = "Display the volume of the current WorldEdit region",
\r
355 privs = {worldedit=true},
\r
356 func = function(name, param)
\r
357 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
358 if pos1 == nil or pos2 == nil then
\r
359 worldedit.player_notify(name, "no region selected")
\r
363 local volume = worldedit.volume(pos1, pos2)
\r
364 local abs = math.abs
\r
365 worldedit.player_notify(name, "current region has a volume of " .. volume .. " nodes ("
\r
366 .. abs(pos2.x - pos1.x) + 1 .. "*"
\r
367 .. abs(pos2.y - pos1.y) + 1 .. "*"
\r
368 .. abs(pos2.z - pos1.z) + 1 .. ")")
\r
372 minetest.register_chatcommand("/deleteblocks", {
\r
374 description = "remove all MapBlocks (16x16x16) containing the selected area from the map",
\r
375 privs = {worldedit=true},
\r
376 func = safe_region(function(name, param)
\r
377 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
378 local success = minetest.delete_area(pos1, pos2)
\r
380 worldedit.player_notify(name, "Area deleted.")
\r
382 worldedit.player_notify(name, "There was an error during deletion of the area.")
\r
387 minetest.register_chatcommand("/set", {
\r
389 description = "Set the current WorldEdit region to <node>",
\r
390 privs = {worldedit=true},
\r
391 func = safe_region(function(name, param)
\r
392 local node = get_node(name, param)
\r
393 if not node then return end
\r
395 local count = worldedit.set(worldedit.pos1[name], worldedit.pos2[name], node)
\r
396 worldedit.player_notify(name, count .. " nodes set")
\r
397 end, check_region),
\r
400 minetest.register_chatcommand("/param2", {
\r
401 params = "<param2>",
\r
402 description = "Set param2 of all nodes in the current WorldEdit region to <param2>",
\r
403 privs = {worldedit=true},
\r
404 func = safe_region(function(name, param)
\r
405 local param2 = tonumber(param)
\r
407 worldedit.player_notify(name, "Invalid or missing param2 argument")
\r
409 elseif param2 < 0 or param2 > 255 then
\r
410 worldedit.player_notify(name, "Param2 is out of range (must be between 0 and 255 inclusive)!")
\r
414 local count = worldedit.set_param2(worldedit.pos1[name], worldedit.pos2[name], param2)
\r
415 worldedit.player_notify(name, count .. " nodes altered")
\r
416 end, check_region),
\r
419 minetest.register_chatcommand("/mix", {
\r
420 params = "<node1> [<weighting1>] [<node2> [<weighting2>]] ...",
\r
421 description = "Fill the current WorldEdit region with a random mix of <node1>, ...",
\r
422 privs = {worldedit=true},
\r
423 func = safe_region(function(name, param)
\r
425 for nodename in param:gmatch("[^%s]+") do
\r
426 if tonumber(nodename) ~= nil and #nodes > 0 then
\r
427 local last_node = nodes[#nodes]
\r
428 for i = 1, tonumber(nodename) do
\r
429 nodes[#nodes + 1] = last_node
\r
432 local node = get_node(name, nodename)
\r
433 if not node then return end
\r
434 nodes[#nodes + 1] = node
\r
438 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
439 local count = worldedit.set(pos1, pos2, nodes)
\r
440 worldedit.player_notify(name, count .. " nodes set")
\r
441 end, check_region),
\r
444 local check_replace = function(name, param)
\r
445 local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$")
\r
446 if found == nil then
\r
447 worldedit.player_notify(name, "invalid usage: " .. param)
\r
450 local newsearchnode = worldedit.normalize_nodename(searchnode)
\r
451 if not newsearchnode then
\r
452 worldedit.player_notify(name, "invalid search node name: " .. searchnode)
\r
455 local newreplacenode = worldedit.normalize_nodename(replacenode)
\r
456 if not newreplacenode then
\r
457 worldedit.player_notify(name, "invalid replace node name: " .. replacenode)
\r
460 return check_region(name, param)
\r
463 minetest.register_chatcommand("/replace", {
\r
464 params = "<search node> <replace node>",
\r
465 description = "Replace all instances of <search node> with <replace node> in the current WorldEdit region",
\r
466 privs = {worldedit=true},
\r
467 func = safe_region(function(name, param)
\r
468 local found, _, search_node, replace_node = param:find("^([^%s]+)%s+(.+)$")
\r
469 local norm_search_node = worldedit.normalize_nodename(search_node)
\r
470 local norm_replace_node = worldedit.normalize_nodename(replace_node)
\r
471 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
\r
472 norm_search_node, norm_replace_node)
\r
473 worldedit.player_notify(name, count .. " nodes replaced")
\r
474 end, check_replace),
\r
477 minetest.register_chatcommand("/replaceinverse", {
\r
478 params = "<search node> <replace node>",
\r
479 description = "Replace all nodes other than <search node> with <replace node> in the current WorldEdit region",
\r
480 privs = {worldedit=true},
\r
481 func = safe_region(function(name, param)
\r
482 local found, _, search_node, replace_node = param:find("^([^%s]+)%s+(.+)$")
\r
483 local norm_search_node = worldedit.normalize_nodename(search_node)
\r
484 local norm_replace_node = worldedit.normalize_nodename(replace_node)
\r
485 local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
\r
486 norm_search_node, norm_replace_node, true)
\r
487 worldedit.player_notify(name, count .. " nodes replaced")
\r
488 end, check_replace),
\r
491 local check_cube = function(name, param)
\r
492 if worldedit.pos1[name] == nil then
\r
493 worldedit.player_notify(name, "no position 1 selected")
\r
496 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
497 if found == nil then
\r
498 worldedit.player_notify(name, "invalid usage: " .. param)
\r
501 local node = get_node(name, nodename)
\r
502 if not node then return nil end
\r
503 return tonumber(w) * tonumber(h) * tonumber(l)
\r
506 minetest.register_chatcommand("/hollowcube", {
\r
507 params = "<width> <height> <length> <node>",
\r
508 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
509 privs = {worldedit=true},
\r
510 func = safe_region(function(name, param)
\r
511 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
512 local node = get_node(name, nodename)
\r
513 local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node, true)
\r
514 worldedit.player_notify(name, count .. " nodes added")
\r
518 minetest.register_chatcommand("/cube", {
\r
519 params = "<width> <height> <length> <node>",
\r
520 description = "Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.",
\r
521 privs = {worldedit=true},
\r
522 func = safe_region(function(name, param)
\r
523 local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
524 local node = get_node(name, nodename)
\r
525 local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node)
\r
526 worldedit.player_notify(name, count .. " nodes added")
\r
530 local check_sphere = function(name, param)
\r
531 if worldedit.pos1[name] == nil then
\r
532 worldedit.player_notify(name, "no position 1 selected")
\r
535 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
536 if found == nil then
\r
537 worldedit.player_notify(name, "invalid usage: " .. param)
\r
540 local node = get_node(name, nodename)
\r
541 if not node then return nil end
\r
542 return math.ceil((4 * math.pi * (tonumber(radius) ^ 3)) / 3) --volume of sphere
\r
545 minetest.register_chatcommand("/hollowsphere", {
\r
546 params = "<radius> <node>",
\r
547 description = "Add hollow sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
548 privs = {worldedit=true},
\r
549 func = safe_region(function(name, param)
\r
550 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
551 local node = get_node(name, nodename)
\r
552 local count = worldedit.sphere(worldedit.pos1[name], tonumber(radius), node, true)
\r
553 worldedit.player_notify(name, count .. " nodes added")
\r
554 end, check_sphere),
\r
557 minetest.register_chatcommand("/sphere", {
\r
558 params = "<radius> <node>",
\r
559 description = "Add sphere centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
560 privs = {worldedit=true},
\r
561 func = safe_region(function(name, param)
\r
562 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
563 local node = get_node(name, nodename)
\r
564 local count = worldedit.sphere(worldedit.pos1[name], tonumber(radius), node)
\r
565 worldedit.player_notify(name, count .. " nodes added")
\r
566 end, check_sphere),
\r
569 local check_dome = function(name, param)
\r
570 if worldedit.pos1[name] == nil then
\r
571 worldedit.player_notify(name, "no position 1 selected")
\r
574 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
575 if found == nil then
\r
576 worldedit.player_notify(name, "invalid usage: " .. param)
\r
579 local node = get_node(name, nodename)
\r
580 if not node then return nil end
\r
581 return math.ceil((2 * math.pi * (tonumber(radius) ^ 3)) / 3) --volume of dome
\r
584 minetest.register_chatcommand("/hollowdome", {
\r
585 params = "<radius> <node>",
\r
586 description = "Add hollow dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
587 privs = {worldedit=true},
\r
588 func = safe_region(function(name, param)
\r
589 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
590 local node = get_node(name, nodename)
\r
591 local count = worldedit.dome(worldedit.pos1[name], tonumber(radius), node, true)
\r
592 worldedit.player_notify(name, count .. " nodes added")
\r
596 minetest.register_chatcommand("/dome", {
\r
597 params = "<radius> <node>",
\r
598 description = "Add dome centered at WorldEdit position 1 with radius <radius>, composed of <node>",
\r
599 privs = {worldedit=true},
\r
600 func = safe_region(function(name, param)
\r
601 local found, _, radius, nodename = param:find("^(%d+)%s+(.+)$")
\r
602 local node = get_node(name, nodename)
\r
603 local count = worldedit.dome(worldedit.pos1[name], tonumber(radius), node)
\r
604 worldedit.player_notify(name, count .. " nodes added")
\r
608 local check_cylinder = function(name, param)
\r
609 if worldedit.pos1[name] == nil then
\r
610 worldedit.player_notify(name, "no position 1 selected")
\r
614 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
615 if found == nil then
\r
617 found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
\r
620 if found == nil then
\r
621 worldedit.player_notify(name, "invalid usage: " .. param)
\r
624 local node = get_node(name, nodename)
\r
625 if not node then return nil end
\r
626 local radius = math.max(tonumber(radius1), tonumber(radius2))
\r
627 return math.ceil(math.pi * (radius ^ 2) * tonumber(length))
\r
630 minetest.register_chatcommand("/hollowcylinder", {
\r
631 params = "x/y/z/? <length> <radius1> [radius2] <node>",
\r
632 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
633 privs = {worldedit=true},
\r
634 func = safe_region(function(name, param)
\r
636 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
637 if found == nil then
\r
639 found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
\r
642 length = tonumber(length)
\r
643 if axis == "?" then
\r
645 axis, sign = worldedit.player_axis(name)
\r
646 length = length * sign
\r
648 local node = get_node(name, nodename)
\r
649 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node, true)
\r
650 worldedit.player_notify(name, count .. " nodes added")
\r
651 end, check_cylinder),
\r
654 minetest.register_chatcommand("/cylinder", {
\r
655 params = "x/y/z/? <length> <radius1> [radius2] <node>",
\r
656 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
657 privs = {worldedit=true},
\r
658 func = safe_region(function(name, param)
\r
660 local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
661 if found == nil then
\r
663 found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")
\r
666 length = tonumber(length)
\r
667 if axis == "?" then
\r
669 axis, sign = worldedit.player_axis(name)
\r
670 length = length * sign
\r
672 local node = get_node(name, nodename)
\r
673 local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node)
\r
674 worldedit.player_notify(name, count .. " nodes added")
\r
675 end, check_cylinder),
\r
678 local check_pyramid = function(name, param)
\r
679 if worldedit.pos1[name] == nil then
\r
680 worldedit.player_notify(name, "no position 1 selected")
\r
683 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
\r
684 if found == nil then
\r
685 worldedit.player_notify(name, "invalid usage: " .. param)
\r
688 local node = get_node(name, nodename)
\r
689 if not node then return nil end
\r
690 height = tonumber(height)
\r
691 return math.ceil(((height * 2 + 1) ^ 2) * height / 3)
\r
694 minetest.register_chatcommand("/hollowpyramid", {
\r
695 params = "x/y/z/? <height> <node>",
\r
696 description = "Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
\r
697 privs = {worldedit=true},
\r
698 func = safe_region(function(name, param)
\r
699 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
\r
700 height = tonumber(height)
\r
701 if axis == "?" then
\r
703 axis, sign = worldedit.player_axis(name)
\r
704 height = height * sign
\r
706 local node = get_node(name, nodename)
\r
707 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true)
\r
708 worldedit.player_notify(name, count .. " nodes added")
\r
709 end, check_pyramid),
\r
712 minetest.register_chatcommand("/pyramid", {
\r
713 params = "x/y/z/? <height> <node>",
\r
714 description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",
\r
715 privs = {worldedit=true},
\r
716 func = safe_region(function(name, param)
\r
717 local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")
\r
718 height = tonumber(height)
\r
719 if axis == "?" then
\r
721 axis, sign = worldedit.player_axis(name)
\r
722 height = height * sign
\r
724 local node = get_node(name, nodename)
\r
725 local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)
\r
726 worldedit.player_notify(name, count .. " nodes added")
\r
727 end, check_pyramid),
\r
730 minetest.register_chatcommand("/spiral", {
\r
731 params = "<length> <height> <space> <node>",
\r
732 description = "Add spiral centered at WorldEdit position 1 with side length <length>, height <height>, space between walls <space>, composed of <node>",
\r
733 privs = {worldedit=true},
\r
734 func = safe_region(function(name, param)
\r
735 local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
736 local node = get_node(name, nodename)
\r
737 local count = worldedit.spiral(worldedit.pos1[name], tonumber(length), tonumber(height), tonumber(space), node)
\r
738 worldedit.player_notify(name, count .. " nodes added")
\r
740 function(name, param)
\r
741 if worldedit.pos1[name] == nil then
\r
742 worldedit.player_notify(name, "no position 1 selected")
\r
745 local found, _, length, height, space, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")
\r
746 if found == nil then
\r
747 worldedit.player_notify(name, "invalid usage: " .. param)
\r
750 local node = get_node(name, nodename)
\r
751 if not node then return nil end
\r
752 return 1 -- TODO: return an useful value
\r
756 minetest.register_chatcommand("/copy", {
\r
757 params = "x/y/z/? <amount>",
\r
758 description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
\r
759 privs = {worldedit=true},
\r
760 func = safe_region(function(name, param)
\r
761 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
762 if found == nil then
\r
763 worldedit.player_notify(name, "invalid usage: " .. param)
\r
766 amount = tonumber(amount)
\r
767 if axis == "?" then
\r
769 axis, sign = worldedit.player_axis(name)
\r
770 amount = amount * sign
\r
773 local count = worldedit.copy(worldedit.pos1[name], worldedit.pos2[name], axis, amount)
\r
774 worldedit.player_notify(name, count .. " nodes copied")
\r
776 function(name, param)
\r
777 local volume = check_region(name, param)
\r
778 return volume and volume * 2 or volume
\r
782 minetest.register_chatcommand("/move", {
\r
783 params = "x/y/z/? <amount>",
\r
784 description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",
\r
785 privs = {worldedit=true},
\r
786 func = safe_region(function(name, param)
\r
787 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
788 local found, _, axis, amount = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
789 if found == nil then
\r
790 worldedit.player_notify(name, "invalid usage: " .. param)
\r
793 amount = tonumber(amount)
\r
794 if axis == "?" then
\r
796 axis, sign = worldedit.player_axis(name)
\r
797 amount = amount * sign
\r
800 local count = worldedit.move(pos1, pos2, axis, amount)
\r
802 pos1[axis] = pos1[axis] + amount
\r
803 pos2[axis] = pos2[axis] + amount
\r
804 worldedit.mark_pos1(name)
\r
805 worldedit.mark_pos2(name)
\r
806 worldedit.player_notify(name, count .. " nodes moved")
\r
807 end, check_region),
\r
810 minetest.register_chatcommand("/stack", {
\r
811 params = "x/y/z/? <count>",
\r
812 description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",
\r
813 privs = {worldedit=true},
\r
814 func = safe_region(function(name, param)
\r
815 local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
816 repetitions = tonumber(repetitions)
\r
817 if axis == "?" then
\r
819 axis, sign = worldedit.player_axis(name)
\r
820 repetitions = repetitions * sign
\r
823 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
824 local count = worldedit.volume(pos1, pos2) * math.abs(repetitions)
\r
825 worldedit.stack(pos1, pos2, axis, repetitions, function()
\r
826 worldedit.player_notify(name, count .. " nodes stacked")
\r
829 function(name, param)
\r
830 local found, _, axis, repetitions = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
831 if found == nil then
\r
832 worldedit.player_notify(name, "invalid usage: " .. param)
\r
836 local count = check_region(name, param)
\r
837 if count then return tonumber(repetitions) * count end
\r
842 minetest.register_chatcommand("/stack2", {
\r
843 params = "<count> <x> <y> <z>",
\r
844 description = "Stack the current WorldEdit region <count> times by offset <x>, <y>, <z>",
\r
845 privs = {worldedit=true},
\r
846 func = safe_region(function(name, param)
\r
847 local repetitions, incs = param:match("(%d+)%s*(.+)")
\r
848 if repetitions == nil then
\r
851 repetitions = tonumber(repetitions)
\r
853 local x, y, z = incs:match("([+-]?%d+) ([+-]?%d+) ([+-]?%d+)")
\r
855 worldedit.player_notify(name, "invalid increments: " .. param)
\r
858 x, y, z = tonumber(x), tonumber(y), tonumber(z)
\r
860 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
861 local count = worldedit.volume(pos1, pos2) * repetitions
\r
862 worldedit.stack2(pos1, pos2, {x=x, y=y, z=z}, repetitions, function()
\r
863 worldedit.player_notify(name, count .. " nodes stacked")
\r
866 function(name, param)
\r
867 local repetitions, incs = param:match("(%d+)%s*(.+)")
\r
868 if repetitions == nil then
\r
869 worldedit.player_notify(name, "invalid count: " .. param)
\r
872 repetitions = tonumber(repetitions)
\r
874 local count = check_region(name, param)
\r
875 if count then return repetitions * count end
\r
881 minetest.register_chatcommand("/stretch", {
\r
882 params = "<stretchx> <stretchy> <stretchz>",
\r
883 description = "Scale the current WorldEdit positions and region by a factor of <stretchx>, <stretchy>, <stretchz> along the X, Y, and Z axes, repectively, with position 1 as the origin",
\r
884 privs = {worldedit=true},
\r
885 func = safe_region(function(name, param)
\r
886 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
887 local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
\r
888 stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)
\r
889 local count, pos1, pos2 = worldedit.stretch(pos1, pos2, stretchx, stretchy, stretchz)
\r
891 --reset markers to scaled positions
\r
892 worldedit.pos1[name] = pos1
\r
893 worldedit.pos2[name] = pos2
\r
894 worldedit.mark_pos1(name)
\r
895 worldedit.mark_pos2(name)
\r
897 worldedit.player_notify(name, count .. " nodes stretched")
\r
899 function(name, param)
\r
900 local found, _, stretchx, stretchy, stretchz = param:find("^(%d+)%s+(%d+)%s+(%d+)$")
\r
901 if found == nil then
\r
902 worldedit.player_notify(name, "invalid usage: " .. param)
\r
905 stretchx, stretchy, stretchz = tonumber(stretchx), tonumber(stretchy), tonumber(stretchz)
\r
906 if stretchx == 0 or stretchy == 0 or stretchz == 0 then
\r
907 worldedit.player_notify(name, "invalid scaling factors: " .. param)
\r
910 local count = check_region(name, param)
\r
912 return stretchx * stretchy * stretchz * count
\r
918 minetest.register_chatcommand("/transpose", {
\r
919 params = "x/y/z/? x/y/z/?",
\r
920 description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",
\r
921 privs = {worldedit=true},
\r
922 func = safe_region(function(name, param)
\r
923 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
924 local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
\r
925 if axis1 == "?" then axis1 = worldedit.player_axis(name) end
\r
926 if axis2 == "?" then axis2 = worldedit.player_axis(name) end
\r
927 local count, pos1, pos2 = worldedit.transpose(pos1, pos2, axis1, axis2)
\r
929 --reset markers to transposed positions
\r
930 worldedit.pos1[name] = pos1
\r
931 worldedit.pos2[name] = pos2
\r
932 worldedit.mark_pos1(name)
\r
933 worldedit.mark_pos2(name)
\r
935 worldedit.player_notify(name, count .. " nodes transposed")
\r
937 function(name, param)
\r
938 local found, _, axis1, axis2 = param:find("^([xyz%?])%s+([xyz%?])$")
\r
939 if found == nil then
\r
940 worldedit.player_notify(name, "invalid usage: " .. param)
\r
943 if axis1 == axis2 then
\r
944 worldedit.player_notify(name, "invalid usage: axes must be different")
\r
947 return check_region(name, param)
\r
951 minetest.register_chatcommand("/flip", {
\r
952 params = "x/y/z/?",
\r
953 description = "Flip the current WorldEdit region along the x/y/z/? axis",
\r
954 privs = {worldedit=true},
\r
955 func = safe_region(function(name, param)
\r
956 if param == "?" then param = worldedit.player_axis(name) end
\r
957 local count = worldedit.flip(worldedit.pos1[name], worldedit.pos2[name], param)
\r
958 worldedit.player_notify(name, count .. " nodes flipped")
\r
960 function(name, param)
\r
961 if param ~= "x" and param ~= "y" and param ~= "z" and param ~= "?" then
\r
962 worldedit.player_notify(name, "invalid usage: " .. param)
\r
965 return check_region(name, param)
\r
969 minetest.register_chatcommand("/rotate", {
\r
970 params = "<axis> <angle>",
\r
971 description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",
\r
972 privs = {worldedit=true},
\r
973 func = safe_region(function(name, param)
\r
974 local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
\r
975 local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
976 if axis == "?" then axis = worldedit.player_axis(name) end
\r
977 local count, pos1, pos2 = worldedit.rotate(pos1, pos2, axis, angle)
\r
979 --reset markers to rotated positions
\r
980 worldedit.pos1[name] = pos1
\r
981 worldedit.pos2[name] = pos2
\r
982 worldedit.mark_pos1(name)
\r
983 worldedit.mark_pos2(name)
\r
985 worldedit.player_notify(name, count .. " nodes rotated")
\r
987 function(name, param)
\r
988 local found, _, axis, angle = param:find("^([xyz%?])%s+([+-]?%d+)$")
\r
989 if found == nil then
\r
990 worldedit.player_notify(name, "invalid usage: " .. param)
\r
993 if angle % 90 ~= 0 or angle % 360 == 0 then
\r
994 worldedit.player_notify(name, "invalid usage: angle must be multiple of 90")
\r
997 return check_region(name, param)
\r
1001 minetest.register_chatcommand("/orient", {
\r
1002 params = "<angle>",
\r
1003 description = "Rotate oriented nodes in the current WorldEdit region around the Y axis by angle <angle> (90 degree increment)",
\r
1004 privs = {worldedit=true},
\r
1005 func = safe_region(function(name, param)
\r
1006 local found, _, angle = param:find("^([+-]?%d+)$")
\r
1007 local count = worldedit.orient(worldedit.pos1[name], worldedit.pos2[name], angle)
\r
1008 worldedit.player_notify(name, count .. " nodes oriented")
\r
1010 function(name, param)
\r
1011 local found, _, angle = param:find("^([+-]?%d+)$")
\r
1012 if found == nil then
\r
1013 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1016 if angle % 90 ~= 0 then
\r
1017 worldedit.player_notify(name, "invalid usage: angle must be multiple of 90")
\r
1020 return check_region(name, param)
\r
1024 minetest.register_chatcommand("/fixlight", {
\r
1026 description = "Fix the lighting in the current WorldEdit region",
\r
1027 privs = {worldedit=true},
\r
1028 func = safe_region(function(name, param)
\r
1029 local count = worldedit.fixlight(worldedit.pos1[name], worldedit.pos2[name])
\r
1030 worldedit.player_notify(name, count .. " nodes updated")
\r
1034 minetest.register_chatcommand("/drain", {
\r
1036 description = "Remove any fluid node within the current WorldEdit region",
\r
1037 privs = {worldedit=true},
\r
1038 func = safe_region(function(name, param)
\r
1039 -- TODO: make an API function for this
\r
1041 local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
\r
1042 for x = pos1.x, pos2.x do
\r
1043 for y = pos1.y, pos2.y do
\r
1044 for z = pos1.z, pos2.z do
\r
1045 local n = minetest.get_node({x=x, y=y, z=z}).name
\r
1046 local d = minetest.registered_nodes[n]
\r
1047 if d ~= nil and (d["drawtype"] == "liquid" or d["drawtype"] == "flowingliquid") then
\r
1048 minetest.remove_node({x=x, y=y, z=z})
\r
1054 worldedit.player_notify(name, count .. " nodes updated")
\r
1058 minetest.register_chatcommand("/hide", {
\r
1060 description = "Hide all nodes in the current WorldEdit region non-destructively",
\r
1061 privs = {worldedit=true},
\r
1062 func = safe_region(function(name, param)
\r
1063 local count = worldedit.hide(worldedit.pos1[name], worldedit.pos2[name])
\r
1064 worldedit.player_notify(name, count .. " nodes hidden")
\r
1068 minetest.register_chatcommand("/suppress", {
\r
1069 params = "<node>",
\r
1070 description = "Suppress all <node> in the current WorldEdit region non-destructively",
\r
1071 privs = {worldedit=true},
\r
1072 func = safe_region(function(name, param)
\r
1073 local node = get_node(name, param)
\r
1074 local count = worldedit.suppress(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1075 worldedit.player_notify(name, count .. " nodes suppressed")
\r
1076 end, check_region),
\r
1079 minetest.register_chatcommand("/highlight", {
\r
1080 params = "<node>",
\r
1081 description = "Highlight <node> in the current WorldEdit region by hiding everything else non-destructively",
\r
1082 privs = {worldedit=true},
\r
1083 func = safe_region(function(name, param)
\r
1084 local node = get_node(name, param)
\r
1085 local count = worldedit.highlight(worldedit.pos1[name], worldedit.pos2[name], node)
\r
1086 worldedit.player_notify(name, count .. " nodes highlighted")
\r
1087 end, check_region),
\r
1090 minetest.register_chatcommand("/restore", {
\r
1092 description = "Restores nodes hidden with WorldEdit in the current WorldEdit region",
\r
1093 privs = {worldedit=true},
\r
1094 func = safe_region(function(name, param)
\r
1095 local count = worldedit.restore(worldedit.pos1[name], worldedit.pos2[name])
\r
1096 worldedit.player_notify(name, count .. " nodes restored")
\r
1100 local function detect_misaligned_schematic(name, pos1, pos2)
\r
1101 pos1, pos2 = worldedit.sort_pos(pos1, pos2)
\r
1102 -- Check that allocate/save can position the schematic correctly
\r
1103 -- The expected behaviour is that the (0,0,0) corner of the schematic stays
\r
1104 -- sat pos1, this only works when the minimum position is actually present
\r
1105 -- in the schematic.
\r
1106 local node = minetest.get_node(pos1)
\r
1107 local have_node_at_origin = node.name ~= "air" and node.name ~= "ignore"
\r
1108 if not have_node_at_origin then
\r
1109 worldedit.player_notify(name,
\r
1110 "Warning: The schematic contains excessive free space and WILL be "..
\r
1111 "misaligned when allocated or loaded. To avoid this, shrink your "..
\r
1112 "area to cover exactly the nodes to be saved."
\r
1117 minetest.register_chatcommand("/save", {
\r
1118 params = "<file>",
\r
1119 description = "Save the current WorldEdit region to \"(world folder)/schems/<file>.we\"",
\r
1120 privs = {worldedit=true},
\r
1121 func = safe_region(function(name, param)
\r
1122 if param == "" then
\r
1123 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1126 if not check_filename(param) then
\r
1127 worldedit.player_notify(name, "Disallowed file name: " .. param)
\r
1130 local result, count = worldedit.serialize(worldedit.pos1[name],
\r
1131 worldedit.pos2[name])
\r
1132 detect_misaligned_schematic(name, worldedit.pos1[name], worldedit.pos2[name])
\r
1134 local path = minetest.get_worldpath() .. "/schems"
\r
1135 -- Create directory if it does not already exist
\r
1136 minetest.mkdir(path)
\r
1138 local filename = path .. "/" .. param .. ".we"
\r
1139 local file, err = io.open(filename, "wb")
\r
1140 if err ~= nil then
\r
1141 worldedit.player_notify(name, "Could not save file to \"" .. filename .. "\"")
\r
1144 file:write(result)
\r
1148 worldedit.player_notify(name, count .. " nodes saved")
\r
1152 minetest.register_chatcommand("/allocate", {
\r
1153 params = "<file>",
\r
1154 description = "Set the region defined by nodes from \"(world folder)/schems/<file>.we\" as the current WorldEdit region",
\r
1155 privs = {worldedit=true},
\r
1156 func = function(name, param)
\r
1157 local pos = get_position(name)
\r
1158 if pos == nil then return end
\r
1160 if param == "" then
\r
1161 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1164 if not check_filename(param) then
\r
1165 worldedit.player_notify(name, "Disallowed file name: " .. param)
\r
1169 local filename = minetest.get_worldpath() .. "/schems/" .. param .. ".we"
\r
1170 local file, err = io.open(filename, "rb")
\r
1171 if err ~= nil then
\r
1172 worldedit.player_notify(name, "could not open file \"" .. filename .. "\"")
\r
1175 local value = file:read("*a")
\r
1178 local version = worldedit.read_header(value)
\r
1179 if version == nil or version == 0 then
\r
1180 worldedit.player_notify(name, "File is invalid!")
\r
1182 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1183 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1186 local nodepos1, nodepos2, count = worldedit.allocate(pos, value)
\r
1188 if not nodepos1 then
\r
1189 worldedit.player_notify(name, "Schematic empty, nothing allocated")
\r
1193 worldedit.pos1[name] = nodepos1
\r
1194 worldedit.mark_pos1(name)
\r
1195 worldedit.pos2[name] = nodepos2
\r
1196 worldedit.mark_pos2(name)
\r
1198 worldedit.player_notify(name, count .. " nodes allocated")
\r
1202 minetest.register_chatcommand("/load", {
\r
1203 params = "<file>",
\r
1204 description = "Load nodes from \"(world folder)/schems/<file>[.we[m]]\" with position 1 of the current WorldEdit region as the origin",
\r
1205 privs = {worldedit=true},
\r
1206 func = function(name, param)
\r
1207 local pos = get_position(name)
\r
1208 if pos == nil then return end
\r
1210 if param == "" then
\r
1211 worldedit.player_notify(name, "invalid usage: " .. param)
\r
1214 if not string.find(param, "^[%w \t.,+-_=!@#$%%^&*()%[%]{};'\"]+$") then
\r
1215 worldedit.player_notify(name, "invalid file name: " .. param)
\r
1219 --find the file in the world path
\r
1220 local testpaths = {
\r
1221 minetest.get_worldpath() .. "/schems/" .. param,
\r
1222 minetest.get_worldpath() .. "/schems/" .. param .. ".we",
\r
1223 minetest.get_worldpath() .. "/schems/" .. param .. ".wem",
\r
1226 for index, path in ipairs(testpaths) do
\r
1227 file, err = io.open(path, "rb")
\r
1233 worldedit.player_notify(name, "could not open file \"" .. param .. "\"")
\r
1236 local value = file:read("*a")
\r
1239 local version = worldedit.read_header(value)
\r
1240 if version == nil or version == 0 then
\r
1241 worldedit.player_notify(name, "File is invalid!")
\r
1243 elseif version > worldedit.LATEST_SERIALIZATION_VERSION then
\r
1244 worldedit.player_notify(name, "File was created with newer version of WorldEdit!")
\r
1248 local count = worldedit.deserialize(pos, value)
\r
1250 worldedit.player_notify(name, count .. " nodes loaded")
\r
1254 minetest.register_chatcommand("/lua", {
\r
1255 params = "<code>",
\r
1256 description = "Executes <code> as a Lua chunk in the global namespace",
\r
1257 privs = {worldedit=true, server=true},
\r
1258 func = function(name, param)
\r
1259 local err = worldedit.lua(param)
\r
1261 worldedit.player_notify(name, "code error: " .. err)
\r
1262 minetest.log("action", name.." tried to execute "..param)
\r
1264 worldedit.player_notify(name, "code successfully executed", false)
\r
1265 minetest.log("action", name.." executed "..param)
\r
1270 minetest.register_chatcommand("/luatransform", {
\r
1271 params = "<code>",
\r
1272 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
1273 privs = {worldedit=true, server=true},
\r
1274 func = safe_region(function(name, param)
\r
1275 local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)
\r
1277 worldedit.player_notify(name, "code error: " .. err, false)
\r
1278 minetest.log("action", name.." tried to execute luatransform "..param)
\r
1280 worldedit.player_notify(name, "code successfully executed", false)
\r
1281 minetest.log("action", name.." executed luatransform "..param)
\r
1286 minetest.register_chatcommand("/mtschemcreate", {
\r
1287 params = "<file>",
\r
1288 description = "Save the current WorldEdit region using the Minetest "..
\r
1289 "Schematic format to \"(world folder)/schems/<filename>.mts\"",
\r
1290 privs = {worldedit=true},
\r
1291 func = safe_region(function(name, param)
\r
1292 if param == nil then
\r
1293 worldedit.player_notify(name, "No filename specified")
\r
1296 if not check_filename(param) then
\r
1297 worldedit.player_notify(name, "Disallowed file name: " .. param)
\r
1301 local path = minetest.get_worldpath() .. "/schems"
\r
1302 -- Create directory if it does not already exist
\r
1303 minetest.mkdir(path)
\r
1305 local filename = path .. "/" .. param .. ".mts"
\r
1306 local ret = minetest.create_schematic(worldedit.pos1[name],
\r
1307 worldedit.pos2[name], worldedit.prob_list[name],
\r
1309 if ret == nil then
\r
1310 worldedit.player_notify(name, "Failed to create Minetest schematic", false)
\r
1312 worldedit.player_notify(name, "Saved Minetest schematic to " .. param, false)
\r
1314 worldedit.prob_list[name] = {}
\r
1318 minetest.register_chatcommand("/mtschemplace", {
\r
1319 params = "<file>",
\r
1320 description = "Load nodes from \"(world folder)/schems/<file>.mts\" with position 1 of the current WorldEdit region as the origin",
\r
1321 privs = {worldedit=true},
\r
1322 func = function(name, param)
\r
1323 if param == "" then
\r
1324 worldedit.player_notify(name, "no filename specified")
\r
1327 if not check_filename(param) then
\r
1328 worldedit.player_notify(name, "Disallowed file name: " .. param)
\r
1332 local pos = get_position(name)
\r
1333 if pos == nil then return end
\r
1335 local path = minetest.get_worldpath() .. "/schems/" .. param .. ".mts"
\r
1336 if minetest.place_schematic(pos, path) == nil then
\r
1337 worldedit.player_notify(name, "failed to place Minetest schematic", false)
\r
1339 worldedit.player_notify(name, "placed Minetest schematic " .. param ..
\r
1340 " at " .. minetest.pos_to_string(pos), false)
\r
1345 minetest.register_chatcommand("/mtschemprob", {
\r
1346 params = "start/finish/get",
\r
1347 description = "Begins node probability entry for Minetest schematics, gets the nodes that have probabilities set, or ends node probability entry",
\r
1348 privs = {worldedit=true},
\r
1349 func = function(name, param)
\r
1350 if param == "start" then --start probability setting
\r
1351 worldedit.set_pos[name] = "prob"
\r
1352 worldedit.prob_list[name] = {}
\r
1353 worldedit.player_notify(name, "select Minetest schematic probability values by punching nodes")
\r
1354 elseif param == "finish" then --finish probability setting
\r
1355 worldedit.set_pos[name] = nil
\r
1356 worldedit.player_notify(name, "finished Minetest schematic probability selection")
\r
1357 elseif param == "get" then --get all nodes that had probabilities set on them
\r
1359 local problist = worldedit.prob_list[name]
\r
1360 if problist == nil then
\r
1363 for k,v in pairs(problist) do
\r
1364 local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100
\r
1365 text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "
\r
1367 worldedit.player_notify(name, "currently set node probabilities:")
\r
1368 worldedit.player_notify(name, text)
\r
1370 worldedit.player_notify(name, "unknown subcommand: " .. param)
\r
1375 minetest.register_on_player_receive_fields(function(player, formname, fields)
\r
1376 if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then
\r
1377 local name = player:get_player_name()
\r
1378 local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}
\r
1379 local index = table.getn(worldedit.prob_list[name]) + 1
\r
1380 worldedit.prob_list[name][index] = prob_entry
\r
1384 minetest.register_chatcommand("/clearobjects", {
\r
1386 description = "Clears all objects within the WorldEdit region",
\r
1387 privs = {worldedit=true},
\r
1388 func = safe_region(function(name, param)
\r
1389 local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name])
\r
1390 worldedit.player_notify(name, count .. " objects cleared")
\r