nodecore-cd2025/mods/nc_api/node_falling_repose.lua
Aaron Suen b7e3048fd7 API cleanup, librarify "visible inventory" functionality.
If we later implement solo-stack open containers, then this will
be reusable for that as well.
2018-11-03 08:46:14 -04:00

109 lines
3.1 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local math, minetest, nodecore, pairs
= math, minetest, nodecore, pairs
local math_random
= math.random
-- LUALOCALS > ---------------------------------------------------------
--[[
Nodes with the "falling_repose" group will not only fall if unsupported
from below, but if there is a sufficient drop off the sides, so simulate
an "angle of repose."
--]]
function nodecore.falling_repose_drop(posfrom, posto, node)
minetest.spawn_falling_node(posto, node, minetest.get_meta(posfrom))
minetest.remove_node(posfrom)
posfrom.y = posfrom.y + 1
return minetest.check_for_falling(posfrom)
end
nodecore.register_on_register_node(function(name, def)
def.groups = def.groups or {}
if def.groups.falling_repose then def.groups.falling_node = 1 end
def.repose_drop = def.repose_drop or nodecore.falling_repose_drop
end)
function minetest.spawn_falling_node(pos, node, meta)
node = node or minetest.get_node(pos)
if node.name == "air" or node.name == "ignore" then
return false
end
local obj = minetest.add_entity(pos, "__builtin:falling_node")
if obj then
obj:get_luaentity():set_node(node, meta or minetest.get_meta(pos):to_table())
minetest.remove_node(pos)
return true
end
return false
end
local function check_empty(pos, dx, dy, dz)
for ndy = dy, 1 do
local p = {x = pos.x + dx, y = pos.y + ndy, z = pos.z + dz}
local node = minetest.get_node(p)
if not minetest.registered_nodes[node.name].buildable_to then return end
end
return {x = pos.x + dx, y = pos.y, z = pos.z + dz}
end
function nodecore.falling_repose_check(pos)
if minetest.check_single_for_falling(pos) then return end
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
local repose = def.groups.falling_repose
if not repose then return end
-- Reposing nodes can always sit comfortably atop
-- a non-moving node; it's only when stacked on other
-- falling nodes that they can slip off.
if not (minetest.registered_nodes[minetest.get_node(
{x = pos.x, y = pos.y - 1, z = pos.z}).name].groups
or {}).falling_node
then return end
local open = {}
local ok = check_empty(pos, 1, -repose, 0)
if ok then open[1] = ok end
ok = check_empty(pos, -1, -repose, 0)
if ok then open[#open + 1] = ok end
ok = check_empty(pos, 0, -repose, 1)
if ok then open[#open + 1] = ok end
ok = check_empty(pos, 0, -repose, -1)
if ok then open[#open + 1] = ok end
if #open < 1 then return end
return def.repose_drop(pos, open[math_random(1, #open)], node)
end
local reposeq
local qqty
local qmax = 100
local function reposeall()
for _, v in pairs(reposeq) do v() end
reposeq = nil
qqty = nil
end
minetest.register_abm({
label = "Falling Repose",
nodenames = {"group:falling_repose"},
neighbors = {"air"},
interval = 2,
chance = 5,
action = function(pos, node)
if not reposeq then
reposeq = {}
qqty = 0
minetest.after(0, reposeall)
end
local f = function() nodecore.falling_repose_check(pos) end
if #reposeq > qmax then
local i = math_random(1, qqty)
if i < qmax then reposeq[i] = f end
else
reposeq[#reposeq + 1] = f
end
qqty = qqty + 1
end
})