a4c5eb3b1b
Apparently LBMs are just not reliable anymore as of 5.4, so we'll have to fall back to the at least eventually-reliable ABM again.
220 lines
5.6 KiB
Lua
220 lines
5.6 KiB
Lua
-- LUALOCALS < ---------------------------------------------------------
|
|
local math, minetest, nodecore, pairs, vector
|
|
= math, minetest, nodecore, pairs, vector
|
|
local math_random
|
|
= math.random
|
|
-- LUALOCALS > ---------------------------------------------------------
|
|
|
|
nodecore.amcoremod()
|
|
|
|
--[[
|
|
Helpers for visible inventory. Use "visinv" node group.
|
|
Sets up on_construct, after_destruct and an ABM to manage
|
|
the visual entities.
|
|
--]]
|
|
|
|
local modname = minetest.get_current_modname()
|
|
|
|
------------------------------------------------------------------------
|
|
-- VISIBLE STACK ENTITY
|
|
|
|
local function getlightcheck(rp, obj, src)
|
|
return function()
|
|
local stack = nodecore.stack_get(rp)
|
|
if not stack or stack:is_empty() then return end
|
|
|
|
local def = minetest.registered_items[stack:get_name()] or {}
|
|
if (def.light_source or 0) ~= src then return end
|
|
|
|
for _, v in pairs(nodecore.get_objects_at_pos(rp)) do
|
|
if v == obj then return true end
|
|
end
|
|
end
|
|
end
|
|
|
|
local check_queue = {}
|
|
local check_queue_dirty
|
|
local function visinv_update_ents(pos)
|
|
pos = vector.round(pos)
|
|
check_queue[minetest.hash_node_position(pos)] = pos
|
|
check_queue_dirty = true
|
|
end
|
|
nodecore.visinv_update_ents = visinv_update_ents
|
|
|
|
local visinv_ents = {}
|
|
local function objremove(ent, obj)
|
|
visinv_ents[ent] = nil
|
|
return (obj or ent.object):remove()
|
|
end
|
|
|
|
local function itemcheck(self)
|
|
local obj = self.object
|
|
local pos = obj:get_pos()
|
|
if not pos then visinv_ents[self] = nil return end
|
|
|
|
local stack = nodecore.stack_get(pos)
|
|
|
|
local sstr = stack:to_string()
|
|
if self.stackstring == sstr then return end
|
|
self.stackstring = sstr
|
|
|
|
if stack:is_empty() then return objremove(self, obj) end
|
|
|
|
local rp = vector.round(pos)
|
|
local def = minetest.registered_items[stack:get_name()] or {}
|
|
local src = def.light_source or 0
|
|
if src > 0 then
|
|
self.light_source = src
|
|
nodecore.dynamic_light_add(rp, src, getlightcheck(rp, obj, src))
|
|
end
|
|
|
|
local props, scale, yaw = nodecore.stackentprops(stack,
|
|
rp.x * 3 + rp.y * 5 + rp.z * 7)
|
|
rp.y = rp.y + scale - 31/64
|
|
|
|
if obj:get_yaw() ~= yaw then
|
|
obj:set_yaw(yaw)
|
|
end
|
|
if not vector.equals(obj:get_pos(), rp) then
|
|
obj:set_pos(rp)
|
|
end
|
|
return obj:set_properties(props)
|
|
end
|
|
|
|
local entname = modname .. ":stackent"
|
|
minetest.register_entity(entname, {
|
|
initial_properties = nodecore.stackentprops(),
|
|
is_stack = true,
|
|
itemcheck = itemcheck
|
|
})
|
|
|
|
local check_retry
|
|
local function check_retry_add(key, val)
|
|
if not check_retry then
|
|
check_retry = {}
|
|
minetest.after(1 + math_random(), function()
|
|
local total = 0
|
|
for k, v in pairs(check_retry) do
|
|
total = total + 1
|
|
check_queue[k] = v
|
|
end
|
|
check_queue_dirty = true
|
|
check_retry = nil
|
|
nodecore.log("warning", "visinv entity retry: " .. total)
|
|
end)
|
|
end
|
|
check_retry[key] = val
|
|
end
|
|
|
|
nodecore.register_globalstep("visinv check", function()
|
|
if not check_queue_dirty then return end
|
|
local batch = check_queue
|
|
check_queue = {}
|
|
check_queue_dirty = nil
|
|
|
|
for ent in pairs(visinv_ents) do
|
|
if (ent.name == entname) and (not ent.gone) then
|
|
local key = ent.poskey
|
|
if key then
|
|
local data = batch[key]
|
|
if data then
|
|
if data.n then
|
|
objremove(ent)
|
|
else
|
|
itemcheck(ent)
|
|
data.n = true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
for poskey, data in pairs(batch) do
|
|
if (not data.n) and (not nodecore.stack_get(data):is_empty()) then
|
|
local obj = minetest.add_entity(data, entname)
|
|
local ent = obj and obj:get_luaentity()
|
|
if ent then
|
|
visinv_ents[ent] = true
|
|
ent.is_stack = true
|
|
ent.poskey = poskey
|
|
itemcheck(ent)
|
|
else
|
|
check_retry_add(poskey, data)
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
|
|
------------------------------------------------------------------------
|
|
-- NODE REGISTRATION HELPERS
|
|
|
|
function nodecore.visinv_on_construct(pos)
|
|
local meta = minetest.get_meta(pos)
|
|
local inv = meta:get_inventory()
|
|
inv:set_size("solo", 1)
|
|
nodecore.visinv_update_ents(pos)
|
|
end
|
|
|
|
function nodecore.visinv_after_destruct(pos)
|
|
nodecore.visinv_update_ents(pos)
|
|
nodecore.fallcheck(pos)
|
|
end
|
|
|
|
nodecore.register_on_register_item(function(_, def)
|
|
if def.type ~= "node" then return end
|
|
|
|
def.groups = def.groups or {}
|
|
|
|
if def.groups.visinv then
|
|
def.on_construct = def.on_construct or nodecore.visinv_on_construct
|
|
def.after_destruct = def.after_destruct or nodecore.visinv_after_destruct
|
|
end
|
|
end)
|
|
|
|
nodecore.register_lbm({
|
|
name = modname .. ":init",
|
|
run_at_every_load = true,
|
|
nodenames = {"group:visinv"},
|
|
action = function(...) return nodecore.visinv_update_ents(...) end
|
|
})
|
|
|
|
nodecore.register_abm({
|
|
label = "visinv check",
|
|
interval = 2,
|
|
chance = 1,
|
|
nodenames = {"group:visinv"},
|
|
action = function(...) return nodecore.visinv_update_ents(...) end
|
|
})
|
|
|
|
------------------------------------------------------------------------
|
|
-- DIG INVENTORY
|
|
|
|
local dug
|
|
local old_node_dig = minetest.node_dig
|
|
minetest.node_dig = function(pos, node, digger, ...)
|
|
nodecore.stack_sounds(pos, "dug")
|
|
local function helper(...)
|
|
dug = nil
|
|
return ...
|
|
end
|
|
dug = {pos = pos, who = digger}
|
|
return helper(old_node_dig(pos, node, digger, ...))
|
|
end
|
|
local old_get_node_drops = minetest.get_node_drops
|
|
minetest.get_node_drops = function(...)
|
|
local drops = old_get_node_drops(...)
|
|
if not dug then return drops end
|
|
drops = drops or {}
|
|
local stack = nodecore.stack_get(dug.pos)
|
|
if stack and not stack:is_empty() then
|
|
local def = stack:get_definition()
|
|
local dmg = def and def.groups and def.groups.damage_touch
|
|
if dug.who and dmg and dmg > 0 then
|
|
nodecore.addphealth(dug.who, -dmg, "hot pickup")
|
|
nodecore.item_eject(dug.pos, stack, 0.001)
|
|
else
|
|
drops[#drops + 1] = stack
|
|
end
|
|
end
|
|
return drops
|
|
end
|