Start implementing //forest, but it isn't finished yet

This commit is contained in:
Starbeamrainbowlabs 2020-09-13 20:32:55 +01:00
parent 0abf9d095b
commit 0b285fa681
No known key found for this signature in database
GPG Key ID: 1BE5172E637709C2
8 changed files with 291 additions and 63 deletions

@ -11,10 +11,13 @@ dofile(worldeditadditions.modpath.."/utils/vector.lua")
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/nodes.lua")
dofile(worldeditadditions.modpath.."/utils/node_identification.lua")
dofile(worldeditadditions.modpath.."/utils/tables.lua") dofile(worldeditadditions.modpath.."/utils/tables.lua")
dofile(worldeditadditions.modpath.."/utils/terrain.lua") dofile(worldeditadditions.modpath.."/utils/terrain.lua")
dofile(worldeditadditions.modpath.."/utils/raycast_adv.lua") -- For the farwand dofile(worldeditadditions.modpath.."/utils/raycast_adv.lua") -- For the farwand
dofile(worldeditadditions.modpath.."/lib/compat/saplingnames.lua")
dofile(worldeditadditions.modpath.."/lib/floodfill.lua") dofile(worldeditadditions.modpath.."/lib/floodfill.lua")
dofile(worldeditadditions.modpath.."/lib/overlay.lua") dofile(worldeditadditions.modpath.."/lib/overlay.lua")
dofile(worldeditadditions.modpath.."/lib/layers.lua") dofile(worldeditadditions.modpath.."/lib/layers.lua")
@ -31,3 +34,4 @@ dofile(worldeditadditions.modpath.."/lib/erode/erode.lua")
dofile(worldeditadditions.modpath.."/lib/count.lua") dofile(worldeditadditions.modpath.."/lib/count.lua")
dofile(worldeditadditions.modpath.."/lib/bonemeal.lua") dofile(worldeditadditions.modpath.."/lib/bonemeal.lua")
dofile(worldeditadditions.modpath.."/lib/forest.lua")

@ -0,0 +1,106 @@
--[[
This file contains sapling alias definitions for a number of different mods.
If your mod is not listed here, please open a pull request to add it :-)
This mod's repository can be found here: https://github.com/sbrl/Minetest-WorldEditAdditions
Adding support for your mod is a 2 step process:
1. Update this file with your definitions
2. Update depends.txt to add a soft dependency on your mod. Find that file here: https://github.com/sbrl/Minetest-WorldEditAdditions/blob/master/worldeditadditions/depends.txt - for example, add something line "my_awesome_mod?" (note the question mark at the end) on a new line(without quotes).
Alternatively, you can register support in your mod directly. Do that by adding a soft dependency on worldeditadditions, and then calling worldeditadditions.normalise_saplingname if worldeditadditions is loaded.
]]--
-- worldeditadditions.register_sapling_alias("")
if minetest.get_modpath("default") then
worldeditadditions.register_sapling_alias_many({
{ "default:sapling", "oak" },
{ "default:bush_sapling", "bush" },
{ "default:pine_sapling", "pine" },
{ "default:pine_bush_sapling", "pine_bush" },
{ "default:aspen_sapling", "aspen" },
{ "default:junglesapling", "jungle" },
{ "default:emergent_jungle_sapling", "jungle_emergent" },
{ "default:acacia_sapling", "acacia" },
{ "default:acacia_bush_sapling", "acacia_bush" },
{ "default:blueberry_bush_sapling", "blueberry_bush" }
})
end
if minetest.get_modpath("moretrees") then
worldeditadditions.register_sapling_alias_many({
{ "moretrees:spruce_sapling_ongen", "spruce" },
{ "moretrees:rubber_tree_sapling_ongen", "rubbe" },
{ "moretrees:beech_sapling_ongen", "beech" },
{ "moretrees:jungletree_sapling_ongen", "jungle_moretrees" },
{ "moretrees:fir_sapling_ongen", "fir" },
{ "moretrees:willow_sapling_ongen", "willow" },
{ "moretrees:poplar_sapling_ongen", "poplar" },
{ "moretrees:poplar_small_sapling_ongen", "poplar_small" },
{ "moretrees:apple_tree_sapling_ongen", "apple" },
{ "moretrees:birch_sapling_ongen", "birch" },
{ "moretrees:palm_sapling_ongen", "palm" },
{ "moretrees:date_palm_sapling_ongen", "palm_date" },
{ "moretrees:sequoia_sapling_ongen", "sequoia" },
{ "moretrees:oak_sapling_ongen", "oak_moretrees" },
{ "moretrees:cedar_sapling_ongen", "cedar" }
})
end
-- ██████ ██████ ██████ ██ ████████ ██████ ███████ ███████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██████ █████ █████ ███████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ██████ ██████ ███████ ███████ ██ ██ ██ ███████ ███████ ███████
if minetest.get_modpath("lemontree") then
worldeditadditions.register_sapling_alias("lemontree:sapling", "lemontree")
end
if minetest.get_modpath("pineapple") then
worldeditadditions.register_sapling_alias("pineapple:sapling", "pineapple")
end
if minetest.get_modpath("baldcypress") then
worldeditadditions.register_sapling_alias("baldcypress:sapling", "baldcypress")
end
if minetest.get_modpath("bamboo") then
worldeditadditions.register_sapling_alias("bamboo:sapling", "bamboo")
end
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")
end
if minetest.get_modpath("clementinetree") then
worldeditadditions.register_sapling_alias("clementinetree:sapling", "clementinetree")
end
if minetest.get_modpath("ebony") then
worldeditadditions.register_sapling_alias("ebony:sapling", "ebony")
end
if minetest.get_modpath("jacaranda") then
worldeditadditions.register_sapling_alias("jacaranda:sapling", "jacaranda")
end
if minetest.get_modpath("larch") then
worldeditadditions.register_sapling_alias("larch:sapling", "larch")
end
if minetest.get_modpath("maple") then
worldeditadditions.register_sapling_alias("maple:sapling", "maple")
end
if minetest.get_modpath("palm") then
worldeditadditions.register_sapling_alias("palm:sapling", "palm")
end
if minetest.get_modpath("pomegranate") then
worldeditadditions.register_sapling_alias("pomegranate:sapling", "pomegranate")
end
if minetest.get_modpath("willow") then
worldeditadditions.register_sapling_alias("willow:sapling", "willow")
end
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")
end

@ -0,0 +1,94 @@
local node_id_air = minetest.get_content_id("air")
local node_id_ignore = minetest.get_content_id("ignore")
--- Determines whether the given node/content id is an airlike node or not.
-- It is recommended that the result of this function be cached.
-- @param id number The content/node id to check.
-- @return bool Whether the given node/content id is an airlike node or not.
function worldeditadditions.is_airlike(id)
-- Do a fast check against air and ignore
if id == node_id_air then
return true
elseif id == node_id_ignore then -- ignore = not loaded yet IIRC (so it could be anything)
return false
end
-- If the node isn't registered, then it might not be an air node
if not minetest.registered_nodes[id] then return false end
if minetest.registered_nodes[id].sunlight_propagates == true then
return true
end
-- Check for membership of the airlike group
local name = minetest.get_name_from_content_id(id)
local airlike_value = minetest.get_item_group(name, "airlike")
if airlike_value ~= nil and airlike_value > 0 then
return true
end
-- Just in case
if worldeditadditions.string_starts(this_node_name, "wielded_light") then
return true
end
-- Just in case
return false
end
--- Determines whether the given node/content id is a liquid-ish node or not.
-- It is recommended that the result of this function be cached.
-- @param id number The content/node id to check.
-- @return bool Whether the given node/content id is a liquid-ish node or not.
function worldeditadditions.is_liquidlike(id)
-- print("[is_liquidlike]")
if id == node_id_ignore then return false end
local node_name = minetest.get_name_from_content_id(id)
if node_name == nil or not minetest.registered_nodes[node_name] then return false end
local liquidtype = minetest.registered_nodes[node_name].liquidtype
-- print("[is_liquidlike]", "id", id, "liquidtype", liquidtype)
if liquidtype == nil or liquidtype == "none" then return false end
-- If it's not none, then it has to be a liquid as the only other values are source and flowing
return true
end
--- Determines whether the given node/content id is a sapling or not.
-- Nodes with the "sapling" group are considered saplings.
-- It is recommended that the result of this function be cached.
-- @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])
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
return false, "Error: An alias against the node name '"..sapling_node_name.."' already exists."
end
sapling_aliases[alias] = sapling_node_name
return true
end
function worldeditadditions.register_sapling_alias_many(tbl)
for i, next in ipairs(tbl) do
local success, msg = worldeditadditions.register_sapling_alias(
next[1],
next[2]
)
if not success then return success, msg end
end
end
--- Returns the current key ⇒ value table of sapling names and aliases.
-- @return table
function worldeditadditions.get_all_sapling_aliases()
return sapling_aliases
end
--- Attempts to normalise a sapling name using the currently registered aliases.
-- @param in_name string The sapling name to normalise
-- @param return_nil_on_failure bool Whether to return nil if we fail to resolve the sapling name with an alias, or return the original node name instead (default: false).
function worldeditadditions.normalise_saplingname(in_name, return_nil_on_failure)
if sapling_aliases[in_name] then return sapling_aliases[in_name]
elseif return_nil_on_failure then return nil
else return in_name end
end

@ -28,65 +28,12 @@ function worldeditadditions.unwind_node_list(list)
return result, #result return result, #result
end end
local node_id_air = minetest.get_content_id("air") function worldeditadditions.registered_nodes_by_group(group)
local node_id_ignore = minetest.get_content_id("ignore") local result = {}
for name, def in pairs(minetest.registered_nodes) do
--- Determines whether the given node/content id is an airlike node or not. if def.groups[group] then
-- It is recommended that the result of this function be cached. result[#result+1] = name
-- @param id number The content/node id to check.
-- @return bool Whether the given node/content id is an airlike node or not.
function worldeditadditions.is_airlike(id)
-- Do a fast check against air and ignore
if id == node_id_air then
return true
elseif id == node_id_ignore then -- ignore = not loaded yet IIRC (so it could be anything)
return false
end end
-- If the node isn't registered, then it might not be an air node
if not minetest.registered_nodes[id] then return false end
if minetest.registered_nodes[id].sunlight_propagates == true then
return true
end end
-- Check for membership of the airlike group return result
local name = minetest.get_name_from_content_id(id)
local airlike_value = minetest.get_item_group(name, "airlike")
if airlike_value ~= nil and airlike_value > 0 then
return true
end
-- Just in case
if worldeditadditions.string_starts(this_node_name, "wielded_light") then
return true
end
-- Just in case
return false
end
--- Determines whether the given node/content id is a liquid-ish node or not.
-- It is recommended that the result of this function be cached.
-- @param id number The content/node id to check.
-- @return bool Whether the given node/content id is a liquid-ish node or not.
function worldeditadditions.is_liquidlike(id)
-- print("[is_liquidlike]")
if id == node_id_ignore then return false end
local node_name = minetest.get_name_from_content_id(id)
if node_name == nil or not minetest.registered_nodes[node_name] then return false end
local liquidtype = minetest.registered_nodes[node_name].liquidtype
-- print("[is_liquidlike]", "id", id, "liquidtype", liquidtype)
if liquidtype == nil or liquidtype == "none" then return false end
-- If it's not none, then it has to be a liquid as the only other values are source and flowing
return true
end
--- Determines whether the given node/content id is a sapling or not.
-- Nodes with the "sapling" group are considered saplings.
-- It is recommended that the result of this function be cached.
-- @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])
return minetest.get_item_group(node_name, "sapling") ~= 0
end end

@ -157,8 +157,9 @@ end
--- Parses a list of strings as a list of weighted nodes - e.g. like in the //mix command. --- Parses a list of strings as a list of weighted nodes - e.g. like in the //mix command.
-- @param parts string[] The list of strings to parse (try worldeditadditions.split) -- @param parts string[] The list of strings to parse (try worldeditadditions.split)
-- @param as_list bool If true, then table.insert() successive { node = string, weight = number } subtables when parsing instead of populating as an associative array. -- @param as_list bool If true, then table.insert() successive { node = string, weight = number } subtables when parsing instead of populating as an associative array.
-- @param func_normalise callable If specified, the given function will be used to normalise node names instead of worldedit.normalize_nodename. A single argument is passed containing the un-normalised node name, and the return value is assumed to be the normalised node name.
-- @returns table A table in the form node_name => weight. -- @returns table A table in the form node_name => weight.
function worldeditadditions.parse_weighted_nodes(parts, as_list) function worldeditadditions.parse_weighted_nodes(parts, as_list, func_normalise)
if as_list == nil then as_list = false end if as_list == nil then as_list = false end
local MODE_EITHER = 1 local MODE_EITHER = 1
local MODE_NODE = 2 local MODE_NODE = 2
@ -170,7 +171,9 @@ function worldeditadditions.parse_weighted_nodes(parts, as_list)
print("i: "..i..", part: "..part) print("i: "..i..", part: "..part)
if mode == MODE_NODE then if mode == MODE_NODE then
print("mode: node"); print("mode: node");
local next = worldedit.normalize_nodename(part) local next
if not func_normalise then next = worldedit.normalize_nodename(part)
else next = func_normalise(part) end
if not next then if not next then
return false, "Error: Invalid node name '"..part.."'" return false, "Error: Invalid node name '"..part.."'"
end end
@ -181,7 +184,10 @@ function worldeditadditions.parse_weighted_nodes(parts, as_list)
local chance = tonumber(part) local chance = tonumber(part)
if not chance then if not chance then
print("not a chance, trying a node name") print("not a chance, trying a node name")
local node_name = worldedit.normalize_nodename(part) local node_name
if not func_normalise then node_name = worldedit.normalize_nodename(part)
else node_name = func_normalise(part) end
if not node_name then if not node_name then
return false, "Error: Invalid number '"..chance.."'" return false, "Error: Invalid number '"..chance.."'"
end end

@ -0,0 +1,36 @@
-- ██████ ██ ██ ███████ ██████ ██ █████ ██ ██
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██ ██ ██ ██ █████ ██████ ██ ███████ ████
-- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
-- ██████ ████ ███████ ██ ██ ███████ ██ ██ ██
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)",
privs = { worldedit = true },
require_pos = 2,
parse = function(params_text)
local success, sapling_list = worldeditadditions.parse_weighted_nodes(
worldeditadditions.split(params_text, "%s+", false),
false,
function(name)
return worldedit.normalize_nodename(
worldeditadditions.normalise_saplingname(name)
)
end
)
return success, 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)
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
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)
end
})

@ -0,0 +1,34 @@
minetest.register_chatcommand("/saplingaliases", {
params = "[aliases|all_saplings]",
description = "Lists all the currently registered sapling aliases (default). A single argument is taken as the mode of operation. Current modes: aliases (default; as described previously), all_saplings (lists all node names with the group \"sapling\")",
privs = { worldedit = true },
func = function(name, params_text)
if name == nil then return end
if params_text == "" or not params_text then
params_text = "aliases"
end
local msg = {}
if params_text == "aliases" then
table.insert(msg, "Currently registered aliases:\n")
local aliases = worldeditadditions.get_all_sapling_aliases()
local display = {}
for node_name, alias in pairs(aliases) do
table.insert(display, { node_name, alias })
end
table.insert(msg, worldeditadditions.make_ascii_table(display))
elseif params_text == "all_saplings" then
local results = worldeditadditions.registered_nodes_by_group("sapling")
table.insert(msg, "Sapling-like nodes:\n")
local str = table.concat(results, "\n")
print(str)
table.insert(msg, str)
else
table.insert(msg, "Unknown mode '")
table.insert(msg, params_text)
table.insert(msg, "' (valid modes: aliases, all_saplings).")
end
worldedit.player_notify(name, table.concat(msg))
end
})

@ -34,6 +34,7 @@ dofile(we_c.modpath.."/commands/convolve.lua")
dofile(we_c.modpath.."/commands/erode.lua") dofile(we_c.modpath.."/commands/erode.lua")
dofile(we_c.modpath.."/commands/count.lua") dofile(we_c.modpath.."/commands/count.lua")
dofile(we_c.modpath.."/commands/forestaliases.lua")
dofile(we_c.modpath.."/commands/subdivide.lua") dofile(we_c.modpath.."/commands/subdivide.lua")