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