a1b2df3941
- New near_unloaded API that supports custom distance parameter, map bounds check, and some optimizations. - Add area automatic unload check support to ABM API and add to applicable ABMs, replacing many old manual checks. Note that the explicit unload check is only strictly necessary when something will happen due to a node NOT being present in the area, e.g. something igniting due to absent coolant, or soaking quantity being reset due to missing sources. In simple cases where the absence of a thing causes the ABM to do nothing, then the standard check can work because the default action is to do nothing anyway.
101 lines
3.0 KiB
Lua
101 lines
3.0 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 nodecore.fallcheck(posfrom)
|
|
end
|
|
|
|
nodecore.register_on_register_item(function(_, def)
|
|
if def.type ~= "node" then return end
|
|
|
|
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)
|
|
|
|
local function check_empty(pos, dx, dy, dz)
|
|
for ndy = dy, 0 do
|
|
local p = {x = pos.x + dx, y = pos.y + ndy, z = pos.z + dz}
|
|
if not nodecore.buildable_to(p) then return end
|
|
end
|
|
return {x = pos.x + dx, y = pos.y, z = pos.z + dz}
|
|
end
|
|
function nodecore.falling_repose_positions(pos, node, def)
|
|
node = node or minetest.get_node(pos)
|
|
def = def or minetest.registered_items[node.name] or {}
|
|
local repose = def.groups and 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.
|
|
local sitdef = minetest.registered_items[minetest.get_node(
|
|
{x = pos.x, y = pos.y - 1, z = pos.z}).name]
|
|
if not (sitdef and sitdef.groups and sitdef.groups.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
|
|
return #open > 0 and open or nil, node, def
|
|
end
|
|
function nodecore.falling_repose_check(pos)
|
|
if minetest.check_single_for_falling(pos) then return end
|
|
local open, node, def = nodecore.falling_repose_positions(pos)
|
|
if not open 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,
|
|
arealoaded = 1,
|
|
action = function(pos)
|
|
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
|
|
})
|