mirror of
https://github.com/sbrl/Minetest-WorldEditAdditions.git
synced 2024-08-20 05:24:20 +02:00
Finish bugfixing //forest!
moretrees still doesn't work with it though, and I don't understand why....
This commit is contained in:
parent
a6fc9712b4
commit
2a592a808c
@ -25,14 +25,15 @@ if minetest.get_modpath("default") then
|
||||
{ "default:emergent_jungle_sapling", "jungle_emergent" },
|
||||
{ "default:acacia_sapling", "acacia" },
|
||||
{ "default:acacia_bush_sapling", "acacia_bush" },
|
||||
{ "default:blueberry_bush_sapling", "blueberry_bush" }
|
||||
{ "default:blueberry_bush_sapling", "blueberry_bush" },
|
||||
-- { "default:large_cactus_seedling", "cactus" } -- Can't be bonemealed yet, but I'd guess it will be implemented eventually. It's not in the sapling category, so we won't detect it for bonemealing anyway
|
||||
})
|
||||
end
|
||||
|
||||
if minetest.get_modpath("moretrees") then
|
||||
worldeditadditions.register_sapling_alias_many({
|
||||
{ "moretrees:spruce_sapling_ongen", "spruce" },
|
||||
{ "moretrees:rubber_tree_sapling_ongen", "rubbe" },
|
||||
{ "moretrees:rubber_tree_sapling_ongen", "rubber" },
|
||||
{ "moretrees:beech_sapling_ongen", "beech" },
|
||||
{ "moretrees:jungletree_sapling_ongen", "jungle_moretrees" },
|
||||
{ "moretrees:fir_sapling_ongen", "fir" },
|
||||
@ -41,7 +42,7 @@ if minetest.get_modpath("moretrees") then
|
||||
{ "moretrees:poplar_small_sapling_ongen", "poplar_small" },
|
||||
{ "moretrees:apple_tree_sapling_ongen", "apple" },
|
||||
{ "moretrees:birch_sapling_ongen", "birch" },
|
||||
{ "moretrees:palm_sapling_ongen", "palm" },
|
||||
{ "moretrees:palm_sapling_ongen", "palm_moretrees" },
|
||||
{ "moretrees:date_palm_sapling_ongen", "palm_date" },
|
||||
{ "moretrees:sequoia_sapling_ongen", "sequoia" },
|
||||
{ "moretrees:oak_sapling_ongen", "oak_moretrees" },
|
||||
@ -57,7 +58,7 @@ end
|
||||
-- ██████ ██████ ██████ ███████ ███████ ██ ██ ██ ███████ ███████ ███████
|
||||
|
||||
if minetest.get_modpath("lemontree") then
|
||||
worldeditadditions.register_sapling_alias("lemontree:sapling", "lemontree")
|
||||
worldeditadditions.register_sapling_alias("lemontree:sapling", "lemon")
|
||||
end
|
||||
if minetest.get_modpath("pineapple") then
|
||||
worldeditadditions.register_sapling_alias("pineapple:sapling", "pineapple")
|
||||
@ -72,10 +73,10 @@ if minetest.get_modpath("birch") then
|
||||
worldeditadditions.register_sapling_alias("birch:sapling", "birch")
|
||||
end
|
||||
if minetest.get_modpath("cherrytree") then
|
||||
worldeditadditions.register_sapling_alias("cherrytree:sapling", "cherrytree")
|
||||
worldeditadditions.register_sapling_alias("cherrytree:sapling", "cherry")
|
||||
end
|
||||
if minetest.get_modpath("clementinetree") then
|
||||
worldeditadditions.register_sapling_alias("clementinetree:sapling", "clementinetree")
|
||||
worldeditadditions.register_sapling_alias("clementinetree:sapling", "clementine")
|
||||
end
|
||||
if minetest.get_modpath("ebony") then
|
||||
worldeditadditions.register_sapling_alias("ebony:sapling", "ebony")
|
||||
@ -102,5 +103,5 @@ if minetest.get_modpath("mahogany") then
|
||||
worldeditadditions.register_sapling_alias("mahogany:sapling", "mahogany")
|
||||
end
|
||||
if minetest.get_modpath("chestnuttree") then
|
||||
worldeditadditions.register_sapling_alias("chestnuttree:sapling", "chestnuttree")
|
||||
worldeditadditions.register_sapling_alias("chestnuttree:sapling", "chestnut")
|
||||
end
|
||||
|
@ -2,16 +2,23 @@
|
||||
-- @module worldeditadditions.layers
|
||||
|
||||
|
||||
function worldeditadditions.forest(pos1, pos2, sapling_weights)
|
||||
function worldeditadditions.forest(pos1, pos2, density_multiplier, sapling_weights)
|
||||
pos1, pos2 = worldedit.sort_pos(pos1, pos2)
|
||||
|
||||
local weight_total = 0
|
||||
for _name,weight in pairs(sapling_weights) do
|
||||
weight_total = weight_total + weight
|
||||
end
|
||||
|
||||
sapling_weights["air"] = math.ceil(weight_total * 100 * 1/density_multiplier)
|
||||
|
||||
-- This command requires the bonemeal mod to be installed
|
||||
-- We check here too because other mods might call this function directly and bypass the chat command system
|
||||
if not minetest.get_modpath("bonemeal") then
|
||||
return false, "Bonemeal mod not loaded"
|
||||
end
|
||||
|
||||
worldeditadditions.overlay(pos1, pos2, sapling_weights)
|
||||
local overlay_stats = worldeditadditions.overlay(pos1, pos2, sapling_weights)
|
||||
|
||||
-- Fetch the nodes in the specified area
|
||||
local manip, area = worldedit.manip_helpers.init(pos1, pos2)
|
||||
@ -19,17 +26,20 @@ function worldeditadditions.forest(pos1, pos2, sapling_weights)
|
||||
local id_air = minetest.get_content_id("air")
|
||||
|
||||
local group_cache = {}
|
||||
local stats = { attempts = {}, failures = 0 }
|
||||
local stats = { attempts = {}, failures = 0, placed = {} }
|
||||
for z = pos2.z, pos1.z, -1 do
|
||||
for x = pos2.x, pos1.x, -1 do
|
||||
for y = pos2.y, pos1.y, -1 do
|
||||
local i = area:index(x, y, z)
|
||||
if not group_cache[data[i]] then
|
||||
group_cache[data[i]] = worldeditadditions.is_sapling(data[i])
|
||||
local node_id = data[i]
|
||||
if not group_cache[node_id] then
|
||||
group_cache[node_id] = worldeditadditions.is_sapling(node_id)
|
||||
end
|
||||
|
||||
if group_cache[data[i]] then
|
||||
if group_cache[node_id] then
|
||||
local did_grow = false
|
||||
local new_id_at_pos
|
||||
local new_name_at_pos
|
||||
for i=1,100 do
|
||||
bonemeal:on_use(
|
||||
{ x = x, y = y, z = z },
|
||||
@ -37,17 +47,26 @@ function worldeditadditions.forest(pos1, pos2, sapling_weights)
|
||||
nil
|
||||
)
|
||||
|
||||
local new_id_at_pos = minetest.get_content_id(minetest.get_node({ z = z, y = y, x = x }).name)
|
||||
new_name_at_pos = minetest.get_node({ z = z, y = y, x = x }).name
|
||||
new_id_at_pos = minetest.get_content_id(new_name_at_pos)
|
||||
if not group_cache[new_id_at_pos] then
|
||||
group_cache[new_id_at_pos] = worldeditadditions.is_sapling(new_id_at_pos)
|
||||
end
|
||||
if not group_cache[new_id_at_pos] then
|
||||
did_grow = true
|
||||
-- Log the number of attempts it took to grow
|
||||
table.insert(stats.attempts, i)
|
||||
-- Update the running total of saplings that grew
|
||||
if not stats.placed[node_id] then
|
||||
stats.placed[node_id] = 0
|
||||
end
|
||||
stats.placed[node_id] = stats.placed[node_id] + 1
|
||||
print("incrementing id", node_id, "to", stats.placed[node_id])
|
||||
break
|
||||
end
|
||||
end
|
||||
if not did_grow then
|
||||
print("[//forest] Failed to grow sapling, detected node id", new_id_at_pos, "name", new_name_at_pos, "was originally", minetest.get_name_from_content_id(node_id))
|
||||
-- We can't set it to air here because then when we save back we would overwrite all the newly grown trees
|
||||
stats.failures = stats.failures + 1
|
||||
end
|
||||
@ -69,14 +88,16 @@ function worldeditadditions.forest(pos1, pos2, sapling_weights)
|
||||
end
|
||||
|
||||
if group_cache[data[i]] then
|
||||
data[i] = air
|
||||
data[i] = id_air
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
stats.successes = #stats.attempts
|
||||
stats.attempts_avg = worldeditadditions.average(stats.attempts)
|
||||
|
||||
-- Save the modified nodes back to disk & return
|
||||
worldedit.manip_helpers.finish(manip, data)
|
||||
--worldedit.manip_helpers.finish(manip, data)
|
||||
return true, stats
|
||||
end
|
||||
|
@ -18,7 +18,7 @@ function worldeditadditions.overlay(pos1, pos2, node_weights)
|
||||
|
||||
-- z y x is the preferred loop order, but that isn't really possible here
|
||||
|
||||
local changes = { updated = 0, skipped_columns = 0 }
|
||||
local changes = { updated = 0, skipped_columns = 0, placed = {} }
|
||||
for z = pos2.z, pos1.z, -1 do
|
||||
for x = pos2.x, pos1.x, -1 do
|
||||
local found_air = false
|
||||
@ -28,10 +28,8 @@ function worldeditadditions.overlay(pos1, pos2, node_weights)
|
||||
local i = area:index(x, y, z)
|
||||
|
||||
local is_air = worldeditadditions.is_airlike(data[i])
|
||||
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
|
||||
-- wielded_light is now handled by worldeditadditions.is_airlike
|
||||
|
||||
local is_ignore = data[i] == node_id_ignore
|
||||
|
||||
if not is_air and not is_ignore then
|
||||
@ -42,6 +40,10 @@ function worldeditadditions.overlay(pos1, pos2, node_weights)
|
||||
data[area:index(x, y + 1, z)] = new_id
|
||||
changes.updated = changes.updated + 1
|
||||
placed_node = true
|
||||
if not changes.placed[new_id] then
|
||||
changes.placed[new_id] = 0
|
||||
end
|
||||
changes.placed[new_id] = changes.placed[new_id] + 1
|
||||
break -- Move on to the next column
|
||||
end
|
||||
elseif not is_ignore then
|
||||
|
@ -57,13 +57,13 @@ end
|
||||
-- @param id number The content/node id to check.
|
||||
-- @return bool Whther the given node/content id is a sapxling or not.
|
||||
function worldeditadditions.is_sapling(id)
|
||||
local node_name = minetest.get_name_from_content_id(data[i])
|
||||
local node_name = minetest.get_name_from_content_id(id)
|
||||
return minetest.get_item_group(node_name, "sapling") ~= 0
|
||||
end
|
||||
|
||||
local sapling_aliases = {}
|
||||
function worldeditadditions.register_sapling_alias(sapling_node_name, alias)
|
||||
if sapling_aliases[sapling_node_name] then
|
||||
if sapling_aliases[sapling_node_name] ~= nil then
|
||||
return false, "Error: An alias against the node name '"..sapling_node_name.."' already exists."
|
||||
end
|
||||
sapling_aliases[alias] = sapling_node_name
|
||||
|
@ -4,7 +4,7 @@ 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)
|
||||
-- print("[make_weighted] seen "..node_name.." @ weight "..weight.." → id "..next_id)
|
||||
for i = 1, weight do
|
||||
table.insert(result, next_id)
|
||||
end
|
||||
@ -37,3 +37,12 @@ function worldeditadditions.registered_nodes_by_group(group)
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
||||
--- Turns a node_name → weight table into a list of { node_name, weight } tables.
|
||||
function worldeditadditions.weighted_to_list(node_weights)
|
||||
local result = {}
|
||||
for node_name, weight in pairs(node_weights) do
|
||||
table.insert(result, { node_name, weight })
|
||||
end
|
||||
return result
|
||||
end
|
||||
|
@ -168,9 +168,9 @@ function worldeditadditions.parse_weighted_nodes(parts, as_list, func_normalise)
|
||||
local mode = MODE_NODE
|
||||
local last_node_name = nil
|
||||
for i, part in ipairs(parts) do
|
||||
print("i: "..i..", part: "..part)
|
||||
-- print("i: "..i..", part: "..part)
|
||||
if mode == MODE_NODE then
|
||||
print("mode: node");
|
||||
-- print("mode: node");
|
||||
local next
|
||||
if not func_normalise then next = worldedit.normalize_nodename(part)
|
||||
else next = func_normalise(part) end
|
||||
@ -180,10 +180,10 @@ function worldeditadditions.parse_weighted_nodes(parts, as_list, func_normalise)
|
||||
last_node_name = next
|
||||
mode = MODE_EITHER
|
||||
elseif mode == MODE_EITHER then
|
||||
print("mode: either");
|
||||
-- print("mode: either");
|
||||
local chance = tonumber(part)
|
||||
if not chance then
|
||||
print("not a chance, trying a node name")
|
||||
-- print("not a chance, trying a node name")
|
||||
local node_name
|
||||
if not func_normalise then node_name = worldedit.normalize_nodename(part)
|
||||
else node_name = func_normalise(part) end
|
||||
@ -198,7 +198,7 @@ function worldeditadditions.parse_weighted_nodes(parts, as_list, func_normalise)
|
||||
last_node_name = node_name
|
||||
mode = MODE_EITHER
|
||||
else
|
||||
print("it's a chance: ", chance, "for", last_node_name)
|
||||
-- print("it's a chance: ", chance, "for", last_node_name)
|
||||
chance = math.floor(chance)
|
||||
if as_list then table.insert(result, { node = last_node_name, weight = chance })
|
||||
else result[last_node_name] = chance end
|
||||
@ -208,7 +208,7 @@ function worldeditadditions.parse_weighted_nodes(parts, as_list, func_normalise)
|
||||
end
|
||||
end
|
||||
if last_node_name then
|
||||
print("caught trailing node name: ", last_node_name)
|
||||
-- print("caught trailing node name: ", last_node_name)
|
||||
if as_list then table.insert(result, { node = last_node_name, weight = 1 })
|
||||
else result[last_node_name] = 1 end
|
||||
end
|
||||
|
@ -4,11 +4,18 @@
|
||||
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
||||
-- ██████ ████ ███████ ██ ██ ███████ ██ ██ ██
|
||||
worldedit.register_command("forest", {
|
||||
params = "<sapling_a> [<chance_a>] <sapling_b> [<chance_b>] [<sapling_N> [<chance_N>]] ...",
|
||||
description = "Plants and grows trees in the defined region according to the given list of sapling names and chances. Saplings are planted using //overlay - so the chances a 1-in-N change of actually planting a sapling at each candidate location. Saplings that fail to grow are subsequently removed (this will affect pre-existing saplings too)",
|
||||
params = "[<density>] <sapling_a> [<chance_a>] <sapling_b> [<chance_b>] [<sapling_N> [<chance_N>]] ...",
|
||||
description = "Plants and grows trees in the defined region according to the given list of sapling names and chances and density factor. The density controls the relative density of the resulting forest, and defaults to 1 (floating-point numbers allowed). Higher chance numbers result in a lower relative chance with respect to other saplings in the list. Saplings that fail to grow are subsequently removed (this will affect pre-existing saplings too).",
|
||||
privs = { worldedit = true },
|
||||
require_pos = 2,
|
||||
parse = function(params_text)
|
||||
local density = 1
|
||||
local match_start = params_text:match("^[%d.]+%s+")
|
||||
if match_start then
|
||||
density = tonumber(match_start)
|
||||
params_text = params_text:sub(#match_start + 1) -- everything starts at 1 in Lua :-/
|
||||
end
|
||||
|
||||
local success, sapling_list = worldeditadditions.parse_weighted_nodes(
|
||||
worldeditadditions.split(params_text, "%s+", false),
|
||||
false,
|
||||
@ -18,19 +25,28 @@ worldedit.register_command("forest", {
|
||||
)
|
||||
end
|
||||
)
|
||||
return success, sapling_list
|
||||
return success, density, sapling_list
|
||||
end,
|
||||
nodes_needed = function(name)
|
||||
-- //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])
|
||||
return (pos2.x - pos1.x) * (pos2.y - pos1.y)
|
||||
end,
|
||||
func = function(name, sapling_list)
|
||||
func = function(name, density, sapling_list)
|
||||
local start_time = worldeditadditions.get_ms_time()
|
||||
local changes = worldeditadditions.forest(worldedit.pos1[name], worldedit.pos2[name], sapling_list)
|
||||
local time_taken = worldeditadditions.get_ms_time() - start_time
|
||||
local success, stats = worldeditadditions.forest(
|
||||
worldedit.pos1[name], worldedit.pos2[name],
|
||||
density,
|
||||
sapling_list
|
||||
)
|
||||
if not success then return success, stats end
|
||||
local time_taken = worldeditadditions.human_time(worldeditadditions.get_ms_time() - start_time)
|
||||
|
||||
minetest.log("action", name .. " used //forest at " .. worldeditadditions.vector.tostring(worldedit.pos1[name]) .. ", replacing " .. changes.updated .. " nodes and skipping " .. changes.skipped_columns .. " columns in " .. time_taken .. "s")
|
||||
return true, changes.updated .. " nodes replaced and " .. changes.skipped_columns .. " columns skipped in " .. worldeditadditions.human_time(time_taken)
|
||||
local distribution_display = worldeditadditions.make_ascii_table(
|
||||
worldeditadditions.node_distribution_to_list(stats.placed, stats.successes)
|
||||
)
|
||||
|
||||
minetest.log("action", name.." used //forest at "..worldeditadditions.vector.tostring(worldedit.pos1[name]).." - "..worldeditadditions.vector.tostring(worldedit.pos2[name])..", "..stats.successes.." trees placed, averaging "..stats.attempts_avg.." growth attempts / tree and "..stats.failures.." failed attempts in "..time_taken)
|
||||
return true, distribution_display.."\n=========================\n"..stats.successes.." trees placed, averaging "..stats.attempts_avg.." growth attempts / tree and "..stats.failures.." failed attempts in "..time_taken
|
||||
end
|
||||
})
|
||||
|
@ -17,6 +17,7 @@ minetest.register_chatcommand("/saplingaliases", {
|
||||
for node_name, alias in pairs(aliases) do
|
||||
table.insert(display, { node_name, alias })
|
||||
end
|
||||
table.sort(display, function(a, b) return a[2] < b[2] end)
|
||||
table.insert(msg, worldeditadditions.make_ascii_table(display))
|
||||
elseif params_text == "all_saplings" then
|
||||
local results = worldeditadditions.registered_nodes_by_group("sapling")
|
||||
|
Loading…
Reference in New Issue
Block a user