return full:find(part, 1, true) == #full - #part + 1\r
end\r
\r
+local description_cache = nil\r
+\r
-- normalizes node "description" `nodename`, returning a string (or nil)\r
worldedit.normalize_nodename = function(nodename)\r
nodename = nodename:gsub("^%s*(.-)%s*$", "%1") -- strip spaces\r
return fullname\r
end\r
nodename = nodename:lower()\r
- for key, value in pairs(minetest.registered_nodes) do\r
- if string_endswith(key, ":" .. nodename) then -- matches name (w/o mod part)\r
+\r
+ for key, _ in pairs(minetest.registered_nodes) do\r
+ if string_endswith(key:lower(), ":" .. nodename) then -- matches name (w/o mod part)\r
return key\r
end\r
end\r
- for key, value in pairs(minetest.registered_nodes) do\r
- local desc = strip_escapes(value.description):gsub("\n.*", "", 1):lower()\r
+\r
+ if description_cache == nil then\r
+ -- cache stripped descriptions\r
+ description_cache = {}\r
+ for key, value in pairs(minetest.registered_nodes) do\r
+ local desc = strip_escapes(value.description):gsub("\n.*", "", 1):lower()\r
+ if desc ~= "" then\r
+ description_cache[key] = desc\r
+ end\r
+ end\r
+ end\r
+\r
+ for key, desc in pairs(description_cache) do\r
if desc == nodename then -- matches description\r
return key\r
end\r
+ end\r
+ for key, desc in pairs(description_cache) do\r
if desc == nodename .. " block" then\r
-- fuzzy description match (e.g. "Steel" == "Steel Block")\r
return key\r
end\r
\r
local match = nil\r
- for key, value in pairs(minetest.registered_nodes) do\r
- if value.description:lower():find(nodename, 1, true) ~= nil then\r
+ for key, value in pairs(description_cache) do\r
+ if value:find(nodename, 1, true) ~= nil then\r
if match ~= nil then\r
return nil\r
end\r
params = "",\r
description = "Get information about the WorldEdit mod",\r
func = function(name)\r
- 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
+ worldedit.player_notify(name, "WorldEdit " .. worldedit.version_string..\r
+ " is available on this server. Type //help to get a list of "..\r
+ "commands, or get more information at "..\r
+ "https://github.com/Uberi/Minetest-WorldEdit")\r
end,\r
})\r
\r
})\r
\r
worldedit.register_command("fixedpos", {\r
- params = "set1/set2 x y z",\r
+ params = "set1/set2 <x> <y> <z>",\r
description = "Set a WorldEdit region position to the position at (<x>, <y>, <z>)",\r
privs = {worldedit=true},\r
parse = function(param)\r
parse = function(param)\r
local param2 = tonumber(param)\r
if not param2 then\r
- return false, "Invalid or missing param2 argument"\r
+ return false\r
elseif param2 < 0 or param2 > 255 then\r
return false, "Param2 is out of range (must be between 0 and 255 inclusive!)"\r
end\r
})\r
\r
worldedit.register_command("mix", {\r
- params = "<node1> [<weighting1>] [<node2> [<weighting2>]] ...",\r
+ params = "<node1> [count1] <node2> [count2] ...",\r
description = "Fill the current WorldEdit region with a random mix of <node1>, ...",\r
privs = {worldedit=true},\r
require_pos = 2,\r
nodes[#nodes + 1] = node\r
end\r
end\r
+ if #nodes == 0 then\r
+ return false\r
+ end\r
return true, nodes\r
end,\r
nodes_needed = check_region,\r
\r
worldedit.register_command("hollowcylinder", {\r
params = "x/y/z/? <length> <radius1> [radius2] <node>",\r
- 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
+ description = "Add hollow cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>",\r
privs = {worldedit=true},\r
require_pos = 1,\r
parse = check_cylinder,\r
\r
worldedit.register_command("cylinder", {\r
params = "x/y/z/? <length> <radius1> [radius2] <node>",\r
- 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
+ description = "Add cylinder at WorldEdit position 1 along the given axis with length <length>, base radius <radius1> (and top radius [radius2]), composed of <node>",\r
privs = {worldedit=true},\r
require_pos = 1,\r
parse = check_cylinder,\r
\r
worldedit.register_command("hollowpyramid", {\r
params = "x/y/z/? <height> <node>",\r
- description = "Add hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",\r
+ description = "Add hollow pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>",\r
privs = {worldedit=true},\r
require_pos = 1,\r
parse = check_pyramid,\r
\r
worldedit.register_command("pyramid", {\r
params = "x/y/z/? <height> <node>",\r
- description = "Add pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",\r
+ description = "Add pyramid centered at WorldEdit position 1 along the given axis with height <height>, composed of <node>",\r
privs = {worldedit=true},\r
require_pos = 1,\r
parse = check_pyramid,\r
\r
worldedit.register_command("copy", {\r
params = "x/y/z/? <amount>",\r
- description = "Copy the current WorldEdit region along the x/y/z/? axis by <amount> nodes",\r
+ description = "Copy the current WorldEdit region along the given axis by <amount> nodes",\r
privs = {worldedit=true},\r
require_pos = 2,\r
parse = function(param)\r
\r
worldedit.register_command("move", {\r
params = "x/y/z/? <amount>",\r
- description = "Move the current WorldEdit region along the x/y/z/? axis by <amount> nodes",\r
+ description = "Move the current WorldEdit region along the given axis by <amount> nodes",\r
privs = {worldedit=true},\r
require_pos = 2,\r
parse = function(param)\r
\r
worldedit.register_command("stack", {\r
params = "x/y/z/? <count>",\r
- description = "Stack the current WorldEdit region along the x/y/z/? axis <count> times",\r
+ description = "Stack the current WorldEdit region along the given axis <count> times",\r
privs = {worldedit=true},\r
require_pos = 2,\r
parse = function(param)\r
\r
worldedit.register_command("transpose", {\r
params = "x/y/z/? x/y/z/?",\r
- description = "Transpose the current WorldEdit region along the x/y/z/? and x/y/z/? axes",\r
+ description = "Transpose the current WorldEdit region along the given axes",\r
privs = {worldedit=true},\r
require_pos = 2,\r
parse = function(param)\r
\r
worldedit.register_command("flip", {\r
params = "x/y/z/?",\r
- description = "Flip the current WorldEdit region along the x/y/z/? axis",\r
+ description = "Flip the current WorldEdit region along the given axis",\r
privs = {worldedit=true},\r
require_pos = 2,\r
parse = function(param)\r
})\r
\r
worldedit.register_command("rotate", {\r
- params = "<axis> <angle>",\r
- description = "Rotate the current WorldEdit region around the axis <axis> by angle <angle> (90 degree increment)",\r
+ params = "x/y/z/? <angle>",\r
+ description = "Rotate the current WorldEdit region around the given axis by angle <angle> (90 degree increment)",\r
privs = {worldedit=true},\r
require_pos = 2,\r
parse = function(param)\r
end,\r
})\r
\r
+local clearcut_cache\r
+\r
+local function clearcut(pos1, pos2)\r
+ -- decide which nodes we consider plants\r
+ if clearcut_cache == nil then\r
+ clearcut_cache = {}\r
+ for name, def in pairs(minetest.registered_nodes) do\r
+ local groups = def.groups or {}\r
+ if (\r
+ -- the groups say so\r
+ groups.flower or groups.grass or groups.flora or groups.plant or\r
+ groups.leaves or groups.tree or groups.leafdecay or groups.sapling or\r
+ -- drawtype heuristic\r
+ (def.is_ground_content and def.buildable_to and\r
+ (def.sunlight_propagates or not def.walkable)\r
+ and def.drawtype == "plantlike") or\r
+ -- if it's flammable, it probably needs to go too\r
+ (def.is_ground_content and not def.walkable and groups.flammable)\r
+ ) then\r
+ clearcut_cache[name] = true\r
+ end\r
+ end\r
+ end\r
+ local plants = clearcut_cache\r
+\r
+ local count = 0\r
+ local prev, any\r
+\r
+ for x = pos1.x, pos2.x do\r
+ for z = pos1.z, pos2.z do\r
+ prev = false\r
+ any = false\r
+ -- first pass: remove floating nodes that would be left over\r
+ for y = pos1.y, pos2.y do\r
+ local n = minetest.get_node({x=x, y=y, z=z}).name\r
+ if plants[n] then\r
+ prev = true\r
+ any = true\r
+ elseif prev then\r
+ local def = minetest.registered_nodes[n] or {}\r
+ local groups = def.groups or {}\r
+ if groups.attached_node or (def.buildable_to and groups.falling_node) then\r
+ minetest.remove_node({x=x, y=y, z=z})\r
+ count = count + 1\r
+ else\r
+ prev = false\r
+ end\r
+ end\r
+ end\r
+\r
+ -- second pass: remove plants, top-to-bottom to avoid item drops\r
+ if any then\r
+ for y = pos2.y, pos1.y, -1 do\r
+ local n = minetest.get_node({x=x, y=y, z=z}).name\r
+ if plants[n] then\r
+ minetest.remove_node({x=x, y=y, z=z})\r
+ count = count + 1\r
+ end\r
+ end\r
+ end\r
+ end\r
+ end\r
+\r
+ return count\r
+end\r
+\r
+worldedit.register_command("clearcut", {\r
+ params = "",\r
+ description = "Remove any plant, tree or foilage-like nodes in the selected region",\r
+ privs = {worldedit=true},\r
+ require_pos = 2,\r
+ nodes_needed = check_region,\r
+ func = function(name)\r
+ local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])\r
+ local count = clearcut(pos1, pos2)\r
+ worldedit.player_notify(name, count .. " nodes removed")\r
+ end,\r
+})\r
+\r
worldedit.register_command("hide", {\r
params = "",\r
description = "Hide all nodes in the current WorldEdit region non-destructively",\r