123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204 |
- --[[
- Copyright (C) 2016 Aftermoth, Zolan Davis
- This program is free software; you can redistribute it and/or modify it
- under the terms of the GNU Lesser General Public License as published
- by the Free Software Foundation; either version 2.1 of the License,
- or (at your option) version 3 of the License.
- http://www.gnu.org/licenses/lgpl-2.1.html
- --]]
- --------------------------------------------------- Global
- droplift = {
- invoke,
- -- function (dropobj, sync)
- -- sync in [ false | 0 | seconds ]. See details.txt
- }
- --------------------------------------------------- Local
- -- Localize for performance.
- local math_floor = math.floor
- local math_max = math.max
- local function obstructed(p)
- local n = minetest.get_node_or_nil(p)
- if not n then return false end
- local ndef = minetest.reg_ns_nodes[n.name]
- return ndef and ndef.walkable
- end
- -- * Local escape *
- local dist = function(p1,p2)
- return ( (p1.y - p2.y)^2
- + (p1.x - p2.x)^2
- + (p1.z - p2.z)^2 )^0.5
- end
- local function escape(ent,pos)
- local q, p, ep, d, dd = pos
- for _,player in ipairs(minetest.get_connected_players()) do
- p = player:get_pos(); p.y = p.y + 1
- d = dist(p,pos)
- if not dd or (d < dd) then dd, q = d, {x=p.x,y=p.y,z=p.z} end
- end
- for x = pos.x - 1, pos.x + 1 do
- p.x = x; for y = pos.y - 1, pos.y + 1 do
- p.y = y; for z = pos.z - 1, pos.z + 1 do
- p.z = z
- if not obstructed(p) then
- d = dist(q,p)
- if not ep or (d < dd) then dd, ep = d, {x=p.x,y=p.y,z=p.z} end
- end
- end
- end
- end
- if ep then ent.object:set_pos(ep) end
- return ep
- end
- -- * Entombment physics *
- -- ---------------- LIFT
- local function lift(obj)
- local p = obj:get_pos()
- if p then
- local ent = obj:get_luaentity()
- if ent.is_entombed and obstructed(p) then
- -- Time
- local t = 1
- local s1 = ent.sync1
- if s1 then
- local sd = ent.sync0+s1-os.time()
- if sd > 0 then t = sd end
- ent.sync0, ent.sync1 = nil, nil
- end
- -- Space
- p = {x = p.x, y = math_floor(p.y - 0.5) + 1.800001, z = p.z}
- obj:set_pos(p)
- if s1 or obstructed(p) then
- minetest.after(t, lift, obj)
- return
- end
- end
- -- Void.
- ent.is_entombed, ent.sync0, ent.sync1 = nil, nil, nil
- end
- end
- -- ---------------- ASYNC
- local k = 0
- local function newhash()
- k = (k==32767 and 1) or k+1
- return k
- end
- local function async(obj, usync)
- local p = obj:get_pos()
- if p then
- local ent = obj:get_luaentity()
- local hash = newhash()
- ent.hash = ent.hash or hash
- if obstructed(p) then
- -- Time.
- if not usync then
- if escape(ent, p) and hash == ent.hash then
- ent.hash = nil
- end
- elseif usync > 0 then
- ent.sync0 = os.time()
- ent.sync1 = usync
- end
- -- Space.
- if hash == ent.hash then
- obj:set_pos({x = p.x, y = math_floor(p.y - 0.5) + 0.800001, z = p.z})
- if not ent.is_entombed then
- ent.is_entombed = true
- minetest.after(1, lift, obj)
- end
- end
- end
- if hash == ent.hash then ent.hash = nil end
- end
- end
- droplift.invoke = function(obj, sync)
- async(obj, (sync and math_max(0,sync)))
- end
- -- * Events *
- local function append_to_core_defns()
- local dropentity=minetest.registered_entities["__builtin:item"]
- -- Ensure consistency across reloads.
- local on_activate_copy = dropentity.on_activate
- dropentity.on_activate = function(ent, staticdata, dtime_s)
- on_activate_copy(ent, staticdata, dtime_s)
- if staticdata ~= "" then
- if minetest.deserialize(staticdata).is_entombed then
- ent.is_entombed = true
- ent.object:setacceleration({x = 0, y = 0, z = 0}) -- Prevents 0.18m reload slippage. Not critical.
- minetest.after(0.1, lift, ent.object)
- end
- end
- ent.object:setvelocity({x = 0, y = 0, z = 0}) -- Prevents resting-buried drops burrowing.
- end
- -- Preserve state across reloads
- local get_staticdata_copy = dropentity.get_staticdata
- dropentity.get_staticdata = function(ent)
- local s = get_staticdata_copy(ent)
- if ent.is_entombed then
- local r = {}
- if s ~= "" then
- r = minetest.deserialize(s)
- end
- r.is_entombed=true
- return minetest.serialize(r)
- end
- return s
- end
- -- Update drops inside newly placed nodes.
- -- Note: only called when player places node, not when `minetest.set_node()` is used!
- local add_node_copy = minetest.add_node
- minetest.add_node = function(pos, node)
- add_node_copy(pos, node)
- -- Notify any dirt nodes nearby that the environment changed.
- dirtspread.on_environment(pos)
- local a = minetest.get_objects_inside_radius(pos, 0.87)
- for _,obj in ipairs(a) do
- local ent = obj:get_luaentity()
- if ent and ent.name == "__builtin:item" then
- async(obj)
- end
- end
- end
- end
- -- Public API function, notify drops in/at a position.
- function droplift.notify(pos)
- local a = minetest.get_objects_inside_radius(pos, 0.87)
- for _, obj in ipairs(a) do
- local ent = obj:get_luaentity()
- if ent and ent.name == "__builtin:item" then
- async(obj)
- end
- end
- end
- append_to_core_defns()
|