init.lua 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. --register stoppers for movestones/pistons
  2. mesecon.mvps_stoppers = {}
  3. mesecon.on_mvps_move = {}
  4. mesecon.mvps_unmov = {}
  5. --- Objects (entities) that cannot be moved
  6. function mesecon.register_mvps_unmov(objectname)
  7. mesecon.mvps_unmov[objectname] = true;
  8. end
  9. function mesecon.is_mvps_unmov(objectname)
  10. return mesecon.mvps_unmov[objectname]
  11. end
  12. -- Nodes that cannot be pushed / pulled by movestones, pistons
  13. function mesecon.is_mvps_stopper(node, pushdir, stack, stackid)
  14. -- unknown nodes are always stoppers
  15. if not minetest.registered_nodes[node.name] then
  16. return true
  17. end
  18. local get_stopper = mesecon.mvps_stoppers[node.name]
  19. if type (get_stopper) == "function" then
  20. get_stopper = get_stopper(node, pushdir, stack, stackid)
  21. end
  22. return get_stopper
  23. end
  24. function mesecon.register_mvps_stopper(nodename, get_stopper)
  25. if get_stopper == nil then
  26. get_stopper = true
  27. end
  28. mesecon.mvps_stoppers[nodename] = get_stopper
  29. end
  30. -- Functions to be called on mvps movement
  31. function mesecon.register_on_mvps_move(callback)
  32. mesecon.on_mvps_move[#mesecon.on_mvps_move+1] = callback
  33. end
  34. local function on_mvps_move(moved_nodes)
  35. for _, callback in ipairs(mesecon.on_mvps_move) do
  36. callback(moved_nodes)
  37. end
  38. end
  39. function mesecon.mvps_process_stack(stack)
  40. -- update mesecons for placed nodes ( has to be done after all nodes have been added )
  41. for _, n in ipairs(stack) do
  42. mesecon.on_placenode(n.pos, minetest.get_node(n.pos))
  43. end
  44. end
  45. -- tests if the node can be pushed into, e.g. air, water, grass
  46. local function node_replaceable(name)
  47. if name == "ignore" then return true end
  48. if minetest.registered_nodes[name] then
  49. return minetest.registered_nodes[name].buildable_to or false
  50. end
  51. return false
  52. end
  53. function mesecon.mvps_get_stack(pos, dir, maximum, all_pull_sticky)
  54. -- determine the number of nodes to be pushed
  55. local nodes = {}
  56. local frontiers = {pos}
  57. while #frontiers > 0 do
  58. local np = frontiers[1]
  59. local nn = minetest.get_node(np)
  60. if not node_replaceable(nn.name) then
  61. table.insert(nodes, {node = nn, pos = np})
  62. if #nodes > maximum then return nil end
  63. -- add connected nodes to frontiers, connected is a vector list
  64. -- the vectors must be absolute positions
  65. local connected = {}
  66. if minetest.registered_nodes[nn.name]
  67. and minetest.registered_nodes[nn.name].mvps_sticky then
  68. connected = minetest.registered_nodes[nn.name].mvps_sticky(np, nn)
  69. end
  70. table.insert(connected, vector.add(np, dir))
  71. -- If adjacent node is sticky block and connects add that
  72. -- position to the connected table
  73. for _, r in ipairs(mesecon.rules.alldirs) do
  74. local adjpos = vector.add(np, r)
  75. local adjnode = minetest.get_node(adjpos)
  76. if minetest.registered_nodes[adjnode.name]
  77. and minetest.registered_nodes[adjnode.name].mvps_sticky then
  78. local sticksto = minetest.registered_nodes[adjnode.name]
  79. .mvps_sticky(adjpos, adjnode)
  80. -- connects to this position?
  81. for _, link in ipairs(sticksto) do
  82. if vector.equals(link, np) then
  83. table.insert(connected, adjpos)
  84. end
  85. end
  86. end
  87. end
  88. if all_pull_sticky then
  89. table.insert(connected, vector.subtract(np, dir))
  90. end
  91. -- Make sure there are no duplicates in frontiers / nodes before
  92. -- adding nodes in "connected" to frontiers
  93. for _, cp in ipairs(connected) do
  94. local duplicate = false
  95. for _, rp in ipairs(nodes) do
  96. if vector.equals(cp, rp.pos) then
  97. duplicate = true
  98. end
  99. end
  100. for _, fp in ipairs(frontiers) do
  101. if vector.equals(cp, fp) then
  102. duplicate = true
  103. end
  104. end
  105. if not duplicate then
  106. table.insert(frontiers, cp)
  107. end
  108. end
  109. end
  110. table.remove(frontiers, 1)
  111. end
  112. return nodes
  113. end
  114. function mesecon.mvps_push(pos, dir, maximum)
  115. return mesecon.mvps_push_or_pull(pos, dir, dir, maximum)
  116. end
  117. function mesecon.mvps_pull_all(pos, dir, maximum)
  118. return mesecon.mvps_push_or_pull(pos, vector.multiply(dir, -1), dir, maximum, true)
  119. end
  120. function mesecon.mvps_pull_single(pos, dir, maximum)
  121. return mesecon.mvps_push_or_pull(pos, vector.multiply(dir, -1), dir, maximum)
  122. end
  123. -- pos: pos of mvps; stackdir: direction of building the stack
  124. -- movedir: direction of actual movement
  125. -- maximum: maximum nodes to be pushed
  126. -- all_pull_sticky: All nodes are sticky in the direction that they are pulled from
  127. function mesecon.mvps_push_or_pull(pos, stackdir, movedir, maximum, all_pull_sticky)
  128. local nodes = mesecon.mvps_get_stack(pos, movedir, maximum, all_pull_sticky)
  129. if not nodes then return end
  130. -- determine if one of the nodes blocks the push / pull
  131. for id, n in ipairs(nodes) do
  132. if mesecon.is_mvps_stopper(n.node, movedir, nodes, id) then
  133. return
  134. end
  135. end
  136. -- remove all nodes
  137. for _, n in ipairs(nodes) do
  138. n.meta = minetest.get_meta(n.pos):to_table()
  139. local node_timer = minetest.get_node_timer(n.pos)
  140. if node_timer:is_started() then
  141. n.node_timer = {node_timer:get_timeout(), node_timer:get_elapsed()}
  142. end
  143. minetest.remove_node(n.pos)
  144. end
  145. -- update mesecons for removed nodes ( has to be done after all nodes have been removed )
  146. for _, n in ipairs(nodes) do
  147. mesecon.on_dignode(n.pos, n.node)
  148. end
  149. -- add nodes
  150. for _, n in ipairs(nodes) do
  151. local np = vector.add(n.pos, movedir)
  152. minetest.set_node(np, n.node)
  153. minetest.get_meta(np):from_table(n.meta)
  154. if n.node_timer then
  155. minetest.get_node_timer(np):set(unpack(n.node_timer))
  156. end
  157. end
  158. local moved_nodes = {}
  159. local oldstack = mesecon.tablecopy(nodes)
  160. for i in ipairs(nodes) do
  161. moved_nodes[i] = {}
  162. moved_nodes[i].oldpos = nodes[i].pos
  163. nodes[i].pos = vector.add(nodes[i].pos, movedir)
  164. moved_nodes[i].pos = nodes[i].pos
  165. moved_nodes[i].node = nodes[i].node
  166. moved_nodes[i].meta = nodes[i].meta
  167. moved_nodes[i].node_timer = nodes[i].node_timer
  168. end
  169. on_mvps_move(moved_nodes)
  170. return true, nodes, oldstack
  171. end
  172. function mesecon.mvps_move_objects(pos, dir, nodestack, movefactor)
  173. local objects_to_move = {}
  174. local dir_k
  175. local dir_l
  176. for k, v in pairs(dir) do
  177. if v ~= 0 then
  178. dir_k = k
  179. dir_l = v
  180. break
  181. end
  182. end
  183. movefactor = movefactor or 1
  184. dir = vector.multiply(dir, movefactor)
  185. for id, obj in pairs(minetest.object_refs) do
  186. local obj_pos = obj:getpos()
  187. local cbox = obj:get_properties().collisionbox
  188. local min_pos = vector.add(obj_pos, vector.new(cbox[1], cbox[2], cbox[3]))
  189. local max_pos = vector.add(obj_pos, vector.new(cbox[4], cbox[5], cbox[6]))
  190. local ok = true
  191. for k, v in pairs(pos) do
  192. local edge1, edge2
  193. if k ~= dir_k then
  194. edge1 = v - 0.51 -- More than 0.5 to move objects near to the stack.
  195. edge2 = v + 0.51
  196. else
  197. edge1 = v - 0.5 * dir_l
  198. edge2 = v + (#nodestack + 0.5 * movefactor) * dir_l
  199. -- Make sure, edge1 is bigger than edge2:
  200. if edge1 > edge2 then
  201. edge1, edge2 = edge2, edge1
  202. end
  203. end
  204. if min_pos[k] > edge2 or max_pos[k] < edge1 then
  205. ok = false
  206. break
  207. end
  208. end
  209. if ok then
  210. local ent = obj:get_luaentity()
  211. if obj:is_player() or (ent and not mesecon.is_mvps_unmov(ent.name)) then
  212. local np = vector.add(obj_pos, dir)
  213. -- Move only if destination is not solid or object is inside stack:
  214. local nn = minetest.get_node(np)
  215. local node_def = minetest.registered_nodes[nn.name]
  216. local obj_offset = dir_l * (obj_pos[dir_k] - pos[dir_k])
  217. if (node_def and not node_def.walkable) or
  218. (obj_offset >= 0 and
  219. obj_offset <= #nodestack - 0.5) then
  220. obj:move_to(np)
  221. end
  222. end
  223. end
  224. end
  225. end
  226. mesecon.register_mvps_stopper("doors:door_steel_b_1")
  227. mesecon.register_mvps_stopper("doors:door_steel_t_1")
  228. mesecon.register_mvps_stopper("doors:door_steel_b_2")
  229. mesecon.register_mvps_stopper("doors:door_steel_t_2")
  230. mesecon.register_mvps_stopper("default:chest_locked")
  231. mesecon.register_on_mvps_move(mesecon.move_hot_nodes)
  232. mesecon.register_on_mvps_move(function(moved_nodes)
  233. for i = 1, #moved_nodes do
  234. local moved_node = moved_nodes[i]
  235. mesecon.on_placenode(moved_node.pos, moved_node.node)
  236. minetest.after(0, function()
  237. minetest.check_for_falling(moved_node.oldpos)
  238. minetest.check_for_falling(moved_node.pos)
  239. end)
  240. local node_def = minetest.registered_nodes[moved_node.node.name]
  241. if node_def and node_def.mesecon and node_def.mesecon.on_mvps_move then
  242. node_def.mesecon.on_mvps_move(moved_node.pos, moved_node.node,
  243. moved_node.oldpos, moved_node.meta)
  244. end
  245. end
  246. end)