display.lua 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. --[[
  2. cldlib based on:
  3. display_lib mod for Minetest - Library to add dynamic display
  4. capabilities to nodes
  5. (c) Pierre-Yves Rollo
  6. This program is free software: you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation, either version 3 of the License, or
  9. (at your option) any later version.
  10. This program is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. GNU General Public License for more details.
  14. You should have received a copy of the GNU General Public License
  15. along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. --]]
  17. -- Miscelaneous values depending on wallmounted param2
  18. local wallmounted_values = {
  19. [0]={dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0}, -- Should never be used
  20. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=1}, -- Should never be used
  21. {dx=-1, dz=0, rx=0, rz=-1, yaw=-math.pi/2, rotate=5},
  22. {dx=1, dz=0, rx=0, rz=1, yaw=math.pi/2, rotate=4},
  23. {dx=0, dz=-1, rx=1, rz=0, yaw=0, rotate=2},
  24. {dx=0, dz=1, rx=-1, rz=0, yaw=math.pi, rotate=3}
  25. }
  26. -- Miscelaneous values depending on facedir param2
  27. local facedir_values = {
  28. [0]={dx=0, dz=-1, rx=1, rz=0, yaw=0, rotate=1},
  29. {dx=-1, dz=0, rx=0, rz=-1, yaw=-math.pi/2, rotate=2},
  30. {dx=0, dz=1, rx=-1, rz=0, yaw=math.pi, rotate=3},
  31. {dx=1, dz=0, rx=0, rz=1, yaw=math.pi/2, rotate=0},
  32. -- Forbiden values :
  33. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  34. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  35. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  36. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  37. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  38. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  39. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  40. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  41. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  42. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  43. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  44. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  45. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  46. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  47. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  48. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  49. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  50. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  51. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  52. {dx=0, dz=0, rx=0, rz=0, yaw=0, rotate=0},
  53. }
  54. -- dx/dy = depth vector, rx/ly = right vector, yaw = yaw of entity,
  55. -- rotate = next facedir/wallmount on rotate
  56. local function get_values(node)
  57. local ndef = minetest.registered_nodes[node.name]
  58. if ndef then
  59. if ndef.paramtype2 == "wallmounted" then
  60. return wallmounted_values[node.param2]
  61. end
  62. if ndef.paramtype2 == "facedir" then
  63. return facedir_values[node.param2]
  64. end
  65. end
  66. end
  67. --- Gets the display entities attached with a node. Removes extra ones
  68. local function get_entities(pos)
  69. local objrefs = {}
  70. local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
  71. if ndef and ndef.display_entities then
  72. for _, objref in ipairs(minetest.get_objects_inside_radius(pos, 0.5)) do
  73. local entity = objref:get_luaentity()
  74. if entity and ndef.display_entities[entity.name] then
  75. if objrefs[entity.name] then
  76. objref:remove()
  77. else
  78. objrefs[entity.name] = objref
  79. end
  80. end
  81. end
  82. end
  83. return objrefs
  84. end
  85. local function clip_pos_prop(posprop)
  86. if posprop then
  87. return math.max(-0.5, math.min(0.5, posprop))
  88. else
  89. return 0
  90. end
  91. end
  92. --- (Create and) place display entities according to the node orientation
  93. local function place_entities(pos)
  94. local node = minetest.get_node(pos)
  95. local ndef = minetest.registered_nodes[node.name]
  96. local values = get_values(node)
  97. local objrefs = get_entities(pos)
  98. if values and ndef and ndef.display_entities then
  99. for entity_name, props in pairs(ndef.display_entities) do
  100. local depth = clip_pos_prop(props.depth)
  101. local height = clip_pos_prop(props.height)
  102. local right = clip_pos_prop(props.right)
  103. if not objrefs[entity_name] then
  104. objrefs[entity_name] = minetest.add_entity(pos, entity_name)
  105. end
  106. objrefs[entity_name]:setpos({
  107. x = pos.x - values.dx * depth + values.rx * right,
  108. y = pos.y + height,
  109. z = pos.z - values.dz * depth + values.rz * right})
  110. objrefs[entity_name]:setyaw(values.yaw)
  111. end
  112. end
  113. return objrefs
  114. end
  115. --- Call on_display_update callback of a node for one of its display entities
  116. local function call_node_on_display_update(pos, objref)
  117. local ndef = minetest.registered_nodes[minetest.get_node(pos).name]
  118. local entity = objref:get_luaentity()
  119. if ndef and ndef.display_entities and entity and ndef.display_entities[entity.name] then
  120. ndef.display_entities[entity.name].on_display_update(pos, objref)
  121. end
  122. end
  123. --- Force entity update
  124. function lcdlib.update_entities(pos)
  125. local objrefs = place_entities(pos)
  126. for _, objref in pairs(objrefs) do
  127. call_node_on_display_update(pos, objref)
  128. end
  129. end
  130. --- On_activate callback for lcdlib entities. Calls on_display_update callbacks
  131. --- of corresponding node for each entity.
  132. function lcdlib.on_activate(entity, staticdata)
  133. if entity then
  134. entity.object:set_armor_groups({immortal=1})
  135. call_node_on_display_update(entity.object:getpos(), entity.object)
  136. end
  137. end
  138. --- On_place callback for lcdlib items. Does nothing more than preventing item
  139. --- from being placed on ceiling or ground
  140. function lcdlib.on_place(itemstack, placer, pointed_thing)
  141. local ndef = itemstack:get_definition()
  142. local above = pointed_thing.above
  143. local under = pointed_thing.under
  144. local dir = {x = under.x - above.x,
  145. y = under.y - above.y,
  146. z = under.z - above.z}
  147. if ndef then
  148. if ndef.paramtype2 == "wallmounted" then
  149. local wdir = minetest.dir_to_wallmounted(dir)
  150. if wdir == 0 or wdir == 1 then
  151. dir = placer:get_look_dir()
  152. dir.y = 0
  153. wdir = minetest.dir_to_wallmounted(dir)
  154. end
  155. return minetest.item_place(itemstack, placer, pointed_thing, wdir)
  156. else
  157. return minetest.item_place(itemstack, placer, pointed_thing, minetest.dir_to_facedir(dir))
  158. end
  159. end
  160. end
  161. --- On_construct callback for lcdlib items. Creates entities and update them.
  162. function lcdlib.on_construct(pos)
  163. lcdlib.update_entities(pos)
  164. end
  165. --- On_destruct callback for lcdlib items. Removes entities.
  166. function lcdlib.on_destruct(pos)
  167. local objrefs = get_entities(pos)
  168. for _, objref in pairs(objrefs) do
  169. objref:remove()
  170. end
  171. end
  172. -- On_rotate (screwdriver) callback for lcdlib items. Prevents axis rotation and reorients entities.
  173. function lcdlib.on_rotate(pos, node, user, mode, new_param2)
  174. if mode ~= 1 then return false end
  175. local values = get_values(node)
  176. if values then
  177. minetest.swap_node(pos, {name = node.name, param1 = node.param1, param2 = values.rotate})
  178. place_entities(pos)
  179. return true
  180. else
  181. return false
  182. end
  183. end
  184. --- Creates display entity with some fields and the on_activate callback
  185. function lcdlib.register_display_entity(entity_name)
  186. if not minetest.registered_entity then
  187. minetest.register_entity(':'..entity_name, {
  188. collisionbox = { 0, 0, 0, 0, 0, 0 },
  189. visual = "upright_sprite",
  190. textures = {},
  191. on_activate = lcdlib.on_activate,
  192. })
  193. end
  194. end