item_entity.lua 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241
  1. -- Minetest: builtin/item_entity.lua
  2. function core.spawn_item(pos, item)
  3. -- Take item in any format
  4. local stack = ItemStack(item)
  5. local obj = core.add_entity(pos, "__builtin:item")
  6. -- Don't use obj if it couldn't be added to the map.
  7. if obj then
  8. obj:get_luaentity():set_item(stack:to_string())
  9. end
  10. return obj
  11. end
  12. -- If item_entity_ttl is not set, enity will have default life time
  13. -- Setting it to -1 disables the feature
  14. local time_to_live = tonumber(core.settings:get("item_entity_ttl"))
  15. if not time_to_live then
  16. time_to_live = 900
  17. end
  18. core.register_entity(":__builtin:item", {
  19. initial_properties = {
  20. hp_max = 1,
  21. physical = true,
  22. collide_with_objects = false,
  23. collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
  24. visual = "wielditem",
  25. visual_size = {x = 0.4, y = 0.4},
  26. textures = {""},
  27. spritediv = {x = 1, y = 1},
  28. initial_sprite_basepos = {x = 0, y = 0},
  29. is_visible = false,
  30. },
  31. itemstring = '',
  32. physical_state = true,
  33. age = 0,
  34. set_item = function(self, itemstring)
  35. self.itemstring = itemstring
  36. local stack = ItemStack(itemstring)
  37. local count = stack:get_count()
  38. local max_count = stack:get_stack_max()
  39. if count > max_count then
  40. count = max_count
  41. self.itemstring = stack:get_name().." "..max_count
  42. end
  43. local s = 0.2 + 0.1 * (count / max_count)
  44. local c = s
  45. local itemtable = stack:to_table()
  46. local itemname = nil
  47. if itemtable then
  48. itemname = stack:to_table().name
  49. end
  50. -- Backwards compatibility: old clients use the texture
  51. -- to get the type of the item
  52. local item_texture = nil
  53. local item_type = ""
  54. if core.registered_items[itemname] then
  55. item_texture = core.registered_items[itemname].inventory_image
  56. item_type = core.registered_items[itemname].type
  57. end
  58. local prop = {
  59. is_visible = true,
  60. visual = "wielditem",
  61. textures = {itemname},
  62. visual_size = {x = s, y = s},
  63. collisionbox = {-c, -c, -c, c, c, c},
  64. automatic_rotate = math.pi * 0.5,
  65. wield_item = itemstring,
  66. }
  67. self.object:set_properties(prop)
  68. end,
  69. get_staticdata = function(self)
  70. return core.serialize({
  71. itemstring = self.itemstring,
  72. always_collect = self.always_collect,
  73. age = self.age,
  74. dropped_by = self.dropped_by
  75. })
  76. end,
  77. on_activate = function(self, staticdata, dtime_s)
  78. if string.sub(staticdata, 1, string.len("return")) == "return" then
  79. local data = core.deserialize(staticdata)
  80. if data and type(data) == "table" then
  81. self.itemstring = data.itemstring
  82. self.always_collect = data.always_collect
  83. if data.age then
  84. self.age = data.age + dtime_s
  85. else
  86. self.age = dtime_s
  87. end
  88. self.dropped_by = data.dropped_by
  89. end
  90. else
  91. self.itemstring = staticdata
  92. end
  93. self.object:set_armor_groups({immortal = 1})
  94. self.object:set_velocity({x = 0, y = 2, z = 0})
  95. self.object:set_acceleration({x = 0, y = -10, z = 0})
  96. self:set_item(self.itemstring)
  97. end,
  98. -- moves items from this stack to an other stack
  99. try_merge_with = function(self, own_stack, object, obj)
  100. -- other item's stack
  101. local stack = ItemStack(obj.itemstring)
  102. -- only merge if items are the same
  103. if own_stack:get_name() == stack:get_name() and
  104. own_stack:get_meta() == stack:get_meta() and
  105. own_stack:get_wear() == stack:get_wear() and
  106. stack:get_free_space() > 0 then
  107. local overflow = false
  108. local count = stack:get_count() + own_stack:get_count()
  109. local max_count = stack:get_stack_max()
  110. if count > max_count then
  111. overflow = true
  112. stack:set_count(max_count)
  113. count = count - max_count
  114. own_stack:set_count(count)
  115. else
  116. self.itemstring = ''
  117. stack:set_count(count)
  118. end
  119. local pos = object:getpos()
  120. pos.y = pos.y + (count - stack:get_count()) / max_count * 0.15
  121. object:moveto(pos, false)
  122. local s, c
  123. if not overflow then
  124. obj.itemstring = stack:to_string()
  125. s = 0.2 + 0.1 * (count / max_count)
  126. c = s
  127. object:set_properties({
  128. visual_size = {x = s, y = s},
  129. collisionbox = {-c, -c, -c, c, c, c},
  130. wield_item = obj.itemstring
  131. })
  132. self.object:remove()
  133. -- merging succeeded
  134. return true
  135. else
  136. s = 0.4
  137. c = 0.3
  138. obj.itemstring = stack:to_string()
  139. object:set_properties({
  140. visual_size = {x = s, y = s},
  141. collisionbox = {-c, -c, -c, c, c, c},
  142. wield_item = obj.itemstring
  143. })
  144. s = 0.2 + 0.1 * (count / max_count)
  145. c = s
  146. self.itemstring = own_stack:to_string()
  147. self.object:set_properties({
  148. visual_size = {x = s, y = s},
  149. collisionbox = {-c, -c, -c, c, c, c},
  150. wield_item = self.itemstring
  151. })
  152. end
  153. end
  154. -- merging didn't succeed
  155. return false
  156. end,
  157. on_step = function(self, dtime)
  158. self.age = self.age + dtime
  159. if time_to_live > 0 and self.age > time_to_live then
  160. self.itemstring = ''
  161. self.object:remove()
  162. return
  163. end
  164. local p = self.object:getpos()
  165. p.y = p.y - 0.5
  166. local node = core.get_node_or_nil(p)
  167. local in_unloaded = (node == nil)
  168. if in_unloaded then
  169. -- Don't infinetly fall into unloaded map
  170. self.object:set_velocity({x = 0, y = 0, z = 0})
  171. self.object:set_acceleration({x = 0, y = 0, z = 0})
  172. self.physical_state = false
  173. self.object:set_properties({physical = false})
  174. return
  175. end
  176. local nn = node.name
  177. -- If node is not registered or node is walkably solid and resting on nodebox
  178. local v = self.object:getvelocity()
  179. if not core.registered_nodes[nn] or (core.registered_nodes[nn].walkable and
  180. core.get_item_group(nn, "slippery") == 0) and v.y == 0 then
  181. if self.physical_state then
  182. local own_stack = ItemStack(self.object:get_luaentity().itemstring)
  183. -- Merge with close entities of the same item
  184. for _, object in ipairs(core.get_objects_inside_radius(p, 0.8)) do
  185. local obj = object:get_luaentity()
  186. if obj and obj.name == "__builtin:item"
  187. and obj.physical_state == false then
  188. if self:try_merge_with(own_stack, object, obj) then
  189. return
  190. end
  191. end
  192. end
  193. self.object:set_velocity({x = 0, y = 0, z = 0})
  194. self.object:set_acceleration({x = 0, y = 0, z = 0})
  195. self.physical_state = false
  196. self.object:set_properties({physical = false})
  197. end
  198. else
  199. if not self.physical_state then
  200. self.object:set_velocity({x = 0, y = 0, z = 0})
  201. self.object:set_acceleration({x = 0, y = -10, z = 0})
  202. self.physical_state = true
  203. self.object:set_properties({physical = true})
  204. elseif minetest.get_item_group(nn, "slippery") ~= 0 then
  205. if math.abs(v.x) < 0.2 and math.abs(v.z) < 0.2 then
  206. self.object:set_velocity({x = 0, y = 0, z = 0})
  207. self.object:set_acceleration({x = 0, y = 0, z = 0})
  208. self.physical_state = false
  209. self.object:set_properties({
  210. physical = false
  211. })
  212. else
  213. self.object:set_acceleration({x = -v.x, y = -10, z = -v.z})
  214. end
  215. end
  216. end
  217. end,
  218. on_punch = function(self, hitter)
  219. local inv = hitter:get_inventory()
  220. if inv and self.itemstring ~= '' then
  221. local left = inv:add_item("main", self.itemstring)
  222. if left and not left:is_empty() then
  223. self.itemstring = left:to_string()
  224. return
  225. end
  226. end
  227. self.itemstring = ''
  228. self.object:remove()
  229. end,
  230. })