3d26b33e5b
Original code by Sofar. Textures by Pithydon. Exposure to air converts live coral to coral skeleton. Live corals drop coral skeleton.
490 lines
12 KiB
Lua
490 lines
12 KiB
Lua
-- mods/default/functions.lua
|
|
|
|
--
|
|
-- Sounds
|
|
--
|
|
|
|
function default.node_sound_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or
|
|
{name = "", gain = 1.0}
|
|
table.dug = table.dug or
|
|
{name = "default_dug_node", gain = 0.25}
|
|
table.place = table.place or
|
|
{name = "default_place_node_hard", gain = 1.0}
|
|
return table
|
|
end
|
|
|
|
function default.node_sound_stone_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or
|
|
{name = "default_hard_footstep", gain = 0.5}
|
|
table.dug = table.dug or
|
|
{name = "default_hard_footstep", gain = 1.0}
|
|
default.node_sound_defaults(table)
|
|
return table
|
|
end
|
|
|
|
function default.node_sound_dirt_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or
|
|
{name = "default_dirt_footstep", gain = 1.0}
|
|
table.dug = table.dug or
|
|
{name = "default_dirt_footstep", gain = 1.5}
|
|
table.place = table.place or
|
|
{name = "default_place_node", gain = 1.0}
|
|
default.node_sound_defaults(table)
|
|
return table
|
|
end
|
|
|
|
function default.node_sound_sand_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or
|
|
{name = "default_sand_footstep", gain = 0.12}
|
|
table.dug = table.dug or
|
|
{name = "default_sand_footstep", gain = 0.24}
|
|
table.place = table.place or
|
|
{name = "default_place_node", gain = 1.0}
|
|
default.node_sound_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
|
|
{name = "default_wood_footstep", gain = 0.5}
|
|
table.dug = table.dug or
|
|
{name = "default_wood_footstep", gain = 1.0}
|
|
default.node_sound_defaults(table)
|
|
return table
|
|
end
|
|
|
|
function default.node_sound_leaves_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or
|
|
{name = "default_grass_footstep", gain = 0.35}
|
|
table.dug = table.dug or
|
|
{name = "default_grass_footstep", gain = 0.7}
|
|
table.dig = table.dig or
|
|
{name = "default_dig_crumbly", gain = 0.4}
|
|
table.place = table.place or
|
|
{name = "default_place_node", gain = 1.0}
|
|
default.node_sound_defaults(table)
|
|
return table
|
|
end
|
|
|
|
function default.node_sound_glass_defaults(table)
|
|
table = table or {}
|
|
table.footstep = table.footstep or
|
|
{name = "default_glass_footstep", gain = 0.5}
|
|
table.dug = table.dug or
|
|
{name = "default_break_glass", gain = 1.0}
|
|
default.node_sound_defaults(table)
|
|
return table
|
|
end
|
|
|
|
|
|
--
|
|
-- Lavacooling
|
|
--
|
|
|
|
default.cool_lava = function(pos, node)
|
|
if node.name == "default:lava_source" then
|
|
minetest.set_node(pos, {name = "default:obsidian"})
|
|
else -- Lava flowing
|
|
minetest.set_node(pos, {name = "default:stone"})
|
|
end
|
|
minetest.sound_play("default_cool_lava",
|
|
{pos = pos, max_hear_distance = 16, gain = 0.25})
|
|
end
|
|
|
|
minetest.register_abm({
|
|
label = "Lava cooling",
|
|
nodenames = {"default:lava_source", "default:lava_flowing"},
|
|
neighbors = {"group:water"},
|
|
interval = 1,
|
|
chance = 1,
|
|
catch_up = false,
|
|
action = function(...)
|
|
default.cool_lava(...)
|
|
end,
|
|
})
|
|
|
|
|
|
--
|
|
-- 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
|
|
--
|
|
|
|
-- wrapping the functions in abm action is necessary to make overriding them possible
|
|
|
|
function default.grow_cactus(pos, node)
|
|
if node.param2 >= 4 then
|
|
return
|
|
end
|
|
pos.y = pos.y - 1
|
|
if minetest.get_item_group(minetest.get_node(pos).name, "sand") == 0 then
|
|
return
|
|
end
|
|
pos.y = pos.y + 1
|
|
local height = 0
|
|
while node.name == "default:cactus" and height < 4 do
|
|
height = height + 1
|
|
pos.y = pos.y + 1
|
|
node = minetest.get_node(pos)
|
|
end
|
|
if height == 4 or node.name ~= "air" then
|
|
return
|
|
end
|
|
minetest.set_node(pos, {name = "default:cactus"})
|
|
return true
|
|
end
|
|
|
|
function default.grow_papyrus(pos, node)
|
|
pos.y = pos.y - 1
|
|
local name = minetest.get_node(pos).name
|
|
if name ~= "default:dirt_with_grass" and name ~= "default:dirt" then
|
|
return
|
|
end
|
|
if not minetest.find_node_near(pos, 3, {"group:water"}) then
|
|
return
|
|
end
|
|
pos.y = pos.y + 1
|
|
local height = 0
|
|
while node.name == "default:papyrus" and height < 4 do
|
|
height = height + 1
|
|
pos.y = pos.y + 1
|
|
node = minetest.get_node(pos)
|
|
end
|
|
if height == 4 or node.name ~= "air" then
|
|
return
|
|
end
|
|
minetest.set_node(pos, {name = "default:papyrus"})
|
|
return true
|
|
end
|
|
|
|
minetest.register_abm({
|
|
label = "Grow cactus",
|
|
nodenames = {"default:cactus"},
|
|
neighbors = {"group:sand"},
|
|
interval = 12,
|
|
chance = 83,
|
|
action = function(...)
|
|
default.grow_cactus(...)
|
|
end
|
|
})
|
|
|
|
minetest.register_abm({
|
|
label = "Grow papyrus",
|
|
nodenames = {"default:papyrus"},
|
|
neighbors = {"default:dirt", "default:dirt_with_grass"},
|
|
interval = 14,
|
|
chance = 71,
|
|
action = function(...)
|
|
default.grow_papyrus(...)
|
|
end
|
|
})
|
|
|
|
|
|
--
|
|
-- dig upwards
|
|
--
|
|
|
|
function default.dig_up(pos, node, digger)
|
|
if digger == nil then return end
|
|
local np = {x = pos.x, y = pos.y + 1, z = pos.z}
|
|
local nn = minetest.get_node(np)
|
|
if nn.name == node.name then
|
|
minetest.node_dig(np, nn, digger)
|
|
end
|
|
end
|
|
|
|
|
|
--
|
|
-- Fence registration helper
|
|
--
|
|
|
|
function default.register_fence(name, def)
|
|
minetest.register_craft({
|
|
output = name .. " 4",
|
|
recipe = {
|
|
{ def.material, 'group:stick', def.material },
|
|
{ def.material, 'group:stick', def.material },
|
|
}
|
|
})
|
|
|
|
local fence_texture = "default_fence_overlay.png^" .. def.texture ..
|
|
"^default_fence_overlay.png^[makealpha:255,126,126"
|
|
-- Allow almost everything to be overridden
|
|
local default_fields = {
|
|
paramtype = "light",
|
|
drawtype = "nodebox",
|
|
node_box = {
|
|
type = "connected",
|
|
fixed = {{-1/8, -1/2, -1/8, 1/8, 1/2, 1/8}},
|
|
-- connect_top =
|
|
-- connect_bottom =
|
|
connect_front = {{-1/16,3/16,-1/2,1/16,5/16,-1/8},
|
|
{-1/16,-5/16,-1/2,1/16,-3/16,-1/8}},
|
|
connect_left = {{-1/2,3/16,-1/16,-1/8,5/16,1/16},
|
|
{-1/2,-5/16,-1/16,-1/8,-3/16,1/16}},
|
|
connect_back = {{-1/16,3/16,1/8,1/16,5/16,1/2},
|
|
{-1/16,-5/16,1/8,1/16,-3/16,1/2}},
|
|
connect_right = {{1/8,3/16,-1/16,1/2,5/16,1/16},
|
|
{1/8,-5/16,-1/16,1/2,-3/16,1/16}},
|
|
},
|
|
connects_to = {"group:fence", "group:wood", "group:tree"},
|
|
inventory_image = fence_texture,
|
|
wield_image = fence_texture,
|
|
tiles = {def.texture},
|
|
sunlight_propagates = true,
|
|
is_ground_content = false,
|
|
groups = {},
|
|
}
|
|
for k, v in pairs(default_fields) do
|
|
if not def[k] then
|
|
def[k] = v
|
|
end
|
|
end
|
|
|
|
-- Always add to the fence group, even if no group provided
|
|
def.groups.fence = 1
|
|
|
|
def.texture = nil
|
|
def.material = nil
|
|
|
|
minetest.register_node(name, def)
|
|
end
|
|
|
|
|
|
--
|
|
-- Leafdecay
|
|
--
|
|
|
|
-- Prevent decay of placed leaves
|
|
|
|
default.after_place_leaves = function(pos, placer, itemstack, pointed_thing)
|
|
if placer and not placer:get_player_control().sneak then
|
|
local node = minetest.get_node(pos)
|
|
node.param2 = 1
|
|
minetest.set_node(pos, node)
|
|
end
|
|
end
|
|
|
|
-- Leafdecay ABM
|
|
|
|
minetest.register_abm({
|
|
label = "Leaf decay",
|
|
nodenames = {"group:leafdecay"},
|
|
neighbors = {"air"},
|
|
interval = 2,
|
|
chance = 10,
|
|
catch_up = false,
|
|
|
|
action = function(pos, node, _, _)
|
|
-- Check if leaf is placed
|
|
if node.param2 ~= 0 then
|
|
return
|
|
end
|
|
|
|
local rad = minetest.registered_nodes[node.name].groups.leafdecay
|
|
-- Assume ignore is a trunk, to make this
|
|
-- work at the border of a loaded area
|
|
if minetest.find_node_near(pos, rad, {"ignore", "group:tree"}) then
|
|
return
|
|
end
|
|
-- Drop stuff
|
|
local itemstacks = minetest.get_node_drops(node.name)
|
|
for _, itemname in ipairs(itemstacks) do
|
|
if itemname ~= node.name or
|
|
minetest.get_item_group(node.name, "leafdecay_drop") ~= 0 then
|
|
local p_drop = {
|
|
x = pos.x - 0.5 + math.random(),
|
|
y = pos.y - 0.5 + math.random(),
|
|
z = pos.z - 0.5 + math.random(),
|
|
}
|
|
minetest.add_item(p_drop, itemname)
|
|
end
|
|
end
|
|
-- Remove node
|
|
minetest.remove_node(pos)
|
|
nodeupdate(pos)
|
|
end
|
|
})
|
|
|
|
|
|
--
|
|
-- Convert dirt to something that fits the environment
|
|
--
|
|
|
|
minetest.register_abm({
|
|
label = "Grass spread",
|
|
nodenames = {"default:dirt"},
|
|
neighbors = {
|
|
"air",
|
|
"group:grass",
|
|
"group:dry_grass",
|
|
"default:snow",
|
|
},
|
|
interval = 6,
|
|
chance = 50,
|
|
catch_up = false,
|
|
action = function(pos, node)
|
|
-- Check for darkness: night, shadow or under a light-blocking node
|
|
-- Returns if ignore above
|
|
local above = {x = pos.x, y = pos.y + 1, z = pos.z}
|
|
if (minetest.get_node_light(above) or 0) < 13 then
|
|
return
|
|
end
|
|
|
|
-- Look for spreading dirt-type neighbours
|
|
local p2 = minetest.find_node_near(pos, 1, "group:spreading_dirt_type")
|
|
if p2 then
|
|
local n3 = minetest.get_node(p2)
|
|
minetest.set_node(pos, {name = n3.name})
|
|
return
|
|
end
|
|
|
|
-- Else, any seeding nodes on top?
|
|
local name = minetest.get_node(above).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({
|
|
label = "Grass covered",
|
|
nodenames = {"group:spreading_dirt_type"},
|
|
interval = 8,
|
|
chance = 50,
|
|
catch_up = false,
|
|
action = function(pos, node)
|
|
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 name ~= "ignore" and nodedef and not ((nodedef.sunlight_propagates or
|
|
nodedef.paramtype == "light") and
|
|
nodedef.liquidtype == "none") then
|
|
minetest.set_node(pos, {name = "default:dirt"})
|
|
end
|
|
end
|
|
})
|
|
|
|
|
|
--
|
|
-- Moss growth on cobble near water
|
|
--
|
|
|
|
minetest.register_abm({
|
|
label = "Moss growth",
|
|
nodenames = {"default:cobble", "stairs:slab_cobble", "stairs:stair_cobble"},
|
|
neighbors = {"group:water"},
|
|
interval = 16,
|
|
chance = 200,
|
|
catch_up = false,
|
|
action = function(pos, node)
|
|
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
|
|
|
|
|
|
--
|
|
-- Coral death near air
|
|
--
|
|
|
|
minetest.register_abm({
|
|
nodenames = {"default:coral_brown", "default:coral_orange"},
|
|
neighbors = {"air"},
|
|
interval = 17,
|
|
chance = 5,
|
|
catch_up = false,
|
|
action = function(pos, node)
|
|
minetest.set_node(pos, {name = "default:coral_skeleton"})
|
|
end,
|
|
})
|