forked from luanti-org/minetest_game
		
	Default: Prevent placing sapling if grown tree intersects protection
Add a global 'intersects protection' function to functions.lua for checking if a specified volume intersects with a protected volume. A 3D lattice of points are checked with an adjustable interval. Add a global 'sapling on place' function to avoid duplicated code in nodes.lua.
This commit is contained in:
		@@ -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
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
--
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user