diff --git a/mods/default/functions.lua b/mods/default/functions.lua index f3bb97cd..a98d091f 100644 --- a/mods/default/functions.lua +++ b/mods/default/functions.lua @@ -481,3 +481,43 @@ minetest.register_abm({ end end }) + + +-- +-- Checks if specified volume intersects a protected volume +-- + +function default.intersects_protection(minp, maxp, player_name, interval) + -- 'interval' is the largest allowed interval for the 3D lattice of checks + + -- Compute the optimal float step 'd' for each axis so that all corners and + -- borders are checked. 'd' will be smaller or equal to 'interval'. + -- Subtracting 1e-4 ensures that the max co-ordinate will be reached by the + -- for loop (which might otherwise not be the case due to rounding errors). + local d = {} + for _, c in pairs({"x", "y", "z"}) do + if maxp[c] > minp[c] then + d[c] = (maxp[c] - minp[c]) / math.ceil((maxp[c] - minp[c]) / interval) - 1e-4 + elseif maxp[c] == minp[c] then + d[c] = 1 -- Any value larger than 0 to avoid division by zero + else -- maxp[c] < minp[c], print error and treat as protection intersected + minetest.log("error", "maxp < minp in 'default.intersects_protection()'") + return true + end + end + + for zf = minp.z, maxp.z, d.z do + local z = math.floor(zf + 0.5) + for yf = minp.y, maxp.y, d.y do + local y = math.floor(yf + 0.5) + for xf = minp.x, maxp.x, d.x do + local x = math.floor(xf + 0.5) + if minetest.is_protected({x = x, y = y, z = z}, player_name) then + return true + end + end + end + end + + return false +end diff --git a/mods/default/nodes.lua b/mods/default/nodes.lua index 31e063d4..05d9f32d 100644 --- a/mods/default/nodes.lua +++ b/mods/default/nodes.lua @@ -500,9 +500,6 @@ minetest.register_node("default:sapling", { sunlight_propagates = true, walkable = false, on_timer = default.grow_sapling, - on_construct = function(pos) - minetest.get_node_timer(pos):start(math.random(2400,4800)) - end, selection_box = { type = "fixed", fixed = {-0.3, -0.5, -0.3, 0.3, 0.35, 0.3} @@ -510,6 +507,23 @@ minetest.register_node("default:sapling", { groups = {snappy = 2, dig_immediate = 3, flammable = 2, attached_node = 1, sapling = 1}, sounds = default.node_sound_leaves_defaults(), + + on_construct = function(pos) + minetest.get_node_timer(pos):start(math.random(2400,4800)) + end, + + on_place = function(itemstack, placer, pointed_thing) + itemstack = default.sapling_on_place(itemstack, placer, pointed_thing, + "default:sapling", + -- minp, maxp to be checked, relative to sapling pos + -- minp_relative.y = 1 because sapling pos has been checked + {x = -2, y = 1, z = -2}, + {x = 2, y = 6, z = 2}, + -- maximum interval of interior volume check + 4) + + return itemstack + end, }) minetest.register_node("default:leaves", { @@ -624,9 +638,6 @@ minetest.register_node("default:junglesapling", { sunlight_propagates = true, walkable = false, on_timer = default.grow_sapling, - on_construct = function(pos) - minetest.get_node_timer(pos):start(math.random(2400,4800)) - end, selection_box = { type = "fixed", fixed = {-0.3, -0.5, -0.3, 0.3, 0.35, 0.3} @@ -634,6 +645,23 @@ minetest.register_node("default:junglesapling", { groups = {snappy = 2, dig_immediate = 3, flammable = 2, attached_node = 1, sapling = 1}, sounds = default.node_sound_leaves_defaults(), + + on_construct = function(pos) + minetest.get_node_timer(pos):start(math.random(2400,4800)) + end, + + on_place = function(itemstack, placer, pointed_thing) + itemstack = default.sapling_on_place(itemstack, placer, pointed_thing, + "default:junglesapling", + -- minp, maxp to be checked, relative to sapling pos + -- minp_relative.y = 1 because sapling pos has been checked + {x = -2, y = 1, z = -2}, + {x = 2, y = 15, z = 2}, + -- maximum interval of interior volume check + 4) + + return itemstack + end, }) @@ -691,9 +719,6 @@ minetest.register_node("default:pine_sapling", { sunlight_propagates = true, walkable = false, on_timer = default.grow_sapling, - on_construct = function(pos) - minetest.get_node_timer(pos):start(math.random(2400,4800)) - end, selection_box = { type = "fixed", fixed = {-0.3, -0.5, -0.3, 0.3, 0.35, 0.3} @@ -701,6 +726,23 @@ minetest.register_node("default:pine_sapling", { groups = {snappy = 2, dig_immediate = 3, flammable = 2, attached_node = 1, sapling = 1}, sounds = default.node_sound_leaves_defaults(), + + on_construct = function(pos) + minetest.get_node_timer(pos):start(math.random(2400,4800)) + end, + + on_place = function(itemstack, placer, pointed_thing) + itemstack = default.sapling_on_place(itemstack, placer, pointed_thing, + "default:pine_sapling", + -- minp, maxp to be checked, relative to sapling pos + -- minp_relative.y = 1 because sapling pos has been checked + {x = -2, y = 1, z = -2}, + {x = 2, y = 12, z = 2}, + -- maximum interval of interior volume check + 4) + + return itemstack + end, }) @@ -758,9 +800,6 @@ minetest.register_node("default:acacia_sapling", { sunlight_propagates = true, walkable = false, on_timer = default.grow_sapling, - on_construct = function(pos) - minetest.get_node_timer(pos):start(math.random(2400,4800)) - end, selection_box = { type = "fixed", fixed = {-0.3, -0.5, -0.3, 0.3, 0.35, 0.3} @@ -768,6 +807,23 @@ minetest.register_node("default:acacia_sapling", { groups = {snappy = 2, dig_immediate = 3, flammable = 2, attached_node = 1, sapling = 1}, sounds = default.node_sound_leaves_defaults(), + + on_construct = function(pos) + minetest.get_node_timer(pos):start(math.random(2400,4800)) + end, + + on_place = function(itemstack, placer, pointed_thing) + itemstack = default.sapling_on_place(itemstack, placer, pointed_thing, + "default:acacia_sapling", + -- minp, maxp to be checked, relative to sapling pos + -- minp_relative.y = 1 because sapling pos has been checked + {x = -4, y = 1, z = -4}, + {x = 4, y = 6, z = 4}, + -- maximum interval of interior volume check + 4) + + return itemstack + end, }) minetest.register_node("default:aspen_tree", { @@ -824,9 +880,6 @@ minetest.register_node("default:aspen_sapling", { sunlight_propagates = true, walkable = false, on_timer = default.grow_sapling, - on_construct = function(pos) - minetest.get_node_timer(pos):start(math.random(2400,4800)) - end, selection_box = { type = "fixed", fixed = {-0.3, -0.5, -0.3, 0.3, 0.35, 0.3} @@ -834,7 +887,25 @@ minetest.register_node("default:aspen_sapling", { groups = {snappy = 2, dig_immediate = 3, flammable = 2, attached_node = 1, sapling = 1}, sounds = default.node_sound_leaves_defaults(), + + on_construct = function(pos) + minetest.get_node_timer(pos):start(math.random(2400,4800)) + end, + + on_place = function(itemstack, placer, pointed_thing) + itemstack = default.sapling_on_place(itemstack, placer, pointed_thing, + "default:aspen_sapling", + -- minp, maxp to be checked, relative to sapling pos + -- minp_relative.y = 1 because sapling pos has been checked + {x = -2, y = 1, z = -2}, + {x = 2, y = 12, z = 2}, + -- maximum interval of interior volume check + 4) + + return itemstack + end, }) + -- -- Ores -- diff --git a/mods/default/trees.lua b/mods/default/trees.lua index dbcdcfaf..7df35666 100644 --- a/mods/default/trees.lua +++ b/mods/default/trees.lua @@ -418,6 +418,7 @@ function default.grow_new_acacia_tree(pos) path, "random", nil, false) end + -- New aspen tree function default.grow_new_aspen_tree(pos) @@ -426,3 +427,48 @@ function default.grow_new_aspen_tree(pos) minetest.place_schematic({x = pos.x - 2, y = pos.y - 1, z = pos.z - 2}, path, "0", nil, false) end + + +-- +-- Sapling 'on place' function to check protection of node and resulting tree volume +-- + +function default.sapling_on_place(itemstack, placer, pointed_thing, + sapling_name, minp_relative, maxp_relative, interval) + -- Position of sapling + local pos = pointed_thing.under + local node = minetest.get_node(pos) + local pdef = minetest.registered_nodes[node.name] + if not pdef or not pdef.buildable_to then + pos = pointed_thing.above + node = minetest.get_node(pos) + pdef = minetest.registered_nodes[node.name] + if not pdef or not pdef.buildable_to then + return itemstack + end + end + + local player_name = placer:get_player_name() + -- Check sapling position for protection + if minetest.is_protected(pos, player_name) then + minetest.record_protection_violation(pos, player_name) + return itemstack + end + -- Check tree volume for protection + if not default.intersects_protection( + vector.add(pos, minp_relative), + vector.add(pos, maxp_relative), + player_name, + interval) then + minetest.set_node(pos, {name = sapling_name}) + if not minetest.setting_getbool("creative_mode") then + itemstack:take_item() + end + else + minetest.record_protection_violation(pos, player_name) + -- Print extra information to explain + minetest.chat_send_player(player_name, "Tree will intersect protection") + end + + return itemstack +end