\r
dofile(minetest.get_modpath("worldedit_commands") .. "/cuboid.lua")\r
dofile(minetest.get_modpath("worldedit_commands") .. "/mark.lua")\r
-local safe_region, check_region = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua")\r
+dofile(minetest.get_modpath("worldedit_commands") .. "/wand.lua")\r
+local safe_region, check_region, reset_pending = dofile(minetest.get_modpath("worldedit_commands") .. "/safe.lua")\r
\r
local function get_position(name) --position 1 retrieval function for when not using `safe_region`\r
local pos1 = worldedit.pos1[name]\r
return pos1\r
end\r
\r
+-- normalize_nodename wrapper for convenience purposes\r
local function get_node(name, nodename)\r
local node = worldedit.normalize_nodename(nodename)\r
if not node then\r
minetest.chat_send_player(name, "WorldEdit -!- " .. message, false)\r
end\r
\r
---determines whether `nodename` is a valid node name, returning a boolean\r
+local function string_endswith(full, part)\r
+ return full:find(part, 1, true) == #full - #part + 1\r
+end\r
+\r
+-- normalizes node "description" `nodename`, returning a string (or nil)\r
worldedit.normalize_nodename = function(nodename)\r
- nodename = nodename:gsub("^%s*(.-)%s*$", "%1")\r
+ nodename = nodename:gsub("^%s*(.-)%s*$", "%1") -- strip spaces\r
if nodename == "" then return nil end\r
- local fullname = ItemStack({name=nodename}):get_name() --resolve aliases of node names to full names\r
- if minetest.registered_nodes[fullname] or fullname == "air" then --directly found node name or alias of nodename\r
+\r
+ local fullname = ItemStack({name=nodename}):get_name() -- resolve aliases\r
+ if minetest.registered_nodes[fullname] or fullname == "air" then -- full name\r
return fullname\r
end\r
for key, value in pairs(minetest.registered_nodes) do\r
- if key:find(":" .. nodename, 1, true) then --found in mod\r
+ if string_endswith(key, ":" .. nodename) then -- matches name (w/o mod part)\r
return key\r
end\r
end\r
- nodename = nodename:lower() --lowercase both for case insensitive comparison\r
+ nodename = nodename:lower() -- lowercase both for case insensitive comparison\r
for key, value in pairs(minetest.registered_nodes) do\r
- if value.description:lower() == nodename then --found in description\r
+ local desc = value.description:lower()\r
+ if desc == nodename then -- matches description\r
+ return key\r
+ end\r
+ if string_endswith(desc, " block") and desc == nodename.." block" then\r
+ -- fuzzy description match (e.g. "Steel" == "Steel Block")\r
return key\r
end\r
end\r
- return nil\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
+ if match ~= nil then\r
+ return nil\r
+ end\r
+ match = key -- substring description match (only if no ambiguities)\r
+ end\r
+ end\r
+ return match\r
end\r
\r
-- Determines the axis in which a player is facing, returning an axis ("x", "y", or "z") and the sign (1 or -1)\r
end,\r
})\r
\r
+-- mostly copied from builtin/chatcommands.lua with minor modifications\r
+minetest.register_chatcommand("/help", {\r
+ privs = {},\r
+ params = "[all/<cmd>]",\r
+ description = "Get help for WorldEdit commands",\r
+ func = function(name, param)\r
+ local function is_we_command(cmd)\r
+ return cmd:sub(0, 1) == "/"\r
+ end\r
+ local function format_help_line(cmd, def)\r
+ local msg = minetest.colorize("#00ffff", "/"..cmd)\r
+ if def.params and def.params ~= "" then\r
+ msg = msg .. " " .. def.params\r
+ end\r
+ if def.description and def.description ~= "" then\r
+ msg = msg .. ": " .. def.description\r
+ end\r
+ return msg\r
+ end\r
+\r
+ if not minetest.check_player_privs(name, "worldedit") then\r
+ return false, "You are not allowed to use any WorldEdit commands."\r
+ end\r
+ if param == "" then\r
+ local msg = ""\r
+ local cmds = {}\r
+ for cmd, def in pairs(minetest.chatcommands) do\r
+ if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then\r
+ cmds[#cmds + 1] = cmd:sub(2) -- strip the /\r
+ end\r
+ end\r
+ table.sort(cmds)\r
+ return true, "Available commands: " .. table.concat(cmds, " ") .. "\n"\r
+ .. "Use '//help <cmd>' to get more information,"\r
+ .. " or '//help all' to list everything."\r
+ elseif param == "all" then\r
+ local cmds = {}\r
+ for cmd, def in pairs(minetest.chatcommands) do\r
+ if is_we_command(cmd) and minetest.check_player_privs(name, def.privs) then\r
+ cmds[#cmds + 1] = format_help_line(cmd, def)\r
+ end\r
+ end\r
+ table.sort(cmds)\r
+ return true, "Available commands:\n"..table.concat(cmds, "\n")\r
+ else\r
+ return minetest.chatcommands["help"].func(name, "/" .. param)\r
+ end\r
+ end,\r
+})\r
+\r
minetest.register_chatcommand("/inspect", {\r
params = "on/off/1/0/true/false/yes/no/enable/disable/<blank>",\r
description = "Enable or disable node inspection",\r
end,\r
})\r
\r
+local function get_node_rlight(pos)\r
+ local vecs = { -- neighboring nodes\r
+ {x= 1, y= 0, z= 0},\r
+ {x=-1, y= 0, z= 0},\r
+ {x= 0, y= 1, z= 0},\r
+ {x= 0, y=-1, z= 0},\r
+ {x= 0, y= 0, z= 1},\r
+ {x= 0, y= 0, z=-1},\r
+ }\r
+ local ret = 0\r
+ for _, v in ipairs(vecs) do\r
+ ret = math.max(ret, minetest.get_node_light(vector.add(pos, v)))\r
+ end\r
+ return ret\r
+end\r
+\r
minetest.register_on_punchnode(function(pos, node, puncher)\r
local name = puncher:get_player_name()\r
if worldedit.inspect[name] then\r
- if minetest.check_player_privs(name, {worldedit=true}) then\r
- local axis, sign = worldedit.player_axis(name)\r
- message = string.format("inspector: %s at %s (param1=%d, param2=%d) punched by %s facing the %s axis",\r
- node.name, minetest.pos_to_string(pos), node.param1, node.param2, name, axis .. (sign > 0 and "+" or "-"))\r
- else\r
- message = "inspector: worldedit privileges required"\r
- end\r
+ local axis, sign = worldedit.player_axis(name)\r
+ message = string.format("inspector: %s at %s (param1=%d, param2=%d, received light=%d) punched facing the %s axis",\r
+ node.name, minetest.pos_to_string(pos), node.param1, node.param2, get_node_rlight(pos), axis .. (sign > 0 and "+" or "-"))\r
worldedit.player_notify(name, message)\r
end\r
end)\r
worldedit.mark_pos1(name)\r
worldedit.mark_pos2(name)\r
worldedit.set_pos[name] = nil\r
+ --make sure the user does not try to confirm an operation after resetting pos:\r
+ reset_pending(name)\r
worldedit.player_notify(name, "region reset")\r
end,\r
})\r
end, check_region),\r
})\r
\r
+minetest.register_chatcommand("/param2", {\r
+ params = "<param2>",\r
+ description = "Set param2 of all nodes in the current WorldEdit region to <param2>",\r
+ privs = {worldedit=true},\r
+ func = safe_region(function(name, param)\r
+ local param2 = tonumber(param)\r
+ if not param2 then\r
+ worldedit.player_notify(name, "Invalid or missing param2 argument")\r
+ return\r
+ elseif param2 < 0 or param2 > 255 then\r
+ worldedit.player_notify(name, "Param2 is out of range (must be between 0 and 255 inclusive)!")\r
+ return\r
+ end\r
+\r
+ local count = worldedit.set_param2(worldedit.pos1[name], worldedit.pos2[name], param2)\r
+ worldedit.player_notify(name, count .. " nodes altered")\r
+ end, check_region),\r
+})\r
+\r
minetest.register_chatcommand("/mix", {\r
params = "<node1> ...",\r
description = "Fill the current WorldEdit region with a random mix of <node1>, ...",\r
end, check_replace),\r
})\r
\r
+local check_cube = function(name, param)\r
+ if worldedit.pos1[name] == nil then\r
+ worldedit.player_notify(name, "no position 1 selected")\r
+ return nil\r
+ end\r
+ local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
+ if found == nil then\r
+ worldedit.player_notify(name, "invalid usage: " .. param)\r
+ return nil\r
+ end\r
+ local node = get_node(name, nodename)\r
+ if not node then return nil end\r
+ return tonumber(w) * tonumber(h) * tonumber(l)\r
+end\r
+\r
+minetest.register_chatcommand("/hollowcube", {\r
+ params = "<width> <height> <length> <node>",\r
+ 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
+ privs = {worldedit=true},\r
+ func = safe_region(function(name, param)\r
+ local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
+ local node = get_node(name, nodename)\r
+ local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node, true)\r
+ worldedit.player_notify(name, count .. " nodes added")\r
+ end, check_cube),\r
+})\r
+\r
+minetest.register_chatcommand("/cube", {\r
+ params = "<width> <height> <length> <node>",\r
+ description = "Add a cube with its ground level centered at WorldEdit position 1 with dimensions <width> x <height> x <length>, composed of <node>.",\r
+ privs = {worldedit=true},\r
+ func = safe_region(function(name, param)\r
+ local found, _, w, h, l, nodename = param:find("^(%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
+ local node = get_node(name, nodename)\r
+ local count = worldedit.cube(worldedit.pos1[name], tonumber(w), tonumber(h), tonumber(l), node)\r
+ worldedit.player_notify(name, count .. " nodes added")\r
+ end, check_cube),\r
+})\r
+\r
local check_sphere = function(name, param)\r
if worldedit.pos1[name] == nil then\r
worldedit.player_notify(name, "no position 1 selected")\r
worldedit.player_notify(name, "no position 1 selected")\r
return nil\r
end\r
- local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")\r
+ -- two radii\r
+ local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
+ if found == nil then\r
+ -- single radius\r
+ found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")\r
+ radius2 = radius1\r
+ end\r
if found == nil then\r
worldedit.player_notify(name, "invalid usage: " .. param)\r
return nil\r
end\r
local node = get_node(name, nodename)\r
if not node then return nil end\r
- return math.ceil(math.pi * (tonumber(radius) ^ 2) * tonumber(length))\r
+ local radius = math.max(tonumber(radius1), tonumber(radius2))\r
+ return math.ceil(math.pi * (radius ^ 2) * tonumber(length))\r
end\r
\r
minetest.register_chatcommand("/hollowcylinder", {\r
- params = "x/y/z/? <length> <radius> <node>",\r
- description = "Add hollow cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",\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
privs = {worldedit=true},\r
func = safe_region(function(name, param)\r
- local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")\r
+ -- two radii\r
+ local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
+ if found == nil then\r
+ -- single radius\r
+ found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")\r
+ radius2 = radius1\r
+ end\r
length = tonumber(length)\r
if axis == "?" then\r
axis, sign = worldedit.player_axis(name)\r
length = length * sign\r
end\r
local node = get_node(name, nodename)\r
- local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius), node, true)\r
+ local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node, true)\r
worldedit.player_notify(name, count .. " nodes added")\r
end, check_cylinder),\r
})\r
\r
minetest.register_chatcommand("/cylinder", {\r
- params = "x/y/z/? <length> <radius> <node>",\r
- description = "Add cylinder at WorldEdit position 1 along the x/y/z/? axis with length <length> and radius <radius>, composed of <node>",\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
privs = {worldedit=true},\r
func = safe_region(function(name, param)\r
- local found, _, axis, length, radius, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")\r
+ -- two radii\r
+ local found, _, axis, length, radius1, radius2, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(%d+)%s+(.+)$")\r
+ if found == nil then\r
+ -- single radius\r
+ found, _, axis, length, radius1, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(%d+)%s+(.+)$")\r
+ radius2 = radius1\r
+ end\r
length = tonumber(length)\r
if axis == "?" then\r
axis, sign = worldedit.player_axis(name)\r
length = length * sign\r
end\r
local node = get_node(name, nodename)\r
- local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius), node)\r
+ local count = worldedit.cylinder(worldedit.pos1[name], axis, length, tonumber(radius1), tonumber(radius2), node)\r
worldedit.player_notify(name, count .. " nodes added")\r
end, check_cylinder),\r
})\r
\r
-minetest.register_chatcommand("/pyramid", {\r
+local check_pyramid = function(name, param)\r
+ if worldedit.pos1[name] == nil then\r
+ worldedit.player_notify(name, "no position 1 selected")\r
+ return nil\r
+ end\r
+ local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")\r
+ if found == nil then\r
+ worldedit.player_notify(name, "invalid usage: " .. param)\r
+ return nil\r
+ end\r
+ local node = get_node(name, nodename)\r
+ if not node then return nil end\r
+ height = tonumber(height)\r
+ return math.ceil(((height * 2 + 1) ^ 2) * height / 3)\r
+end\r
+ \r
+minetest.register_chatcommand("/hollowpyramid", {\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 hollow pyramid centered at WorldEdit position 1 along the x/y/z/? axis with height <height>, composed of <node>",\r
privs = {worldedit=true},\r
func = safe_region(function(name, param)\r
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")\r
height = height * sign\r
end\r
local node = get_node(name, nodename)\r
- local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)\r
+ local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node, true)\r
worldedit.player_notify(name, count .. " nodes added")\r
- end,\r
- function(name, param)\r
- if worldedit.pos1[name] == nil then\r
- worldedit.player_notify(name, "no position 1 selected")\r
- return nil\r
- end\r
+ end, check_pyramid),\r
+})\r
+\r
+minetest.register_chatcommand("/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
+ privs = {worldedit=true},\r
+ func = safe_region(function(name, param)\r
local found, _, axis, height, nodename = param:find("^([xyz%?])%s+([+-]?%d+)%s+(.+)$")\r
- if found == nil then\r
- worldedit.player_notify(name, "invalid usage: " .. param)\r
- return nil\r
+ height = tonumber(height)\r
+ if axis == "?" then\r
+ axis, sign = worldedit.player_axis(name)\r
+ height = height * sign\r
end\r
local node = get_node(name, nodename)\r
- if not node then return nil end\r
- height = tonumber(height)\r
- return math.ceil(((height * 2 + 1) ^ 2) * height / 3)\r
- end),\r
+ local count = worldedit.pyramid(worldedit.pos1[name], axis, height, node)\r
+ worldedit.player_notify(name, count .. " nodes added")\r
+ end, check_pyramid),\r
})\r
\r
minetest.register_chatcommand("/spiral", {\r
end\r
local node = get_node(name, nodename)\r
if not node then return nil end\r
- return check_region(name, param)\r
+ return 1 -- TODO: return an useful value\r
end),\r
})\r
\r
description = "Executes <code> as a Lua chunk in the global namespace",\r
privs = {worldedit=true, server=true},\r
func = function(name, param)\r
- local admin = minetest.setting_get("name")\r
- if not admin or not name == admin then\r
- worldedit.player_notify(name, "this command can only be run by the server administrator")\r
- return\r
- end\r
local err = worldedit.lua(param)\r
if err then\r
worldedit.player_notify(name, "code error: " .. err)\r
+ minetest.log("action", name.." tried to execute "..param)\r
else\r
worldedit.player_notify(name, "code successfully executed", false)\r
+ minetest.log("action", name.." executed "..param)\r
end\r
end,\r
})\r
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
privs = {worldedit=true, server=true},\r
func = safe_region(function(name, param)\r
- local admin = minetest.setting_get("name")\r
- if not admin or not name == admin then\r
- worldedit.player_notify(name, "this command can only be run by the server administrator")\r
- return\r
- end\r
-\r
local err = worldedit.luatransform(worldedit.pos1[name], worldedit.pos2[name], param)\r
if err then\r
worldedit.player_notify(name, "code error: " .. err, false)\r
+ minetest.log("action", name.." tried to execute luatransform "..param)\r
else\r
worldedit.player_notify(name, "code successfully executed", false)\r
+ minetest.log("action", name.." executed luatransform "..param)\r
end\r
end),\r
})\r
return\r
end\r
for k,v in pairs(problist) do\r
- local prob = math.floor(((v["prob"] / 256) * 100) * 100 + 0.5) / 100\r
- text = text .. minetest.pos_to_string(v["pos"]) .. ": " .. prob .. "% | "\r
+ local prob = math.floor(((v.prob / 256) * 100) * 100 + 0.5) / 100\r
+ text = text .. minetest.pos_to_string(v.pos) .. ": " .. prob .. "% | "\r
end\r
worldedit.player_notify(name, "currently set node probabilities:")\r
worldedit.player_notify(name, text)\r
end,\r
})\r
\r
-minetest.register_on_player_receive_fields(\r
- function(player, formname, fields)\r
- if (formname == "prob_val_enter") and (fields.text ~= "") then\r
- local name = player:get_player_name()\r
- local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}\r
- local index = table.getn(worldedit.prob_list[name]) + 1\r
- worldedit.prob_list[name][index] = prob_entry\r
- end\r
+minetest.register_on_player_receive_fields(function(player, formname, fields)\r
+ if formname == "prob_val_enter" and not (fields.text == "" or fields.text == nil) then\r
+ local name = player:get_player_name()\r
+ local prob_entry = {pos=worldedit.prob_pos[name], prob=tonumber(fields.text)}\r
+ local index = table.getn(worldedit.prob_list[name]) + 1\r
+ worldedit.prob_list[name][index] = prob_entry\r
end\r
-)\r
+end)\r
\r
minetest.register_chatcommand("/clearobjects", {\r
params = "",\r
local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name])\r
worldedit.player_notify(name, count .. " objects cleared")\r
end),\r
-})
\ No newline at end of file
+})\r