init.lua 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. --[[
  2. Copyright (C) 2016 Aftermoth, Zolan Davis
  3. This program is free software; you can redistribute it and/or modify it
  4. under the terms of the GNU Lesser General Public License as published
  5. by the Free Software Foundation; either version 2.1 of the License,
  6. or (at your option) version 3 of the License.
  7. http://www.gnu.org/licenses/lgpl-2.1.html
  8. --]]
  9. --------------------------------------------------- Global
  10. droplift = {
  11. invoke,
  12. -- function (dropobj, sync)
  13. -- sync in [ false | 0 | seconds ]. See details.txt
  14. }
  15. --------------------------------------------------- Local
  16. -- Localize for performance.
  17. local math_floor = math.floor
  18. local math_max = math.max
  19. local function obstructed(p)
  20. local n = minetest.get_node_or_nil(p)
  21. if not n then return false end
  22. local ndef = minetest.reg_ns_nodes[n.name]
  23. return ndef and ndef.walkable
  24. end
  25. -- * Local escape *
  26. local dist = function(p1,p2)
  27. return ( (p1.y - p2.y)^2
  28. + (p1.x - p2.x)^2
  29. + (p1.z - p2.z)^2 )^0.5
  30. end
  31. local function escape(ent,pos)
  32. local q, p, ep, d, dd = pos
  33. for _,player in ipairs(minetest.get_connected_players()) do
  34. p = player:getpos(); p.y = p.y + 1
  35. d = dist(p,pos)
  36. if not dd or (d < dd) then dd, q = d, {x=p.x,y=p.y,z=p.z} end
  37. end
  38. for x = pos.x - 1, pos.x + 1 do
  39. p.x = x; for y = pos.y - 1, pos.y + 1 do
  40. p.y = y; for z = pos.z - 1, pos.z + 1 do
  41. p.z = z
  42. if not obstructed(p) then
  43. d = dist(q,p)
  44. if not ep or (d < dd) then dd, ep = d, {x=p.x,y=p.y,z=p.z} end
  45. end
  46. end
  47. end
  48. end
  49. if ep then ent.object:set_pos(ep) end
  50. return ep
  51. end
  52. -- * Entombment physics *
  53. -- ---------------- LIFT
  54. local function lift(obj)
  55. local p = obj:getpos()
  56. if p then
  57. local ent = obj:get_luaentity()
  58. if ent.is_entombed and obstructed(p) then
  59. -- Time
  60. local t = 1
  61. local s1 = ent.sync1
  62. if s1 then
  63. local sd = ent.sync0+s1-os.time()
  64. if sd > 0 then t = sd end
  65. ent.sync0, ent.sync1 = nil, nil
  66. end
  67. -- Space
  68. p = {x = p.x, y = math_floor(p.y - 0.5) + 1.800001, z = p.z}
  69. obj:set_pos(p)
  70. if s1 or obstructed(p) then
  71. minetest.after(t, lift, obj)
  72. return
  73. end
  74. end
  75. -- Void.
  76. ent.is_entombed, ent.sync0, ent.sync1 = nil, nil, nil
  77. end
  78. end
  79. -- ---------------- ASYNC
  80. local k = 0
  81. local function newhash()
  82. k = (k==32767 and 1) or k+1
  83. return k
  84. end
  85. local function async(obj, usync)
  86. local p = obj:getpos()
  87. if p then
  88. local ent = obj:get_luaentity()
  89. local hash = newhash()
  90. ent.hash = ent.hash or hash
  91. if obstructed(p) then
  92. -- Time.
  93. if not usync then
  94. if escape(ent, p) and hash == ent.hash then
  95. ent.hash = nil
  96. end
  97. elseif usync > 0 then
  98. ent.sync0 = os.time()
  99. ent.sync1 = usync
  100. end
  101. -- Space.
  102. if hash == ent.hash then
  103. obj:set_pos({x = p.x, y = math_floor(p.y - 0.5) + 0.800001, z = p.z})
  104. if not ent.is_entombed then
  105. ent.is_entombed = true
  106. minetest.after(1, lift, obj)
  107. end
  108. end
  109. end
  110. if hash == ent.hash then ent.hash = nil end
  111. end
  112. end
  113. droplift.invoke = function(obj, sync)
  114. async(obj, (sync and math_max(0,sync)))
  115. end
  116. -- * Events *
  117. local function append_to_core_defns()
  118. local dropentity=minetest.registered_entities["__builtin:item"]
  119. -- Ensure consistency across reloads.
  120. local on_activate_copy = dropentity.on_activate
  121. dropentity.on_activate = function(ent, staticdata, dtime_s)
  122. on_activate_copy(ent, staticdata, dtime_s)
  123. if staticdata ~= "" then
  124. if minetest.deserialize(staticdata).is_entombed then
  125. ent.is_entombed = true
  126. ent.object:setacceleration({x = 0, y = 0, z = 0}) -- Prevents 0.18m reload slippage. Not critical.
  127. minetest.after(0.1, lift, ent.object)
  128. end
  129. end
  130. ent.object:setvelocity({x = 0, y = 0, z = 0}) -- Prevents resting-buried drops burrowing.
  131. end
  132. -- Preserve state across reloads
  133. local get_staticdata_copy = dropentity.get_staticdata
  134. dropentity.get_staticdata = function(ent)
  135. local s = get_staticdata_copy(ent)
  136. if ent.is_entombed then
  137. local r = {}
  138. if s ~= "" then
  139. r = minetest.deserialize(s)
  140. end
  141. r.is_entombed=true
  142. return minetest.serialize(r)
  143. end
  144. return s
  145. end
  146. -- Update drops inside newly placed nodes.
  147. -- Note: only called when player places node, not when `minetest.set_node()` is used!
  148. local add_node_copy = minetest.add_node
  149. minetest.add_node = function(pos, node)
  150. add_node_copy(pos, node)
  151. -- Notify any dirt nodes nearby that the environment changed.
  152. dirtspread.on_environment(pos)
  153. local a = minetest.get_objects_inside_radius(pos, 0.87)
  154. for _,obj in ipairs(a) do
  155. local ent = obj:get_luaentity()
  156. if ent and ent.name == "__builtin:item" then
  157. async(obj)
  158. end
  159. end
  160. end
  161. end
  162. -- Public API function, notify drops in/at a position.
  163. function droplift.notify(pos)
  164. local a = minetest.get_objects_inside_radius(pos, 0.87)
  165. for _, obj in ipairs(a) do
  166. local ent = obj:get_luaentity()
  167. if ent and ent.name == "__builtin:item" then
  168. async(obj)
  169. end
  170. end
  171. end
  172. append_to_core_defns()