mirror of
https://github.com/minetest/minetest_game.git
synced 2024-12-23 15:20:19 +01:00
2199be5108
Allow water to turn cobble slab and stairs to turn into mossy versions. There is no crafting recipe for mossy stairs and mossy slabs, the stair/slab API has been modified to allow for a recipeitem that is `nil`, which will omit adding a crafting recipe for these two items. The API documentation is updated. The slabs and stairs will turn mossy when water is adjacent, just like cobblestone. You can either farm mossy versions by placing them in water for a while, then collecting them, or run water over your craft.
484 lines
12 KiB
Lua
484 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({
|
|
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({
|
|
nodenames = {"default:cactus"},
|
|
neighbors = {"group:sand"},
|
|
interval = 12,
|
|
chance = 83,
|
|
action = function(...)
|
|
default.grow_cactus(...)
|
|
end
|
|
})
|
|
|
|
minetest.register_abm({
|
|
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
|
|
--
|
|
|
|
default.leafdecay_trunk_cache = {}
|
|
default.leafdecay_enable_cache = true
|
|
-- Spread the load of finding trunks
|
|
default.leafdecay_trunk_find_allow_accumulator = 0
|
|
|
|
minetest.register_globalstep(function(dtime)
|
|
local finds_per_second = 5000
|
|
default.leafdecay_trunk_find_allow_accumulator =
|
|
math.floor(dtime * finds_per_second)
|
|
end)
|
|
|
|
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
|
|
|
|
minetest.register_abm({
|
|
nodenames = {"group:leafdecay"},
|
|
neighbors = {"air", "group:liquid"},
|
|
-- A low interval and a high inverse chance spreads the load
|
|
interval = 2,
|
|
chance = 5,
|
|
|
|
action = function(p0, node, _, _)
|
|
--print("leafdecay ABM at "..p0.x..", "..p0.y..", "..p0.z..")")
|
|
local do_preserve = false
|
|
local d = minetest.registered_nodes[node.name].groups.leafdecay
|
|
if not d or d == 0 then
|
|
--print("not groups.leafdecay")
|
|
return
|
|
end
|
|
local n0 = minetest.get_node(p0)
|
|
if n0.param2 ~= 0 then
|
|
--print("param2 ~= 0")
|
|
return
|
|
end
|
|
local p0_hash = nil
|
|
if default.leafdecay_enable_cache then
|
|
p0_hash = minetest.hash_node_position(p0)
|
|
local trunkp = default.leafdecay_trunk_cache[p0_hash]
|
|
if trunkp then
|
|
local n = minetest.get_node(trunkp)
|
|
local reg = minetest.registered_nodes[n.name]
|
|
-- Assume ignore is a trunk, to make the thing
|
|
-- work at the border of the active area
|
|
if n.name == "ignore" or (reg and reg.groups.tree and
|
|
reg.groups.tree ~= 0) then
|
|
--print("cached trunk still exists")
|
|
return
|
|
end
|
|
--print("cached trunk is invalid")
|
|
-- Cache is invalid
|
|
table.remove(default.leafdecay_trunk_cache, p0_hash)
|
|
end
|
|
end
|
|
if default.leafdecay_trunk_find_allow_accumulator <= 0 then
|
|
return
|
|
end
|
|
default.leafdecay_trunk_find_allow_accumulator =
|
|
default.leafdecay_trunk_find_allow_accumulator - 1
|
|
-- Assume ignore is a trunk, to make the thing
|
|
-- work at the border of the active area
|
|
local p1 = minetest.find_node_near(p0, d, {"ignore", "group:tree"})
|
|
if p1 then
|
|
do_preserve = true
|
|
if default.leafdecay_enable_cache then
|
|
--print("caching trunk")
|
|
-- Cache the trunk
|
|
default.leafdecay_trunk_cache[p0_hash] = p1
|
|
end
|
|
end
|
|
if not do_preserve then
|
|
-- Drop stuff other than the node itself
|
|
local itemstacks = minetest.get_node_drops(n0.name)
|
|
for _, itemname in ipairs(itemstacks) do
|
|
if minetest.get_item_group(n0.name, "leafdecay_drop") ~= 0 or
|
|
itemname ~= n0.name then
|
|
local p_drop = {
|
|
x = p0.x - 0.5 + math.random(),
|
|
y = p0.y - 0.5 + math.random(),
|
|
z = p0.z - 0.5 + math.random(),
|
|
}
|
|
minetest.add_item(p_drop, itemname)
|
|
end
|
|
end
|
|
-- Remove node
|
|
minetest.remove_node(p0)
|
|
nodeupdate(p0)
|
|
end
|
|
end
|
|
})
|
|
|
|
|
|
--
|
|
-- Convert dirt to something that fits the environment
|
|
--
|
|
|
|
minetest.register_abm({
|
|
nodenames = {"default:dirt"},
|
|
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}
|
|
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",
|
|
"default:dirt_with_snow",
|
|
},
|
|
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({
|
|
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
|
|
})
|