init.lua 4.7 KB

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