Default: Prevent placing sapling if grown tree intersects protection
[oweals/minetest_game.git] / mods / default / functions.lua
index dc46726afd28d22c430836d8e007d80a7f1ad3ac..a98d091f0dc38535b68e51ce196bc302eb30fabe 100644 (file)
@@ -49,6 +49,18 @@ function default.node_sound_sand_defaults(table)
        return table
 end
 
+function default.node_sound_gravel_defaults(table)
+       table = table or {}
+       table.footstep = table.footstep or
+                       {name = "default_gravel_footstep", gain = 0.5}
+       table.dug = table.dug or
+                       {name = "default_gravel_footstep", gain = 1.0}
+       table.place = table.place or
+                       {name = "default_place_node", gain = 1.0}
+       default.node_sound_defaults(table)
+       return table
+end
+
 function default.node_sound_wood_defaults(table)
        table = table or {}
        table.footstep = table.footstep or
@@ -110,6 +122,21 @@ minetest.register_abm({
 })
 
 
+--
+-- optimized helper to put all items in an inventory into a drops list
+--
+function default.get_inventory_drops(pos, inventory, drops)
+       local inv = minetest.get_meta(pos):get_inventory()
+       local n = #drops
+       for i = 1, inv:get_size(inventory) do
+               local stack = inv:get_stack(inventory, i)
+               if stack:get_count() > 0 then
+                       drops[n+1] = stack:to_table()
+                       n = n + 1
+               end
+       end
+end
+
 --
 -- Papyrus and cactus growing
 --
@@ -353,38 +380,71 @@ minetest.register_abm({
 
 
 --
--- Grass growing on well-lit dirt
+-- Convert dirt to something that fits the environment
 --
 
 minetest.register_abm({
        nodenames = {"default:dirt"},
-       neighbors = {"air"},
+       neighbors = {
+               "default:dirt_with_grass",
+               "default:dirt_with_dry_grass",
+               "default:dirt_with_snow",
+               "group:grass",
+               "group:dry_grass",
+               "default:snow",
+       },
        interval = 6,
        chance = 67,
        catch_up = false,
        action = function(pos, node)
+               -- Most likely case, half the time it's too dark for this.
                local above = {x = pos.x, y = pos.y + 1, z = pos.z}
-               local name = minetest.get_node(above).name
-               local nodedef = minetest.registered_nodes[name]
-               if nodedef and (nodedef.sunlight_propagates or nodedef.paramtype == "light") and
-                               nodedef.liquidtype == "none" and
-                               (minetest.get_node_light(above) or 0) >= 13 then
-                       if name == "default:snow" or name == "default:snowblock" then
-                               minetest.set_node(pos, {name = "default:dirt_with_snow"})
-                       else
-                               minetest.set_node(pos, {name = "default:dirt_with_grass"})
+               if (minetest.get_node_light(above) or 0) < 13 then
+                       return
+               end
+
+               -- Look for likely neighbors.
+               local p2 = minetest.find_node_near(pos, 1, {"default:dirt_with_grass",
+                               "default:dirt_with_dry_grass", "default:dirt_with_snow"})
+               if p2 then
+                       -- But the node needs to be under air in this case.
+                       local n2 = minetest.get_node(above)
+                       if n2 and n2.name == "air" then
+                               local n3 = minetest.get_node(p2)
+                               minetest.set_node(pos, {name = n3.name})
+                               return
                        end
                end
+
+               -- Anything on top?
+               local n2 = minetest.get_node(above)
+               if not n2 then
+                       return
+               end
+
+               local name = n2.name
+               -- Snow check is cheapest, so comes first.
+               if name == "default:snow" then
+                       minetest.set_node(pos, {name = "default:dirt_with_snow"})
+               -- Most likely case first.
+               elseif minetest.get_item_group(name, "grass") ~= 0 then
+                       minetest.set_node(pos, {name = "default:dirt_with_grass"})
+               elseif minetest.get_item_group(name, "dry_grass") ~= 0 then
+                       minetest.set_node(pos, {name = "default:dirt_with_dry_grass"})
+               end
        end
 })
 
-
 --
 -- Grass and dry grass removed in darkness
 --
 
 minetest.register_abm({
-       nodenames = {"default:dirt_with_grass", "default:dirt_with_dry_grass"},
+       nodenames = {
+               "default:dirt_with_grass",
+               "default:dirt_with_dry_grass",
+               "default:dirt_with_snow",
+       },
        interval = 8,
        chance = 50,
        catch_up = false,
@@ -406,12 +466,58 @@ minetest.register_abm({
 --
 
 minetest.register_abm({
-       nodenames = {"default:cobble"},
+       nodenames = {"default:cobble", "stairs:slab_cobble", "stairs:stair_cobble"},
        neighbors = {"group:water"},
        interval = 16,
        chance = 200,
        catch_up = false,
        action = function(pos, node)
-               minetest.set_node(pos, {name = "default:mossycobble"})
+               if node.name == "default:cobble" then
+                       minetest.set_node(pos, {name = "default:mossycobble"})
+               elseif node.name == "stairs:slab_cobble" then
+                       minetest.set_node(pos, {name = "stairs:slab_mossycobble", param2 = node.param2})
+               elseif node.name == "stairs:stair_cobble" then
+                       minetest.set_node(pos, {name = "stairs:stair_mossycobble", param2 = node.param2})
+               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