mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-11-26 09:03:01 +00:00
Upgrade //overlay to support a mix of nodes
This commit is contained in:
parent
435964daec
commit
e26d5b1580
7 changed files with 102 additions and 27 deletions
12
README.md
12
README.md
|
@ -8,7 +8,7 @@ If you can dream of it, it probably belongs here!
|
||||||
## Quick Command Reference
|
## Quick Command Reference
|
||||||
|
|
||||||
- [`//floodfill [<replace_node> [<radius>]]`](#floodfill-replace_node-radius-floodfill)
|
- [`//floodfill [<replace_node> [<radius>]]`](#floodfill-replace_node-radius-floodfill)
|
||||||
- [`//overlay <node_name>`](#overlay-node_name)
|
- [`//overlay <node_name_a> [<chance_a>] <node_name_b> [<chance_b>] [<node_name_N> [<chance_N>]] ...`](#overlay-node_name)
|
||||||
- [`//ellipsoid <rx> <ry> <rz> <node_name>`](#ellipsoid-rx-ry-rz-node_name)
|
- [`//ellipsoid <rx> <ry> <rz> <node_name>`](#ellipsoid-rx-ry-rz-node_name)
|
||||||
- [`//hollowellipsoid <rx> <ry> <rz> <node_name>`](#hollowellipsoid-rx-ry-rz-node_name)
|
- [`//hollowellipsoid <rx> <ry> <rz> <node_name>`](#hollowellipsoid-rx-ry-rz-node_name)
|
||||||
- [`//torus <major_radius> <minor_radius> <node_name>`](#torus-major_radius-minor_radius-node_name)
|
- [`//torus <major_radius> <minor_radius> <node_name>`](#torus-major_radius-minor_radius-node_name)
|
||||||
|
@ -35,19 +35,25 @@ Floods all connected nodes of the same type starting at _pos1_ with <replace_nod
|
||||||
//floodfill glass 25
|
//floodfill glass 25
|
||||||
```
|
```
|
||||||
|
|
||||||
### `//overlay <node_name>`
|
### `//overlay <node_name_a> [<chance_a>] <node_name_b> [<chance_b>] [<node_name_N> [<chance_N>]] ...`
|
||||||
Places <replace_node> 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 <replace_node>.
|
Places `<node_name_a>` 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 `<node_name_a>`. Optionally supports a mix of node names and chances, as `//mix` (WorldEdit) and `//replacemix` (WorldEditAdditions) does.
|
||||||
|
|
||||||
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.
|
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.
|
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.
|
||||||
|
|
||||||
|
Note also that columns without any air nodes in them at all are also skipped, so try `//expand y 1` to add an extra layer to your defined region.
|
||||||
|
|
||||||
```
|
```
|
||||||
//overlay grass
|
//overlay grass
|
||||||
//overlay glass
|
//overlay glass
|
||||||
//overlay grass_with_dirt
|
//overlay grass_with_dirt
|
||||||
|
//overlay grass_with_dirt 10 dirt
|
||||||
|
//overlay grass_with_dirt 10 dirt 2 sand 1
|
||||||
|
//overlay sandstone dirt 2 sand 5
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
### `//ellipsoid <rx> <ry> <rz> <node_name>`
|
### `//ellipsoid <rx> <ry> <rz> <node_name>`
|
||||||
Creates a solid ellipsoid at position 1 with the radius `(rx, ry, rz)`.
|
Creates a solid ellipsoid at position 1 with the radius `(rx, ry, rz)`.
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ worldeditadditions = {}
|
||||||
worldeditadditions.modpath = minetest.get_modpath("worldeditadditions")
|
worldeditadditions.modpath = minetest.get_modpath("worldeditadditions")
|
||||||
dofile(worldeditadditions.modpath.."/utils/strings.lua")
|
dofile(worldeditadditions.modpath.."/utils/strings.lua")
|
||||||
dofile(worldeditadditions.modpath.."/utils/numbers.lua")
|
dofile(worldeditadditions.modpath.."/utils/numbers.lua")
|
||||||
|
dofile(worldeditadditions.modpath.."/utils/nodes.lua")
|
||||||
|
|
||||||
dofile(worldeditadditions.modpath.."/utils.lua")
|
dofile(worldeditadditions.modpath.."/utils.lua")
|
||||||
dofile(worldeditadditions.modpath.."/lib/floodfill.lua")
|
dofile(worldeditadditions.modpath.."/lib/floodfill.lua")
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
--- Overlap command. Places a specified node on top of each column.
|
--- Overlap command. Places a specified node on top of each column.
|
||||||
-- @module worldeditadditions.overlay
|
-- @module worldeditadditions.overlay
|
||||||
|
|
||||||
function worldeditadditions.overlay(pos1, pos2, target_node)
|
function worldeditadditions.overlay(pos1, pos2, node_weights)
|
||||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||||
-- pos2 will always have the highest co-ordinates now
|
-- pos2 will always have the highest co-ordinates now
|
||||||
|
|
||||||
|
@ -10,7 +10,9 @@ function worldeditadditions.overlay(pos1, pos2, target_node)
|
||||||
local data = manip:get_data()
|
local data = manip:get_data()
|
||||||
|
|
||||||
local node_id_air = minetest.get_content_id("air")
|
local node_id_air = minetest.get_content_id("air")
|
||||||
local node_id_target = minetest.get_content_id(target_node)
|
local node_id_ignore = minetest.get_content_id("ignore")
|
||||||
|
|
||||||
|
local node_ids, node_ids_count = worldeditadditions.make_weighted(node_weights)
|
||||||
|
|
||||||
-- minetest.log("action", "pos1: " .. worldeditadditions.vector.tostring(pos1))
|
-- minetest.log("action", "pos1: " .. worldeditadditions.vector.tostring(pos1))
|
||||||
-- minetest.log("action", "pos2: " .. worldeditadditions.vector.tostring(pos2))
|
-- minetest.log("action", "pos2: " .. worldeditadditions.vector.tostring(pos2))
|
||||||
|
@ -24,16 +26,26 @@ function worldeditadditions.overlay(pos1, pos2, target_node)
|
||||||
local placed_node = false
|
local placed_node = false
|
||||||
|
|
||||||
for y = pos2.y, pos1.y, -1 do
|
for y = pos2.y, pos1.y, -1 do
|
||||||
if data[area:index(x, y, z)] ~= node_id_air then
|
local i = area:index(x, y, z)
|
||||||
|
|
||||||
|
local is_air = data[i] == node_id_air
|
||||||
|
if not is_air then -- wielded_light nodes are airlike too
|
||||||
|
local this_node_name = minetest.get_name_from_content_id(data[i])
|
||||||
|
is_air = is_air or worldeditadditions.string_starts(this_node_name, "wielded_light")
|
||||||
|
end
|
||||||
|
local is_ignore = data[i] == node_id_ignore
|
||||||
|
|
||||||
|
if not is_air and not is_ignore then
|
||||||
if found_air then
|
if found_air then
|
||||||
|
local new_id = node_ids[math.random(node_ids_count)]
|
||||||
-- We've found an air block previously, so it must be above us
|
-- We've found an air block previously, so it must be above us
|
||||||
-- Replace the node above us with the target block
|
-- Replace the node above us with the target block
|
||||||
data[area:index(x, y + 1, z)] = node_id_target
|
data[area:index(x, y + 1, z)] = new_id
|
||||||
changes.updated = changes.updated + 1
|
changes.updated = changes.updated + 1
|
||||||
placed_node = true
|
placed_node = true
|
||||||
break -- Move on to the next column
|
break -- Move on to the next column
|
||||||
end
|
end
|
||||||
else
|
elseif not is_ignore then
|
||||||
found_air = true
|
found_air = true
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -22,18 +22,10 @@ function worldeditadditions.replacemix(pos1, pos2, target_node, target_node_chan
|
||||||
local distribution = {}
|
local distribution = {}
|
||||||
|
|
||||||
-- Generate the list of node ids
|
-- Generate the list of node ids
|
||||||
local node_ids_replace = {}
|
local node_ids_replace, node_ids_replace_count = worldeditadditions.make_weighted(replacements)
|
||||||
for node_name, weight in pairs(replacements) do
|
for node_name, weight in pairs(replacements) do
|
||||||
local next_id = minetest.get_content_id(node_name)
|
distribution[minetest.get_content_id(node_name)] = 0
|
||||||
for i = 1, weight do
|
|
||||||
table.insert(
|
|
||||||
node_ids_replace,
|
|
||||||
next_id
|
|
||||||
)
|
|
||||||
end
|
end
|
||||||
distribution[next_id] = 0
|
|
||||||
end
|
|
||||||
local node_ids_replace_count = #node_ids_replace
|
|
||||||
|
|
||||||
for i in area:iterp(pos1, pos2) do
|
for i in area:iterp(pos1, pos2) do
|
||||||
|
|
||||||
|
|
16
worldeditadditions/utils/nodes.lua
Normal file
16
worldeditadditions/utils/nodes.lua
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
--- Makes an associative table of node_name => weight into a list of node ids.
|
||||||
|
-- Node names with a heigher weight are repeated more times.
|
||||||
|
function worldeditadditions.make_weighted(tbl)
|
||||||
|
local result = {}
|
||||||
|
for node_name, weight in pairs(tbl) do
|
||||||
|
local next_id = minetest.get_content_id(node_name)
|
||||||
|
print("[make_weighted] seen "..node_name.." @ weight "..weight.." → id "..next_id)
|
||||||
|
for i = 1, weight do
|
||||||
|
table.insert(
|
||||||
|
result,
|
||||||
|
next_id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return result, #result
|
||||||
|
end
|
|
@ -73,6 +73,10 @@ function worldeditadditions.str_padstart(str, len, char)
|
||||||
return string.rep(char, len - #str) .. str
|
return string.rep(char, len - #str) .. str
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function worldeditadditions.string_starts(str,start)
|
||||||
|
return string.sub(str,1,string.len(start))==start
|
||||||
|
end
|
||||||
|
|
||||||
--- Turns an associative node_id → count table into a human-readable list.
|
--- Turns an associative node_id → count table into a human-readable list.
|
||||||
-- Works well with worldeditadditions.make_ascii_table().
|
-- Works well with worldeditadditions.make_ascii_table().
|
||||||
function worldeditadditions.node_distribution_to_list(distribution, nodes_total)
|
function worldeditadditions.node_distribution_to_list(distribution, nodes_total)
|
||||||
|
@ -122,3 +126,48 @@ function worldeditadditions.make_ascii_table(data, total)
|
||||||
-- TODO: Add multi-column support here
|
-- TODO: Add multi-column support here
|
||||||
return table.concat(result, "\n")
|
return table.concat(result, "\n")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function worldeditadditions.parse_weighted_nodes(parts)
|
||||||
|
local MODE_EITHER = 1
|
||||||
|
local MODE_NODE = 2
|
||||||
|
|
||||||
|
local result = {}
|
||||||
|
local mode = MODE_NODE
|
||||||
|
local last_node_name = nil
|
||||||
|
for i, part in ipairs(parts) do
|
||||||
|
print("i: "..i..", part: "..part)
|
||||||
|
if mode == MODE_NODE then
|
||||||
|
print("mode: node");
|
||||||
|
local next = worldedit.normalize_nodename(part)
|
||||||
|
if not next then
|
||||||
|
return false, "Error: Invalid node name '"..part.."'"
|
||||||
|
end
|
||||||
|
last_node_name = next
|
||||||
|
mode = MODE_EITHER
|
||||||
|
elseif mode == MODE_EITHER then
|
||||||
|
print("mode: either");
|
||||||
|
local chance = tonumber(part)
|
||||||
|
if not chance then
|
||||||
|
local node_name = worldedit.normalize_nodename(part)
|
||||||
|
if not node_name then
|
||||||
|
return false, "Error: Invalid number '"..chance.."'"
|
||||||
|
end
|
||||||
|
if last_node_name then
|
||||||
|
result[last_node_name] = 1
|
||||||
|
end
|
||||||
|
last_node_name = node_name
|
||||||
|
mode = MODE_EITHER
|
||||||
|
else
|
||||||
|
result[last_node_name] = math.floor(chance)
|
||||||
|
mode = MODE_NODE
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if last_node_name then
|
||||||
|
result[last_node_name] = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
return true, result
|
||||||
|
end
|
||||||
|
|
|
@ -4,25 +4,24 @@
|
||||||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||||
-- ██████ ████ ███████ ██ ██ ███████ ██ ██ ██
|
-- ██████ ████ ███████ ██ ██ ███████ ██ ██ ██
|
||||||
worldedit.register_command("overlay", {
|
worldedit.register_command("overlay", {
|
||||||
params = "<replace_node>",
|
params = "<replace_node_a> [<chance_a>] <replace_node_b> [<chance_b>] [<replace_node_N> [<chance_N>]] ...",
|
||||||
description = "Places <replace_node> 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 <replace_node>.",
|
description = "Places <replace_node_a> 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 <replace_node_a>. Optionally supports a mix of nodes and chances, as in //mix and //replacemix.",
|
||||||
privs = { worldedit = true },
|
privs = { worldedit = true },
|
||||||
require_pos = 2,
|
require_pos = 2,
|
||||||
parse = function(params_text)
|
parse = function(params_text)
|
||||||
local target_node = worldedit.normalize_nodename(params_text)
|
local success, node_list = worldeditadditions.parse_weighted_nodes(
|
||||||
if not target_node then
|
worldeditadditions.split(params_text, "%s+", false)
|
||||||
return false, "Error: Invalid node name"
|
)
|
||||||
end
|
return success, node_list
|
||||||
return true, target_node
|
|
||||||
end,
|
end,
|
||||||
nodes_needed = function(name)
|
nodes_needed = function(name)
|
||||||
-- //overlay only modifies up to 1 node per column in the selected region
|
-- //overlay only modifies up to 1 node per column in the selected region
|
||||||
local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
|
local pos1, pos2 = worldedit.sort_pos(worldedit.pos1[name], worldedit.pos2[name])
|
||||||
return (pos2.x - pos1.x) * (pos2.y - pos1.y)
|
return (pos2.x - pos1.x) * (pos2.y - pos1.y)
|
||||||
end,
|
end,
|
||||||
func = function(name, target_node)
|
func = function(name, node_list)
|
||||||
local start_time = os.clock()
|
local start_time = os.clock()
|
||||||
local changes = worldeditadditions.overlay(worldedit.pos1[name], worldedit.pos2[name], target_node)
|
local changes = worldeditadditions.overlay(worldedit.pos1[name], worldedit.pos2[name], node_list)
|
||||||
local time_taken = os.clock() - start_time
|
local time_taken = os.clock() - start_time
|
||||||
|
|
||||||
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")
|
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")
|
||||||
|
|
Loading…
Reference in a new issue