item_entity.lua 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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.setting_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. infotext = "",
  31. },
  32. itemstring = '',
  33. physical_state = true,
  34. age = 0,
  35. set_item = function(self, itemstring)
  36. self.itemstring = itemstring
  37. local stack = ItemStack(itemstring)
  38. local count = stack:get_count()
  39. local max_count = stack:get_stack_max()
  40. if count > max_count then
  41. count = max_count
  42. self.itemstring = stack:get_name().." "..max_count
  43. end
  44. local s = 0.2 + 0.1 * (count / max_count)
  45. local c = s
  46. local itemtable = stack:to_table()
  47. local itemname = nil
  48. local description = ""
  49. if itemtable then
  50. itemname = stack:to_table().name
  51. end
  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. description = core.registered_items[itemname].description
  58. end
  59. local prop = {
  60. is_visible = true,
  61. visual = "wielditem",
  62. textures = {itemname},
  63. visual_size = {x = s, y = s},
  64. collisionbox = {-c, -c, -c, c, c, c},
  65. automatic_rotate = math.pi * 0.5,
  66. infotext = description,
  67. }
  68. self.object:set_properties(prop)
  69. end,
  70. get_staticdata = function(self)
  71. return core.serialize({
  72. itemstring = self.itemstring,
  73. always_collect = self.always_collect,
  74. age = self.age,
  75. dropped_by = self.dropped_by
  76. })
  77. end,
  78. on_activate = function(self, staticdata, dtime_s)
  79. if string.sub(staticdata, 1, string.len("return")) == "return" then
  80. local data = core.deserialize(staticdata)
  81. if data and type(data) == "table" then
  82. self.itemstring = data.itemstring
  83. self.always_collect = data.always_collect
  84. if data.age then
  85. self.age = data.age + dtime_s
  86. else
  87. self.age = dtime_s
  88. end
  89. self.dropped_by = data.dropped_by
  90. end
  91. else
  92. self.itemstring = staticdata
  93. end
  94. self.object:set_armor_groups({immortal = 1})
  95. self.object:setvelocity({x = 0, y = 2, z = 0})
  96. self.object:setacceleration({x = 0, y = -10, z = 0})
  97. self:set_item(self.itemstring)
  98. end,
  99. try_merge_with = function(self, own_stack, object, obj)
  100. local stack = ItemStack(obj.itemstring)
  101. if own_stack:get_name() == stack:get_name() and stack:get_free_space() > 0 then
  102. local overflow = false
  103. local count = stack:get_count() + own_stack:get_count()
  104. local max_count = stack:get_stack_max()
  105. if count > max_count then
  106. overflow = true
  107. count = count - max_count
  108. else
  109. self.itemstring = ''
  110. end
  111. local pos = object:getpos()
  112. pos.y = pos.y + (count - stack:get_count()) / max_count * 0.15
  113. object:moveto(pos, false)
  114. local s, c
  115. local max_count = stack:get_stack_max()
  116. local name = stack:get_name()
  117. if not overflow then
  118. obj.itemstring = name .. " " .. count
  119. s = 0.2 + 0.1 * (count / max_count)
  120. c = s
  121. object:set_properties({
  122. visual_size = {x = s, y = s},
  123. collisionbox = {-c, -c, -c, c, c, c}
  124. })
  125. self.object:remove()
  126. -- merging succeeded
  127. return true
  128. else
  129. s = 0.4
  130. c = 0.3
  131. object:set_properties({
  132. visual_size = {x = s, y = s},
  133. collisionbox = {-c, -c, -c, c, c, c}
  134. })
  135. obj.itemstring = name .. " " .. max_count
  136. s = 0.2 + 0.1 * (count / max_count)
  137. c = s
  138. self.object:set_properties({
  139. visual_size = {x = s, y = s},
  140. collisionbox = {-c, -c, -c, c, c, c}
  141. })
  142. self.itemstring = name .. " " .. count
  143. end
  144. end
  145. -- merging didn't succeed
  146. return false
  147. end,
  148. on_step = function(self, dtime)
  149. self.age = self.age + dtime
  150. if time_to_live > 0 and self.age > time_to_live then
  151. self.itemstring = ''
  152. self.object:remove()
  153. return
  154. end
  155. local p = self.object:getpos()
  156. p.y = p.y - 0.5
  157. local node = core.get_node_or_nil(p)
  158. local in_unloaded = (node == nil)
  159. if in_unloaded then
  160. -- Don't infinetly fall into unloaded map
  161. self.object:setvelocity({x = 0, y = 0, z = 0})
  162. self.object:setacceleration({x = 0, y = 0, z = 0})
  163. self.physical_state = false
  164. self.object:set_properties({physical = false})
  165. return
  166. end
  167. local nn = node.name
  168. -- If node is not registered or node is walkably solid and resting on nodebox
  169. local v = self.object:getvelocity()
  170. if not core.registered_nodes[nn] or core.registered_nodes[nn].walkable and v.y == 0 then
  171. if self.physical_state then
  172. local own_stack = ItemStack(self.object:get_luaentity().itemstring)
  173. -- Merge with close entities of the same item
  174. for _, object in ipairs(core.get_objects_inside_radius(p, 0.8)) do
  175. local obj = object:get_luaentity()
  176. if obj and obj.name == "__builtin:item"
  177. and obj.physical_state == false then
  178. if self:try_merge_with(own_stack, object, obj) then
  179. return
  180. end
  181. end
  182. end
  183. self.object:setvelocity({x = 0, y = 0, z = 0})
  184. self.object:setacceleration({x = 0, y = 0, z = 0})
  185. self.physical_state = false
  186. self.object:set_properties({physical = false})
  187. end
  188. else
  189. if not self.physical_state then
  190. self.object:setvelocity({x = 0, y = 0, z = 0})
  191. self.object:setacceleration({x = 0, y = -10, z = 0})
  192. self.physical_state = true
  193. self.object:set_properties({physical = true})
  194. end
  195. end
  196. end,
  197. on_punch = function(self, hitter)
  198. local inv = hitter:get_inventory()
  199. if inv and self.itemstring ~= '' then
  200. local left = inv:add_item("main", self.itemstring)
  201. if left and not left:is_empty() then
  202. self.itemstring = left:to_string()
  203. return
  204. end
  205. end
  206. self.itemstring = ''
  207. self.object:remove()
  208. end,
  209. })