1
0
mirror of https://github.com/minetest/minetest_game.git synced 2024-12-23 15:20:19 +01:00
minetest_game/mods/default/functions.lua
Auke Kok 2199be5108 Stairs: Add mossy cobble slab and stair
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.
2016-06-14 23:28:17 +01:00

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
})