2018-04-07 23:28:59 +02:00
|
|
|
--[[
|
|
|
|
|
|
|
|
lumberjack
|
|
|
|
==========
|
|
|
|
|
|
|
|
Copyright (C) 2018 Joachim Stolberg
|
|
|
|
|
|
|
|
LGPLv2.1+
|
|
|
|
See LICENSE.txt for more information
|
|
|
|
|
2018-04-09 22:55:12 +02:00
|
|
|
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
|
2018-04-07 23:28:59 +02:00
|
|
|
and moved to the players inventory.
|
2018-04-09 22:55:12 +02:00
|
|
|
|
|
|
|
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'
|
2018-04-07 23:28:59 +02:00
|
|
|
|
|
|
|
]]--
|
|
|
|
|
|
|
|
lumberjack = {}
|
|
|
|
|
|
|
|
local MY_PARAM1_VAL = 7 -- to identify placed nodes
|
|
|
|
|
2018-04-09 22:55:12 +02:00
|
|
|
-- Necessary number of points for dug trees and placed sapling to get lumberjack privs
|
|
|
|
local LUMBERJACK_TREE_POINTS = tonumber(minetest.setting_get("lumberjack_points")) or 400
|
|
|
|
local LUMBERJACK_SAPL_POINTS = LUMBERJACK_TREE_POINTS / 6
|
|
|
|
|
|
|
|
local lTrees = {} -- List of registered tree items
|
2018-04-07 23:28:59 +02:00
|
|
|
|
2018-04-08 22:27:05 +02:00
|
|
|
--
|
2018-04-17 20:14:26 +02:00
|
|
|
-- Check if used tool is some kind of axe and is used by a player
|
2018-04-08 22:27:05 +02:00
|
|
|
--
|
|
|
|
local function chopper_tool(digger)
|
2018-04-17 20:14:26 +02:00
|
|
|
if digger and digger:is_player() then
|
|
|
|
local tool = digger:get_wielded_item()
|
|
|
|
if tool then
|
|
|
|
local caps = tool:get_tool_capabilities()
|
|
|
|
return caps.groupcaps and caps.groupcaps.choppy
|
|
|
|
end
|
|
|
|
end
|
2018-04-08 22:27:05 +02:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2018-04-16 22:19:31 +02:00
|
|
|
--
|
|
|
|
-- 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)
|
2018-04-17 20:14:26 +02:00
|
|
|
if minetest.get_node(newpos).name == "air" then
|
|
|
|
minetest.add_node(newpos, {name="lumberjack:step", param2=facedir})
|
|
|
|
end
|
2018-04-16 22:19:31 +02:00
|
|
|
end
|
|
|
|
|
2018-05-30 22:48:13 +02:00
|
|
|
local function on_punch(pos, node, puncher, pointed_thing)
|
2018-04-17 20:14:26 +02:00
|
|
|
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
|
2018-04-16 22:19:31 +02:00
|
|
|
end
|
2018-04-20 20:17:10 +02:00
|
|
|
minetest.node_punch(pos, node, puncher, pointed_thing)
|
2018-04-16 22:19:31 +02:00
|
|
|
end
|
|
|
|
|
2018-04-08 22:27:05 +02:00
|
|
|
--
|
|
|
|
-- 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
|
2018-04-09 22:55:12 +02:00
|
|
|
uses = uses * 9
|
2018-04-08 22:27:05 +02:00
|
|
|
tool:add_wear(65535 * num_nodes / uses)
|
|
|
|
digger:set_wielded_item(tool)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2018-04-07 23:28:59 +02:00
|
|
|
--
|
2018-04-17 20:14:26 +02:00
|
|
|
-- Remove all treen nodes including steps in the given area
|
2018-04-07 23:28:59 +02:00
|
|
|
--
|
2018-04-16 22:10:08 +02:00
|
|
|
local function remove_items(pos1, pos2, name)
|
2018-04-07 23:28:59 +02:00
|
|
|
local cnt = 0
|
|
|
|
for _,pos in ipairs(minetest.find_nodes_in_area(pos1, pos2, name)) do
|
|
|
|
minetest.remove_node(pos)
|
2018-04-16 22:19:31 +02:00
|
|
|
remove_steps(pos)
|
2018-04-07 23:28:59 +02:00
|
|
|
cnt = cnt + 1
|
|
|
|
end
|
|
|
|
return cnt
|
|
|
|
end
|
|
|
|
|
2018-04-08 22:27:05 +02:00
|
|
|
--
|
2018-04-10 13:06:08 +02:00
|
|
|
-- Check for tree nodes on the next higher level
|
2018-04-17 20:14:26 +02:00
|
|
|
-- We have to check more than one level, because Ethereal allows stem gaps
|
2018-04-08 22:27:05 +02:00
|
|
|
--
|
2018-04-09 22:55:12 +02:00
|
|
|
local function is_top_tree_node(pos, name)
|
2018-04-08 22:27:05 +02:00
|
|
|
local pos1 = {x=pos.x-1, y=pos.y+1, z=pos.z-1}
|
2018-04-17 20:14:26 +02:00
|
|
|
local pos2 = {x=pos.x+1, y=pos.y+3, z=pos.z+1}
|
2018-04-08 22:27:05 +02:00
|
|
|
for _,pos in ipairs(minetest.find_nodes_in_area(pos1, pos2, name)) do
|
2018-04-09 22:55:12 +02:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
return true
|
|
|
|
end
|
|
|
|
|
2018-04-10 13:06:08 +02:00
|
|
|
--
|
|
|
|
-- Check for the necessary number of points and grant lumberjack privs if level is reached
|
|
|
|
--
|
2018-04-09 22:55:12 +02:00
|
|
|
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)
|
2018-04-08 22:27:05 +02:00
|
|
|
end
|
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
2018-04-10 13:06:08 +02:00
|
|
|
--
|
|
|
|
-- Decrement sapling points
|
|
|
|
--
|
2018-04-09 22:55:12 +02:00
|
|
|
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
|
|
|
|
|
2018-04-07 23:28:59 +02:00
|
|
|
--
|
|
|
|
-- 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
|
2018-04-17 20:14:26 +02:00
|
|
|
-- We have to check more than one level, because Ethereal allows stem gaps
|
2018-04-16 22:10:08 +02:00
|
|
|
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)
|
2018-04-07 23:28:59 +02:00
|
|
|
if cnt == 0 then break end
|
|
|
|
num_nodes = num_nodes + cnt
|
2018-04-17 20:14:26 +02:00
|
|
|
level = level + 3
|
2018-04-07 23:28:59 +02:00
|
|
|
end
|
|
|
|
return num_nodes
|
|
|
|
end
|
|
|
|
|
2018-04-09 22:55:12 +02:00
|
|
|
|
2018-04-07 23:28:59 +02:00
|
|
|
--
|
|
|
|
-- Add tree items to the players inventory
|
|
|
|
--
|
2018-04-12 21:54:30 +02:00
|
|
|
local function add_to_inventory(digger, name, len, pos)
|
2018-04-07 23:28:59 +02:00
|
|
|
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)
|
2018-04-12 21:54:30 +02:00
|
|
|
else
|
|
|
|
minetest.item_drop(items, digger, pos)
|
2018-04-07 23:28:59 +02:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
--
|
2018-04-10 13:06:08 +02:00
|
|
|
-- Remove the complete tree if the destroyed node belongs to a tree
|
2018-04-07 23:28:59 +02:00
|
|
|
--
|
|
|
|
local function after_dig_node(pos, oldnode, oldmetadata, digger)
|
2018-04-09 22:55:12 +02:00
|
|
|
-- Player placed node?
|
|
|
|
if oldnode.param1 ~= 0 then return end
|
2018-04-17 20:14:26 +02:00
|
|
|
remove_steps(pos)
|
2019-01-28 08:14:12 +01:00
|
|
|
-- don't remove whole tree?
|
2018-04-17 20:14:26 +02:00
|
|
|
if not digger or digger:get_player_control().sneak then return end
|
2019-01-28 08:14:12 +01:00
|
|
|
-- 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
|
2018-04-09 22:55:12 +02:00
|
|
|
-- Or root nodes?
|
2018-04-07 23:28:59 +02:00
|
|
|
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
|
2018-04-09 22:55:12 +02:00
|
|
|
-- Fell the tree
|
2018-04-07 23:28:59 +02:00
|
|
|
local num_nodes = remove_tree(pos, radius, oldnode.name)
|
2018-04-12 21:54:30 +02:00
|
|
|
add_to_inventory(digger, oldnode.name, num_nodes, pos)
|
2018-04-08 22:27:05 +02:00
|
|
|
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))
|
2018-04-07 23:28:59 +02:00
|
|
|
minetest.sound_play("tree_falling", {pos = pos, max_hear_distance = 16})
|
|
|
|
end
|
|
|
|
|
|
|
|
--
|
2018-04-10 13:06:08 +02:00
|
|
|
-- Mark node as "placed by player"
|
2018-04-07 23:28:59 +02:00
|
|
|
--
|
|
|
|
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
|
|
|
|
|
2018-04-08 22:27:05 +02:00
|
|
|
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
|
2018-04-10 20:43:15 +02:00
|
|
|
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
|
2018-04-08 22:27:05 +02:00
|
|
|
end
|
|
|
|
local node = minetest.get_node(pos)
|
2018-04-09 23:36:59 +02:00
|
|
|
if node.param1 ~= 0 then
|
|
|
|
return true
|
|
|
|
end
|
2018-04-09 22:55:12 +02:00
|
|
|
if is_top_tree_node(pos, node.name) or needed_points(digger) then
|
2018-04-08 22:27:05 +02:00
|
|
|
return true
|
|
|
|
end
|
2018-04-09 22:55:12 +02:00
|
|
|
minetest.chat_send_player(digger:get_player_name(), "[Lumberjack Mod] From the top, please")
|
2018-04-08 22:27:05 +02:00
|
|
|
return false
|
|
|
|
end
|
|
|
|
|
|
|
|
minetest.register_privilege("lumberjack",
|
|
|
|
{description = "Gives you the rights to fell a tree at once",
|
|
|
|
give_to_singleplayer = true})
|
|
|
|
|
2018-04-16 22:10:08 +02:00
|
|
|
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 = {choppy = 2},
|
|
|
|
})
|
|
|
|
|
2018-04-08 22:27:05 +02:00
|
|
|
|
2018-04-07 23:41:34 +02:00
|
|
|
--
|
|
|
|
-- Register the tree node to the lumberjack mod.
|
2018-04-10 13:06:08 +02:00
|
|
|
-- '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).
|
2018-04-07 23:41:34 +02:00
|
|
|
--
|
2018-04-09 22:55:12 +02:00
|
|
|
function lumberjack.register_tree(tree_name, sapling_name, radius, stem_height_min)
|
|
|
|
|
|
|
|
-- check tree attributes
|
|
|
|
local data = minetest.registered_nodes[tree_name]
|
2018-04-07 23:41:34 +02:00
|
|
|
if data == nil then
|
2018-04-09 22:55:12 +02:00
|
|
|
error("[lumberjack] "..tree_name.." is no valid item")
|
2018-04-07 23:41:34 +02:00
|
|
|
end
|
2018-04-08 22:27:05 +02:00
|
|
|
if data.after_dig_node then
|
2018-04-09 22:55:12 +02:00
|
|
|
error("[lumberjack] "..tree_name.." has already an 'after_dig_node' function")
|
2018-04-07 23:41:34 +02:00
|
|
|
end
|
2018-04-08 22:27:05 +02:00
|
|
|
if data.on_construct then
|
2018-04-09 22:55:12 +02:00
|
|
|
error("[lumberjack] "..tree_name.." has already an 'on_construct' function")
|
2018-04-08 22:27:05 +02:00
|
|
|
end
|
|
|
|
if data.can_dig then
|
2018-04-09 22:55:12 +02:00
|
|
|
error("[lumberjack] "..tree_name.." has already a 'can_dig' function")
|
2018-04-08 22:27:05 +02:00
|
|
|
end
|
|
|
|
if not data.groups.choppy then
|
2018-04-09 22:55:12 +02:00
|
|
|
error("[lumberjack] "..tree_name.." has no 'choppy' property")
|
|
|
|
end
|
|
|
|
|
2018-04-10 13:06:08 +02:00
|
|
|
-- check sapling attributes
|
2018-04-09 22:55:12 +02:00
|
|
|
if minetest.registered_nodes[sapling_name].after_place_node then
|
|
|
|
error("[lumberjack] "..sapling_name.." has already an 'after_place_node' function")
|
2018-04-08 22:27:05 +02:00
|
|
|
end
|
2018-04-09 22:55:12 +02:00
|
|
|
|
|
|
|
minetest.override_item(tree_name, {
|
2018-04-08 22:27:05 +02:00
|
|
|
after_dig_node = after_dig_node,
|
|
|
|
on_construct = on_construct,
|
|
|
|
can_dig = can_dig,
|
2018-04-16 22:10:08 +02:00
|
|
|
on_punch = on_punch,
|
2018-04-08 22:27:05 +02:00
|
|
|
})
|
2018-04-09 22:55:12 +02:00
|
|
|
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}
|
2018-04-07 23:41:34 +02:00
|
|
|
end
|
|
|
|
|
2018-04-09 22:55:12 +02:00
|
|
|
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, 3)
|
|
|
|
lumberjack.register_tree("default:aspen_tree", "default:aspen_sapling", 0, 5)
|
|
|
|
lumberjack.register_tree("default:pine_tree", "default:pine_sapling", 0, 3)
|
2018-04-07 23:28:59 +02:00
|
|
|
|
2018-04-16 22:10:08 +02:00
|
|
|
if minetest.get_modpath("ethereal") and minetest.global_exists("ethereal") then
|
2018-04-09 22:55:12 +02:00
|
|
|
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)
|
2018-04-07 23:28:59 +02:00
|
|
|
end
|
2018-04-08 22:27:05 +02:00
|
|
|
|
2019-01-29 06:09:26 +01:00
|
|
|
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
|