123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367 |
- --[[
- lumberjack
- ==========
- Copyright (C) 2018 Joachim Stolberg
- LGPLv2.1+
- See LICENSE.txt for more information
- Mod to completely cut trees by destroying only one block.
- This mod allows to destroy the bottom of the tree and the whole tree is felled
- and moved to the players inventory.
- To distinguish between "grown" trees and placed tree nodes, the attribute
- 'node.param1' is used to identify placed nodes.
- The number of necessary lumberjack points has to be configured via 'settingtypes.txt'
- ]]--
- lumberjack = {}
- local MY_PARAM1_VAL = 7 -- to identify placed nodes
- -- Necessary number of points for dug trees and placed sapling to get lumberjack privs
- local LUMBERJACK_TREE_POINTS = tonumber(minetest.settings:get("lumberjack_points")) or 400
- local LUMBERJACK_SAPL_POINTS = LUMBERJACK_TREE_POINTS / 6
- local lTrees = {} -- List of registered tree items
- --
- -- Check if used tool is some kind of axe and is used by a player
- --
- local function chopper_tool(digger)
- if digger and digger:is_player() then
- local tool = digger:get_wielded_item()
- if tool:get_name() == "screwdriver:screwdriver" then
- return true
- end
- if tool then
- local caps = tool:get_tool_capabilities()
- return caps.groupcaps and caps.groupcaps.choppy
- end
- end
- return false
- end
- --
- -- Remove/add tree steps
- --
- local function remove_steps(pos)
- local pos1 = {x=pos.x-1, y=pos.y, z=pos.z-1}
- local pos2 = {x=pos.x+1, y=pos.y, z=pos.z+1}
- for _,pos in ipairs(minetest.find_nodes_in_area(pos1, pos2, "lumberjack:step")) do
- minetest.remove_node(pos)
- end
- end
- local function add_steps(pos, digger)
- local facedir = minetest.dir_to_facedir(digger:get_look_dir(), false)
- local dir = minetest.facedir_to_dir((facedir + 2) % 4)
- local newpos = vector.add(pos, dir)
- if minetest.get_node(newpos).name == "air" then
- minetest.add_node(newpos, {name="lumberjack:step", param2=facedir})
- end
- end
- local function on_punch(pos, node, puncher, pointed_thing)
- if chopper_tool(puncher) and node.param1 == 0 then -- grown tree?
- if not minetest.is_protected(pos, puncher:get_player_name()) then
- add_steps(pos, puncher)
- end
- end
- minetest.node_punch(pos, node, puncher, pointed_thing)
- end
- --
- -- tool wearing
- --
- local function add_wear(digger, node, num_nodes)
- local tool = digger:get_wielded_item()
- if tool then
- local caps = tool:get_tool_capabilities()
- if caps.groupcaps and caps.groupcaps.choppy then
- local uses = caps.groupcaps.choppy.uses or 10
- uses = uses * 9
- tool:add_wear(65535 * num_nodes / uses)
- digger:set_wielded_item(tool)
- end
- end
- end
- --
- -- Remove all treen nodes including steps in the given area
- --
- local function remove_items(pos1, pos2, name)
- local cnt = 0
- for _,pos in ipairs(minetest.find_nodes_in_area(pos1, pos2, name)) do
- minetest.remove_node(pos)
- remove_steps(pos)
- cnt = cnt + 1
- end
- return cnt
- end
- --
- -- Check for tree nodes on the next higher level
- -- We have to check more than one level, because Ethereal allows stem gaps
- --
- local function is_top_tree_node(pos, name)
- local pos1 = {x=pos.x-1, y=pos.y+1, z=pos.z-1}
- local pos2 = {x=pos.x+1, y=pos.y+3, z=pos.z+1}
- for _,pos in ipairs(minetest.find_nodes_in_area(pos1, pos2, name)) do
- return false
- end
- return true
- end
- --
- -- Check for the necessary number of points and grant lumberjack privs if level is reached
- --
- local function check_points(player)
- local points = tonumber(player:get_attribute("lumberjack_tree_points") or LUMBERJACK_TREE_POINTS)
- points = points + tonumber(player:get_attribute("lumberjack_sapl_points") or LUMBERJACK_SAPL_POINTS)
- if points > 0 then
- return false
- elseif points == 0 then
- local privs = minetest.get_player_privs(player:get_player_name())
- privs.lumberjack = true
- minetest.set_player_privs(player:get_player_name(), privs)
- player:set_attribute("lumberjack_tree_points", "-1")
- player:set_attribute("lumberjack_sapl_points", "-1")
- minetest.chat_send_player(player:get_player_name(), "You got lumberjack privs now")
- minetest.log("action", player:get_player_name().." got lumberjack privs")
- end
- return true
- end
- --
- -- Maintain lumberjack points and grant lumberjack privs if level is reached
- --
- local function needed_points(digger)
- local points = tonumber(digger:get_attribute("lumberjack_tree_points") or LUMBERJACK_TREE_POINTS)
- if points > 0 then
- digger:set_attribute("lumberjack_tree_points", tostring(points - 1))
- end
- if points == 0 then
- return check_points(digger)
- end
- return false
- end
- --
- -- Decrement sapling points
- --
- local function after_place_sapling(pos, placer)
- local points = tonumber(placer:get_attribute("lumberjack_sapl_points") or LUMBERJACK_SAPL_POINTS)
- if points > 0 then
- placer:set_attribute("lumberjack_sapl_points", tostring(points - 1))
- end
- if points == 0 then
- check_points(placer)
- end
- end
- --
- -- Remove the complete tree and return the number of removed items
- --
- local function remove_tree(pos, radius, name)
- local level = 1
- local num_nodes = 0
- while true do
- -- We have to check more than one level, because Ethereal allows stem gaps
- local pos1 = {x=pos.x-radius, y=pos.y+level, z=pos.z-radius}
- local pos2 = {x=pos.x+radius, y=pos.y+level+2, z=pos.z+radius}
- local cnt = remove_items(pos1, pos2, name)
- if cnt == 0 then break end
- num_nodes = num_nodes + cnt
- level = level + 3
- end
- return num_nodes
- end
- --
- -- Add tree items to the players inventory
- --
- local function add_to_inventory(digger, name, len, pos)
- local inv = digger:get_inventory()
- local items = ItemStack(name .. " " .. len)
- if inv and items and inv:room_for_item("main", items) then
- inv:add_item("main", items)
- else
- minetest.item_drop(items, digger, pos)
- end
- end
- --
- -- Remove the complete tree if the destroyed node belongs to a tree
- --
- local function after_dig_node(pos, oldnode, oldmetadata, digger)
- -- Player placed node?
- if oldnode.param1 ~= 0 then return end
- remove_steps(pos)
- -- don't remove whole tree?
- if not digger or digger:get_player_control().sneak then return end
- -- Get tree parameters
- local height_min = 3
- local radius = 0
- local registered_tree = lTrees[oldnode.name]
- if registered_tree then
- height_min = registered_tree.height_min or height_min
- radius = registered_tree.radius or radius
- end
- -- Or root nodes?
- local test_pos = {x=pos.x, y=pos.y+height_min-1, z=pos.z}
- if minetest.get_node(test_pos).name ~= oldnode.name then return end
- -- Fell the tree
- local num_nodes = remove_tree(pos, radius, oldnode.name)
- add_to_inventory(digger, oldnode.name, num_nodes, pos)
- add_wear(digger, oldnode, num_nodes)
- minetest.log("action", digger:get_player_name().." fells "..oldnode.name..
- " ("..num_nodes.." items)".." at "..minetest.pos_to_string(pos))
- minetest.sound_play("tree_falling", {pos = pos, max_hear_distance = 16})
- end
- --
- -- Mark node as "placed by player"
- --
- local function on_construct(pos)
- local node = minetest.get_node(pos)
- if node then
- minetest.swap_node(pos, {name=node.name, param1=MY_PARAM1_VAL, param2=node.param2})
- end
- end
- local function can_dig(pos, digger)
- if not digger then
- return true
- end
- if minetest.is_protected(pos, digger:get_player_name()) then
- return false
- end
- if minetest.check_player_privs(digger:get_player_name(), "lumberjack") then
- if chopper_tool(digger) then
- return true
- else
- minetest.chat_send_player(digger:get_player_name(), "[Lumberjack Mod] You have to use an axe")
- return false
- end
- end
- local node = minetest.get_node(pos)
- if node.param1 ~= 0 then
- return true
- end
- if is_top_tree_node(pos, node.name) or needed_points(digger) then
- return true
- end
- minetest.chat_send_player(digger:get_player_name(), "[Lumberjack Mod] From the top, please")
- return false
- end
- minetest.register_privilege("lumberjack",
- {description = "Gives you the rights to fell a tree at once",
- give_to_singleplayer = true})
- minetest.register_node("lumberjack:step", {
- description = "Lumberjack Step",
- drawtype = "nodebox",
- tiles = {"lumberjack_steps.png"},
- node_box = {
- type = "fixed",
- fixed = {
- { -0.5, -0.5, 0.49, 0.5, 0.5, 0.5},
- },
- },
- paramtype2 = "facedir",
- is_ground_content = false,
- climbable = true,
- paramtype = "light",
- use_texture_alpha = true,
- sunlight_propagates = true,
- walkable = false,
- pointable = false,
- drop = "",
- groups = {flammable=1, not_in_creative_inventory=1},
- })
- --
- -- Register the tree node to the lumberjack mod.
- -- 'tree_name' is the tree item name,, e.g. "default:tree"
- -- 'sapling_name' is the tree sapling name, e.g. "default:sapling"
- -- 'radius' the the range in nodes (+x/-x/+z/-z), where all available tree nodes will be removed.
- -- 'stem_height_min' is the minimum number of tree nodes, to be a valid stem (and not the a root item).
- --
- function lumberjack.register_tree(tree_name, sapling_name, radius, stem_height_min)
- -- check tree attributes
- local data = minetest.registered_nodes[tree_name]
- if data == nil then
- error("[lumberjack] "..tree_name.." is no valid item")
- end
- if data.after_dig_node then
- error("[lumberjack] "..tree_name.." has already an 'after_dig_node' function")
- end
- if data.on_construct then
- error("[lumberjack] "..tree_name.." has already an 'on_construct' function")
- end
- if data.can_dig then
- error("[lumberjack] "..tree_name.." has already a 'can_dig' function")
- end
- if not data.groups.choppy then
- error("[lumberjack] "..tree_name.." has no 'choppy' property")
- end
- -- check sapling attributes
- if minetest.registered_nodes[sapling_name].after_place_node then
- error("[lumberjack] "..sapling_name.." has already an 'after_place_node' function")
- end
- minetest.override_item(tree_name, {
- after_dig_node = after_dig_node,
- on_construct = on_construct,
- can_dig = can_dig,
- on_punch = on_punch,
- })
- minetest.override_item(sapling_name, {
- after_place_node = after_place_sapling
- })
- lTrees[tree_name] = {radius=radius, height_min=stem_height_min, choppy=data.groups.choppy}
- end
- lumberjack.register_tree("default:tree", "default:sapling", 1, 2)
- lumberjack.register_tree("default:jungletree", "default:junglesapling", 1, 5)
- lumberjack.register_tree("default:acacia_tree", "default:acacia_sapling", 2, 2)
- lumberjack.register_tree("default:aspen_tree", "default:aspen_sapling", 0, 5)
- lumberjack.register_tree("default:pine_tree", "default:pine_sapling", 0, 3)
- if minetest.get_modpath("ethereal") and minetest.global_exists("ethereal") then
- lumberjack.register_tree("ethereal:palm_trunk", "ethereal:palm_sapling", 1, 3)
- lumberjack.register_tree("ethereal:mushroom_trunk", "ethereal:mushroom_sapling", 1, 3)
- lumberjack.register_tree("ethereal:birch_trunk", "ethereal:birch_sapling", 0, 3)
- lumberjack.register_tree("ethereal:banana_trunk", "ethereal:banana_tree_sapling", 1, 3)
- lumberjack.register_tree("ethereal:willow_trunk", "ethereal:willow_sapling", 4, 3)
- lumberjack.register_tree("ethereal:frost_tree", "ethereal:frost_tree_sapling", 1, 3)
- end
- if minetest.get_modpath("moretrees") and minetest.global_exists("moretrees") then
- lumberjack.register_tree("moretrees:beech_trunk", "moretrees:beech_sapling", 1, 3)
- lumberjack.register_tree("moretrees:apple_tree_trunk", "moretrees:apple_tree_sapling", 8, 3)
- lumberjack.register_tree("moretrees:oak_trunk", "moretrees:oak_sapling", 13,5 )
- lumberjack.register_tree("moretrees:sequoia_trunk", "moretrees:sequoia_sapling", 9, 3)
- lumberjack.register_tree("moretrees:birch_trunk", "moretrees:birch_sapling", 12,5)
- lumberjack.register_tree("moretrees:palm_trunk", "moretrees:palm_sapling", 5, 3)
- -- lumberjack.register_tree("moretrees:palm_fruit_trunk", "moretrees:palm_sapling", 5, 3)
- lumberjack.register_tree("moretrees:spruce_trunk", "moretrees:spruce_sapling", 1, 3)
- lumberjack.register_tree("moretrees:pine_trunk", "moretrees:pine_sapling", 0, 3)
- lumberjack.register_tree("moretrees:willow_trunk", "moretrees:willow_sapling",1,3)
- lumberjack.register_tree("moretrees:rubber_tree_trunk", "moretrees:rubber_tree_sapling", 7, 3)
- -- lumberjack.register_tree("moretrees:jungletree_trunk", "moretrees:jungletree_sapling", 1, 5) -- crashes
- lumberjack.register_tree("moretrees:fir_trunk", "moretrees:fir_sapling", 5, 3) -- below leaves by 5
- end
|