nc_skyrealm-cd2025/item_falling.lua
2022-11-04 19:23:47 -04:00

177 lines
4.6 KiB
Lua

-- LUALOCALS < ---------------------------------------------------------
local ItemStack, math, minetest, nodecore, string, type, vector
= ItemStack, math, minetest, nodecore, string, type, vector
local math_random, string_format
= math.random, string.format
-- LUALOCALS > ---------------------------------------------------------
local modname = minetest.get_current_modname()
local api = _G[modname]
local modstore = api.store
local storekey = "falling"
local s = modstore:get_string(storekey)
local cache = s and s ~= "" and minetest.deserialize(s) or {}
local function getfallingthings(ipos)
local poskey = minetest.pos_to_string(ipos)
local found = cache[poskey] or {item = {}, node = {}}
return found, function()
cache[poskey] = found
return modstore:set_string(storekey, minetest.serialize(cache))
end
end
local function fallingfor(self)
local pos = self.object:get_pos()
if not (pos and api.in_sky_realm(pos)) then return end
local vel = self.object:get_velocity()
if (not vel) or vel.y > -30 then return end
local oy = pos.y
while true do
local node = minetest.get_node(pos)
if node.name == "ignore" then break end
if node.name ~= "air" then return end
pos.y = pos.y - 1
end
pos.y = oy
return api.island_near(pos) or api.island_near(pos, {x = 0, y = 1, z = 0})
end
local function itemeffects(pos, thing)
api.teleportsound(pos)
if type(thing) == "string" then
thing = ItemStack(thing):get_name()
thing = thing and minetest.registered_items[thing]
end
return api.particleburst(pos, vector.new(0.25, 0.25, 0.25), thing)
end
local function poof(self, def)
if not def then return self.object:remove() end
local pos = self.object:get_pos()
itemeffects(pos, def)
return self.object:remove()
end
local function itemshortdesc(stack)
stack = ItemStack(stack)
local copy = ItemStack(stack:get_name())
copy:set_count(stack:get_count())
return copy:to_string()
end
nodecore.register_item_entity_step(function(self)
local ipos = fallingfor(self)
if not ipos then return end
local pt, save = getfallingthings(ipos)
pt = pt.item
local stack = ItemStack(self.itemstring)
local iname = stack:get_name()
for i = 1, #pt do
local pts = ItemStack(pt[i])
stack = pts:add_item(stack)
pt[i] = pts:to_string()
if stack:is_empty() then break end
end
if not stack:is_empty() then
pt[#pt + 1] = stack:to_string()
end
save()
nodecore.log("action", string_format("skyrealm %s dropped item %q at %s",
api.islandhash(ipos), itemshortdesc(stack),
minetest.pos_to_string(self.object:get_pos(), 0)))
local def = minetest.registered_items[iname]
return poof(self, def)
end)
nodecore.register_falling_node_step(function(self)
local ipos = fallingfor(self)
if not ipos then return end
local pt, save = getfallingthings(ipos)
pt = pt.node
pt[#pt + 1] = {
n = self.node,
m = self.meta
}
save()
nodecore.log("action", string_format("skyrealm %s dropped node %q at %s",
api.islandhash(ipos), self.node.name,
minetest.pos_to_string(self.get_pos(), 0)))
local def = minetest.registered_items[self.node.name]
return poof(self, def)
end)
local function findspot_start(pos)
if nodecore.near_unloaded(pos, nil, 5) then return end
for rel in nodecore.settlescan() do
local p = vector.add(pos, rel)
if nodecore.buildable_to(p) then return p end
end
end
local function findspot(pos)
pos = findspot_start(pos)
if not pos then return end
local tpos = vector.add(pos, {
x = math_random() * 10 - 5,
y = math_random() * 10 + 5,
z = math_random() * 10 - 5,
})
for hit in minetest.raycast(pos, tpos, false, false) do
if hit.above and not vector.equals(pos, hit.above)
and hit.under and not vector.equals(pos, hit.under)
and nodecore.buildable_to(hit.above) then return hit.above end
end
return tpos
end
function api.return_falling(pos, ipos)
local data, save = getfallingthings(ipos)
local pt = data.item
for i = #pt, 1, -1 do
local p = findspot(pos)
if p then
itemeffects(p, pt[i])
nodecore.item_eject(p, pt[i], 1)
nodecore.log("action", string_format(
"skyrealm %s item %q arrived at %s",
api.islandhash(ipos), itemshortdesc(pt[i]),
minetest.pos_to_string(p)))
pt[i] = nil
else
break
end
end
pt = data.node
for i = #pt, 1, -1 do
local p = findspot(pos)
if p then
local obj = minetest.add_entity(p, "__builtin:falling_node")
if obj then
itemeffects(p, pt[i].n.name)
obj:get_luaentity():set_node(pt[i].n, pt[i].m)
nodecore.log("action", string_format(
"skyrealm %s node %q arrived at %s",
api.islandhash(ipos), pt[i],
minetest.pos_to_string(p)))
pt[i] = nil
else
break
end
else
break
end
end
save()
end