]> git.lizzy.rs Git - worldedit.git/blobdiff - worldedit_commands/init.lua
Improve node name normalization again
[worldedit.git] / worldedit_commands / init.lua
index 32bbc6c1e98abb566a3596e66e44aea86207e310..1cdb107712d8aadcd89dad8b1d980d366fd6b570 100644 (file)
@@ -154,6 +154,8 @@ local function string_endswith(full, part)
        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
@@ -164,16 +166,30 @@ worldedit.normalize_nodename = function(nodename)
                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
@@ -181,8 +197,8 @@ worldedit.normalize_nodename = function(nodename)
        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
@@ -216,7 +232,10 @@ worldedit.register_command("about", {
        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
@@ -428,7 +447,7 @@ worldedit.register_command("p", {
 })\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
@@ -537,7 +556,7 @@ worldedit.register_command("param2", {
        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
@@ -551,7 +570,7 @@ worldedit.register_command("param2", {
 })\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
@@ -571,6 +590,9 @@ worldedit.register_command("mix", {
                                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
@@ -771,7 +793,7 @@ end
 \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
@@ -792,7 +814,7 @@ worldedit.register_command("hollowcylinder", {
 \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
@@ -825,7 +847,7 @@ end
      \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
@@ -845,7 +867,7 @@ worldedit.register_command("hollowpyramid", {
 \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
@@ -890,7 +912,7 @@ worldedit.register_command("spiral", {
 \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
@@ -917,7 +939,7 @@ worldedit.register_command("copy", {
 \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
@@ -949,7 +971,7 @@ worldedit.register_command("move", {
 \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
@@ -1041,7 +1063,7 @@ worldedit.register_command("stretch", {
 \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
@@ -1071,7 +1093,7 @@ worldedit.register_command("transpose", {
 \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
@@ -1089,8 +1111,8 @@ worldedit.register_command("flip", {
 })\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
@@ -1180,6 +1202,85 @@ worldedit.register_command("drain", {
        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