minetest-mm/mods/Minetest-WorldEdit/worldedit_commands/manipulations.lua
2024-12-19 12:55:40 +01:00

366 lines
10 KiB
Lua

local S = minetest.get_translator("worldedit_commands")
local function check_region(name)
return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
end
worldedit.register_command("deleteblocks", {
params = "",
description = S("Remove all MapBlocks (16x16x16) containing the selected area from the map"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
nodes_needed = check_region,
func = function(name)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local success = minetest.delete_area(pos1, pos2)
if success then
return true, S("Area deleted.")
else
return false, S("There was an error during deletion of the area.")
end
end,
})
worldedit.register_command("clearobjects", {
params = "",
description = S("Clears all objects within the WorldEdit region"),
category = S("Node manipulation"), -- not really, but it doesn't fit anywhere else
privs = {worldedit=true},
require_pos = 2,
nodes_needed = check_region,
func = function(name)
local count = worldedit.clear_objects(worldedit.pos1[name], worldedit.pos2[name])
return true, S("@1 objects cleared", count)
end,
})
worldedit.register_command("set", {
params = "<node>",
description = S("Set the current WorldEdit region to <node>"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
local node = worldedit.normalize_nodename(param)
if not node then
return false, S("invalid node name: @1", param)
end
return true, node
end,
nodes_needed = check_region,
func = function(name, node)
local count = worldedit.set(worldedit.pos1[name], worldedit.pos2[name], node)
return true, S("@1 nodes set", count)
end,
})
worldedit.register_command("param2", {
params = "<param2>",
description = S("Set param2 of all nodes in the current WorldEdit region to <param2>"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
local param2 = tonumber(param)
if not param2 then
return false
elseif param2 < 0 or param2 > 255 then
return false, S("Param2 is out of range (must be between 0 and 255 inclusive!)")
end
return true, param2
end,
nodes_needed = check_region,
func = function(name, param2)
local count = worldedit.set_param2(worldedit.pos1[name], worldedit.pos2[name], param2)
return true, S("@1 nodes altered", count)
end,
})
worldedit.register_command("mix", {
params = "<node1> [count1] <node2> [count2] ...",
description = S("Fill the current WorldEdit region with a random mix of <node1>, ..."),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
local nodes = {}
for nodename in param:gmatch("[^%s]+") do
if tonumber(nodename) ~= nil and #nodes > 0 then
local last_node = nodes[#nodes]
for i = 1, tonumber(nodename) do
nodes[#nodes + 1] = last_node
end
else
local node = worldedit.normalize_nodename(nodename)
if not node then
return false, S("invalid node name: @1", nodename)
end
nodes[#nodes + 1] = node
end
end
if #nodes == 0 then
return false
end
return true, nodes
end,
nodes_needed = check_region,
func = function(name, nodes)
local pos1, pos2 = worldedit.pos1[name], worldedit.pos2[name]
local count = worldedit.set(pos1, pos2, nodes)
return true, S("@1 nodes set", count)
end,
})
local check_replace = function(param)
local found, _, searchnode, replacenode = param:find("^([^%s]+)%s+(.+)$")
if found == nil then
return false
end
local newsearchnode = worldedit.normalize_nodename(searchnode)
if not newsearchnode then
return false, S("invalid search node name: @1", searchnode)
end
local newreplacenode = worldedit.normalize_nodename(replacenode)
if not newreplacenode then
return false, S("invalid replace node name: @1", replacenode)
end
return true, newsearchnode, newreplacenode
end
worldedit.register_command("replace", {
params = "<search node> <replace node>",
description = S("Replace all instances of <search node> with <replace node> in the current WorldEdit region"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
parse = check_replace,
nodes_needed = check_region,
func = function(name, search_node, replace_node)
local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
search_node, replace_node)
return true, S("@1 nodes replaced", count)
end,
})
worldedit.register_command("replaceinverse", {
params = "<search node> <replace node>",
description = S("Replace all nodes other than <search node> with <replace node> in the current WorldEdit region"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
parse = check_replace,
nodes_needed = check_region,
func = function(name, search_node, replace_node)
local count = worldedit.replace(worldedit.pos1[name], worldedit.pos2[name],
search_node, replace_node, true)
return true, S("@1 nodes replaced", count)
end,
})
worldedit.register_command("fixlight", {
params = "",
description = S("Fix the lighting in the current WorldEdit region"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
nodes_needed = check_region,
func = function(name)
local count = worldedit.fixlight(worldedit.pos1[name], worldedit.pos2[name])
return true, S("@1 nodes updated", count)
end,
})
local drain_cache
local function drain(pos1, pos2)
if drain_cache == nil then
drain_cache = {}
for name, d in pairs(minetest.registered_nodes) do
if d.drawtype == "liquid" or d.drawtype == "flowingliquid" then
drain_cache[name] = true
end
end
end
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
local count = 0
local get_node, remove_node = minetest.get_node, minetest.remove_node
for x = pos1.x, pos2.x do
for y = pos1.y, pos2.y do
for z = pos1.z, pos2.z do
local p = vector.new(x, y, z)
local n = get_node(p).name
if drain_cache[n] then
remove_node(p)
count = count + 1
end
end
end
end
return count
end
worldedit.register_command("drain", {
params = "",
description = S("Remove any fluid node within the current WorldEdit region"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
nodes_needed = check_region,
func = function(name)
local count = drain(worldedit.pos1[name], worldedit.pos2[name])
return true, S("@1 nodes updated", count)
end,
})
local clearcut_cache
local function clearcut(pos1, pos2)
-- decide which nodes we consider plants
if clearcut_cache == nil then
clearcut_cache = {}
for name, def in pairs(minetest.registered_nodes) do
local groups = def.groups or {}
if (
-- the groups say so
groups.flower or groups.grass or groups.flora or groups.plant or
groups.leaves or groups.tree or groups.leafdecay or groups.sapling or
-- drawtype heuristic
(def.is_ground_content and def.buildable_to and
(def.sunlight_propagates or not def.walkable)
and def.drawtype == "plantlike") or
-- if it's flammable, it probably needs to go too
(def.is_ground_content and not def.walkable and groups.flammable)
) then
clearcut_cache[name] = true
end
end
end
local plants = clearcut_cache
local count = 0
local prev, any
local get_node, remove_node = minetest.get_node, minetest.remove_node
for x = pos1.x, pos2.x do
for z = pos1.z, pos2.z do
prev = false
any = false
-- first pass: remove floating nodes that would be left over
for y = pos1.y, pos2.y do
local pos = vector.new(x, y, z)
local n = get_node(pos).name
if plants[n] then
prev = true
any = true
elseif prev then
local def = minetest.registered_nodes[n] or {}
local groups = def.groups or {}
if groups.attached_node or (def.buildable_to and groups.falling_node) then
remove_node(pos)
count = count + 1
else
prev = false
end
end
end
-- second pass: remove plants, top-to-bottom to avoid item drops
if any then
for y = pos2.y, pos1.y, -1 do
local pos = vector.new(x, y, z)
local n = get_node(pos).name
if plants[n] then
remove_node(pos)
count = count + 1
end
end
end
end
end
return count
end
worldedit.register_command("clearcut", {
params = "",
description = S("Remove any plant, tree or foliage-like nodes in the selected region"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
nodes_needed = check_region,
func = function(name)
local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
local count = clearcut(pos1, pos2)
return true, S("@1 nodes removed", count)
end,
})
worldedit.register_command("hide", {
params = "",
description = S("Hide all nodes in the current WorldEdit region non-destructively"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
nodes_needed = check_region,
func = function(name)
local count = worldedit.hide(worldedit.pos1[name], worldedit.pos2[name])
return true, S("@1 nodes hidden", count)
end,
})
worldedit.register_command("suppress", {
params = "<node>",
description = S("Suppress all <node> in the current WorldEdit region non-destructively"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
local node = worldedit.normalize_nodename(param)
if not node then
return false, S("invalid node name: @1", param)
end
return true, node
end,
nodes_needed = check_region,
func = function(name, node)
local count = worldedit.suppress(worldedit.pos1[name], worldedit.pos2[name], node)
return true, S("@1 nodes suppressed", count)
end,
})
worldedit.register_command("highlight", {
params = "<node>",
description = S("Highlight <node> in the current WorldEdit region by hiding everything else non-destructively"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
parse = function(param)
local node = worldedit.normalize_nodename(param)
if not node then
return false, S("invalid node name: @1", param)
end
return true, node
end,
nodes_needed = check_region,
func = function(name, node)
local count = worldedit.highlight(worldedit.pos1[name], worldedit.pos2[name], node)
return true, S("@1 nodes highlighted", count)
end,
})
worldedit.register_command("restore", {
params = "",
description = S("Restores nodes hidden with WorldEdit in the current WorldEdit region"),
category = S("Node manipulation"),
privs = {worldedit=true},
require_pos = 2,
nodes_needed = check_region,
func = function(name)
local count = worldedit.restore(worldedit.pos1[name], worldedit.pos2[name])
return true, S("@1 nodes restored", count)
end,
})