Major rewrite

Making invoke safer, and incidentally more useful.
- Stronger insurance against multiple timers on the same object.
- Sync control for better integration with processes that may exploit it.
- Optional actions provided by invoke removed from main lift cycle to reduce footprint.
master
Aftermoth 2016-05-01 18:50:36 +12:00
parent 93987c7fb8
commit 47a967c485
1 changed files with 94 additions and 56 deletions

150
init.lua
View File

@ -1,4 +1,3 @@
--[[
Copyright (C) 2016 Aftermoth, Zolan Davis
@ -12,10 +11,28 @@ http://www.gnu.org/licenses/lgpl-2.1.html
--]]
--------------------------------------------------- Global
droplift = {}
droplift = {
invoke,
-- function (dropobj, sync)
-- sync in [ false | 0 | seconds ]. See details.txt
}
local function in_walkable(p)
--------------------------------------------------- Local
-- minetest.get_us_time is not defined in 0.4.10
local seconds = 0.0
if not minetest.get_us_time then
minetest.register_globalstep(function(dtime)
seconds = seconds + dtime
end)
minetest.get_us_time = function()
return seconds * 1000000
end
end
local function obstructed(p)
local n = minetest.get_node_or_nil(p)
return n and minetest.registered_nodes[n.name].walkable
end
@ -23,7 +40,6 @@ end
-- * Local escape *
-- get nearest player in range (taxicab)
local function near_player(dpos)
local near = 8.5
local pp, d, ppos
@ -36,24 +52,20 @@ local function near_player(dpos)
ppos = pp
end
end
if near < 8.5 then return ppos else return false end
return ( near < 8.5 and ppos )
end
local function usign(r)
if r < 0 then return -1 else return 1 end
return ( r < 0 and -1 ) or 1
end
local function quick_escape(ent,pos)
local function escape(ent,pos)
local bias = {x = 1, y = 1, z = 1}
local o = {a="x", b="y", c="z"}
local pref = near_player(pos)
if pref then
bias = {x = usign(pref.x - pos.x), y = usign(pref.y - pos.y), z = usign(pref.z - pos.z)}
local mag={x=math.abs(pref.x - pos.x), y=math.abs(pref.y - pos.y), z=math.abs(pref.z - pos.z)}
local mag={x = math.abs(pref.x - pos.x), y = math.abs(pref.y - pos.y), z = math.abs(pref.z - pos.z)}
if mag.z > mag.y then
if mag.y > mag.x then
o={a="z",b="y",c="x"}
@ -76,7 +88,7 @@ local function quick_escape(ent,pos)
for b = pos[o.b] + bias[o.b], pos[o.b] - bias[o.b], -bias[o.b] do
for c = pos[o.c] + bias[o.c], pos[o.c] - bias[o.c], -bias[o.c] do
p = {[o.a]=a, [o.b]=b, [o.c]=c}
if not in_walkable(p) then
if not obstructed(p) then
ent.object:setpos(p)
return p
end
@ -90,57 +102,85 @@ end
-- * Entombment physics *
-- ---------------- LIFT
local function disentomb(obj, reset)
local function lift(obj)
local p = obj:getpos()
if p then
local ent = obj:get_luaentity()
local w = in_walkable(p)
local brace = math.floor(p.y - 0.5) + 0.800001
if ent.is_entombed then
if w then
p = {x = p.x, y = brace + 1, z = p.z}
obj:setpos(p)
if ent.is_entombed and obstructed(p) then
-- Time
local t = 1
local s1 = ent.sync1
if s1 then
local sd = ent.sync0+s1-minetest.get_us_time()
if sd > 0 then t = sd/1000000 end
ent.sync0, ent.sync1 = nil, nil
end
ent.is_entombed = in_walkable(p)
elseif w and not (reset and quick_escape(ent,p)) then
obj:setpos({x = p.x, y = brace, z = p.z})
ent.is_entombed = true
end
if ent.is_entombed then
obj:setvelocity({x = 0, y = 0, z = 0})
obj:setacceleration({x = 0, y = 0, z = 0})
minetest.after(1.0, disentomb, obj, false)
end
end
-- Space
p = {x = p.x, y = math.floor(p.y - 0.5) + 1.800001, z = p.z}
obj:setpos(p)
if s1 or obstructed(p) then
obj:setvelocity({x = 0, y = 0, z = 0})
obj:setacceleration({x = 0, y = 0, z = 0})
minetest.after(t, lift, obj)
return
end
end -- if w
-- Void.
ent.is_entombed, ent.sync0, ent.sync1 = nil, nil, nil
end -- if p
end
function droplift.invoke(obj, entomb)
disentomb(obj, not entomb)
-- ---------------- ASYNC
local counter = function()
local k = 0
return function()
k = (k==9973 and 1) or k+1
return k
end
end
local newhash = counter()
local function async(obj, usync)
local p = obj:getpos()
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 = minetest.get_us_time()
ent.sync1 = usync
end
-- Space.
if hash == ent.hash then
obj:setpos({x = p.x, y = math.floor(p.y - 0.5) + 0.800001, z = p.z})
obj:setvelocity({x = 0, y = 0, z = 0})
obj:setacceleration({x = 0, y = 0, z = 0})
if not ent.is_entombed then
ent.is_entombed = true
minetest.after(1, lift, obj)
end
end
end -- if w
if hash == ent.hash then ent.hash = nil end
end -- if p
end
droplift.invoke = function(obj, sync)
async(obj, (sync and math.max(0,sync)*1000000))
end
-- * Events *
-- Poll until defaults are ready before continuing.
local function wait_itemstring(ent, c)
if ent.itemstring == "" then
if c < 10 then
minetest.after(0.1, wait_itemstring, ent, c + 1)
end
return
end
disentomb(ent.object, false)
end
local function append_to_core_defns()
local dropentity=minetest.registered_entities["__builtin:item"]
@ -151,11 +191,10 @@ local function append_to_core_defns()
if staticdata ~= "" then
if minetest.deserialize(staticdata).is_entombed then
ent.is_entombed = true
minetest.after(0.1, wait_itemstring, ent, 1)
else
ent.object:setvelocity({x = 0, y = 0, z = 0})
minetest.after(0.1, lift, ent.object)
end
end
ent.object:setvelocity({x = 0, y = 0, z = 0})
end
-- Preserve state across reloads
@ -181,7 +220,7 @@ local function append_to_core_defns()
for _,obj in ipairs(a) do
local ent = obj:get_luaentity()
if ent and ent.name == "__builtin:item" then
disentomb(obj, true)
async(obj)
end
end
end
@ -189,5 +228,4 @@ local function append_to_core_defns()
end
append_to_core_defns()