Default: Prevent placing sapling if grown tree intersects protection
authorparamat <mat.gregory@virginmedia.com>
Tue, 19 Jul 2016 00:01:59 +0000 (01:01 +0100)
committerparamat <mat.gregory@virginmedia.com>
Wed, 27 Jul 2016 20:39:22 +0000 (21:39 +0100)
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.

mods/default/functions.lua
mods/default/nodes.lua
mods/default/trees.lua

index f3bb97cdd5664a8ca8595f7b8f39aaf56745140d..a98d091f0dc38535b68e51ce196bc302eb30fabe 100644 (file)
@@ -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
index 31e063d4341bc40ff298bf918b35927ef1d9e18e..05d9f32d329e2a9120431f2f0c6e543d3dcb13a3 100644 (file)
@@ -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
 --
index dbcdcfaf287295b0fe9231e533fb13861d2ee4ca..7df35666515a4d868f1e7957165aeb1b35385603 100644 (file)
@@ -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