diff --git a/README.md b/README.md index c6d8c84..2b9707b 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,26 @@ # Minetest-WorldEditAdditions -Extra tools and commands to extend WorldEdit for Minetest +> Extra tools and commands to extend WorldEdit for Minetest + +## Current commands: + +### `//floodfill [ []]` +Floods all connected nodes of the same type starting at _pos1_ with (which defaults to `water_source`), in a sphere with a radius of (which defaults to 50). + +``` +//floodfill +//floodfill water_source 50 +//floodfill glass 25 +``` + +### `//overlay ` +Places in the last contiguous air space encountered above the first non-air node. In other words, overlays all top-most nodes in the specified area with . + +Will also work in caves, as it scans columns of nodes from top to bottom, skipping every non-air node until it finds one - and only then will it start searching for a node to place the target node on top of. + +Note that all-air columns are skipped - so if you experience issues with it not overlaying correctly, try `//expand down 1` to add an extra node's space to your defined region. + +``` +//overlay grass +//overlay glass +//overlay grass_with_dirt +``` diff --git a/worldeditadditions/floodfill.lua b/worldeditadditions/floodfill.lua index 4877e84..efe25f2 100644 --- a/worldeditadditions/floodfill.lua +++ b/worldeditadditions/floodfill.lua @@ -47,7 +47,9 @@ function worldedit.floodfill(start_pos, radius, replace_node) -- Calculate the area we want to modify local pos1 = vector.add(start_pos, { x = radius, y = 0, z = radius }) local pos2 = vector.subtract(start_pos, { x = radius, y = radius, z = radius }) - pos1, pos2 = worldedit.sort_pos(pos1, pos2) -- Just in case + pos1, pos2 = worldedit.sort_pos(pos1, pos2) + -- pos2 will always have the highest co-ordinates now + -- Fetch the nodes in the specified area local manip, area = worldedit.manip_helpers.init(pos1, pos2) diff --git a/worldeditadditions/init.lua b/worldeditadditions/init.lua index 7f72611..f4d66f1 100644 --- a/worldeditadditions/init.lua +++ b/worldeditadditions/init.lua @@ -9,3 +9,4 @@ worldeditadditions = {} dofile(minetest.get_modpath("worldeditadditions") .. "/utils.lua") dofile(minetest.get_modpath("worldeditadditions") .. "/floodfill.lua") +dofile(minetest.get_modpath("worldeditadditions") .. "/overlay.lua") diff --git a/worldeditadditions/overlay.lua b/worldeditadditions/overlay.lua new file mode 100644 index 0000000..6b6a9e1 --- /dev/null +++ b/worldeditadditions/overlay.lua @@ -0,0 +1,52 @@ +--- Overlap command. Places a specified node on top of +-- @module worldeditadditions.overlay + +function worldedit.overlay(pos1, pos2, target_node) + pos1, pos2 = worldedit.sort_pos(pos1, pos2) + -- pos2 will always have the highest co-ordinates now + + -- Fetch the nodes in the specified area + local manip, area = worldedit.manip_helpers.init(pos1, pos2) + local data = manip:get_data() + + local node_id_air = minetest.get_content_id("air") + local node_id_target = minetest.get_content_id(target_node) + + minetest.log("action", "pos1: " .. worldeditadditions.vector.tostring(pos1)) + minetest.log("action", "pos2: " .. worldeditadditions.vector.tostring(pos2)) + + -- z y x is the preferred loop order, but that isn't really possible here + + local changes = { updated = 0, skipped_columns = 0 } + for z = pos2.z, pos1.z, -1 do + for x = pos2.x, pos1.x, -1 do + local found_air = false + local placed_node = false + + for y = pos2.y, pos1.y, -1 do + if data[area:index(x, y, z)] ~= node_id_air then + if found_air then + -- We've found an air block previously, so it must be above us + -- Replace the node above us with the target block + data[area:index(x, y + 1, z)] = node_id_target + changes.updated = changes.updated + 1 + placed_node = true + break -- Move on to the next column + end + else + found_air = true + end + end + + if not placed_node then + changes.skipped_columns = changes.skipped_columns + 1 + end + end + end + + + -- Save the modified nodes back to disk & return + worldedit.manip_helpers.finish(manip, data) + + return changes +end diff --git a/worldeditadditions_commands/init.lua b/worldeditadditions_commands/init.lua index 9e49b55..91cfee4 100644 --- a/worldeditadditions_commands/init.lua +++ b/worldeditadditions_commands/init.lua @@ -25,8 +25,8 @@ local function parse_params_floodfill(params_text) end minetest.register_chatcommand("/floodfill", { - params = " []", - description = "Floods all connected nodes of the same type starting at pos1 with , in a box-shape with a radius of , which defaults to 50.", + params = "[ []]", + description = "Floods all connected nodes of the same type starting at pos1 with (which defaults to `water_source`), in a sphere with a radius of (which defaults to 50).", privs = { worldedit = true }, func = safe_region(function(name, params_text) local replace_node, radius = parse_params_floodfill(params_text) @@ -44,7 +44,41 @@ minetest.register_chatcommand("/floodfill", { minetest.log("action", name .. " used floodfill at " .. worldeditadditions.vector.tostring(worldedit.pos1[name]) .. ", replacing " .. nodes_replaced .. " nodes in " .. time_taken .. "s") end, function(name, params_text) local replace_node, radius = parse_params_floodfill(params_text) - - return math.ceil(((4 * math.pi * (tonumber(radius) ^ 3)) / 3) / 2) -- Volume of a hemisphere + -- Volume of a hemisphere + return math.ceil(((4 * math.pi * (tonumber(radius) ^ 3)) / 3) / 2) + end) +}) + + +minetest.register_chatcommand("/overlay", { + params = "", + description = "Places in the last contiguous air space encountered above the first non-air node. In other words, overlays all top-most nodes in the specified area with .", + privs = { worldedit = true }, + func = safe_region(function(name, params_text) + local target_node = worldedit.normalize_nodename(params_text) + + if not target_node then + worldedit.player_notify(name, "Error: Invalid node name.") + return false + end + + local start_time = os.clock() + local changes = worldedit.overlay(worldedit.pos1[name], worldedit.pos2[name], target_node) + local time_taken = os.clock() - start_time + + worldedit.player_notify(name, changes.updated .. " nodes replaced and " .. changes.skipped_columns .. " columns skipped in " .. time_taken .. "s") + minetest.log("action", name .. " used overlay at " .. worldeditadditions.vector.tostring(worldedit.pos1[name]) .. ", replacing " .. changes.updated .. " nodes and skipping " .. changes.skipped_columns .. " columns in " .. time_taken .. "s") + end, function(name, params_text) + if not worldedit.normalize_nodename(params_text) then + worldedit.player_notify(name, "Error: Invalid node name '" .. params_text .. "'.") + end + + local pos1 = worldedit.pos1[name] + local pos2 = worldedit.pos2[name] + pos1, pos2 = worldedit.sort_pos(pos1, pos2) + + local vol = vector.subtract(pos2, pos1) + + return vol.x*vol.z end) })