diff --git a/mods/treecapitator/.gitignore b/mods/treecapitator/.gitignore new file mode 100644 index 0000000..b5913a4 --- /dev/null +++ b/mods/treecapitator/.gitignore @@ -0,0 +1,5 @@ +## Generic ignorable patterns and files +*~ +.*.swp +debug.txt +*.patch diff --git a/mods/treecapitator/.luacheckrc b/mods/treecapitator/.luacheckrc new file mode 100644 index 0000000..851754b --- /dev/null +++ b/mods/treecapitator/.luacheckrc @@ -0,0 +1,15 @@ +read_globals = { + "dump", + "vector", + "DIR_DELIM", + minetest = { + fields = { + check_for_falling = { + read_only = false + } + }, + other_fields = true + } +} +globals = {"treecapitator"} +ignore = {"421", "423"} diff --git a/mods/treecapitator/LICENSE.txt b/mods/treecapitator/LICENSE.txt new file mode 100644 index 0000000..4a4737b --- /dev/null +++ b/mods/treecapitator/LICENSE.txt @@ -0,0 +1,3 @@ +The code of this mod is licensed under MIT. +The sound from http://www.freesound.org/people/ecfike/sounds/139952/, +edited with audacity, is licensed under CC0. diff --git a/mods/treecapitator/README.md b/mods/treecapitator/README.md new file mode 100644 index 0000000..2726b4a --- /dev/null +++ b/mods/treecapitator/README.md @@ -0,0 +1,50 @@ +[Mod] treecapitator [treecapitator] + +This mod is makes digging a trunk node of a tree destroy the whole tree +including the leaves and the fruits. There are many settings, such as dropping +no leaves but only saplings (as leafdecay does it).
+Holding shift you can avoid its function to dig the single trunk node.
+Neighbour trees are detected and preserved.
+This mod is based on Jeja's timber mod, however, the code was revised over and +over, so it became a different mod (according to the +[broom theory](https://www.youtube.com/watch?v=51n-EBigXmg) that is).
+[There I got the name.](http://www.minecraftforum.net/topic/1009577-147-daftpvfs-mods-treecapitator-ingameinfo-crystalwing-startinginv-floatingruins/) + +**Depends:** see [depends.txt](https://raw.githubusercontent.com/HybridDog/treecapitator/master/depends.txt)
+**License:** see [LICENSE.txt](https://raw.githubusercontent.com/HybridDog/treecapitator/master/LICENSE.txt)
+**Download:** [zip](https://github.com/HybridDog/treecapitator/archive/master.zip), [tar.gz](https://github.com/HybridDog/treecapitator/archive/master.tar.gz) + +![I'm a screenshot!](https://forum.minetest.net/download/file.php?id=571)
+↑ you can only see the animation if your browser supports apng + +If you got ideas or found bugs, please tell them to me. + +[How to install a mod?](http://wiki.minetest.net/Installing_Mods) + + +[13.04.2015] Added in trees for moretrees, working out getting proper naming + for capitating to work. + +[14.03.2015] Added sound of a falling tree (taken from there + http://www.freesound.org/people/ecfike/sounds/139952/). + Made "drop_items" and "drop_leaf" both "true" by default. (mintpick) + +TODO: +* fix neighbour tree detection, wrong nodes become dug (test with vertical + trunk fruit), + pertains to non-v6 apple tree and jungle tree +* use range_up and range_down for other trees too for greater precision in + neighbour detection +* improve moretrees support (see issue #2) +* moretrees acacia +* more precise neighbour detection for moretrees (cedar), fix moretree + jungletree ignoring default jungletrees' leaves +* technic chainsaw +* Test if pine trees are different on v6 and non-v6 mapgen +* Add a priority and priority condition to allow handling cone pine trees + differently +* Get rid of the cutting_leaves arguments because the problem is fixed in + minetest +* consider neighbour tree's stem and not only head during neighbour detection +* add dfs head search (and a parameter to enable it) +* continue the documentation diff --git a/mods/treecapitator/api.lua b/mods/treecapitator/api.lua new file mode 100644 index 0000000..b0bff09 --- /dev/null +++ b/mods/treecapitator/api.lua @@ -0,0 +1,94 @@ +-- the table containing the tree definitions +treecapitator.trees = {} + +-- Warn if a tree definition passed to treecapitator.register_tree contains +-- invalid options, partly for backwards compatibility. +-- Not all options are checked. +local function check_tree_definition(tr) + if type(tr.trees) ~= "table" then + minetest.log("warning", + "[treecapitator] Invalid tree definition (trees field)") + end + if tr.type ~= "acacia" then + if type(tr.leaves) ~= "table" then + minetest.log("warning", + "[treecapitator] Invalid tree definition (leaves field)") + end + if type(tr.range) ~= "number" then + minetest.log("warning", + "[treecapitator] Invalid tree definition (range field)") + end + end +end + +-- For the usage of this function, see trees.lua. +local after_dig_wrap +local after_dig_nodes = {} +function treecapitator.register_tree(tree_definition) + check_tree_definition(tree_definition) + local tr = {} + for name, value in pairs(tree_definition) do + tr[name] = value + end + tr.fruits = tr.fruits or {} + tr.type = tr.type or "default" + treecapitator.trees[#treecapitator.trees+1] = tr + if treecapitator.after_register[tr.type] then + treecapitator.after_register[tr.type](tr) + end + + for i = 1,#tr.trees do + local nodename = tr.trees[i] + local data = minetest.registered_nodes[nodename] + if not data then + error(nodename .. " has to be registered before calling " .. + "treecapitator.register_tree.") + end + local func = after_dig_wrap + local prev_after_dig = data.after_dig_node + if prev_after_dig + and not after_dig_nodes[nodename] then + func = function(pos, oldnode, oldmetadata, digger) + prev_after_dig(pos, oldnode, oldmetadata, digger) + treecapitator.capitate_tree(pos, digger) + end + end + minetest.override_item(nodename, {after_dig_node = func}) + after_dig_nodes[nodename] = true + end +end + +function treecapitator.capitation_allowed() + return not treecapitator.capitation_usually_disallowed +end + + +-- Example of overriding this function +if treecapitator.no_hand_capitation then + -- disallow capitating trees if no proper tool is used + treecapitator.capitation_usually_disallowed = true + local allowed = treecapitator.capitation_allowed + function treecapitator.capitation_allowed(pos, digger) + local def = minetest.registered_nodes[ + minetest.get_node{x=pos.x, y=pos.y+1, z=pos.z}.name + ] + return def and def.groups and minetest.get_dig_params(def.groups, + digger:get_wielded_item():get_tool_capabilities()).wear > 0 + or allowed(pos, digger) + end +end + +-- test if trunk nodes were redefined +minetest.after(2, function() + for nodename in pairs(after_dig_nodes) do + if not minetest.registered_nodes[nodename].after_dig_node then + error(nodename .. " didn't keep after_dig_node.") + end + end + after_dig_nodes = nil +end) + +-- wrapping is necessary, someone may overwrite treecapitator.capitate_tree +function after_dig_wrap(pos, _,_, digger) + treecapitator.capitate_tree(pos, digger) +end diff --git a/mods/treecapitator/documentation/doc.md b/mods/treecapitator/documentation/doc.md new file mode 100644 index 0000000..2780a42 --- /dev/null +++ b/mods/treecapitator/documentation/doc.md @@ -0,0 +1,147 @@ +# Registering a Tree + +We need to register trees so that they are known to the treecapitator mod. +Since this mod detects neighbouring trees and should ignore user-placed trunks, +the we should define a tree as strictly as possible. + +I have grouped the tree definitions to different types because, for example, +an apple tree requires a different detection algorithm than a palm tree. + +## Default Tree + +This is probably the most common type of tree in minetest. + +```lua +treecapitator.register_tree({ + type = "default", + trees = {, , ...}, + leaves = {, , ...}, + range = , + range_up = , + range_down = , + fruits = {, , ...}, + trunk_fruit_vertical = , + cutting_leaves = , + stem_type = , + stem_height_min = , + requisite_leaves = {, , ...}, +}) +``` + +* Nodes the tree consists of + * `trees` + The trunk nodes of the straight stem. + Rotated trunks (param2 > 0) are usually ignored. + * `leaves` + Nodes of the tree head which represent leaves.
+ In comparison to `fruits`, these nodes are affected + by the drop_leaf setting. + * `fruits` + Like `leaves` but unaffected by the drop_leaf setting.
+ This list can also contain trunk nodes which are spread around + in the tree head. + * `trunk_fruit_vertical` + If set to true, a trunk node which is in `trees` and `fruits` is removed + even if it is not rotated (param2 > 0). + * `requisite_leaves` + If defined, abort capitating if one of the nodes in this list were not + found in the tree head.
+ We can use this if, for example, the tree head always + has green and red leaves and never has only one of them. +* Possible tree sizes + * `range` + The size of the AABB around the biggest possible tree head. + `range_up` and `range_down` can override the size in +Y and -Y + directions.
+ Note that if the tree has a thick stem, the range needs to be + a bit bigger because the tree head centre can be offset.
+ Example: `range = 2` represents a 5x5x5 cube. + * `range_up`, defaults to `range` + Head size in +Y direction, ranging from the highest trunk node to + the top of the tree head. + * `range_down`, defaults to `range` + Like `range_up` but in -Y direction. + * `stem_type` + We can ignore this, or set it to `"2x2"` or `"+"`.
+ `"2x2"` represents a 4 nodes thick stem, `"+"` represents a 5 + nodes thick stem which looks like a plus when viewed from above, + and no stem type represents a simple 1 node thick stem. + * `stem_height_min` + This value determines the minimum number of trunk nodes + the stem consists of; it is only used to identify + neighbouring trees, which should be preserved. +* Other parameters + * `cutting_leaves`, deprecated + used as workaround for moretrees#34; + only enable this for buggy trees where leaves can be in the stem + + +## Acacia Tree + + +## Palm Tree + + +## Moretrees Tree + +This type represents a complex tree from moretrees mod. +The tree can have long branches and other things +which the default tree type algorithm cannot easily detect. +The algorithm for this type is relatively simple, so for a good tree detection, +we should use the default type instead if possible. + + +```lua +treecapitator.register_tree({ + type = "moretrees", + trees = {, , ...}, + leaves = {, , ...}, + range = , + fruits = {, , ...}, + height = , + max_nodes = , + num_trunks_min = , + num_trunks_max = , + num_leaves_min = , + num_leaves_max = , +}) +``` + +Many parameters mean the same as the default type tree parameters. +* `height` + maximum tree height +* `max_nodes` + maximum number of nodes the tree is allowed to consist of +* num_trunks_min… + + +# Other Functions and Variables + +* `treecapitator.capitation_allowed(pos, player)` + Returns true if the player `player` is allowed to remove the tree + at `pos`.
+ We can override this function so that capitation transpires only + under certain contitions, e.g. only if the player has a chainsaw. +* `treecapitator.capitation_usually_disallowed` + The value returned by the initial `treecapitator.capitation_allowed` + function.
+ We can use this if multiple mods add a tool which allows capitating trees, + i.e. if those mods override `treecapitator.capitation_allowed`. +* `treecapitator.no_hand_capitation` + If set to true, players cannot capitate trees with the bare hand. +* `treecapitator.capitate_tree(pos, player)` + This function tests if the player can capitate a tree, + e.g. it calls `treecapitator.capitation_allowed`, + and then capitates the tree if possible.
+ It is usually invoked in the trees' trunk nodes' `after_dig_node` + node definition field. + The `after_dig_node`s are added/overridden automatically + after registering a tree. + + +# To Do + +The doc is not finished. +I invite the person who reads this to have a look at init.lua and trees.lua, +and help to continue the API documentation. + diff --git a/mods/treecapitator/init.lua b/mods/treecapitator/init.lua new file mode 100644 index 0000000..a389218 --- /dev/null +++ b/mods/treecapitator/init.lua @@ -0,0 +1,996 @@ +local load_time_start = minetest.get_us_time() + + +------------------------------------- Settings --------------------------------- + +-- default settings +treecapitator = { + drop_items = false, + drop_leaf = false, + play_sound = true, + moretrees_support = false, + no_hand_capitation = false, + delay = 0, + stem_height_min = 3, + after_register = {}, +} + +-- load custom settings +for name,v in pairs(treecapitator) do + local setting = "treecapitator." .. name + --local typ = type(v) + local neuv + if type(v) == "boolean" then + neuv = minetest.settings:get_bool(setting) + else--if typ == "number" then + neuv = tonumber(minetest.settings:get(setting)) + end + if neuv ~= nil then + treecapitator[name] = neuv + end +end + + +-------------------------- Common functions ------------------------------------ + +local poshash = minetest.hash_node_position + +local function hash2(x, y) + return y * 0x10000 + x +end + +-- don't use minetest.get_node more times for the same position (caching) +local known_nodes +local function clean_cache() + known_nodes = {} + setmetatable(known_nodes, {__mode = "kv"}) +end +clean_cache() + +local function remove_node(pos) + known_nodes[poshash(pos)] = {name="air", param2=0} + minetest.remove_node(pos) + minetest.check_for_falling(pos) +end + +local function get_node(pos) + local vi = poshash(pos) + local node = known_nodes[vi] + if node then + return node + end + node = minetest.get_node(pos) + known_nodes[vi] = node + return node +end + +--definitions of functions for the destruction of nodes +local destroy_node, drop_leaf, remove_leaf +if treecapitator.drop_items then + function drop_leaf(pos, item) + minetest.add_item(pos, item) + end + + function destroy_node(pos, node) + local drops = minetest.get_node_drops(node.name) + for _,item in pairs(drops) do + minetest.add_item(pos, item) + end + remove_node(pos) + end +else + if minetest.settings:get_bool"creative_mode" then + function drop_leaf(_, item, inv) + if inv + and inv:room_for_item("main", item) + and not inv:contains_item("main", item) then + inv:add_item("main", item) + end + end + else + function drop_leaf(pos, item, inv) + if inv + and inv:room_for_item("main", item) then + inv:add_item("main", item) + else + minetest.add_item(pos, item) + end + end + end + + destroy_node = function(pos, node, digger) + known_nodes[poshash(pos)] = {name="air", param2=0} + minetest.node_dig(pos, node, digger) + end +end + +if not treecapitator.drop_leaf then + function remove_leaf(pos, node, inv) + local leaves_drops = minetest.get_node_drops(node.name) + for _, itemname in pairs(leaves_drops) do + if itemname ~= node.name then + drop_leaf(pos, itemname, inv) + end + end + remove_node(pos) --remove the leaves + end +else + function remove_leaf(pos, node, _, digger) + destroy_node(pos, node, digger) + end +end + +local function tree_fall_sound(pos) + if treecapitator.play_sound then + minetest.sound_play("tree_falling", { + gain = 0.2 + 0.2 * math.random() ^ 2, + pitch = 0.7 + 0.5 * math.random(), + pos = pos, + max_hear_distance = 32 + }, true) + end +end + + +local function table_contains(t, v) + for i = 1,#t do + if t[i] == v then + return true + end + end + return false +end + + +-- the functions for the available types +local capitate_funcs = {} + + +------------------------ Function for regular trees ---------------------------- + +-- tests if the node is a trunk which could belong to the same tree sort +local function is_trunk_of_tree(trees, node) + -- param2 is not longer tested to be 0 but smaller than 4 + -- because sometimes the trunk is a bit rotated + return node.param2 < 4 + and trees ^ node.name +end + +-- test if the trunk node there is the top trunk node of a neighbour tree +-- if so, constrain the possible leaves positions +local function get_a_tree(pos, tab, tr, xo,yo,zo) + local p = {x=pos.x + xo, y=pos.y + yo, z=pos.z + zo} + + -- tests if a trunk is at the current pos + local nd = get_node(p) + if not is_trunk_of_tree(tr.trees, nd) then + return false + end + + -- search for a leaves or fruit node next to the trunk + local leaf = get_node{x=p.x, y=p.y+1, z=p.z}.name + if not tr.leaves ^ leaf + and not tr.fruits ^ leaf then + local leaf = get_node{x=p.x, y=p.y, z=p.z+1}.name + if not tr.leaves ^ leaf + and not tr.fruits ^ leaf then + return false + end + end + + local y_top_trunk = p.y + -- search for the requisite amount of stem trunk nodes + for _ = 1, tr.stem_height_min-1 do + p.y = p.y-1 + if not is_trunk_of_tree(tr.trees, get_node(p)) then + return false + end + end + p.y = y_top_trunk + + local r = tr.range + local r_up = tr.range_up or r + local r_down = tr.range_down or r + + if yo > 0 then + -- the neighbour tree is higher than the one which needs to be capitated + -- therefore tag the stem trunks + for _ = 1, tr.stem_height_min do + tab[poshash(p)] = true + p.y = p.y-1 + end + + -- tag trunks below the minimum stem height (no 2x2 and + type support) + for _ = tr.stem_height_min+1, r_down do + p.y = p.y-1 + if not is_trunk_of_tree(tr.trees, get_node(p)) then + break + end + tab[poshash(p)] = true + end + -- actually only -r_down + yo - 1 to -r_down need to be added + p.y = y_top_trunk + end + + -- reduce x and z avoidance range for thick stem neighbour trees + if tr.stem_type == "2x2" then + r = r - 1 + elseif tr.stem_type == "+" then + r = r - 2 + end + + -- tag head positions which should not be removed + local z1 = math.max(-r + zo, -r) + local z2 = math.min(r + zo, r) + local y1 = math.max(-r_down + yo, -r_down) + local y2 = math.min(r_up + yo, r_up) + local x1 = math.max(-r + xo, -r) + local x2 = math.min(r + xo, r) + for z = z1,z2 do + for y = y1,y2 do + local i = poshash{x=x1, y=y, z=z} + for _ = x1,x2 do + tab[i] = true + i = i+1 + end + end + end + return true +end + +-- returns positions for leaves allowed to be dug +local function find_valid_head_ps(pos, head_ps, trunktop_ps, tr) + -- exclude the stem nodes + local before_stems = {} + for i = 1,#trunktop_ps do + local p = vector.subtract(trunktop_ps[i], pos) + before_stems[hash2(p.x, p.z)] = p.y+1 + end + + local r = tr.range + local r_up = tr.range_up or r + local r_down = tr.range_down or r + + -- firstly, detect neighbour trees of the same sort to not hurt them + local tab = {} + local rx2 = 2 * r + local rupdown = r_up + r_down + for z = -rx2, rx2 do + for x = -rx2, rx2 do + local bot = before_stems[hash2(x, z)] or -rupdown + for y = rupdown, bot, -1 do + if get_a_tree(pos, tab, tr, x,y,z) then + break + end + end + end + end + -- now, get the head positions without the neighbouring trees + local n = #head_ps + for z = -r,r do + for x = -r,r do + local bot = before_stems[hash2(x, z)] or -r_down + for y = bot,r_up do + local p = {x=x, y=y, z=z} + if not tab[poshash(p)] then + n = n+1 + head_ps[n] = vector.add(pos, p) + end + end + end + end + return n +end + +-- adds the stem to the trunks +local function get_stem(trunktop_ps, trunks, tr, head_ps) + if tr.cutting_leaves then + treecapitator.moretrees34(trunktop_ps, trunks, tr, head_ps, + get_node, is_trunk_of_tree) + return + end + for i = 1,#trunktop_ps do + local pos = trunktop_ps[i] + local node = get_node(pos) + while is_trunk_of_tree(tr.trees, node) do + trunks[#trunks+1] = {pos, node} + pos = {x=pos.x, y=pos.y+1, z=pos.z} + node = get_node(pos) + end + + -- renew trunk top position + pos.y = pos.y-1 + trunktop_ps[i] = pos + end +end + +-- part of healthy stem searching +local function here_neat_stemps(pos, tr) + local ps = {} + for i = 1,#tr.stem_offsets do + local o = tr.stem_offsets[i] + local p = {x = pos.x + o[1], y = pos.y, z = pos.z + o[2]} + -- air test is too simple (makeshift solution) + if get_node(p).name ~= "air" then + return + end + p.y = p.y+1 + if not is_trunk_of_tree(tr.trees, get_node(p)) then + return + end + ps[#ps+1] = p + end + return ps +end + +-- gives stem positions of a healthy tree +local function find_neat_stemps(pos, tr) + for i = 1,#tr.stem_offsets do + local o = tr.stem_offsets[i] + local p = {x = pos.x - o[1], y = pos.y, z = pos.z - o[2]} + local ps = here_neat_stemps(p, tr) + if ps then + return ps + end + end + -- nothing found +end + +-- part of incomplete stem searching +local function here_incomplete_stemps(pos, tr) + local ps = {} + for i = 1,#tr.stem_offsets do + local o = tr.stem_offsets[i] + local p = {x = pos.x + o[1], y = pos.y+1, z = pos.z + o[2]} + if is_trunk_of_tree(tr.trees, get_node(p)) then + p.y = p.y-1 + local node = get_node(p) + if is_trunk_of_tree(tr.trees, node) then + -- stem wasn't chopped enough + return {} + end + -- air test is too simple (makeshift solution) + if node.name == "air" then + p.y = p.y+1 + ps[#ps+1] = p + end + end + end + -- #ps ∈ [3] + return ps +end + +-- gives stem positions of an eroded tree +local function find_incomplete_stemps(pos, tr) + local ps + local stemcount = 0 + for i = 1,#tr.stem_offsets do + local o = tr.stem_offsets[i] + local p = {x = pos.x - o[1], y = pos.y, z = pos.z - o[2]} + local cps = here_incomplete_stemps(p, tr) + local cnt = #cps + if cnt == 0 then + -- player needs to chop more + return + end + if stemcount < cnt then + stemcount = #cps + ps = cps + end + end + return ps +end + +-- returns the lowest trunk node positions +local function get_stem_ps(pos, tr) + if not tr.stem_type then + -- 1x1 stem + return {{x=pos.x, y=pos.y+1, z=pos.z}} + end + return find_neat_stemps(pos, tr) + or find_incomplete_stemps(pos, tr) +end + +-- gets the middle position of the tree head +local function get_head_center(trunktop_ps, stem_type) + if stem_type == "2x2" then + -- return the highest position + local pos = trunktop_ps[1] + for i = 2,#trunktop_ps do + local p = trunktop_ps[i] + if p.y > pos.y then + pos = p + end + end + return pos + elseif stem_type == "+" then + -- return the middle position + local mid = vector.new() + for i = 1,#trunktop_ps do + mid = vector.add(mid, trunktop_ps[i]) + end + return vector.round(vector.divide(mid, #trunktop_ps)) + else + return trunktop_ps[1] + end +end + +function capitate_funcs.default(pos, tr, _, digger) + local trees = tr.trees + + -- get the stem trunks + local trunks = {} + local trunktop_ps = get_stem_ps(pos, tr) + if not trunktop_ps then + return + end + local head_ps = {} + get_stem(trunktop_ps, trunks, tr, head_ps) + + local leaves = tr.leaves + local fruits = tr.fruits + local hcp = get_head_center(trunktop_ps, tr.stem_type) + + -- abort if the tree lacks leaves/fruits + local ln = get_node{x=hcp.x, y=hcp.y+1, z=hcp.z} + if not leaves ^ ln.name + and not fruits ^ ln.name then + local leaf = get_node{x=hcp.x, y=hcp.y, z=hcp.z+1}.name + if not leaves ^ leaf + and not fruits ^ leaf then + return + end + end + + -- get leaves, fruits and stem fruits + local leaves_found = {} + local n = find_valid_head_ps(hcp, head_ps, trunktop_ps, tr) + local leaves_toremove = {} + local fruits_toremove = {} + for i = 1,n do + local p = head_ps[i] + local node = get_node(p) + local nodename = node.name + if not is_trunk_of_tree(trees, node) then + if leaves ^ nodename then + leaves_found[nodename] = true + leaves_toremove[#leaves_toremove+1] = {p, node} + elseif fruits ^ nodename then + fruits_toremove[#fruits_toremove+1] = {p, node} + end + elseif tr.trunk_fruit_vertical + and fruits ^ nodename then + trunks[#trunks+1] = {p, node} + end + end + + if tr.requisite_leaves then + -- abort if specific leaves weren't found + for i = 1,#tr.requisite_leaves do + if not leaves_found[tr.requisite_leaves[i]] then + return + end + end + end + + -- remove fruits at first due to attachment + -- and disable nodeupdate temporarily + local nodeupdate = minetest.check_for_falling + minetest.check_for_falling = function() end + for i = 1,#fruits_toremove do + destroy_node(fruits_toremove[i][1], fruits_toremove[i][2], digger) + end + minetest.check_for_falling = nodeupdate + local inv = digger:get_inventory() + for i = 1,#leaves_toremove do + remove_leaf(leaves_toremove[i][1], leaves_toremove[i][2], inv, digger) + end + for i = 1,#trunks do + destroy_node(trunks[i][1], trunks[i][2], digger) + end + + -- tree was capitated, play sound + tree_fall_sound(pos) + return true +end + +-- metatable for shorter code: trees ^ name ≙ name ∈ trees +local mt_default = { + __pow = table_contains +} +treecapitator.after_register.default = function(tr) + setmetatable(tr.trees, mt_default) + setmetatable(tr.leaves, mt_default) + setmetatable(tr.fruits, mt_default) + tr.range_up = tr.range_up or tr.range + tr.range_down = tr.range_down or tr.range + tr.stem_height_min = tr.stem_height_min or treecapitator.stem_height_min + + if tr.stem_type == "2x2" then + tr.stem_offsets = { + {0,0}, {1,0}, + {0,1}, {1,1}, + } + elseif tr.stem_type == "+" then + tr.stem_offsets = { + {0,0}, + {0,1}, + {-1,0}, {1,0}, + {0,-1}, + } + end +end + + +--------------------- Acacia tree function ------------------------------------- + +function capitate_funcs.acacia(pos, tr, node_above, digger) + local trunk = tr.trees[1] + + -- fill tab with the stem trunks + local tab, n = {{{x=pos.x, y=pos.y+1, z=pos.z}, node_above}}, 2 + local np = {x=pos.x, y=pos.y+2, z=pos.z} + local nd = get_node(np) + while trunk == nd.name + and nd.param2 < 4 do + tab[n] = {vector.new(np), nd} + n = n+1 + np.y = np.y+1 + nd = get_node(np) + end + np.y = np.y-1 + + local inv = digger:get_inventory() + for z = -1,1,2 do + for x = -1,1,2 do + -- add the other trunks to tab + local p = vector.new(np) + p.x = p.x+x + p.z = p.z+z + local nd = get_node(p) + if nd.name ~= trunk then + p.y = p.y+1 + nd = get_node(p) + if nd.name ~= trunk then + return + end + end + tab[n] = {vector.new(p), nd} + + p.x = p.x+x + p.z = p.z+z + p.y = p.y+1 + + if get_node(p).name ~= trunk then + return + end + tab[n+1] = {vector.new(p), nd} + n = n+2 + + -- get neighbouring acacia trunks for delimiting + local no_rms = {} + for z = -4,4 do + for x = -4,4 do + if math.abs(x+z) ~= 8 + and (x ~= 0 or z ~= 0) then + if get_node{x=p.x+x, y=p.y, z=p.z+z}.name == trunk + and get_node{x=p.x+x, y=p.y+1, z=p.z+z}.name == tr.leaf then + for z = math.max(-4, z-2), math.min(4, z+2) do + for x = math.max(-4, x-2), math.min(4, x+2) do + no_rms[(z+4)*9 + x+4] = true + end + end + end + end + end + end + + -- remove leaves + p.y = p.y+1 + local i = 0 + for z = -4,4 do + for x = -4,4 do + if not no_rms[i] then + local p = {x=p.x+x, y=p.y, z=p.z+z} + local node = get_node(p) + if node.name == tr.leaf then + remove_leaf(p, node, inv, digger) + end + end + i = i+1 + end + end + end + end + + -- play the sound, then dig the stem + tree_fall_sound(pos) + for i = 1,n-1 do + local pos_stem, node = unpack(tab[i]) + destroy_node(pos_stem, node, digger) + end + return true +end + + +----------------------- Palm tree function ------------------------------------- + +-- the 17 vectors used for walking the stem +local palm_stem_dirs +do + palm_stem_dirs = { + {0,1,0} + } + local n = 2 + for i = -1,1,2 do + palm_stem_dirs[n] = {i,0,0} + palm_stem_dirs[n+1] = {0,0,i} + n = n+2 + end + for i = -1,1,2 do + palm_stem_dirs[n] = {i,0,i} + palm_stem_dirs[n+1] = {i,0,-i} + n = n+2 + end + for i = -1,1,2 do + palm_stem_dirs[n] = {i,1,0} + palm_stem_dirs[n+1] = {0,1,i} + n = n+2 + end + for i = -1,1,2 do + palm_stem_dirs[n] = {i,1,i} + palm_stem_dirs[n+1] = {i,1,-i} + n = n+2 + end + for i = 1,17 do + local p = palm_stem_dirs[i] + palm_stem_dirs[i] = vector.new(unpack(p)) + end +end + +local pos_from_hash = minetest.get_position_from_hash + +-- gets a list of leaves positions +local function get_palm_head(hcp, tr, max_forbi) + local pos = {x=hcp.x, y=hcp.y+1, z=hcp.z} + local leaves = {} + if get_node(pos).name ~= tr.leaves then + -- search hub position + for xo = -1,1 do + for zo = -1,1 do + local p = {x=pos.x+xo, y=pos.y, z=pos.z+zo} + if get_node(p).name == tr.leaves then + pos = p + end + end + end + end + -- collect leaves + leaves[poshash(pos)] = true + for i = -1,1 do + for j = -1,1 do + -- don't search around the corner except max_forbi time(s) + local dirs = {{0,0}, {i,0}, {0,j}, {i,j}, {-i,0}, {0,-j}} + local avoids = {} + local todo = {pos} + local sp = 1 + while sp > 0 do + local p = todo[sp] + sp = sp-1 + -- only walk the "forbidden" dir if still allowed + local forbic = avoids[poshash(p)] or 0 + local dirc = 6 + if forbic == max_forbi then + dirc = dirc - 2 + end + -- walk the directions + for i = 1,dirc do + -- increase forbidden when needed + local forbinc = forbic + if i > 4 then + forbinc = forbinc+1 + end + local xz = dirs[i] + for y = -1,2 do + local p = {x=p.x+xz[1], y=p.y+y, z=p.z+xz[2]} + local ph = poshash(p) + local forbi = avoids[ph] + if not forbi + or forbi > forbinc then + avoids[ph] = forbinc + local dif = vector.subtract(p, pos) + if get_node(p).name == tr.leaves + and math.abs(dif.x) <= tr.range + and math.abs(dif.z) <= tr.range + and dif.y <= tr.range_up + and dif.y >= -tr.range_down then + sp = sp+1 + todo[sp] = p + leaves[ph] = true + end + end + end + end + end + end + end + local ps = {} + local num_leaves = 0 + for ph in pairs(leaves) do + num_leaves = num_leaves+1 + ps[num_leaves] = pos_from_hash(ph) + end + return ps, num_leaves +end + +-- returns positions for palm leaves allowed to be dug +local function palm_find_valid_head_ps(pos, head_ps, tr) + local r = tr.range + local r_up = tr.range_up or r + local r_down = tr.range_down or r + + -- firstly, detect neighbour palms' leaves to not hurt them + local tab = {} + local rx2 = 2 * r + local rupdown = r_up + r_down + for z = -rx2, rx2 do + for y = -rupdown, rupdown do + for x = -rx2, rx2 do + local hcp = {x=pos.x+x, y=pos.y+y, z=pos.z+z} + if not vector.equals(hcp, pos) + and get_node(hcp).name == tr.trunk_top then + local leaves, num_leaves = get_palm_head(hcp, tr, 0) + for i = 1, num_leaves do + tab[poshash(leaves[i])] = true + end + end + end + end + end + -- now, get the leaves positions without the neighbouring leaves + local leaves,lc = get_palm_head(pos, tr, tr.max_forbi) + local n = #head_ps + for i = 1,lc do + local p = leaves[i] + if not tab[poshash(p)] then + n = n+1 + head_ps[n] = p + end + end + return n +end + +function capitate_funcs.palm(startpos, tr, node_above, digger) + local trunk = tr.trees[1] + + -- walk the stem up to the fruit carrier + local pos_current = {x=startpos.x, y=startpos.y+1, z=startpos.z} + local trunks = {{pos_current, node_above}} + local trunk_found = true + local nohori = false + local hcp + while trunk_found + and not hcp do + trunk_found = false + for i = 1,17 do + local hori = i > 1 and i < 10 + if not hori + or not nohori then + local p = vector.add(pos_current, palm_stem_dirs[i]) + local node = get_node(p) + if node.name == trunk then + trunk_found = true + trunks[#trunks+1] = {p, node} + pos_current = p + nohori = hori + break + end + if node.name == tr.trunk_top then + hcp = p + trunks[#trunks+1] = {p, node} + break + end + end + end + end + if not hcp then + return + end + + -- collect coconuts + local fruits = {} + for zo = -1,1 do + for xo = -1,1 do + local p = {x=hcp.x+xo, y=hcp.y, z=hcp.z+zo} + local node = get_node(p) + if node.name:sub(1, #tr.fruit) == tr.fruit then + fruits[#fruits+1] = {p, node} + end + end + end + + -- find the leaves of the palm + local leaves_ps = {} + local lc = palm_find_valid_head_ps(hcp, leaves_ps, tr) + + tree_fall_sound(pos_current) + + local nodeupdate = minetest.check_for_falling + minetest.check_for_falling = function() end + for i = 1,#fruits do + local pos, node = unpack(fruits[i]) + destroy_node(pos, node, digger) + end + minetest.check_for_falling = nodeupdate + + for i = 1,#trunks do + local pos, node = unpack(trunks[i]) + destroy_node(pos, node, digger) + end + + local inv = digger:get_inventory() + for i = 1,lc do + local pos = leaves_ps[i] + remove_leaf(pos, get_node(pos), inv, digger) + end + return true +end + + +---------------------- A moretrees capitation function ------------------------- + +-- table iteration instead of recursion +local function get_tab(pos, func, max) + local todo = {pos} + local n = 1 + local tab_avoid = {[poshash(pos)] = true} + local tab_done,num = {pos},2 + while n ~= 0 do + local p = todo[n] + n = n-1 + --[[ + for i = -1,1,2 do + for _,p2 in pairs{ + {x=p.x+i, y=p.y, z=p.z}, + {x=p.x, y=p.y+i, z=p.z}, + {x=p.x, y=p.y, z=p.z+i}, + } do]] + for i = -1,1 do + for j = -1,1 do + for k = -1,1 do + local p2 = {x=p.x+i, y=p.y+j, z=p.z+k} + local vi = poshash(p2) + if not tab_avoid[vi] + and func(p2) then + n = n+1 + todo[n] = p2 + + tab_avoid[vi] = true + + tab_done[num] = p2 + num = num+1 + + if max + and num > max then + return false + end + end + end + end + end + end + return tab_done +end + +function capitate_funcs.moretrees(pos, tr, _, digger) + local trees = tr.trees + local leaves = tr.leaves + local fruits = tr.fruits + local minx = pos.x-tr.range + local maxx = pos.x+tr.range + local minz = pos.z-tr.range + local maxz = pos.z+tr.range + local maxy = pos.y+tr.height + local num_trunks = 0 + local num_leaves = 0 + local ps = get_tab({x=pos.x, y=pos.y+1, z=pos.z}, function(p) + if p.x < minx + or p.x > maxx + or p.z < minz + or p.z > maxz + or p.y > maxy then + return false + end + local nam = get_node(p).name + if table_contains(trees, nam) then + num_trunks = num_trunks+1 + elseif table_contains(leaves, nam) then + num_leaves = num_leaves+1 + elseif not table_contains(fruits, nam) then + return false + end + return true + end, tr.max_nodes) + if not ps then + print"no ps found" + return + end + if num_trunks < tr.num_trunks_min + or num_trunks > tr.num_trunks_max then + print("wrong trunks num: "..num_trunks) + return + end + if num_leaves < tr.num_leaves_min + or num_leaves > tr.num_leaves_max then + print("wrong leaves num: "..num_leaves) + return + end + tree_fall_sound(pos) + local inv = digger:get_inventory() + for _,p in pairs(ps) do + local node = get_node(p) + local nodename = node.name + if table_contains(leaves, nodename) then + remove_leaf(p, node, inv, digger) + else + destroy_node(p, node, digger) + end + end + return true +end + + +--------------------------- api interface -------------------------------------- + +-- the function which is used for capitating the tree +local capitating = false --necessary if minetest.node_dig is used +function treecapitator.capitate_tree(pos, digger) + if capitating + or not digger + or digger:get_player_control().sneak + or not treecapitator.capitation_allowed(pos, digger) then + return + end + local t1 = minetest.get_us_time() + capitating = true + local node_above = get_node{x=pos.x, y=pos.y+1, z=pos.z} + for i = 1,#treecapitator.trees do + local tr = treecapitator.trees[i] + if table_contains(tr.trees, node_above.name) + and node_above.param2 < 4 + and capitate_funcs[tr.type](pos, tr, node_above, digger) then + break + end + end + clean_cache() + capitating = false + minetest.log("info", "[treecapitator] tree capitated at (" .. + pos.x .. "|" .. pos.y .. "|" .. pos.z .. ") after ca. " .. + (minetest.get_us_time() - t1) / 1000000 .. " s") +end + +-- delayed capitating +local delay = treecapitator.delay +if delay > 0 then + local oldfunc = treecapitator.capitate_tree + function treecapitator.capitate_tree(...) + minetest.after(delay, function(...) + oldfunc(...) + end, ...) + end +end + + +local path = minetest.get_modpath"treecapitator" .. DIR_DELIM +dofile(path .. "api.lua") +dofile(path .. "trees.lua") +dofile(path .. "moretrees34.lua") + + +local time = (minetest.get_us_time() - load_time_start) / 1000000 +local msg = "[treecapitator] loaded after ca. " .. time .. " seconds." +if time > 0.01 then + print(msg) +else + minetest.log("info", msg) +end diff --git a/mods/treecapitator/mod.conf b/mods/treecapitator/mod.conf new file mode 100644 index 0000000..03269f8 --- /dev/null +++ b/mods/treecapitator/mod.conf @@ -0,0 +1,2 @@ +name = treecapitator +optional_depends = default, farming_plus, moretrees, ethereal diff --git a/mods/treecapitator/moretrees34.lua b/mods/treecapitator/moretrees34.lua new file mode 100644 index 0000000..b7490a6 --- /dev/null +++ b/mods/treecapitator/moretrees34.lua @@ -0,0 +1,87 @@ +-- This provides a workaround for the cutting leaves moretrees problem. + +function treecapitator.moretrees34(trunktop_ps, trunks, tr, head_ps, + get_node, is_trunk_of_tree +) + local trees = tr.trees + for i = 1,#trunktop_ps do + -- add the usual trunks + local pos = trunktop_ps[i] + local node = get_node(pos) + while is_trunk_of_tree(trees, node) do + trunks[#trunks+1] = {pos, node} + pos = {x=pos.x, y=pos.y+1, z=pos.z} + node = get_node(pos) + end + + -- meddle with the lacunarity + local ys = pos.y + local ye + local detected_trunks = {} + + -- search upwards until the gap is big enough or the tree ended + local foundleaves = 0 + while true do + if is_trunk_of_tree(trees, node) then + foundleaves = 0 + detected_trunks[pos.y] = node + pos.y = pos.y+1 + node = get_node(pos) + elseif tr.leaves ^ node.name + or tr.fruits ^ node.name then + foundleaves = foundleaves+1 + if foundleaves > tr.cutting_leaves then + -- cutting leaves count exceeded + ye = pos.y-foundleaves + break + end + pos.y = pos.y+1 + node = get_node(pos) + else + -- above the tree + ye = pos.y-1 + break + end + end + + -- search downwards until enough trunks are found above each other + -- or no such trunks are found + local ytop = ys-1 + local y = ye + local last_test = ys + tr.stem_height_min + while y >= last_test do + if detected_trunks[y] then + local too_short + for ty = y - tr.stem_height_min + 1, y-1 do + if not detected_trunks[y] then + too_short = true + y = ty-1 + break + end + end + if not too_short then + -- upper end found + ytop = y + break + end + end + y = y-1 + end + + if ytop >= ys then + -- add trunks and leaves/fruits + for y = ys, ytop do + local p = {x=pos.x, y=y, z=pos.z} + if detected_trunks[y] then + trunks[#trunks+1] = {p, detected_trunks[y]} + else + head_ps[#head_ps+1] = p + end + end + end + + -- renew trunk top position + pos.y = ytop + trunktop_ps[i] = pos + end +end diff --git a/mods/treecapitator/settingtypes.txt b/mods/treecapitator/settingtypes.txt new file mode 100644 index 0000000..bbe0c3d --- /dev/null +++ b/mods/treecapitator/settingtypes.txt @@ -0,0 +1,24 @@ +# drop items, else get them in the inventory +treecapitator.drop_items (drop items) bool false + +# if set to false, do not drop leaves, just saplings (leafdecay behaviour) +treecapitator.drop_leaf (give leaves) bool false + +# play a sound after digging a tree +treecapitator.play_sound (play sound) bool true + +# capitate moretrees' trees, experimental, use with caution +treecapitator.moretrees_support (support moretrees) bool false + +# lets trees become capitated seconds later, only works if it's > 0 +treecapitator.delay (delay) float 0 0 + +# If set to true, capitating only works with tools with trunk digging +# capabilities and not with the bare hand, so when a tool is worn. +treecapitator.no_hand_capitation (Disallow bare hand capitation) bool false + +# considers trunks as neighbour trees if there're >= stem_height_min trunks +# if set too low, vertical trunk fruits may be recognized as neighbour tree +# if set too high, short stemmed neighbour trees are not recognited +# stem_height_min can explicitly be overridden in tree definitions +treecapitator.stem_height_min (minimum neighbour stem height) int 3 2 diff --git a/mods/treecapitator/sounds/tree_falling.ogg b/mods/treecapitator/sounds/tree_falling.ogg new file mode 100644 index 0000000..3e838ac Binary files /dev/null and b/mods/treecapitator/sounds/tree_falling.ogg differ diff --git a/mods/treecapitator/trees.lua b/mods/treecapitator/trees.lua new file mode 100644 index 0000000..3b5dc8f --- /dev/null +++ b/mods/treecapitator/trees.lua @@ -0,0 +1,468 @@ +if minetest.get_modpath("default") then + if minetest.get_mapgen_setting"mg_name" == "v6" then + treecapitator.register_tree{ + trees = {"default:tree"}, + leaves = {"default:leaves"}, + range = 2, + fruits = {"default:apple"} + } + + treecapitator.register_tree({ + trees = {"default:jungletree"}, + leaves = {"default:jungleleaves"}, + range = 3 + }) + else + treecapitator.register_tree{ + trees = {"default:tree"}, + leaves = {"default:leaves"}, + range = 2, + range_up = 4, + range_down = 0, + fruits = {"default:apple", "default:tree"}, + trunk_fruit_vertical = true + } + + treecapitator.register_tree({ + trees = {"default:jungletree"}, + leaves = {"default:jungleleaves"}, + fruits = {"default:jungletree"}, + range = 4, + range_up = 14, + range_down = 5, + trunk_fruit_vertical = true, + stem_height_min = 12, + }) + + treecapitator.register_tree({ + trees = {"default:jungletree"}, + leaves = {"default:jungleleaves"}, + fruits = {"default:jungletree"}, + range = 4, + range_up = 14, + range_down = 3, + trunk_fruit_vertical = true, + stem_type = "2x2", + stem_height_min = 12, + }) + end + + treecapitator.register_tree({ + trees = {"default:pine_tree"}, + leaves = {"default:pine_needles"}, + -- the +2 height is used to also support the coned pine trees + range_up = 2 +2, + range_down = 6, + range = 3, + }) + + treecapitator.register_tree({ + trees = {"default:acacia_tree"}, + leaf = "default:acacia_leaves", + no_param2test = true, + --leavesrange = 4, + type = "acacia" + }) + + treecapitator.register_tree({ + trees = {"default:aspen_tree"}, + leaves = {"default:aspen_leaves"}, + range = 4, + }) +end + +if minetest.get_modpath("farming_plus") then + treecapitator.register_tree({ + trees = {"default:tree"}, + leaves = {"farming_plus:banana_leaves"}, + range = 2, + fruits = {"farming_plus:banana"} + }) + + treecapitator.register_tree({ + trees = {"default:tree"}, + leaves = {"farming_plus:cocoa_leaves"}, + range = 2, + fruits = {"farming_plus:cocoa"} + }) +end + +if treecapitator.moretrees_support +and minetest.get_modpath("moretrees") then + treecapitator.register_tree({ + trees = {"moretrees:acacia_trunk"}, + leaves = {"moretrees:acacia_leaves"}, + range = 10, + }) + + treecapitator.register_tree{ + trees = {"moretrees:poplar_trunk"}, + leaves = {"moretrees:poplar_leaves"}, + range_up = 5, + range_down = 17, + range = 2, + } + + local dates = {"moretrees:dates_fn", "moretrees:dates_m0", + "moretrees:dates_n"} + for i = 0, 4 do + dates[#dates+1] = "moretrees:dates_f" .. i + end + dates[#dates+1] = "moretrees:date_palm_trunk" + treecapitator.register_tree{ + trees = { + "moretrees:date_palm_trunk", + "moretrees:date_palm_mfruit_trunk", + "moretrees:date_palm_ffruit_trunk" + }, + leaves = {"moretrees:date_palm_leaves"}, + fruits = dates, + trunk_fruit_vertical = true, + range = 11, + range_up = 15, + range_down = 0, + } + + treecapitator.register_tree{ + trees = {"moretrees:apple_tree_trunk"}, + leaves = {"moretrees:apple_tree_leaves"}, + fruits = {"default:apple", "moretrees:apple_tree_trunk"}, + trunk_fruit_vertical = true, + range = 9, + range_up = 3, + range_down = 4, + } + + treecapitator.register_tree{ + trees = {"moretrees:beech_trunk"}, + leaves = {"moretrees:beech_leaves"}, + range = 4, + range_down = 2, + range_up = 3, + fruits = {"moretrees:beech_trunk"}, + trunk_fruit_vertical = true + } + + treecapitator.register_tree{ + trees = {"moretrees:birch_trunk"}, + leaves = {"moretrees:birch_leaves"}, + fruits = {"moretrees:birch_trunk"}, + trunk_fruit_vertical = true, + cutting_leaves = 3, + stem_height_min = 4, + range = 8, + range_down = 13, + range_up = 10, + } + + treecapitator.register_tree{ + trees = {"moretrees:fir_trunk"}, + leaves = {"moretrees:fir_leaves", "moretrees:fir_leaves_bright"}, + range_up = 2, + range_down = 21, + range = 7, + fruits = {"moretrees:fir_cone", "moretrees:fir_trunk"}, + trunk_fruit_vertical = true + } + + treecapitator.register_tree({ + trees = {"moretrees:jungletree_trunk"}, + leaves = {"moretrees:jungletree_leaves_green", + "jungletree_leaves_yellow", "jungletree_leaves_red"}, + range = 8, + }) + + treecapitator.register_tree{ + trees = {"moretrees:oak_trunk"}, + leaves = {"moretrees:oak_leaves"}, + fruits = {"moretrees:acorn", "moretrees:oak_trunk"}, + trunk_fruit_vertical = true, + stem_type = "+", + range = 11, + range_up = 11, + range_down = 1, + } + + -- needs special type + treecapitator.register_tree({ + trees = {"moretrees:cedar_trunk"}, + leaves = {"moretrees:cedar_leaves"}, + range = 10, + range_up = 1, + range_down = 19, + trunk_fruit_vertical = true, + fruits = {"moretrees:cedar_cone", "moretrees:cedar_trunk"} + }) + + treecapitator.register_tree{ + trees = {"moretrees:rubber_tree_trunk", + "moretrees:rubber_tree_trunk_empty"}, + leaves = {"moretrees:rubber_tree_leaves"}, + fruits = {"moretrees:rubber_tree_trunk", + "moretrees:rubber_tree_trunk_empty"}, + trunk_fruit_vertical = true, + stem_type = "2x2", + range = 8, + range_down = 1, + range_up = 8, + } + + treecapitator.register_tree{ + trees = {"moretrees:sequoia_trunk"}, + leaves = {"moretrees:sequoia_leaves"}, + fruits = {"moretrees:sequoia_trunk"}, + trunk_fruit_vertical = true, + stem_type = "+", + range = 10, + range_up = 3, + range_down = 33, + cutting_leaves = 6, + stem_height_min = 6, + } + + treecapitator.register_tree{ + trees = {"moretrees:spruce_trunk"}, + leaves = {"moretrees:spruce_leaves"}, + fruits = {"moretrees:spruce_cone", "moretrees:spruce_trunk"}, + trunk_fruit_vertical = true, + cutting_leaves = 1, + stem_type = "+", + range = 10, + range_down = 25, + range_up = 5, + } + + treecapitator.register_tree{ + trees = {"moretrees:willow_trunk"}, + leaves = {"moretrees:willow_leaves"}, + fruits = {"moretrees:willow_trunk"}, + trunk_fruit_vertical = true, + stem_type = "+", + range = 13, + range_up = 6, + range_down = 6, + } + + treecapitator.register_tree{ -- small and 2x2 jungletree at once + trees = {"moretrees:jungletree_trunk"}, + leaves = {"default:jungleleaves", "moretrees:jungletree_leaves_red"}, + fruits = {"moretrees:jungletree_trunk"}, + requisite_leaves = {"moretrees:jungletree_leaves_red"}, + trunk_fruit_vertical = true, + stem_height_min = 4, + cutting_leaves = 5, + stem_type = "2x2", + range = 8, -- 5 small + range_up = 2, -- 1 small + range_down = 17, -- 6 small + } + + treecapitator.register_tree{ + trees = {"moretrees:jungletree_trunk"}, + leaves = {"default:jungleleaves", "moretrees:jungletree_leaves_yellow", + "moretrees:jungletree_leaves_red"}, + fruits = {"moretrees:jungletree_trunk"}, + requisite_leaves = {"moretrees:jungletree_leaves_yellow"}, + trunk_fruit_vertical = true, + cutting_leaves = 5, + stem_type = "+", + range = 8, + range_up = 4, + range_down = 16, + } + + treecapitator.register_tree{ + trees = {"moretrees:palm_trunk"}, + trunk_top = "moretrees:palm_fruit_trunk", + leaves = "moretrees:palm_leaves", + fruit = "moretrees:coconut", + range = 10, + range_up = 7, + range_down = 4, + max_forbi = 2, + type = "palm", + } + + --~ treecapitator.register_tree({ + --~ trees = {"moretrees:sequoia_trunk"}, + --~ leaves = {"moretrees:sequoia_leaves"}, + --~ range = 8, + + + --~ height = 17, + --~ max_nodes = 8000, + --~ num_trunks_min = 5, + --~ num_trunks_max = 400, + --~ num_leaves_min = 10, + --~ num_leaves_max = 4000, + --~ type = "moretrees", + --~ }) + + --~ treecapitator.register_tree({ + --~ trees = {"moretrees:willow_trunk"}, + --~ leaves = {"moretrees:willow_leaves"}, + --~ range = 11, + --~ height = 17, + --~ max_nodes = 8000, + --~ num_trunks_min = 5, + --~ num_trunks_max = 400, + --~ num_leaves_min = 10, + --~ num_leaves_max = 4000, + --~ type = "moretrees", + --~ }) +end + +-- code from amadin and narrnika +if minetest.get_modpath("ethereal") then + treecapitator.register_tree({--jungle [эвкалипт] + trees = {"default:jungletree"}, + leaves = {"default:jungleleaves"}, + range = 3, + height = 20, + max_nodes = 145, + num_trunks_min = 0, + num_trunks_max = 35, + num_leaves_min = 0, + num_leaves_max = 110, + type = "moretrees", + }) + treecapitator.register_tree({--pine [кедр] + trees = {"default:pinetree"}, -- this may need to be changed to pine_tree + leaves = {"ethereal:pineleaves"}, + range = 6, + type = "default", + }) + treecapitator.register_tree({--orange [апельсиновое дерево] + trees = {"default:tree"}, + leaves = {"default:leaves", "ethereal:orange_leaves"}, + fruits = {"default:apple", "ethereal:orange"}, + range = 2, + type = "default", + }) + treecapitator.register_tree({--acacia [акация] + trees = {"ethereal:acacia_trunk"}, + leaves = {"ethereal:acacia_leaves"}, + range = 10, + height = 10, + max_nodes = 122, + num_trunks_min = 0, + num_trunks_max = 22, + num_leaves_min = 0, + num_leaves_max = 100, + type = "moretrees", + }) + treecapitator.register_tree({--banana [банановое дерево] + trees = {"ethereal:banana_trunk"}, + leaves = {"ethereal:bananaleaves"}, + fruits = {"ethereal:banana"}, + range = 3, + height = 7, + max_nodes = 28, + num_trunks_min = 0, + num_trunks_max = 4, + num_leaves_min = 0, + num_leaves_max = 20, + type = "moretrees", + }) + treecapitator.register_tree({--coconut [кокосовое дерево] + trees = {"ethereal:palm_trunk"}, + leaves = {"ethereal:palmleaves"}, + fruits = {"ethereal:coconut"}, + range = 4, + height = 9, + max_nodes = 37, + num_trunks_min = 0, + num_trunks_max = 8, + num_leaves_min = 0, + num_leaves_max = 25, + type = "moretrees", + }) + treecapitator.register_tree({--willow [ива] + trees = {"ethereal:willow_trunk"}, + leaves = {"ethereal:willow_twig"}, + range = 10, + height = 13, + max_nodes = 540, + num_trunks_min = 0, + num_trunks_max = 90, + num_leaves_min = 0, + num_leaves_max = 450, + type = "moretrees", + }) + treecapitator.register_tree({--moshroom [гриб] + trees = {"ethereal:mushroom_trunk"}, + leaves = {"ethereal:mushroom", "ethereal:mushroom_pore"}, + range = 4, + height = 10, + max_nodes = 100, + num_trunks_min = 0, + num_trunks_max = 32, + num_leaves_min = 0, + num_leaves_max = 80, + type = "moretrees", + }) + treecapitator.register_tree({--sakura + trees = {"ethereal:sakura_trunk"}, + leaves = {"ethereal:sakura_leaves", "ethereal:sakura_leaves2"}, + range = 7, + height = 10, + max_nodes = 228, + num_trunks_min = 0, + num_trunks_max = 24, + num_leaves_min = 0, + num_leaves_max = 204, + type = "moretrees", + }) + treecapitator.register_tree({--lemon + trees = {"default:tree"}, + leaves = {"ethereal:lemon_leaves"}, + fruits = {"ethereal:lemon"}, + range = 4, + range_up = 8, + stem_height_min = 3, + }) + treecapitator.register_tree({--frosttree + trees = {"ethereal:frost_tree"}, + leaves = {"ethereal:frost_leaves"}, + stem_type = "2x2", + stem_height_min = 3, + range = 5, + range_up = 19, + }) + treecapitator.register_tree({--birch + trees = {"ethereal:birch_trunk"}, + leaves = {"ethereal:birch_leaves"}, + range = 3, + range_up = 7, + stem_height_min = 3, + }) + treecapitator.register_tree({--yellowtree + trees = {"ethereal:yellow_trunk"}, + leaves = {"ethereal:yellowleaves"}, + fruits = {"ethereal:golden_apple"}, + range = 5, + height = 19, + max_nodes = 149, + num_trunks_min = 0, + num_trunks_max = 54, + num_leaves_min = 0, + num_leaves_max = 85, + stem_height_min = 13, + type = "moretrees", + }) + treecapitator.register_tree({--olive + trees = {"ethereal:olive_trunk"}, + leaves = {"ethereal:olive_leaves"}, + fruits = {"ethereal:olive"}, + range = 4, + height = 9, + max_nodes = 94, + num_trunks_min = 0, + num_trunks_max = 27, + num_leaves_min = 0, + num_leaves_max = 59, + stem_height_min = 8, + type = "moretrees", + }) + +end