mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-11-22 15:33:00 +00:00
//replacemix: add command
This commit is contained in:
parent
47514a5748
commit
c0b40266c8
6 changed files with 228 additions and 2 deletions
52
README.md
52
README.md
|
@ -18,8 +18,9 @@ If you can dream of it, it probably belongs here!
|
||||||
- [`//maze3d <replace_node> [<path_length> [<path_width> [<path_depth> [<seed>]]]]`](#maze3d-replace_node-seed)
|
- [`//maze3d <replace_node> [<path_length> [<path_width> [<path_depth> [<seed>]]]]`](#maze3d-replace_node-seed)
|
||||||
- [`//bonemeal [<strength> [<chance>]]`](#bonemeal-strength-chance)
|
- [`//bonemeal [<strength> [<chance>]]`](#bonemeal-strength-chance)
|
||||||
- [`//walls <replace_node>`](#walls-replace_node)
|
- [`//walls <replace_node>`](#walls-replace_node)
|
||||||
|
- [`//replacemix <target_node> [<chance>] <replace_node_a> [<chance_a>] [<replace_node_b> [<chance_b>]] [<replace_node_N> [<chance_N>]] ....`]
|
||||||
- [`//count`](#count)
|
- [`//count`](#count)
|
||||||
- [`//multi <command_a> <command_b> .....`](#multi-command_a-command_b-command_c-)
|
- [`//multi <command_a> <command_b> ....`](#multi-command_a-command_b-command_c-)
|
||||||
- [`//y`](#y)
|
- [`//y`](#y)
|
||||||
- [`//n`](#n)
|
- [`//n`](#n)
|
||||||
|
|
||||||
|
@ -141,6 +142,55 @@ Creates vertical walls of `<replace_node>` around the inside edges of the define
|
||||||
//walls goldblock
|
//walls goldblock
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `//replacemix <target_node> [<chance>] <replace_node_a> [<chance_a>] [<replace_node_b> [<chance_b>]] [<replace_node_N> [<chance_N>]] ...`
|
||||||
|
Replaces a given node with a random mix of other nodes. Functions like `//mix`.
|
||||||
|
|
||||||
|
This command is best explained with examples:
|
||||||
|
|
||||||
|
```
|
||||||
|
//replacemix dirt stone
|
||||||
|
```
|
||||||
|
|
||||||
|
The above functions just like `//replace` - nothing special going on here. It replaces all `dirt` nodes with `stone`.
|
||||||
|
|
||||||
|
Let's make it more interesting:
|
||||||
|
|
||||||
|
```
|
||||||
|
//replacemix dirt 5 stone
|
||||||
|
```
|
||||||
|
|
||||||
|
The above replaces 1 in every 5 `dirt` nodes with `stone`. Let's get even fancier:
|
||||||
|
|
||||||
|
```
|
||||||
|
//replacemix stone stone_with_diamond stone_with_gold
|
||||||
|
```
|
||||||
|
|
||||||
|
The above replaces `stone` nodes with a random mix of `stone_with_diamond` and `stone_with_gold` nodes. But wait - there's more!
|
||||||
|
|
||||||
|
```
|
||||||
|
//replacemix stone stone_with_diamond stone_with_gold 4
|
||||||
|
```
|
||||||
|
|
||||||
|
The above replaces `stone` nodes with a random mix of `stone_with_diamond` and `stone_with_gold` nodes as before, but this time in the ratio 1:4 (i.e. for every `stone_with_diamond` node there will be 4 `stone_with_gold` nodes). Note that the `1` for `stone_with_diamond` is implicit there.
|
||||||
|
|
||||||
|
If we wanted to put all of the above features together into a single command, then we might do this:
|
||||||
|
|
||||||
|
```
|
||||||
|
//replacemix dirt 3 sandstone 10 dry_dirt cobble 2
|
||||||
|
```
|
||||||
|
|
||||||
|
The above replaces 1 in 3 `dirt` nodes with a mix of `sandstone`, `dry_dirt`, and `cobble` nodes in the ratio 10:1:2. Awesome!
|
||||||
|
|
||||||
|
Here are all the above examples together:
|
||||||
|
|
||||||
|
```
|
||||||
|
//replacemix dirt stone
|
||||||
|
//replacemix dirt 5 stone
|
||||||
|
//replacemix stone stone_with_diamond stone_with_gold
|
||||||
|
//replacemix stone stone_with_diamond stone_with_gold 4
|
||||||
|
//replacemix dirt 3 sandstone 10 dry_dirt cobble 2
|
||||||
|
```
|
||||||
|
|
||||||
### `//count`
|
### `//count`
|
||||||
Counts all the nodes in the defined region and returns the result along with calculated percentages (note that if the chat window used a monospace font, the returned result would be a perfect table. If someone has a ~~hack~~ solution to make the columns line up neatly, please [open an issue](https://github.com/sbrl/Minetest-WorldEditAdditions/issues/new) :D)
|
Counts all the nodes in the defined region and returns the result along with calculated percentages (note that if the chat window used a monospace font, the returned result would be a perfect table. If someone has a ~~hack~~ solution to make the columns line up neatly, please [open an issue](https://github.com/sbrl/Minetest-WorldEditAdditions/issues/new) :D)
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,7 @@ dofile(worldeditadditions.modpath.."/lib/overlay.lua")
|
||||||
dofile(worldeditadditions.modpath.."/lib/ellipsoid.lua")
|
dofile(worldeditadditions.modpath.."/lib/ellipsoid.lua")
|
||||||
dofile(worldeditadditions.modpath.."/lib/torus.lua")
|
dofile(worldeditadditions.modpath.."/lib/torus.lua")
|
||||||
dofile(worldeditadditions.modpath.."/lib/walls.lua")
|
dofile(worldeditadditions.modpath.."/lib/walls.lua")
|
||||||
|
dofile(worldeditadditions.modpath.."/lib/replacemix.lua")
|
||||||
dofile(worldeditadditions.modpath.."/lib/maze2d.lua")
|
dofile(worldeditadditions.modpath.."/lib/maze2d.lua")
|
||||||
dofile(worldeditadditions.modpath.."/lib/maze3d.lua")
|
dofile(worldeditadditions.modpath.."/lib/maze3d.lua")
|
||||||
|
|
||||||
|
|
56
worldeditadditions/lib/replacemix.lua
Normal file
56
worldeditadditions/lib/replacemix.lua
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
--- Like //mix, but replaces a given node instead.
|
||||||
|
-- @module worldeditadditions.replacemix
|
||||||
|
|
||||||
|
-- ██████ ███████ ██████ ██ █████ ██████ ███████ ███ ███ ██ ██ ██
|
||||||
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██
|
||||||
|
-- ██████ █████ ██████ ██ ███████ ██ █████ ██ ████ ██ ██ ███
|
||||||
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||||
|
-- ██ ██ ███████ ██ ███████ ██ ██ ██████ ███████ ██ ██ ██ ██ ██
|
||||||
|
function worldeditadditions.replacemix(pos1, pos2, target_node, target_node_chance, replacements)
|
||||||
|
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_target = minetest.get_content_id(target_node)
|
||||||
|
|
||||||
|
-- Initialise statistics
|
||||||
|
local changed = 0
|
||||||
|
local candidates = 0
|
||||||
|
local distribution = {}
|
||||||
|
|
||||||
|
-- Generate the list of node ids
|
||||||
|
local node_ids_replace = {}
|
||||||
|
for node_name, weight in pairs(replacements) do
|
||||||
|
local next_id = minetest.get_content_id(node_name)
|
||||||
|
for i = 1, weight do
|
||||||
|
table.insert(
|
||||||
|
node_ids_replace,
|
||||||
|
next_id
|
||||||
|
)
|
||||||
|
end
|
||||||
|
distribution[next_id] = 0
|
||||||
|
end
|
||||||
|
local node_ids_replace_count = #node_ids_replace
|
||||||
|
|
||||||
|
for i in area:iterp(pos1, pos2) do
|
||||||
|
|
||||||
|
if data[i] == node_id_target then
|
||||||
|
candidates = candidates + 1
|
||||||
|
if math.random(target_node_chance) == 1 then
|
||||||
|
local next_id = node_ids_replace[math.random(node_ids_replace_count)]
|
||||||
|
data[i] = next_id
|
||||||
|
changed = changed + 1
|
||||||
|
-- We initialised this above
|
||||||
|
distribution[next_id] = distribution[next_id] + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Save the modified nodes back to disk & return
|
||||||
|
worldedit.manip_helpers.finish(manip, data)
|
||||||
|
|
||||||
|
return true, changed, candidates, distribution
|
||||||
|
end
|
|
@ -73,7 +73,27 @@ 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.make_ascii_table(data)
|
--- Turns an associative node_id → count table into a human-readable list.
|
||||||
|
-- Works well with worldeditadditions.make_ascii_table().
|
||||||
|
function worldeditadditions.node_distribution_to_list(distribution, nodes_total)
|
||||||
|
local distribution_data = {}
|
||||||
|
for node_id, count in pairs(distribution) do
|
||||||
|
table.insert(distribution_data, {
|
||||||
|
count,
|
||||||
|
tostring(worldeditadditions.round((count / nodes_total) * 100, 2)).."%",
|
||||||
|
minetest.get_name_from_content_id(node_id)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
return distribution_data
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Makes a human-readable table of data.
|
||||||
|
-- Data should be a 2D array - i.e. a table of tables. The nested tables should
|
||||||
|
-- contain a list of items for a single row.
|
||||||
|
-- If total is specified, then a grand total is printed at the bottom - this is
|
||||||
|
-- useful when you want to print a node list - works well with
|
||||||
|
-- worldeditadditions.node_distribution_to_list().
|
||||||
|
function worldeditadditions.make_ascii_table(data, total)
|
||||||
local extra_padding = 2
|
local extra_padding = 2
|
||||||
local result = {}
|
local result = {}
|
||||||
local max_lengths = {}
|
local max_lengths = {}
|
||||||
|
@ -94,6 +114,11 @@ function worldeditadditions.make_ascii_table(data)
|
||||||
result[#result+1] = table.concat(row_result, "")
|
result[#result+1] = table.concat(row_result, "")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if total then
|
||||||
|
result[#result+1] = string.rep("=", 6 + #tostring(total) + 6).."\n"..
|
||||||
|
"Total "..total.." nodes\n"
|
||||||
|
end
|
||||||
|
|
||||||
-- TODO: Add multi-column support here
|
-- TODO: Add multi-column support here
|
||||||
return table.concat(result, "\n")
|
return table.concat(result, "\n")
|
||||||
end
|
end
|
||||||
|
|
93
worldeditadditions_commands/commands/replacemix.lua
Normal file
93
worldeditadditions_commands/commands/replacemix.lua
Normal file
|
@ -0,0 +1,93 @@
|
||||||
|
local we_c = worldeditadditions_commands
|
||||||
|
|
||||||
|
-- ██████ ███████ ██████ ██ █████ ██████ ███████ ███ ███ ██ ██ ██
|
||||||
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ████ ████ ██ ██ ██
|
||||||
|
-- ██████ █████ ██████ ██ ███████ ██ █████ ██ ████ ██ ██ ███
|
||||||
|
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||||
|
-- ██ ██ ███████ ██ ███████ ██ ██ ██████ ███████ ██ ██ ██ ██ ██
|
||||||
|
worldedit.register_command("replacemix", {
|
||||||
|
params = "<target_node> [<chance>] <replace_node_a> [<chance_a>] [<replace_node_b> [<chance_b>]] [<replace_node_N> [<chance_N>]] ...",
|
||||||
|
description = "Replaces target_node with a mix of other nodes. Functions simmilarly to //mix. <chance> is optional and the chance to replace the target node at all. replace_node_a is the node to replace target_node with. If multiple nodes are specified in a space separated list, then when replacing an instance of target_node one is randomly chosen from the list. Just like with //mix, if a positive integer is present after a replace_node, that adds a weighting to that particular node making it more common.",
|
||||||
|
privs = { worldedit = true },
|
||||||
|
require_pos = 2,
|
||||||
|
parse = function(params_text)
|
||||||
|
if not params_text or params_text == "" then
|
||||||
|
return false, "Error: No arguments specified"
|
||||||
|
end
|
||||||
|
|
||||||
|
local parts = worldeditadditions.split(params_text, "%s+", false)
|
||||||
|
|
||||||
|
local target_node = nil
|
||||||
|
local target_node_chance = 1
|
||||||
|
local replace_nodes = {}
|
||||||
|
|
||||||
|
local mode = "target_node"
|
||||||
|
local last_node_name = nil
|
||||||
|
for i, part in ipairs(parts) do
|
||||||
|
if mode == "target_node" then
|
||||||
|
target_node = worldedit.normalize_nodename(part)
|
||||||
|
if not target_node then
|
||||||
|
return false, "Error: Invalid target_node name"
|
||||||
|
end
|
||||||
|
mode = "target_chance"
|
||||||
|
elseif mode == "target_chance" and tonumber(part) then
|
||||||
|
target_node_chance = tonumber(part)
|
||||||
|
mode = "replace_node"
|
||||||
|
elseif (mode == "target_chance" and not tonumber(part)) or mode == "replace_node" then
|
||||||
|
mode = "replace_node"
|
||||||
|
if tonumber(part) then
|
||||||
|
if not last_node_name then
|
||||||
|
return false, "Error: No previous node name was found (this is a probably a bug)."
|
||||||
|
end
|
||||||
|
replace_nodes[last_node_name] = math.floor(tonumber(part))
|
||||||
|
else
|
||||||
|
if last_node_name and not replace_nodes[last_node_name] then
|
||||||
|
replace_nodes[last_node_name] = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
local node_name = worldedit.normalize_nodename(part)
|
||||||
|
if not node_name then
|
||||||
|
return false, "Error: Invalid replace node name '"..part.."'."
|
||||||
|
end
|
||||||
|
last_node_name = node_name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if not replace_nodes[last_node_name] then
|
||||||
|
replace_nodes[last_node_name] = 1
|
||||||
|
end
|
||||||
|
|
||||||
|
-- We unconditionally math.floor here because when we tried to test for it directly it was unreliable
|
||||||
|
return true, target_node, target_node_chance, replace_nodes
|
||||||
|
end,
|
||||||
|
nodes_needed = function(name) -- target_node, target_node_chance, replace_nodes
|
||||||
|
return worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
|
||||||
|
end,
|
||||||
|
func = function(name, target_node, target_node_chance, replace_nodes)
|
||||||
|
local start_time = os.clock()
|
||||||
|
|
||||||
|
local success, changed, candidates, distribution = worldeditadditions.replacemix(
|
||||||
|
worldedit.pos1[name], worldedit.pos2[name],
|
||||||
|
target_node,
|
||||||
|
target_node_chance,
|
||||||
|
replace_nodes
|
||||||
|
)
|
||||||
|
if not success then
|
||||||
|
return success, changed
|
||||||
|
end
|
||||||
|
|
||||||
|
local nodes_total = worldedit.volume(worldedit.pos1[name], worldedit.pos2[name])
|
||||||
|
local percentage_replaced = worldeditadditions.round((changed / candidates)*100, 2)
|
||||||
|
local distribution_table = worldeditadditions.make_ascii_table(
|
||||||
|
worldeditadditions.node_distribution_to_list(distribution, changed),
|
||||||
|
changed
|
||||||
|
)
|
||||||
|
|
||||||
|
local time_taken = os.clock() - start_time
|
||||||
|
|
||||||
|
|
||||||
|
minetest.log("action", name .. " used //replacemix at "..worldeditadditions.vector.tostring(worldedit.pos1[name]).." - "..worldeditadditions.vector.tostring(worldedit.pos2[name])..", replacing " .. changed.." nodes (out of "..nodes_total.." nodes) in "..time_taken.."s")
|
||||||
|
|
||||||
|
return true, distribution_table..changed.." out of "..candidates.." (~"..percentage_replaced.."%) candidates replaced in "..time_taken.."s"
|
||||||
|
end
|
||||||
|
})
|
|
@ -23,6 +23,7 @@ dofile(we_c.modpath.."/commands/ellipsoid.lua")
|
||||||
dofile(we_c.modpath.."/commands/torus.lua")
|
dofile(we_c.modpath.."/commands/torus.lua")
|
||||||
dofile(we_c.modpath.."/commands/walls.lua")
|
dofile(we_c.modpath.."/commands/walls.lua")
|
||||||
dofile(we_c.modpath.."/commands/maze.lua")
|
dofile(we_c.modpath.."/commands/maze.lua")
|
||||||
|
dofile(we_c.modpath.."/commands/replacemix.lua")
|
||||||
|
|
||||||
dofile(we_c.modpath.."/commands/count.lua")
|
dofile(we_c.modpath.."/commands/count.lua")
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue