started to add treecapitator

This commit is contained in:
2026-09-12 03:29:43 -06:00
parent a0a0acfe1a
commit cc0fa41e3b
12 changed files with 1891 additions and 0 deletions
+5
View File
@@ -0,0 +1,5 @@
## Generic ignorable patterns and files
*~
.*.swp
debug.txt
*.patch
+15
View File
@@ -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"}
+3
View File
@@ -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.
+50
View File
@@ -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).<br/>
Holding shift you can avoid its function to dig the single trunk node.<br/>
Neighbour trees are detected and preserved.<br/>
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).<br/>
[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)<br/>
**License:** see [LICENSE.txt](https://raw.githubusercontent.com/HybridDog/treecapitator/master/LICENSE.txt)<br/>
**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)<br/>
↑ 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
+94
View File
@@ -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
+147
View File
@@ -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 = {<node1>, <node2>, ...},
leaves = {<node1>, <node2>, ...},
range = <range>,
range_up = <range>,
range_down = <range>,
fruits = {<node1>, <node2>, ...},
trunk_fruit_vertical = <some_boolean>,
cutting_leaves = <leaves_count>,
stem_type = <some_string>,
stem_height_min = <height>,
requisite_leaves = {<node1>, <node2>, ...},
})
```
* 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. <br/>
In comparison to `fruits`, these nodes are affected
by the drop_leaf setting.
* `fruits`
Like `leaves` but unaffected by the drop_leaf setting. <br/>
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. <br/>
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. <br/>
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. <br/>
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 `"+"`. <br/>
`"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 = {<node1>, <node2>, ...},
leaves = {<node1>, <node2>, ...},
range = <range>,
fruits = {<node1>, <node2>, ...},
height = <height>,
max_nodes = <max_nodes>,
num_trunks_min = <some_number>,
num_trunks_max = <some_number>,
num_leaves_min = <some_number>,
num_leaves_max = <some_number>,
})
```
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`. <br/>
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. <br/>
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. <br/>
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.
+996
View File
@@ -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
+2
View File
@@ -0,0 +1,2 @@
name = treecapitator
optional_depends = default, farming_plus, moretrees, ethereal
+87
View File
@@ -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
+24
View File
@@ -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 <delay> 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
Binary file not shown.
+468
View File
@@ -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