summaryrefslogtreecommitdiff
path: root/moretrees
diff options
context:
space:
mode:
authorVanessa Ezekowitz <vanessaezekowitz@gmail.com>2016-07-02 21:07:49 -0400
committerVanessa Ezekowitz <vanessaezekowitz@gmail.com>2016-07-02 21:07:49 -0400
commitdd3d366a77e77fd93f6db9243a8d6719335e6895 (patch)
treed9fbb6c0b9992b32952a239bee19403d657c9123 /moretrees
parentf18f36ac0d5416311b74160942e5f4946742c7d1 (diff)
downloaddreambuilder_modpack-dd3d366a77e77fd93f6db9243a8d6719335e6895.tar
dreambuilder_modpack-dd3d366a77e77fd93f6db9243a8d6719335e6895.tar.gz
dreambuilder_modpack-dd3d366a77e77fd93f6db9243a8d6719335e6895.tar.bz2
dreambuilder_modpack-dd3d366a77e77fd93f6db9243a8d6719335e6895.tar.xz
dreambuilder_modpack-dd3d366a77e77fd93f6db9243a8d6719335e6895.zip
updated areas, biome_lib, plantlife, moreblocks, moretrees, pipeworks,
and worldedit mods
Diffstat (limited to 'moretrees')
-rw-r--r--moretrees/LICENSE9
-rw-r--r--moretrees/biome_defs.lua34
-rw-r--r--moretrees/cocos_palm.lua283
-rw-r--r--moretrees/crafts.lua83
-rw-r--r--moretrees/date_palm.lua746
-rw-r--r--moretrees/default_settings.txt62
-rw-r--r--moretrees/depends.txt2
-rw-r--r--moretrees/init.lua9
-rw-r--r--moretrees/node_defs.lua7
-rw-r--r--moretrees/textures/moretrees_coconut_0.pngbin0 -> 877 bytes
-rw-r--r--moretrees/textures/moretrees_coconut_1.pngbin0 -> 429 bytes
-rw-r--r--moretrees/textures/moretrees_coconut_2.pngbin0 -> 1009 bytes
-rw-r--r--moretrees/textures/moretrees_date.pngbin0 -> 2118 bytes
-rw-r--r--moretrees/textures/moretrees_date_nut_bar.pngbin0 -> 685 bytes
-rw-r--r--moretrees/textures/moretrees_date_nut_batter.pngbin0 -> 608 bytes
-rw-r--r--moretrees/textures/moretrees_date_nut_cake.pngbin0 -> 695 bytes
-rw-r--r--moretrees/textures/moretrees_date_nut_snack.pngbin0 -> 450 bytes
-rw-r--r--moretrees/textures/moretrees_date_palm_leaves.pngbin0 -> 757 bytes
-rw-r--r--moretrees/textures/moretrees_date_palm_sapling.pngbin0 -> 261 bytes
-rw-r--r--moretrees/textures/moretrees_date_palm_trunk.pngbin0 -> 1196 bytes
-rw-r--r--moretrees/textures/moretrees_date_palm_trunk_top.pngbin0 -> 673 bytes
-rw-r--r--moretrees/textures/moretrees_date_palm_wood.pngbin0 -> 735 bytes
-rw-r--r--moretrees/textures/moretrees_dates.pngbin0 -> 9421 bytes
-rw-r--r--moretrees/textures/moretrees_dates_f0.pngbin0 -> 3848 bytes
-rw-r--r--moretrees/textures/moretrees_dates_f1.pngbin0 -> 4137 bytes
-rw-r--r--moretrees/textures/moretrees_dates_f2.pngbin0 -> 9070 bytes
-rw-r--r--moretrees/textures/moretrees_dates_f3.pngbin0 -> 8295 bytes
-rw-r--r--moretrees/textures/moretrees_dates_f4.pngbin0 -> 9237 bytes
-rw-r--r--moretrees/textures/moretrees_dates_fn.pngbin0 -> 2589 bytes
-rw-r--r--moretrees/textures/moretrees_dates_m0.pngbin0 -> 4138 bytes
-rw-r--r--moretrees/textures/moretrees_dates_n.pngbin0 -> 2107 bytes
-rw-r--r--moretrees/tree_biomes.txt2
-rw-r--r--moretrees/tree_models.lua34
33 files changed, 1268 insertions, 3 deletions
diff --git a/moretrees/LICENSE b/moretrees/LICENSE
index c4742e6..7cffc99 100644
--- a/moretrees/LICENSE
+++ b/moretrees/LICENSE
@@ -4,6 +4,15 @@ Minetest mod moretrees
All source code:
© 2013, Vanessa Ezekowitz <vanessaezekowitz@gmail.com>
Published under the terms and conditions of the WTFPL.
+Date & cocos palm code (date_palm.lua, cocos_palm.lua)
+ © 2016, Rogier <rogier777@gmail.com>
+ Published under the terms and conditions of the WTFPL.
+All date & date palm textures, date-based food, cocos flower & green coconuts:
+ © 2016, Rogier <rogier777@gmail.com>
+ Published under the terms and conditions of CC-BY-SA-3.0 Unported.
+ - Three of the date palm textures are modifications of existing moretrees textures
+ - The green coconuts are a modification of the brown coconut
+ - The date cake batter is a modification of the acorn muffin batter
All sapling textures (textures/*_sapling.png):
© 2013, Tim Huppertz <mitroman@naturalnet.de>
Published under the terms and conditions of CC-BY-SA-3.0 Unported.
diff --git a/moretrees/biome_defs.lua b/moretrees/biome_defs.lua
index 48efee0..46228f5 100644
--- a/moretrees/biome_defs.lua
+++ b/moretrees/biome_defs.lua
@@ -24,6 +24,40 @@ moretrees.palm_biome = {
max_count = 10,
}
+moretrees.date_palm_biome = {
+ surface = "default:desert_sand",
+ avoid_nodes = moretrees.avoidnodes,
+ avoid_radius = 10,
+ seed_diff = 339,
+ min_elevation = -1,
+ max_elevation = 10,
+ near_nodes = {"default:water_source"},
+ near_nodes_size = 20,
+ near_nodes_count = 100,
+ near_nodes_vertical = 20,
+ temp_min = -0.20,
+ humidity_max = 0.20,
+ rarity = 10,
+ max_count = 30,
+}
+
+moretrees.date_palm_biome_2 = {
+ surface = "default:desert_sand",
+ avoid_nodes = moretrees.avoidnodes,
+ avoid_radius = 10,
+ seed_diff = 340,
+ min_elevation = 11,
+ max_elevation = 30,
+ near_nodes = {"default:water_source"},
+ near_nodes_size = 1,
+ near_nodes_count = 1,
+ near_nodes_vertical = 30,
+ temp_min = -0.20,
+ humidity_max = 0.20,
+ rarity = 10,
+ max_count = 30,
+}
+
moretrees.apple_tree_biome = {
surface = "default:dirt_with_grass",
avoid_nodes = moretrees.avoidnodes,
diff --git a/moretrees/cocos_palm.lua b/moretrees/cocos_palm.lua
new file mode 100644
index 0000000..8dea25f
--- /dev/null
+++ b/moretrees/cocos_palm.lua
@@ -0,0 +1,283 @@
+local S = moretrees.intllib
+
+-- © 2016, Rogier <rogier777@gmail.com>
+
+-- Some constants
+
+local coconut_drop_ichance = 8
+
+-- Make the cocos palm fruit trunk a real trunk (it is generated as a fruit)
+local trunk = minetest.registered_nodes["moretrees:palm_trunk"]
+local ftrunk = {}
+local gftrunk = {}
+for k,v in pairs(trunk) do
+ ftrunk[k] = v
+ gftrunk[k] = v
+end
+ftrunk.tiles = {}
+gftrunk.tiles = {}
+for k,v in pairs(trunk.tiles) do
+ ftrunk.tiles[k] = v
+ gftrunk.tiles[k] = v
+end
+ftrunk.drop = "moretrees:palm_trunk"
+gftrunk.drop = "moretrees:palm_trunk"
+ftrunk.after_destruct = function(pos, oldnode)
+ local coconuts = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y, z=pos.z-1}, {x=pos.x+1, y=pos.y, z=pos.z+1}, {"group:moretrees_coconut"})
+ for _,coconutpos in pairs(coconuts) do
+ -- minetest.dig_node(coconutpos) does not cause nearby coconuts to be dropped :-( ...
+ --minetest.dig_node(coconutpos)
+ local items = minetest.get_node_drops(minetest.get_node(coconutpos).name)
+ minetest.remove_node(coconutpos)
+ for _, itemname in pairs(items) do
+ minetest.add_item(coconutpos, itemname)
+ end
+ end
+end
+-- Make the different trunk types distinguishable (but barely)
+ftrunk.tiles[1] = "moretrees_palm_trunk_top.png^[transformR90"
+gftrunk.tiles[1] = "moretrees_palm_trunk_top.png^[transformR180"
+gftrunk.description = gftrunk.description.." (gen)"
+minetest.register_node("moretrees:palm_fruit_trunk", ftrunk)
+minetest.register_node("moretrees:palm_fruit_trunk_gen", gftrunk)
+
+local coconut_regrow_abm_spec = {
+ nodenames = { "moretrees:palm_fruit_trunk" },
+ interval = moretrees.coconut_flower_interval,
+ chance = moretrees.coconut_flower_chance,
+ action = function(pos, node, active_object_count, active_object_count_wider)
+ local coconuts = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y, z=pos.z-1}, {x=pos.x+1, y=pos.y, z=pos.z+1}, "group:moretrees_coconut")
+ -- Expected growth interval increases exponentially with number of coconuts already hanging.
+ -- Also: if more coconuts are hanging, the chance of picking an empty spot decreases as well...
+ if math.random(2^#coconuts) <= 2 then
+ -- Grow in area of 3x3 round trunk
+ local dx=math.floor(math.random(3)-2)
+ local dz=math.floor(math.random(3)-2)
+ local coconutpos = {x=pos.x+dx, y=pos.y, z=pos.z+dz}
+ local coconutnode = minetest.get_node(coconutpos)
+ if coconutnode.name == "air" then
+ minetest.set_node(coconutpos, {name="moretrees:coconut_0"})
+ end
+ end
+ end
+}
+if moretrees.coconuts_regrow then
+ minetest.register_abm(coconut_regrow_abm_spec)
+end
+
+-- Spawn initial coconuts
+
+-- Spawn initial coconuts
+-- (Instead of coconuts, a generated-palm fruit trunk is generated with the tree. This
+-- ABM converts the trunk to a regular fruit trunk, and spawns some coconuts)
+minetest.register_abm({
+ nodenames = { "moretrees:palm_fruit_trunk_gen" },
+ interval = 1,
+ chance = 1,
+ action = function(pos, node, active_object_count, active_object_count_wider)
+ minetest.swap_node(pos, {name="moretrees:palm_fruit_trunk"})
+ local poslist = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y, z=pos.z-1}, {x=pos.x+1, y=pos.y, z=pos.z+1}, "air")
+ local genlist = {}
+ for k,v in pairs(poslist) do
+ genlist[k] = {x = math.random(100), pos = v}
+ end
+ table.sort(genlist, function(a, b) return a.x < b.x; end)
+ local gen
+ local count = 0
+ for _,gen in pairs(genlist) do
+ minetest.set_node(gen.pos, {name = "moretrees:coconut_3"})
+ count = count + 1
+ if count == 4 then
+ break
+ end
+ end
+ end,
+})
+
+-- Register coconuts, and make them regrow
+
+local coconut_growfn = function(pos, elapsed)
+ local node = minetest.get_node(pos)
+ local delay = moretrees.coconut_grow_interval
+ if not node then
+ return
+ elseif not moretrees.coconuts_regrow then
+ -- Regrowing has been turned off. Make coconust grow instantly
+ minetest.swap_node(pos, {name="moretrees:coconut_3"})
+ return
+ elseif node.name == "moretrees:coconut_3" then
+ -- Drop coconuts (i.e. remove them), so that new coconuts can grow.
+ -- Coconuts will drop as items with a small chance
+ if math.random(coconut_drop_ichance) == 1 then
+ if moretrees.coconut_item_drop_ichance > 0 and math.random(moretrees.coconut_item_drop_ichance) == 1 then
+ local items = minetest.get_node_drops(minetest.get_node(pos).name)
+ for _, itemname in pairs(items) do
+ minetest.add_item(pos, itemname)
+ end
+ end
+ minetest.remove_node(pos)
+ end
+ else
+ -- Grow coconuts to the next stage
+ local offset = string.len("moretrees:coconut_x")
+ local n = string.sub(node.name, offset)
+ minetest.swap_node(pos, {name=string.sub(node.name, 1, offset-1)..n+1})
+ end
+ -- Don't catch up when elapsed time is large. Regular visits are needed for growth...
+ local timer = minetest.get_node_timer(pos)
+ timer:start(delay + math.random(moretrees.coconut_grow_interval))
+end
+
+local coconut_starttimer = function(pos, elapsed)
+ local timer = minetest.get_node_timer(pos)
+ local base_interval = moretrees.coconut_grow_interval * 2 / 3
+ timer:set(base_interval + math.random(base_interval), elapsed or 0)
+end
+
+for _,suffix in ipairs({"_0", "_1", "_2", "_3", ""}) do
+ local name
+ if suffix == "_0" then
+ name = S("Coconut Flower")
+ else
+ name = S("Coconut")
+ end
+ local drop = ""
+ local coco_group = 1
+ local tile = "moretrees_coconut"..suffix..".png"
+ local timerfn = coconut_growfn
+ local constructfn = coconut_starttimer
+ if suffix == "_3" then
+ drop = "moretrees:coconut"
+ tile = "moretrees_coconut.png"
+ elseif suffix == "" then
+ drop = nil
+ coco_group = nil
+ timerfn = nil
+ constructfn = nil
+ end
+ local coconutdef = {
+ description = name,
+ tiles = {tile},
+ drawtype = "plantlike",
+ paramtype = "light",
+ sunlight_propagates = true,
+ walkable = false,
+ groups = { fleshy=3, dig_immediate=3, flammable=2, moretrees_coconut=coco_group },
+ inventory_image = tile.."^[transformR180",
+ wield_image = tile.."^[transformR180",
+ sounds = default.node_sound_defaults(),
+ drop = drop,
+ selection_box = {
+ type = "fixed",
+ fixed = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3}
+ },
+ on_timer = timerfn,
+ on_construct = constructfn,
+
+ }
+ minetest.register_node("moretrees:coconut"..suffix, coconutdef)
+end
+
+-- convert exisiting cocos palms. This is a bit tricky...
+-- Try to make sure that this is indeed a generated tree, and not manually-placed trunks and/or coconuts
+if moretrees.coconuts_convert_existing_palms then
+ local spec = {
+ name = "moretrees:convert_existing_cocos_palms_to_regrow_coconuts",
+ nodenames = "moretrees:coconut",
+ action = function(pos, node, active_object_count, active_object_count_wider)
+ local trunks
+ local cvtrunks
+ local leaves
+ local coconuts
+ -- One regular trunk must be adjacent to the coconut
+ trunks = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y, z=pos.z-1}, {x=pos.x+1, y=pos.y, z=pos.z+1}, "moretrees:palm_trunk")
+ if #trunks ~= 1 then
+ return
+ end
+ local tpos = trunks[1]
+ -- 1 or 2 other trunks must be one level below to the trunk being converted.
+ trunks = minetest.find_nodes_in_area({x=tpos.x-1, y=tpos.y-1, z=tpos.z-1}, {x=tpos.x+1, y=tpos.y-1, z=tpos.z+1}, "moretrees:palm_trunk")
+ if #trunks < 1 or #trunks > 2 then
+ return
+ end
+ -- 1 or 2 other trunks must be two levels below to the trunk being converted.
+ trunks = minetest.find_nodes_in_area({x=tpos.x-1, y=tpos.y-2, z=tpos.z-1}, {x=tpos.x+1, y=tpos.y-2, z=tpos.z+1}, "moretrees:palm_trunk")
+ if #trunks < 1 or #trunks > 2 then
+ return
+ end
+ -- 1 or 2 trunks must at the level of the trunk being converted.
+ cvtrunks = minetest.find_nodes_in_area({x=tpos.x-1, y=tpos.y, z=tpos.z-1}, {x=tpos.x+1, y=tpos.y, z=tpos.z+1}, "moretrees:palm_trunk")
+ if #cvtrunks < 1 or #cvtrunks > 2 then
+ return
+ end
+ -- No trunks may be one level above the trunk being converted.
+ trunks = minetest.find_nodes_in_area({x=tpos.x-1, y=tpos.y+1, z=tpos.z-1}, {x=tpos.x+1, y=tpos.y+1, z=tpos.z+1}, "moretrees:palm_trunk")
+ if #trunks ~= 0 then
+ return
+ end
+ -- Leaves must be one level above the trunk being converted.
+ leaves = minetest.find_nodes_in_area({x=tpos.x-1, y=tpos.y+1, z=tpos.z-1}, {x=tpos.x+1, y=tpos.y+1, z=tpos.z+1}, "moretrees:palm_leaves")
+ if #leaves == 0 then
+ return
+ end
+ -- Leaves must be two levels above the trunk being converted.
+ leaves = minetest.find_nodes_in_area({x=tpos.x-1, y=tpos.y+2, z=tpos.z-1}, {x=tpos.x+1, y=tpos.y+2, z=tpos.z+1}, "moretrees:palm_leaves")
+ if #leaves == 0 then
+ return
+ end
+ -- No cocos fruit trunk may already be adjacent to the coconut
+ trunks = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y, z=pos.z-1}, {x=pos.x+1, y=pos.y, z=pos.z+1}, "moretrees:palm_fruit_trunk")
+ if #trunks ~= 0 then
+ return
+ end
+ -- No cocos fruit trunk may be adjacent to or below the trunk being converted.
+ trunks = minetest.find_nodes_in_area({x=tpos.x-1, y=tpos.y-2, z=tpos.z-1}, {x=tpos.x+1, y=tpos.y, z=tpos.z+1}, "moretrees:palm_fruit_trunk")
+ if #trunks ~= 0 then
+ return
+ end
+ -- Convert trunk and all coconuts nearby. Maybe convert 2 trunks, just in case...
+ for _, tpos in pairs(cvtrunks) do
+ minetest.swap_node(tpos, {name = "moretrees:palm_fruit_trunk"})
+ coconuts = minetest.find_nodes_in_area({x=tpos.x-1, y=tpos.y, z=tpos.z-1}, {x=tpos.x+1, y=tpos.y, z=tpos.z+1}, "moretrees:coconut")
+ for _, coconutpos in pairs(coconuts) do
+ minetest.set_node(coconutpos, {name = "moretrees:coconut_3"})
+ end
+ end
+ end,
+ }
+ if minetest.register_lbm then
+ minetest.register_lbm(spec)
+ else
+ spec.interval = 3691
+ spec.chance = 10
+ minetest.register_abm(spec)
+ end
+end
+
+-- If regrowing was previously disabled, but is enabled now, make sure timers are started for existing coconuts
+if moretrees.coconuts_regrow then
+ local spec = {
+ name = "moretrees:restart_coconut_regrow_timer",
+ nodenames = "group:moretrees_coconut",
+ action = function(pos, node, active_object_count, active_object_count_wider)
+ local timer = minetest.get_node_timer(pos)
+ if not timer:is_started() then
+ coconut_starttimer(pos)
+ else
+ local timeout = timer:get_timeout()
+ local elapsed = timer:get_elapsed()
+ if timeout - elapsed > moretrees.coconut_grow_interval * 4/3 then
+ coconut_starttimer(pos, math.random(moretrees.coconut_grow_interval * 4/3))
+ end
+ end
+ end,
+ }
+ if minetest.register_lbm then
+ minetest.register_lbm(spec)
+ else
+ spec.interval = 3659
+ spec.chance = 10
+ minetest.register_abm(spec)
+ end
+end
+
diff --git a/moretrees/crafts.lua b/moretrees/crafts.lua
index 395f602..9a0982b 100644
--- a/moretrees/crafts.lua
+++ b/moretrees/crafts.lua
@@ -63,6 +63,35 @@ minetest.register_craftitem("moretrees:raw_coconut", {
on_use = minetest.item_eat(4),
})
+minetest.register_craftitem("moretrees:date", {
+ description = S("Date"),
+ inventory_image = "moretrees_date.png",
+ on_use = minetest.item_eat(1),
+})
+
+minetest.register_craftitem("moretrees:date_nut_snack", {
+ description = S("Date & nut snack"),
+ inventory_image = "moretrees_date_nut_snack.png",
+ on_use = minetest.item_eat(4),
+})
+
+minetest.register_craftitem("moretrees:date_nut_batter", {
+ description = S("Date-nut cake batter"),
+ inventory_image = "moretrees_date_nut_batter.png",
+})
+
+minetest.register_craftitem("moretrees:date_nut_cake", {
+ description = S("Date-nut cake"),
+ inventory_image = "moretrees_date_nut_cake.png",
+ on_use = minetest.item_eat(32),
+})
+
+minetest.register_craftitem("moretrees:date_nut_bar", {
+ description = S("Date-nut energy bar"),
+ inventory_image = "moretrees_date_nut_bar.png",
+ on_use = minetest.item_eat(4),
+})
+
minetest.register_craftitem("moretrees:acorn_muffin_batter", {
description = S("Acorn Muffin batter"),
inventory_image = "moretrees_acorn_muffin_batter.png",
@@ -111,6 +140,60 @@ end
minetest.register_craft({
type = "shapeless",
+ output = "moretrees:date_nut_snack",
+ recipe = {
+ "moretrees:date",
+ "moretrees:date",
+ "moretrees:date",
+ "moretrees:spruce_nuts",
+ "moretrees:cedar_nuts",
+ "moretrees:fir_nuts",
+ }
+})
+
+-- The date-nut cake is an exceptional food item due to its highly
+-- concentrated nature (32 food units). Because of that, it requires
+-- many different ingredients, and, starting from the base ingredients
+-- found or harvested in nature, it requires many steps to prepare.
+local flour
+if minetest.registered_nodes["farming:flour"] then
+ flour = "farming:flour"
+else
+ flour = "moretrees:acorn_muffin_batter"
+end
+minetest.register_craft({
+ type = "shapeless",
+ output = "moretrees:date_nut_batter",
+ recipe = {
+ "moretrees:date_nut_snack",
+ "moretrees:date_nut_snack",
+ "moretrees:date_nut_snack",
+ "moretrees:coconut_milk",
+ "moretrees:date_nut_snack",
+ "moretrees:raw_coconut",
+ "moretrees:coconut_milk",
+ flour,
+ "moretrees:raw_coconut",
+ },
+ replacements = {
+ { "moretrees:coconut_milk", "vessels:drinking_glass 2" }
+ }
+})
+
+minetest.register_craft({
+ type = "cooking",
+ output = "moretrees:date_nut_cake",
+ recipe = "moretrees:date_nut_batter",
+})
+
+minetest.register_craft({
+ type = "shapeless",
+ output = "moretrees:date_nut_bar 8",
+ recipe = {"moretrees:date_nut_cake"},
+})
+
+minetest.register_craft({
+ type = "shapeless",
output = "moretrees:acorn_muffin_batter",
recipe = {
"moretrees:acorn",
diff --git a/moretrees/date_palm.lua b/moretrees/date_palm.lua
new file mode 100644
index 0000000..0c40b72
--- /dev/null
+++ b/moretrees/date_palm.lua
@@ -0,0 +1,746 @@
+-- Date palms.
+--
+-- Date palms grow in hot and dry desert, but they require water. This makes them
+-- a bit harder to find. If found in the middle of the desert, their presence
+-- indicates a water source below the surface.
+--
+-- As an additional feature (which can be disabled), dates automatically regrow after
+-- harvesting (provided a male tree is sufficiently nearby).
+-- If regrowing is enabled, then ripe dates will not hang forever. Most will disappear
+-- (e.g. eaten by birds, ...), and a small fraction will drop as items.
+
+-- © 2016, Rogier <rogier777@gmail.com>
+-- License: WTFPL
+
+local S = moretrees.intllib
+
+-- Some constants
+
+local dates_drop_ichance = 4
+local stems_drop_ichance = 4
+local flowers_wither_ichance = 3
+
+-- implementation
+
+local dates_regrow_prob
+if moretrees.dates_regrow_unpollinated_percent <= 0 then
+ dates_regrow_prob = 0
+elseif moretrees.dates_regrow_unpollinated_percent >= 100 then
+ dates_regrow_prob = 1
+else
+ dates_regrow_prob = 1 - math.pow(moretrees.dates_regrow_unpollinated_percent/100, 1/flowers_wither_ichance)
+end
+
+-- Make the date palm fruit trunk a real trunk (it is generated as a fruit)
+local trunk = minetest.registered_nodes["moretrees:date_palm_trunk"]
+local ftrunk = {}
+local fftrunk = {}
+local mftrunk = {}
+for k,v in pairs(trunk) do
+ ftrunk[k] = v
+end
+ftrunk.tiles = {}
+for k,v in pairs(trunk.tiles) do
+ ftrunk.tiles[k] = v
+end
+ftrunk.drop = "moretrees:date_palm_trunk"
+ftrunk.after_destruct = function(pos, oldnode)
+ local dates = minetest.find_nodes_in_area({x=pos.x-2, y=pos.y, z=pos.z-2}, {x=pos.x+2, y=pos.y, z=pos.z+2}, {"group:moretrees_dates"})
+ for _,datespos in pairs(dates) do
+ -- minetest.dig_node(datespos) does not cause nearby dates to be dropped :-( ...
+ local items = minetest.get_node_drops(minetest.get_node(datespos).name)
+ minetest.remove_node(datespos)
+ for _, itemname in pairs(items) do
+ minetest.add_item(datespos, itemname)
+ end
+ end
+end
+for k,v in pairs(ftrunk) do
+ mftrunk[k] = v
+ fftrunk[k] = v
+end
+fftrunk.tiles = {}
+mftrunk.tiles = {}
+for k,v in pairs(trunk.tiles) do
+ fftrunk.tiles[k] = v
+ mftrunk.tiles[k] = v
+end
+-- Make the different types of trunk distinguishable (but not too easily)
+ftrunk.tiles[1] = "moretrees_date_palm_trunk_top.png^[transformR180"
+ftrunk.description = ftrunk.description.." (gen)"
+fftrunk.tiles[1] = "moretrees_date_palm_trunk_top.png^[transformR90"
+mftrunk.tiles[1] = "moretrees_date_palm_trunk_top.png^[transformR-90"
+minetest.register_node("moretrees:date_palm_fruit_trunk", ftrunk)
+minetest.register_node("moretrees:date_palm_ffruit_trunk", fftrunk)
+minetest.register_node("moretrees:date_palm_mfruit_trunk", mftrunk)
+
+-- ABM to grow new date blossoms
+local date_regrow_abm_spec = {
+ nodenames = { "moretrees:date_palm_ffruit_trunk", "moretrees:date_palm_mfruit_trunk" },
+ interval = moretrees.dates_flower_interval,
+ chance = moretrees.dates_flower_chance,
+ action = function(pos, node, active_object_count, active_object_count_wider)
+ local dates = minetest.find_nodes_in_area({x=pos.x-2, y=pos.y, z=pos.z-2}, {x=pos.x+2, y=pos.y, z=pos.z+2}, "group:moretrees_dates")
+
+ -- New blossom interval increases exponentially with number of dates already hanging
+ -- In addition: if more dates are hanging, the chance of picking an empty spot decreases as well...
+ if math.random(2^#dates) <= 2 then
+ -- Grow in area of 5x5 round trunk; higher probability in 3x3 area close to trunk
+ local dx=math.floor((math.random(50)-18)/16)
+ local dz=math.floor((math.random(50)-18)/16)
+ local datepos = {x=pos.x+dx, y=pos.y, z=pos.z+dz}
+ local datenode = minetest.get_node(datepos)
+ if datenode.name == "air" then
+ if node.name == "moretrees:date_palm_ffruit_trunk" then
+ minetest.set_node(datepos, {name="moretrees:dates_f0"})
+ else
+ minetest.set_node(datepos, {name="moretrees:dates_m0"})
+ end
+ end
+ end
+ end
+}
+if moretrees.dates_regrow_pollinated or moretrees.dates_regrow_unpollinated_percent > 0 then
+ minetest.register_abm(date_regrow_abm_spec)
+end
+
+-- Choose male or female palm, and spawn initial dates
+-- (Instead of dates, a dates fruit trunk is generated with the tree. This
+-- ABM converts the trunk to a female or male fruit trunk, and spawns some
+-- hanging dates)
+minetest.register_abm({
+ nodenames = { "moretrees:date_palm_fruit_trunk" },
+ interval = 1,
+ chance = 1,
+ action = function(pos, node, active_object_count, active_object_count_wider)
+ local type
+ if math.random(100) <= moretrees.dates_female_percent then
+ type = "f"
+ minetest.swap_node(pos, {name="moretrees:date_palm_ffruit_trunk"})
+ else
+ type = "m"
+ minetest.swap_node(pos, {name="moretrees:date_palm_mfruit_trunk"})
+ end
+ local dates1 = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y, z=pos.z-1}, {x=pos.x+1, y=pos.y, z=pos.z+1}, "air")
+ local genpos
+ for _,genpos in pairs(dates1) do
+ if math.random(100) <= 20 then
+ if type == "m" then
+ minetest.set_node(genpos, {name = "moretrees:dates_n"})
+ else
+ minetest.set_node(genpos, {name = "moretrees:dates_f4"})
+ end
+ end
+ end
+ local dates2 = minetest.find_nodes_in_area({x=pos.x-2, y=pos.y, z=pos.z-2}, {x=pos.x+2, y=pos.y, z=pos.z+2}, "air")
+ for _,genpos in pairs(dates2) do
+ if math.random(100) <= 5 then
+ if type == "m" then
+ minetest.set_node(genpos, {name = "moretrees:dates_n"})
+ else
+ minetest.set_node(genpos, {name = "moretrees:dates_f4"})
+ end
+ end
+ end
+ end,
+})
+
+-- Dates growing functions.
+
+-- This is a bit complex, as the purpose is to find male flowers at horizontal distances of over
+-- 100 nodes. As searching such a large area is time consuming, this is optimized in four ways:
+-- - The search result (the locations of male trees) is cached, so that it can be used again
+-- - Only 1/9th of the desired area is searched at a time. A new search is only performed if no male
+-- flowers are found in the previously searched parts.
+-- - Search results are shared with other female palms nearby.
+-- - If previous searches for male palms have consumed too much CPU time, the search is skipped
+-- (This means no male palms will be found, and the pollination of the flowers affected will be
+-- delayed. If this happens repeatedly, eventually, the female flowers will wither...)
+-- A caching method was selected that is suited for the case where most date trees are long-lived,
+-- and where the number of trees nearby is limited:
+-- - Locations of male palms are stored as metadata for every female palm. This means that a player
+-- visiting a remote area with some date palms will not cause extensive searches for male palms as
+-- long overdue blossoming ABMs are triggered for every date palm.
+-- - Even when male palms *are* cut down, a cache refill will only be performed if the cached results do not
+-- contain a male palm with blossoms.
+-- The method will probably perform suboptimally:
+-- - If female palms are frequently chopped down and replanted.
+-- Freshly grown palms will need to search for male palms again
+-- (this is mitigated by the long blossoming interval, which increases the chance that search
+-- results have already been shared)
+-- - If an area contains a large number of male and female palms.
+-- In this area, every female palm will have an almost identical list of male palm locations
+-- as metadata.
+-- - If all male palms within range of a number of female palms have been chopped down (with possibly
+-- new ones planted). Although an attempt was made to share search results in this case as well,
+-- a number of similar searches will unavoidably be performed by the different female palms.
+-- - If no male palms are in range of a female palm. In that case, there will be frequent searches
+-- for newly-grown male palms.
+
+-- Search statistics - used to limit the search load.
+local sect_search_stats = {} -- Search statistics - server-wide
+local function reset_sect_search_stats()
+ sect_search_stats.count = 0 -- # of searches
+ sect_search_stats.skip = 0 -- # of times skipped
+ sect_search_stats.sum = 0 -- total time spent
+ sect_search_stats.min = 999999999 -- min time spent
+ sect_search_stats.max = 0 -- max time spent
+end
+reset_sect_search_stats()
+sect_search_stats.last_us = 0 -- last time a search was done (microseconds, max: 2^32)
+sect_search_stats.last_s = 0 -- last time a search was done (system time in seconds)
+
+-- Find male trunks in one section (=1/9 th) of the searchable area.
+-- sect is -4 to 4, where 0 is the center section
+local function find_fruit_trunks_near(ftpos, sect)
+ local r = moretrees.dates_pollination_distance + 2 * math.sqrt(2)
+ local sect_hr = math.floor(r / 3 + 0.9999)
+ local sect_vr = math.floor(r / 2 + 0.9999)
+ local t0us = core.get_us_time()
+ local t0s = os.time()
+
+ -- Compute elapsed time since last search.
+ -- Unfortunately, the time value wraps after about 71 minutes (2^32 microseconds),
+ -- so it must be corrected to obtain the actual elapsed time.
+ if t0us < sect_search_stats.last_us then
+ -- Correct a simple wraparound.
+ -- This is not sufficient, as the time value may have wrapped more than once...
+ sect_search_stats.last_us = sect_search_stats.last_us - 2^32
+ end
+ if t0s - sect_search_stats.last_s > 2^32/1000000 then
+ -- One additional correction is enough for our purposes.
+ -- For exact results, more corrections may be needed though...
+ -- (and even not applying this correction at all would still only yield
+ -- a minimal risk of a non-serious miscalculation...)
+ sect_search_stats.last_us = sect_search_stats.last_us - 2^32
+ end
+
+ -- Skip the search if it is consuming too much CPU time
+ if sect_search_stats.count > 0 and moretrees.dates_blossom_search_iload > 0
+ and sect_search_stats.sum / sect_search_stats.count > moretrees.dates_blossom_search_time_treshold
+ and t0us - sect_search_stats.last_us < moretrees.dates_blossom_search_iload * (sect_search_stats.sum / sect_search_stats.count) then
+ sect_search_stats.skip = sect_search_stats.skip + 1
+ return nil
+ end
+
+ local all_palms = minetest.find_nodes_in_area(
+ { x = ftpos.x + 2 * sect.x * sect_hr - sect_hr,
+ y = ftpos.y - sect_vr,
+ z = ftpos.z + 2 * sect.z * sect_hr - sect_hr },
+ { x = ftpos.x + 2 * sect.x * sect_hr + sect_hr,
+ y = ftpos.y + sect_vr,
+ z = ftpos.z + 2 * sect.z * sect_hr + sect_hr },
+ {"moretrees:date_palm_mfruit_trunk", "moretrees:date_palm_ffruit_trunk"})
+
+ -- Collect different palms in separate lists.
+ local female_palms = {}
+ local male_palms = {}
+ local all_male_palms = {}
+ for _, pos in pairs(all_palms) do
+ if pos.x ~= ftpos.x or pos.y ~= ftpos.y or pos.z ~= ftpos.z then
+ local node = minetest.get_node(pos)
+ if node and node.name == "moretrees:date_palm_ffruit_trunk" then
+ table.insert(female_palms,pos)
+ elseif node then
+ table.insert(all_male_palms,pos)
+ -- In sector 0, all palms are of interest.
+ -- In other sectors, forget about palms that are too far away.
+ if sect == 0 then
+ table.insert(male_palms,pos)
+ else
+ local ssq = 0
+ for _, c in pairs({"x", "z"}) do
+ local dc = pos[c] - ftpos[c]
+ ssq = ssq + dc * dc
+ end
+ if math.sqrt(ssq) <= r then
+ table.insert(male_palms,pos)
+ end
+ end
+ end
+ end
+ end
+
+ -- Update search statistics
+ local t1us = core.get_us_time()
+ if t1us < t0us then
+ -- Wraparound. Assume the search lasted less than 2^32 microseconds (~71 min)
+ -- (so no need to apply another correction)
+ t0us = t0us - 2^32
+ end
+ sect_search_stats.last_us = t0us
+ sect_search_stats.last_s = t0s
+ sect_search_stats.count = sect_search_stats.count + 1
+ sect_search_stats.sum = sect_search_stats.sum + t1us-t0us
+ if t1us - t0us < sect_search_stats.min then
+ sect_search_stats.min = t1us - t0us
+ end
+ if t1us - t0us > sect_search_stats.max then
+ sect_search_stats.max = t1us - t0us
+ end
+
+ return male_palms, female_palms, all_male_palms
+end
+
+local function dates_print_search_stats(log)
+ local stats
+ if sect_search_stats.count > 0 then
+ stats = string.format("Male date tree searching stats: searches: %d/%d: average: %d µs (%d..%d)",
+ sect_search_stats.count, sect_search_stats.count + sect_search_stats.skip,
+ sect_search_stats.sum/sect_search_stats.count, sect_search_stats.min, sect_search_stats.max)
+ else
+ stats = string.format("Male date tree searching stats: searches: 0/0: average: (no searches yet)")
+ end
+ if log then
+ minetest.log("action", "[moretrees] " .. stats)
+ end
+ return true, stats
+end
+
+minetest.register_chatcommand("dates_stats", {
+ description = "Print male date palm search statistics",
+ params = "|chat|log|reset",
+ privs = { server = true },
+ func = function(name, param)
+ param = string.lower(string.trim(param))
+ if param == "" or param == "chat" then
+ return dates_print_search_stats(false)
+ elseif param == "log" then
+ return dates_print_search_stats(true)
+ elseif param == "reset" then
+ reset_sect_search_stats()
+ return true
+ else
+ return false, "Invalid subcommand; expected: '' or 'chat' or 'log' or 'reset'"
+ end
+ end,
+})
+
+-- Find the female trunk near the female flowers to be pollinated
+local function find_female_trunk(fbpos)
+ local trunks = minetest.find_nodes_in_area({x=fbpos.x-2, y=fbpos.y, z=fbpos.z-2},
+ {x=fbpos.x+2, y=fbpos.y, z=fbpos.z+2},
+ "moretrees:date_palm_ffruit_trunk")
+ local ftpos
+ local d = 99
+ for x, pos in pairs(trunks) do
+ local ssq = 0
+ for _, c in pairs({"x", "z"}) do
+ local dc = pos[c] - fbpos[c]
+ ssq = ssq + dc * dc
+ end
+ if math.sqrt(ssq) < d then
+ ftpos = pos
+ d = math.sqrt(ssq)
+ end
+ end
+ return ftpos
+end
+
+-- Find male blossom near a male trunk,
+-- the male blossom must be in range of a specific female blossom as well
+local function find_male_blossom_near_trunk(fbpos, mtpos)
+ local r = moretrees.dates_pollination_distance
+ local blossoms = minetest.find_nodes_in_area({x=mtpos.x-2, y=mtpos.y, z=mtpos.z-2},
+ {x=mtpos.x+2, y=mtpos.y, z=mtpos.z+2},
+ "moretrees:dates_m0")
+ for x, mbpos in pairs(blossoms) do
+ local ssq = 0
+ for _, c in pairs({"x", "z"}) do
+ local dc = mbpos[c] - fbpos[c]
+ ssq = ssq + dc * dc
+ end
+ if math.sqrt(ssq) <= r then
+ return mbpos
+ end
+ end
+
+end
+
+-- Find a male blossom in range of a specific female blossom,
+-- using a nested list of male blossom positions
+local function find_male_blossom_in_mpalms(ftpos, fbpos, mpalms)
+ -- Process the elements of mpalms.sect (index -4 .. 4) in random order
+ -- First, compute the order in which the sectors will be searched
+ local sect_index = {}
+ local sect_rnd = {}
+ for i = -4,4 do
+ local n = math.random(1023)
+ sect_index[n] = i
+ table.insert(sect_rnd, n)
+ end
+ table.sort(sect_rnd)
+
+ -- Search the sectors
+ local sect_old = 0
+ local sect_time = minetest.get_gametime()
+ for _, n in pairs(sect_rnd) do
+ -- Record the oldest sector, so that it can be searched if no male
+ -- blossoms were found
+ if not mpalms.sect_time[sect_index[n]] then
+ sect_old = sect_index[n]
+ sect_time = 0
+ elseif mpalms.sect_time[sect_index[n]] < sect_time then
+ sect_old = sect_index[n]
+ sect_time = mpalms.sect_time[sect_index[n]]
+ end
+ if mpalms.sect[sect_index[n]] and #mpalms.sect[sect_index[n]] then
+ for px, mtpos in pairs(mpalms.sect[sect_index[n]]) do
+ local node = minetest.get_node(mtpos)
+ if node and node.name == "moretrees:date_palm_mfruit_trunk" then
+ local mbpos = find_male_blossom_near_trunk(fbpos, mtpos)
+ if mbpos then
+ return mbpos
+ end
+ elseif node and node.name ~= "ignore" then
+ -- no more male trunk here.
+ mpalms.sect[sect_index[n]][px] = nil
+ end
+ end
+ end
+ end
+ return nil, sect_old
+end
+
+-- Find a male blossom in range of a specific female blossom,
+-- using the cache associated with the given female trunk
+-- If necessary, recompute part of the cache
+local last_search_result = {}
+local function find_male_blossom_with_ftrunk(fbpos,ftpos)
+ local meta = minetest.get_meta(ftpos)
+ local mpalms
+ local cache_changed = true
+
+ -- Load cache. If distance has changed, start with empty cache instead.
+ local mpalms_dist = meta:get_int("male_palms_dist")
+ if mpalms_dist and mpalms_dist == moretrees.dates_pollination_distance then
+ mpalms = meta:get_string("male_palms")
+ if mpalms and mpalms ~= "" then
+ mpalms = minetest.deserialize(mpalms)
+ cache_changed = false
+ end
+ end
+ if not mpalms or not mpalms.sect then
+ mpalms = {}
+ mpalms.sect = {}
+ mpalms.sect_time = {}
+ meta:set_int("male_palms_dist", moretrees.dates_pollination_distance)
+ cache_changed = true
+ end
+ local fpalms_list
+ local all_mpalms_list
+ local sector0_searched = false
+
+ -- Always make sure that sector 0 is cached
+ if not mpalms.sect[0] then
+ mpalms.sect[0], fpalms_list, all_mpalms_list = find_fruit_trunks_near(ftpos, {x = 0, z = 0})
+ mpalms.sect_time[0] = minetest.get_gametime()
+ sector0_searched = true
+ cache_changed = true
+ last_search_result.female = fpalms_list
+ last_search_result.male = all_mpalms_list
+ end
+
+ -- Find male palms
+ local mbpos, sect_old = find_male_blossom_in_mpalms(ftpos, fbpos, mpalms)
+
+ -- If not found, (re)generate the cache for an additional sector. But don't search it yet (for performance reasons)
+ -- (Use the globally cached results if possible)
+ if not mbpos and not sector0_searched then
+ if not mpalms.sect_time[0] or mpalms.sect_time[0] == 0 or math.random(3) == 1 then
+ -- Higher probability of re-searching the center sector
+ sect_old = 0
+ end
+ -- Use globally cached result if possible
+ mpalms.sect[sect_old] = nil
+ if sect_old == 0 and mpalms.sect_time[0] and mpalms.sect_time[0] > 0
+ and last_search_result.male and #last_search_result.male then
+ for _, pos in pairs(last_search_result.female) do
+ if pos.x == ftpos.x and pos.y == ftpos.y and pos.z == ftpos.z then
+ mpalms.sect[sect_old] = last_search_result.male
+ -- Next time, don't use the cached result
+ mpalms.sect_time[sect_old] = nil
+ cache_changed = true
+ end
+ end
+ end
+ -- Else do a new search
+ if not mpalms.sect[sect_old] then
+ mpalms.sect[sect_old], fpalms_list, all_mpalms_list = find_fruit_trunks_near(ftpos, {x = (sect_old + 4) % 3 - 1, z = (sect_old + 4) / 3 - 1})
+ cache_changed = true
+ if sect_old == 0 then
+ -- Save the results if it is sector 0
+ -- (chance of reusing results from another sector are smaller)
+ last_search_result.female = fpalms_list
+ last_search_result.male = all_mpalms_list
+ end
+ if mpalms.sect[sect_old] then
+ mpalms.sect_time[sect_old] = minetest.get_gametime()
+ else
+ mpalms.sect_time[sect_old] = nil
+ end
+ end
+ end
+
+ -- Share search results with other female trunks in the same area
+ -- Note that the list of female trunks doesn't (shouldn't :-) contain the current female trunk.
+ if fpalms_list and #fpalms_list and #all_mpalms_list then
+ local all_mpalms = {}
+ all_mpalms.sect = {}
+ all_mpalms.sect_time = {}
+ all_mpalms.sect[0] = all_mpalms_list
+ -- Don't set sect_time[0], so that the cached sector will be re-searched soon (if necessary)
+ local all_mpalms_serialized = minetest.serialize(all_mpalms)
+ for _, pos in pairs(fpalms_list) do
+ local fmeta = minetest.get_meta(pos)
+ local fdist = fmeta:get_int("male_palms_dist")
+ if not fdist or fdist ~= moretrees.dates_pollination_distance then
+ fmeta:set_string("male_palms", all_mpalms_serialized)
+ fmeta:set_int("male_palms_dist", moretrees.dates_pollination_distance)
+ end
+ end
+ end
+
+ -- Save cache.
+ if cache_changed then
+ meta:set_string("male_palms", minetest.serialize(mpalms))
+ end
+
+ return mbpos
+end
+
+-- Find a male blossom in range of a specific female blossom
+local function find_male_blossom(fbpos)
+ local ftpos = find_female_trunk(fbpos)
+ if ftpos then
+ return find_male_blossom_with_ftrunk(fbpos, ftpos)
+ end
+ return nil
+end
+
+-- Growing function for dates
+local dates_growfn = function(pos, elapsed)
+ local node = minetest.get_node(pos)
+ local delay = moretrees.dates_grow_interval
+ local r = moretrees.dates_pollination_distance
+ local action
+ if not node then
+ return
+ elseif not moretrees.dates_regrow_pollinated and dates_regrow_prob == 0 then
+ -- Regrowing of dates is disabled.
+ if string.find(node.name, "moretrees:dates_f") then
+ minetest.swap_node(pos, {name="moretrees:dates_f4"})
+ elseif string.find(node.name, "moretrees:dates_m") then
+ minetest.swap_node(pos, {name="moretrees:dates_n"})
+ else
+ minetest.remove_node(pos)
+ end
+ return
+ elseif node.name == "moretrees:dates_f0" and math.random(100) <= 100 * dates_regrow_prob then
+ -- Dates grow unpollinated
+ minetest.swap_node(pos, {name="moretrees:dates_f1"})
+ action = "nopollinate"
+ elseif node.name == "moretrees:dates_f0" and moretrees.dates_regrow_pollinated and find_male_blossom(pos) then
+ -- Pollinate flowers
+ minetest.swap_node(pos, {name="moretrees:dates_f1"})
+ action = "pollinate"
+ elseif string.match(node.name, "0$") then
+ -- Make female unpollinated and male flowers last a bit longer
+ if math.random(flowers_wither_ichance) == 1 then
+ if node.name == "moretrees:dates_f0" then
+ minetest.swap_node(pos, {name="moretrees:dates_fn"})
+ else
+ minetest.swap_node(pos, {name="moretrees:dates_n"})
+ end
+ action = "wither"
+ else
+ action = "nowither"
+ end
+ elseif node.name == "moretrees:dates_f4" then
+ -- Remove dates, and optionally drop them as items
+ if math.random(dates_drop_ichance) == 1 then
+ if moretrees.dates_item_drop_ichance > 0 and math.random(moretrees.dates_item_drop_ichance) == 1 then
+ local items = minetest.get_node_drops(minetest.get_node(pos).name)
+ for _, itemname in pairs(items) do
+ minetest.add_item(pos, itemname)
+ end
+ end
+ minetest.swap_node(pos, {name="moretrees:dates_n"})
+ action = "drop"
+ else
+ action = "nodrop"
+ end
+ elseif string.match(node.name, "n$") then
+ -- Remove stems.
+ if math.random(stems_drop_ichance) == 1 then
+ minetest.remove_node(pos)
+ return "stemdrop"
+ end
+ action = "nostemdrop"
+ else
+ -- Grow dates
+ local offset = 18
+ local n = string.sub(node.name, offset)
+ minetest.swap_node(pos, {name=string.sub(node.name, 1, offset-1)..n+1})
+ action = "grow"
+ end
+ -- Don't catch up when elapsed time is large. Regular visits are needed for growth...
+ local timer = minetest.get_node_timer(pos)
+ timer:start(delay + math.random(moretrees.dates_grow_interval))
+ return action
+end
+
+-- Alternate growth function for dates.
+-- It calls the primary growth function, but also measures CPU time consumed.
+-- Use this function to analyze date growing performance.
+local stat = {}
+stat.count = 0
+local dates_growfn_profiling = function(pos, elapsed)
+ local t0 = core.get_us_time()
+ local action = dates_growfn(pos, elapsed)
+ local t1 = core.get_us_time()
+ if t1 < t0 then
+ t1 = t1 + 2^32
+ end
+ stat.count = stat.count + 1
+ if not stat[action] then
+ stat[action] = {}
+ stat[action].count = 0
+ stat[action].sum = 0
+ stat[action].min = 9999999999
+ stat[action].max = 0
+ end
+ stat[action].count = stat[action].count + 1
+ stat[action].sum = stat[action].sum + t1-t0
+ if t1-t0 < stat[action].min then
+ stat[action].min = t1-t0
+ end
+ if t1-t0 > stat[action].max then
+ stat[action].max = t1-t0
+ end
+
+ if stat.count % 10 == 0 then
+ io.write(".")
+ io.flush()
+ end
+ if stat.count % 100 == 0 then
+ print(string.format("Date grow statistics %5d:", stat.count))
+ local sum = 0
+ local count = 0
+ if sect_search_stats.count > 0 and stat.pollinate and stat.pollinate.count > 0 then
+ print(string.format("\t%-12s: %6d (%4.1f%%): %6dus (%d..%d)",
+ "search", sect_search_stats.count,
+ 100*sect_search_stats.count/stat.pollinate.count,
+ sect_search_stats.sum/sect_search_stats.count,
+ sect_search_stats.min, sect_search_stats.max))
+ else
+ print(string.format("\t%-12s: %6d (%4.1f%%): %6dus (%d..%d)",
+ "search", sect_search_stats.count,
+ 0, 0, 0, 0))
+ end
+ for action,data in pairs(stat) do
+ if action ~= "count" then
+ count = count + data.count
+ sum = sum + data.sum
+ print(string.format("\t%-12s: %6d (%4.1f%%): %6dus (%d..%d)",
+ action, data.count,
+ 100*data.count/stat.count, data.sum/data.count,
+ data.min, data.max))
+ end
+ end
+ print(string.format("\t%-12s: %6d ( 100%%): %6dus",
+ "TOTAL", count, sum/count))
+ end
+end
+
+-- Register dates
+
+local dates_starttimer = function(pos, elapsed)
+ local timer = minetest.get_node_timer(pos)
+ local base_interval = moretrees.dates_grow_interval * 2 / 3
+ timer:set(base_interval + math.random(base_interval), elapsed or 0)
+end
+
+local dates_drop = {
+ items = {
+ {items = { "moretrees:date" }},
+ {items = { "moretrees:date" }},
+ {items = { "moretrees:date" }},
+ {items = { "moretrees:date" }},
+ {items = { "moretrees:date" }, rarity = 2 },
+ {items = { "moretrees:date" }, rarity = 2 },
+ {items = { "moretrees:date" }, rarity = 2 },
+ {items = { "moretrees:date" }, rarity = 2 },
+ {items = { "moretrees:date" }, rarity = 5 },
+ {items = { "moretrees:date" }, rarity = 5 },
+ {items = { "moretrees:date" }, rarity = 5 },
+ {items = { "moretrees:date" }, rarity = 5 },
+ {items = { "moretrees:date" }, rarity = 20 },
+ {items = { "moretrees:date" }, rarity = 20 },
+ {items = { "moretrees:date" }, rarity = 20 },
+ {items = { "moretrees:date" }, rarity = 20 },
+ }
+}
+
+for _,suffix in ipairs({"f0", "f1", "f2", "f3", "f4", "m0", "fn", "n"}) do
+ local name
+ if suffix == "f0" or suffix == "m0" then
+ name = S("Date Flowers")
+ elseif suffix == "n" or suffix == "fn" then
+ name = S("Date Stem")
+ else
+ name = S("Dates")
+ end
+ local dropfn = suffix == "f4" and dates_drop or ""
+ local datedef = {
+ description = name,
+ tiles = {"moretrees_dates_"..suffix..".png"},
+ visual_scale = 2,
+ drawtype = "plantlike",
+ paramtype = "light",
+ sunlight_propagates = true,
+ walkable = false,
+ groups = { fleshy=3, dig_immediate=3, flammable=2, moretrees_dates=1 },
+ inventory_image = "moretrees_dates_"..suffix..".png^[transformR0",
+ wield_image = "moretrees_dates_"..suffix..".png^[transformR90",
+ sounds = default.node_sound_defaults(),
+ drop = dropfn,
+ selection_box = {
+ type = "fixed",
+ fixed = {-0.3, -0.3, -0.3, 0.3, 3.5, 0.3}
+ },
+ on_timer = dates_growfn,
+ on_construct = (moretrees.dates_regrow_pollinated or moretrees.dates_regrow_unpollinated_percent > 0)
+ and dates_starttimer,
+
+ }
+ minetest.register_node("moretrees:dates_"..suffix, datedef)
+end
+
+-- If regrowing was previously disabled, but is enabled now, make sure timers are started for existing dates
+if moretrees.dates_regrow_pollinated or moretrees.dates_regrow_unpollinated_percent > 0 then
+ local spec = {
+ name = "moretrees:restart_dates_regrow_timer",
+ nodenames = "group:moretrees_dates",
+ action = function(pos, node, active_object_count, active_object_count_wider)
+ local timer = minetest.get_node_timer(pos)
+ if not timer:is_started() then
+ dates_starttimer(pos)
+ else
+ local timeout = timer:get_timeout()
+ local elapsed = timer:get_elapsed()
+ if timeout - elapsed > moretrees.dates_grow_interval * 4/3 then
+ dates_starttimer(pos, math.random(moretrees.dates_grow_interval * 4/3))
+ end
+ end
+ end,
+ }
+ if minetest.register_lbm then
+ minetest.register_lbm(spec)
+ else
+ spec.interval = 3557
+ spec.chance = 10
+ minetest.register_abm(spec)
+ end
+end
+
diff --git a/moretrees/default_settings.txt b/moretrees/default_settings.txt
index 1bb85f5..0a5fc88 100644
--- a/moretrees/default_settings.txt
+++ b/moretrees/default_settings.txt
@@ -6,6 +6,7 @@ moretrees.enable_apple_tree = true
moretrees.enable_oak = true
moretrees.enable_sequoia = true
moretrees.enable_palm = true
+moretrees.enable_date_palm = true
moretrees.enable_cedar = true
moretrees.enable_rubber_tree = true
moretrees.enable_willow = true
@@ -57,6 +58,67 @@ moretrees.firs_remove_default_trees = false
moretrees.firs_remove_interval = 2
moretrees.firs_remove_chance = 150
+-- Cocos palm settings
+
+moretrees.coconuts_regrow = true
+moretrees.coconuts_convert_existing_palms = true -- Converting existing palm trees will make coconuts regrow on them as well
+ -- Else, they will only regrow on newly-spawned palms
+ -- However, conversion is not an exact science, and although an attempt is
+ -- made to detect whether a trunk belongs to an actual palm, some coconut trunks
+ -- and some coconuts may be incorrectly converted.
+moretrees.coconut_flower_interval = 59
+moretrees.coconut_flower_chance = 67
+moretrees.coconut_grow_interval = 2 * moretrees.coconut_flower_interval * moretrees.coconut_flower_chance
+ -- Actual interval will randomly vary between 67% and 133% of this value
+ -- 2 * 59 * 67 ~ 2 hours. So flowers become coconuts in about 6 hours
+moretrees.coconut_item_drop_ichance = 10 -- inverse probability of ripe coconuts dropping as items (instead of disappearing)
+
+-- Date palm settings
+
+-- Suggested configuration alternatives:
+-- - Dates grow only when pollinated:
+-- - Set dates_regrow_pollinated to true
+-- - Set dates_regrow_unpollinated_percent to 0
+-- - Dates grow without pollination. Pollination disabled:
+-- - Set dates_regrow_pollinated to false
+-- - Set dates_regrow_unpollinated_percent to some larger positive value, e.g. 95
+-- - Dates grow, but more and faster if male flowers are nearby
+-- - Set dates_regrow_pollinated to true
+-- - Set dates_regrow_unpollinated_percent to some small positive value, e.g. 33
+-- - Optional but recommended: Reduce the pollination distance, e.g. to 30
+
+-- Note that it should not be necessary to disable pollination for performance
+-- reasons. A lot of effort has gone into ensuring that date growing will not cause lag.
+--
+-- If lag is suspected, use the chat command '/dates_stats' to obtain the male dates
+-- search time, as well as the counts of total number of searches requested and the
+-- number of searches actually performed.
+
+moretrees.dates_regrow_pollinated = true -- Enable pollination. If enabled, male trees are required for dates to grow.
+ -- If disabled, dates_regrow_unpollinated_percent must be non-zero for dates to regrow.
+moretrees.dates_regrow_unpollinated_percent = 0 -- Percentage of female dates becoming dates without being pollinated.
+ -- If 0, dates_regrow_pollinated must be enabled for dates to grow.
+moretrees.dates_female_percent = 57 -- Ratio of female to male trees - tune this to improve # of generated trees that actually bear fruit
+ -- ~57% gives near optimal results for groups of 3 random trees, while it is only slightly suboptimal
+ -- for groups of 2 and 4 random trees (~2% less fruit than optimal).
+ -- Optimal values per group size: 2: 50%, 3: 57.78%, 4: 63%, 5: 66.9%, 6: 69.9%, [...], 12: 79.8%
+ -- So 57% is optimal for small groups of trees. As larger groups have more female palms anyway, a
+ -- less than optimal proportion of female to male trees is not a problem.
+moretrees.dates_pollination_distance = 120
+moretrees.dates_blossom_search_time_treshold = 1000 -- If average male blossom search time (in microseconds) exceeds this, start limiting the search load.
+moretrees.dates_blossom_search_iload = 10 -- Inverse fraction of CPU time that male blossom searching search may consume.
+ -- As searching a large area (radius: dates_pollination_distance/3 per attempt) can cause lag,
+ -- this limits the search frequency server-wide so that the impact on server lag is minimised
+ -- For personal servers, this can be set lower, or even to 1 or 0 (0 disables load limiting).
+ -- Obtain the current average search time using /dates_stats
+moretrees.dates_flower_interval = 59
+moretrees.dates_flower_chance = 181
+moretrees.dates_grow_interval = 2 * moretrees.dates_flower_interval * moretrees.dates_flower_chance
+ -- As date palms have a high yield, don't grow dates too fast
+ -- The actual interval will vary randomly between 67% and 133% of this value.
+ -- 2 * 59 * 181 ~ 6 hours. So by default flowers become dates in about one (human) day.
+moretrees.dates_item_drop_ichance = 10 -- inverse probability of ripe dates dropping as items (instead of disappearing)
+
-- Sapling settings
moretrees.sapling_interval = 500
diff --git a/moretrees/depends.txt b/moretrees/depends.txt
index c666762..7896571 100644
--- a/moretrees/depends.txt
+++ b/moretrees/depends.txt
@@ -1,6 +1,8 @@
default
biome_lib
+vessels
stairs?
moreblocks?
intllib?
+farming?
diff --git a/moretrees/init.lua b/moretrees/init.lua
index 843958b..2c50fd9 100644
--- a/moretrees/init.lua
+++ b/moretrees/init.lua
@@ -73,6 +73,8 @@ moretrees.cutting_tools = {
dofile(modpath.."/tree_models.lua")
dofile(modpath.."/node_defs.lua")
+dofile(modpath.."/date_palm.lua")
+dofile(modpath.."/cocos_palm.lua")
dofile(modpath.."/biome_defs.lua")
dofile(modpath.."/saplings.lua")
dofile(modpath.."/crafts.lua")
@@ -85,6 +87,7 @@ if moretrees.spawn_saplings then
moretrees.spawn_oak_object = "moretrees:oak_sapling_ongen"
moretrees.spawn_sequoia_object = "moretrees:sequoia_sapling_ongen"
moretrees.spawn_palm_object = "moretrees:palm_sapling_ongen"
+ moretrees.spawn_date_palm_object = "moretrees:date_palm_sapling_ongen"
moretrees.spawn_cedar_object = "moretrees:cedar_sapling_ongen"
moretrees.spawn_rubber_tree_object = "moretrees:rubber_tree_sapling_ongen"
moretrees.spawn_willow_object = "moretrees:willow_sapling_ongen"
@@ -100,6 +103,7 @@ else
moretrees.spawn_oak_object = moretrees.oak_model
moretrees.spawn_sequoia_object = moretrees.sequoia_model
moretrees.spawn_palm_object = moretrees.palm_model
+ moretrees.spawn_date_palm_object = moretrees.date_palm_model
moretrees.spawn_cedar_object = moretrees.cedar_model
moretrees.spawn_rubber_tree_object = moretrees.rubber_tree_model
moretrees.spawn_willow_object = moretrees.willow_model
@@ -131,6 +135,11 @@ if moretrees.enable_palm then
biome_lib:register_generate_plant(moretrees.palm_biome, moretrees.spawn_palm_object)
end
+if moretrees.enable_date_palm then
+ biome_lib:register_generate_plant(moretrees.date_palm_biome, moretrees.spawn_date_palm_object)
+ biome_lib:register_generate_plant(moretrees.date_palm_biome_2, moretrees.spawn_date_palm_object)
+end
+
if moretrees.enable_cedar then
biome_lib:register_generate_plant(moretrees.cedar_biome, moretrees.spawn_cedar_object)
end
diff --git a/moretrees/node_defs.lua b/moretrees/node_defs.lua
index 94b7ed0..b1df5f7 100644
--- a/moretrees/node_defs.lua
+++ b/moretrees/node_defs.lua
@@ -8,7 +8,8 @@ moretrees.treelist = {
{"oak", "Oak Tree", "acorn", "Acorn", {-0.2, -0.5, -0.2, 0.2, 0, 0.2}, 0.8 },
{"sequoia", "Giant Sequoia"},
{"birch", "Birch Tree"},
- {"palm", "Palm Tree", "coconut", "Coconut", {-0.2, -0.5, -0.2, 0.2, 0, 0.2}, 1.0 },
+ {"palm", "Palm Tree", "palm_fruit_trunk_gen", "Palm Tree", {-0.2, -0.5, -0.2, 0.2, 0, 0.2}, 1.0 },
+ {"date_palm", "Date Palm Tree", "date_palm_fruit_trunk", "Date Palm Tree", {0, 0, 0, 0, 0, 0}, 0.0 },
{"spruce", "Spruce Tree", "spruce_cone", "Spruce Cone", {-0.2, -0.5, -0.2, 0.2, 0, 0.2}, 0.8 },
{"cedar", "Cedar Tree", "cedar_cone", "Cedar Cone", {-0.2, -0.5, -0.2, 0.2, 0, 0.2}, 0.8 },
{"willow", "Willow Tree"},
@@ -123,6 +124,8 @@ for i in ipairs(moretrees.treelist) do
if treename == "palm" then
droprarity = 20
decay = moretrees.palm_leafdecay_radius
+ elseif treename == "date_palm" then
+ decay = moretrees.palm_leafdecay_radius
end
local moretrees_leaves_inventory_image = nil
@@ -430,3 +433,5 @@ minetest.register_alias("moretrees:pine_sapling", "moretrees:cedar_sapling")
minetest.register_alias("moretrees:pine_leaves", "moretrees:cedar_leaves")
minetest.register_alias("moretrees:pine_cone", "moretrees:cedar_cone")
minetest.register_alias("moretrees:pine_nuts", "moretrees:cedar_nuts")
+
+minetest.register_alias("moretrees:dates", "moretrees:dates_f4")
diff --git a/moretrees/textures/moretrees_coconut_0.png b/moretrees/textures/moretrees_coconut_0.png
new file mode 100644
index 0000000..644a65f
--- /dev/null
+++ b/moretrees/textures/moretrees_coconut_0.png
Binary files differ
diff --git a/moretrees/textures/moretrees_coconut_1.png b/moretrees/textures/moretrees_coconut_1.png
new file mode 100644
index 0000000..e2889bc
--- /dev/null
+++ b/moretrees/textures/moretrees_coconut_1.png
Binary files differ
diff --git a/moretrees/textures/moretrees_coconut_2.png b/moretrees/textures/moretrees_coconut_2.png
new file mode 100644
index 0000000..86c8cf5
--- /dev/null
+++ b/moretrees/textures/moretrees_coconut_2.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date.png b/moretrees/textures/moretrees_date.png
new file mode 100644
index 0000000..ed743b5
--- /dev/null
+++ b/moretrees/textures/moretrees_date.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date_nut_bar.png b/moretrees/textures/moretrees_date_nut_bar.png
new file mode 100644
index 0000000..6895001
--- /dev/null
+++ b/moretrees/textures/moretrees_date_nut_bar.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date_nut_batter.png b/moretrees/textures/moretrees_date_nut_batter.png
new file mode 100644
index 0000000..47d8e08
--- /dev/null
+++ b/moretrees/textures/moretrees_date_nut_batter.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date_nut_cake.png b/moretrees/textures/moretrees_date_nut_cake.png
new file mode 100644
index 0000000..5084e71
--- /dev/null
+++ b/moretrees/textures/moretrees_date_nut_cake.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date_nut_snack.png b/moretrees/textures/moretrees_date_nut_snack.png
new file mode 100644
index 0000000..1766dab
--- /dev/null
+++ b/moretrees/textures/moretrees_date_nut_snack.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date_palm_leaves.png b/moretrees/textures/moretrees_date_palm_leaves.png
new file mode 100644
index 0000000..de0f569
--- /dev/null
+++ b/moretrees/textures/moretrees_date_palm_leaves.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date_palm_sapling.png b/moretrees/textures/moretrees_date_palm_sapling.png
new file mode 100644
index 0000000..9596028
--- /dev/null
+++ b/moretrees/textures/moretrees_date_palm_sapling.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date_palm_trunk.png b/moretrees/textures/moretrees_date_palm_trunk.png
new file mode 100644
index 0000000..b8fa986
--- /dev/null
+++ b/moretrees/textures/moretrees_date_palm_trunk.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date_palm_trunk_top.png b/moretrees/textures/moretrees_date_palm_trunk_top.png
new file mode 100644
index 0000000..329ba46
--- /dev/null
+++ b/moretrees/textures/moretrees_date_palm_trunk_top.png
Binary files differ
diff --git a/moretrees/textures/moretrees_date_palm_wood.png b/moretrees/textures/moretrees_date_palm_wood.png
new file mode 100644
index 0000000..c531e3c
--- /dev/null
+++ b/moretrees/textures/moretrees_date_palm_wood.png
Binary files differ
diff --git a/moretrees/textures/moretrees_dates.png b/moretrees/textures/moretrees_dates.png
new file mode 100644
index 0000000..cd8d578
--- /dev/null
+++ b/moretrees/textures/moretrees_dates.png
Binary files differ
diff --git a/moretrees/textures/moretrees_dates_f0.png b/moretrees/textures/moretrees_dates_f0.png
new file mode 100644
index 0000000..b75d59a
--- /dev/null
+++ b/moretrees/textures/moretrees_dates_f0.png
Binary files differ
diff --git a/moretrees/textures/moretrees_dates_f1.png b/moretrees/textures/moretrees_dates_f1.png
new file mode 100644
index 0000000..535150b
--- /dev/null
+++ b/moretrees/textures/moretrees_dates_f1.png
Binary files differ
diff --git a/moretrees/textures/moretrees_dates_f2.png b/moretrees/textures/moretrees_dates_f2.png
new file mode 100644
index 0000000..e2e299c
--- /dev/null
+++ b/moretrees/textures/moretrees_dates_f2.png
Binary files differ
diff --git a/moretrees/textures/moretrees_dates_f3.png b/moretrees/textures/moretrees_dates_f3.png
new file mode 100644
index 0000000..eef43f0
--- /dev/null
+++ b/moretrees/textures/moretrees_dates_f3.png
Binary files differ
diff --git a/moretrees/textures/moretrees_dates_f4.png b/moretrees/textures/moretrees_dates_f4.png
new file mode 100644
index 0000000..78f38f9
--- /dev/null
+++ b/moretrees/textures/moretrees_dates_f4.png
Binary files differ
diff --git a/moretrees/textures/moretrees_dates_fn.png b/moretrees/textures/moretrees_dates_fn.png
new file mode 100644
index 0000000..db88913
--- /dev/null
+++ b/moretrees/textures/moretrees_dates_fn.png
Binary files differ
diff --git a/moretrees/textures/moretrees_dates_m0.png b/moretrees/textures/moretrees_dates_m0.png
new file mode 100644
index 0000000..4ff61e7
--- /dev/null
+++ b/moretrees/textures/moretrees_dates_m0.png
Binary files differ
diff --git a/moretrees/textures/moretrees_dates_n.png b/moretrees/textures/moretrees_dates_n.png
new file mode 100644
index 0000000..c12a3d9
--- /dev/null
+++ b/moretrees/textures/moretrees_dates_n.png
Binary files differ
diff --git a/moretrees/tree_biomes.txt b/moretrees/tree_biomes.txt
index fdba90d..22e1032 100644
--- a/moretrees/tree_biomes.txt
+++ b/moretrees/tree_biomes.txt
@@ -6,6 +6,8 @@ jungle tree - 5 to +10 above +15 water, 20 10 dirt_with_grass 329 5
fir above +25 -20 to +10 n/a n/a dirt_with_grass 359 8
firs on snow above +15 -20 to +10 n/a n/a snow:snow 359 8
palm - 1 to + 1 +15 to +32 water, 15 10 sand 330 5
+date palm - 1 to +10 above +39 water, 20h,20v 100 desert_sand 339 10
+date palm +11 to +30 above +39 water, 1h,30v 1 desert_sand 340 10
apple + 1 to +10 +23 to +32 n/a n/a dirt_with grass 331 15
oak 0 to +10 + 4 to +16 n/a n/a dirt_with grass 332 15
sequoia 0 to +10 -30 to +50 n/a n/a dirt_with grass 333 10
diff --git a/moretrees/tree_models.lua b/moretrees/tree_models.lua
index c534089..791bddb 100644
--- a/moretrees/tree_models.lua
+++ b/moretrees/tree_models.lua
@@ -88,8 +88,13 @@ moretrees.birch_model2={
thin_branches=true
}
+-- Coconuts can't be generated as fruit, because there is no support for the
+-- special fruit trunks that allow coconuts to regrow at the correct position
+-- in the tree.
+-- So, a placeholder fruit trunk is spawned. An ABM will convert it to the final
+-- fruit trunk, and generate the actual coconuts.
moretrees.palm_model={
- axiom="FFcccccc&FFFFFddd[^&&&GR][^///&&&GR][^//////&&&GR][^***&&&GR]FA//A//A//A//A//A",
+ axiom="FFcccccc&FFFFFdddRA//A//A//A//A//A",
rules_a="[&fb&bbb[++f--&ffff&ff][--f++&ffff&ff]&ffff&bbbb&b]",
rules_b="f",
rules_c="/",
@@ -101,7 +106,32 @@ moretrees.palm_model={
random_level=0,
trunk_type="single",
thin_branches=true,
- fruit="moretrees:coconut",
+ fruit="moretrees:palm_fruit_trunk_gen",
+ fruit_chance=0
+}
+
+-- Dates can't be generated as fruit, because there is no support for the
+-- special (male and female) fruit trunks that allow dates to regrow at the
+-- correct position in the tree.
+-- So, a generic fruit trunk is spawned. An ABM will convert it to a male
+-- or female fruit trunk, and generate the actual dates.
+moretrees.date_palm_model={
+ axiom="TTTTddddddddddccccccccccRT[TGGGGT]"..
+ "ccccc[&&a]ccccc[&&a]ccccc[&&a]ccccc[&&a]ccccc[&&a]ccccc[&&a]"..
+ "GGccccc[&a]ccccc[&a]ccccc[&a]ccccc[&a]ccccc[&a]ccccc[&a]"..
+ "GGccccc[a]ccccc[a]ccccc[a]ccccc[a]ccccc[a]ccccc[a]",
+ rules_a="Gffb&bbb[++f--&ffff&ff][--f++&ffff&ff]&ff&ff&bb&bb&bb",
+ rules_b="f",
+ rules_c="/",
+ rules_d="F",
+ trunk="moretrees:date_palm_trunk",
+ leaves="moretrees:date_palm_leaves",
+ angle=18,
+ iterations=1,
+ random_level=0,
+ trunk_type="single",
+ thin_branches=false,
+ fruit="moretrees:date_palm_fruit_trunk",
fruit_chance=0
}