bettertrees = { path = minetest.get_modpath('bettertrees'), mods = { default = minetest.global_exists('default'), armor = minetest.global_exists('3d_armor'), mcl = minetest.global_exists('mcl_core'), -- stripped_tree = minetest.global_exists('stripped_tree'), -- hunger_ng = minetest.global_exists('hunger_ng') xdecor = minetest.global_exists('xdecor'), moretrees = minetest.global_exists('moretrees'), }, config = { bushy = minetest.settings:get_bool('bettertrees.bushy', true), cheap_bushy = minetest.settings:get_bool('bettertrees.cheap_bushy', false), web = minetest.settings:get_bool('bettertrees.web', true), web_min_rand = 60*20, web_max_rand = 60*30, } } local S = minetest.get_translator() dofile(bettertrees.path .. '/functions.lua') if bettertrees.mods.default then bettertrees.stick = 'default:stick' bettertrees.stick_sound = default.node_sound_wood_defaults() bettertrees.stick_node = ':' .. bettertrees.stick elseif bettertrees.mods.mcl then bettertrees.stick = 'mcl_core:stick' bettertrees.stick_sound = mcl_sounds.node_sound_wood_defaults() bettertrees.stick_node = ':' .. bettertrees.stick else bettertrees.stick = 'bettertrees:stick' bettertrees.stick_sound = nil bettertrees.stick_node = bettertrees.stick end --- Runs code to place hidden node that regenerates leaves ---@param pos table ---@param node_name string local function spawn_hidden(pos, node_name) minetest.set_node(pos, { name = "bettertrees:hidden" }) local meta = minetest.get_meta(pos) meta:set_string("leaf", node_name) end --- Runs code to break leaves, by either dropping them or decaying them into sticks ---@param pos table ---@param node_name string ---@param decay boolean local function break_leaf(pos, node_name, decay) if decay == true and bettertrees.stick then minetest.swap_node(pos, { name = bettertrees.stick }) minetest.check_single_for_falling(pos) else minetest.dig_node(pos) end spawn_hidden(pos, node_name) end if bettertrees.config.web then if bettertrees.mods.xdecor then minetest.override_item("bettertrees:spider_web", { description = S("Spider Web"), on_construct = function(pos) local time = math.random(bettertrees.config.web_min_rand, bettertrees.config.web_max_rand) minetest.get_node_timer(pos):start(time) end, on_timer = function(pos, elapsed) if math.random(200) == 1 then minetest.swap_node(pos, { name = 'bettertrees:spider_web_broken' }) end return true end, }) -- register alias with xdecor:cobweb else minetest.register_node("bettertrees:spider_web", { description = S("Spider Web"), drawtype = "plantlike", tiles = {"xdecor_cobweb.png"}, inventory_image = "xdecor_cobweb.png", move_resistance = 8, walkable = false, selection_box = {type = "regular"}, groups = {snappy = 3, flammable = 3}, is_ground_content = false, sounds = default.node_sound_leaves_defaults(), on_construct = function(pos) local time = math.random(bettertrees.config.web_min_rand, bettertrees.config.web_max_rand) minetest.get_node_timer(pos):start(time) end, on_timer = function(pos, elapsed) if math.random(200) == 1 then minetest.swap_node(pos, { name = 'bettertrees:spider_web_broken' }) end return true end, }) -- register alias with xdecor:cobweb end minetest.register_node("bettertrees:spider_web_broken", { description = S("Broken Spider Web"), drawtype = "plantlike", tiles = {"xdecor_cobweb.png"}, inventory_image = "xdecor_cobweb.png", move_resistance = 8, walkable = false, selection_box = {type = "regular"}, groups = {snappy = 3, flammable = 3}, is_ground_content = false, sounds = default.node_sound_leaves_defaults(), on_construct = function(pos) local time = math.random(bettertrees.config.web_min_rand, bettertrees.config.web_max_rand) minetest.get_node_timer(pos):start(time) end, on_timer = function(pos, elapsed) if math.random(200) == 1 then minetest.swap_node(pos, { name = 'air' }) end return true end, }) end -- Regrow leaves or fruit, adapted from Tenplus1's regrow mod, licensed under MIT minetest.register_node('bettertrees:hidden', { drawtype = "airlike", paramtype = "light", sunlight_propagates = true, walkable = false, pointable = false, diggable = false, buildable_to = true, drop = "", groups = { not_in_creative_inventory = 1 }, -- once placed start random timer between 20 and 30 minutes on_construct = function(pos) local time = math.random(60 * 20, 60 * 30) minetest.get_node_timer(pos):start(time) end, -- when timer reached check which leaf or fruit to place if tree still exists on_timer = function(pos, elapsed) local meta = minetest.get_meta(pos) if not meta then return end local fruit = meta:get_string("fruit") or "" local leaf = meta:get_string("leaf") or "" if fruit == "" then if leaf == "" or not minetest.find_node_near(pos, 1, leaf) then leaf = "air" end minetest.set_node(pos, { name = leaf }) else if leaf == "" or fruit == "" or not minetest.find_node_near(pos, 1, leaf) then fruit = "air" end minetest.set_node(pos, { name = fruit }) end end }) if bettertrees.stick then -- Basically strip leaves off branches minetest.register_craft({ type = 'shapeless', output = bettertrees.stick, recipe = {'group:leaves'} -- replacements = 'pile of leaves?' }) minetest.register_node(bettertrees.stick_node, { description = S('Stick'), drawtype = 'mesh', paramtype = 'light', paramtype2 = 'none', selection_box = { type = 'fixed', fixed = {-8 / 16, -8 / 16, -8 / 16, 8 / 16, -7 / 16, 8 / 16} }, mesh = 'extrusion_mesh_16.obj', tiles = {'default_stick.png'}, use_texture_alpha = 'clip', inventory_image = "default_stick.png", wield_image = "default_stick.png", floodable = true, walkable = false, sunlight_propagates = true, buildable_to = true, is_ground_content = false, groups = { stick = 1, falling_node = 1, oddly_breakable_by_hand = 3, snappy = 3, flammable = 2, choppy = 3, attached_node = 1, dig_immediate = 3, }, sounds = bettertrees.stick_sound }) end minetest.register_on_mods_loaded(function() local tree_nodes = {} local dirt_nodes = {} for node_name, def in pairs(minetest.registered_nodes) do if def.groups and def.groups.leaves then local override = {} if not string.match(node_name, "bush") then override = { walkable = false, climbable = true, -- Climbing through leaves should slow players down move_resistance = 3, waving = 2, -- Make leaves fall when placed after_place_node = function(pos, oldnode, oldmeta, drops) minetest.spawn_falling_node(pos) end, -- Adapted from Tenplus1's regrow mod, licensed under MIT after_dig_node = function(pos, oldnode, oldmetadata, digger) -- if node has been placed by player then do not regrow if oldnode.param2 > 0 then return end -- replace leaf with regrowth node, set leaf name spawn_hidden(pos, node_name) end, on_construct = function(pos) local time = math.random(60 * 20, 60 * 30) minetest.get_node_timer(pos):start(time) end, -- when timer reached, check which leaf to remove on_timer = function(pos, elapsed) if math.random(200) == 1 then local pos2 = table.copy(pos) pos2.y = pos2.y - 1 local check_node = minetest.get_node(pos2) if check_node.name == 'air' then break_leaf(pos, node_name, true) return false end end return true end } end if bettertrees.config.bushy then override.drawtype = 'mesh' override.mesh = bettertrees.config.bushy_cheap and "bushy_leaves_cheap_model.obj" or "bushy_leaves_full_model.obj" end minetest.override_item(node_name, override) elseif def.groups and def.groups.tree then table.insert(tree_nodes, node_name) local groups = table.copy(def.groups) groups.oddly_breakable_by_hand = 0 -- groups.falling_node = 1 minetest.override_item(node_name, { groups = groups, on_punch = function(pos, node, puncher, pointed_thing) local wielded_item = puncher:get_wielded_item(); if wielded_item:is_empty() then -- take a little damage local hp = puncher:get_hp() puncher:set_hp(hp - 0.1, 'node_damage') end minetest.node_punch(pos, node, puncher, pointed_thing) end, on_dig = function(pos, node, digger) -- this is where we start the logic to fell the tree end, on_destruct = function(pos) local meta = minetest.get_meta(pos) if not meta then return end if meta:contains('bettertrees_uuid') then meta:set_string('bettertrees_uuid', '') end end, }) elseif def.groups and (def.groups.soil or def.groups.snowy) then table.insert(dirt_nodes, node_name) elseif def.groups and def.groups.leafdecay_drop then minetest.override_item(node_name, { after_dig_node = function(pos, oldnode, oldmetadata, digger) -- if node has been placed by player then do not regrow if oldnode.param2 > 0 then return end -- replace leaf with regrowth node, set leaf name minetest.set_node(pos, { name = "bettertrees:hidden" }) local meta = minetest.get_meta(pos) meta:set_string("leaf", minetest.get_node(minetest.find_node_near(pos, 1, 'group:leaves')).name) meta:set_string("fruit", node_name) end, on_destruct = function(pos) local meta = minetest.get_meta(pos) if not meta then return end if meta:contains('bettertrees_uuid') then meta:set_string('bettertrees_uuid', '') end end, }) end end minetest.log('verbose', 'bettertrees: tree_nodes = '..minetest.serialize(tree_nodes)) minetest.log('verbose', 'bettertrees: dirt_nodes = '..minetest.serialize(dirt_nodes)) if bettertrees.stick then minetest.register_decoration({ name = bettertrees.stick, deco_type = 'simple', place_on = dirt_nodes, sidelen = 8, decoration = bettertrees.stick, rotation = 'random', spawn_by = tree_nodes, num_spawn_by = 1, fill_ratio = 0.1, }) end end) -- chance of limbs breaking while player is in leaves minetest.register_globalstep(function(dtime) local players = minetest.get_connected_players() for i, player in ipairs(players) do if minetest.is_player(player) then local pos = player:get_pos() local pos2 = table.copy(pos) pos2.y = pos2.y + 1 local nodes = minetest.find_nodes_in_area(pos, pos2, 'group:leaves', true) for node_name, node_poss in pairs(nodes) do for i, node_pos in ipairs(node_poss) do local chance = 200 -- calculate "weight" of armor and deduct from `chance` if bettertrees.mods.armor then local sum_gravity = 1 local worn_armor = armor:get_weared_armor_elements(player) for el, i in ipairs(worn_armor) do if el.groups and el.groups.physics_gravity then sum_gravity = sum_gravity + (el.groups.physics_gravity * 10) end end chance = chance / sum_gravity end if math.random(chance) == 1 then break_leaf(node_pos, node_name, false) end end end end end end) og_spawn_tree = minetest.spawn_tree -- attempt to patch minetest.swawn_tree to assign uuid to nodes function minetest.spawn_tree(pos, tree_def) local result = og_spawn_tree(pos, tree_def) local nodes = [] if (tree_def.fruit) then table.insert(nodes, tree_def.fruit) end if (tree_def.trunk) then table.insert(nodes, tree_def.trunk) end if (tree_def.leaves) then table.insert(nodes, tree_def.leaves) end assign_uuid({ x = pos.x - 1, y = pos.y - 1, z = pos.z - 1 }, { x = pos.x + 1, y = pos.y + 3, z = pos.z + 1 }, nodes) return result end if bettertrees.mods.default then dofile(bettertrees.path .. '/default.lua') end if bettertrees.mods.moretrees then dofile(bettertrees.path .. '/moretrees.lua') end