Finish bugfixing //forest!

moretrees still doesn't work with it though, and I don't understand 
why....
This commit is contained in:
Starbeamrainbowlabs 2020-09-14 01:19:15 +01:00
parent a6fc9712b4
commit 2a592a808c
Signed by: sbrl
GPG key ID: 1BE5172E637709C2
8 changed files with 88 additions and 38 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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
})

View file

@ -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")