init.lua 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. if not minetest.global_exists("itemburn") then itemburn = {} end
  2. itemburn.modpath = minetest.get_modpath("itemburn")
  3. -- Localize for performance.
  4. local vector_round = vector.round
  5. local math_random = math.random
  6. local math_min = math.min
  7. local abs = math.abs
  8. local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
  9. -- Use an offset for finding the node under the drop,
  10. -- to allow thin slab shielding for lava, etc.
  11. itemburn.footstep=-0.25
  12. function itemburn.get_fs()
  13. return itemburn.footstep
  14. end
  15. -- mods/default/item_entity.lua
  16. local builtin_item = minetest.registered_entities["__builtin:item"]
  17. local item = {
  18. set_item = function(self, itemstring)
  19. builtin_item.set_item(self, itemstring)
  20. local stack = ItemStack(itemstring)
  21. local itemdef = minetest.registered_items[stack:get_name()]
  22. if itemdef and itemdef.groups.flammable ~= 0 then
  23. self.flammable = itemdef.groups.flammable
  24. end
  25. end,
  26. -- Copy from builtin, with modifications.
  27. get_staticdata = function(self)
  28. return core.serialize({
  29. itemstring = self.itemstring,
  30. age = self.age,
  31. dropped_by = self.dropped_by,
  32. stuck_arrow = self.stuck_arrow,
  33. })
  34. end,
  35. -- Copy from builtin, with modifications.
  36. on_activate = function(self, staticdata, dtime_s)
  37. if string.sub(staticdata, 1, string.len("return")) == "return" then
  38. local data = core.deserialize(staticdata)
  39. if data and type(data) == "table" then
  40. self.itemstring = data.itemstring
  41. self.age = (data.age or 0) + dtime_s
  42. self.dropped_by = data.dropped_by
  43. self.stuck_arrow = data.stuck_arrow
  44. end
  45. else
  46. self.itemstring = staticdata
  47. end
  48. self.object:set_armor_groups({immortal = 1})
  49. -- Leave stuck arrows where they last were.
  50. if not self.stuck_arrow then
  51. self.object:set_velocity({x = 0, y = 2, z = 0})
  52. self.object:set_acceleration({x = 0, y = -gravity, z = 0})
  53. end
  54. self._collisionbox = self.initial_properties.collisionbox
  55. self:set_item()
  56. if self.stuck_arrow then
  57. self.object:set_properties({
  58. automatic_rotate = 0,
  59. physical = true,
  60. collide_with_objects = true,
  61. collisionbox = {-0.2, -0.2, -0.2, 0.2, 0.2, 0.2},
  62. })
  63. end
  64. end,
  65. burn_up = function(self, lava)
  66. -- disappear in a smoke puff
  67. self.itemstring = ""
  68. local p = self.object:get_pos()
  69. minetest.sound_play("default_item_smoke", {
  70. pos = p,
  71. max_hear_distance = 8,
  72. }, true)
  73. minetest.add_particlespawner({
  74. amount = 3,
  75. time = 0.1,
  76. minpos = {x = p.x - 0.1, y = p.y + 0.1, z = p.z - 0.1 },
  77. maxpos = {x = p.x + 0.1, y = p.y + 0.2, z = p.z + 0.1 },
  78. minvel = {x = 0, y = 2.5, z = 0},
  79. maxvel = {x = 0, y = 2.5, z = 0},
  80. minacc = {x = -0.15, y = -0.02, z = -0.15},
  81. maxacc = {x = 0.15, y = -0.01, z = 0.15},
  82. minexptime = 4,
  83. maxexptime = 6,
  84. minsize = 5,
  85. maxsize = 5,
  86. collisiondetection = true,
  87. texture = "default_item_smoke.png"
  88. })
  89. if lava then
  90. local node = minetest.get_node(p)
  91. lava_extras.spawn_particles(p, node)
  92. end
  93. self.object:remove()
  94. end,
  95. melt_in_lava = function(self, lpos)
  96. local p = self.object:get_pos()
  97. ambiance.sound_play("default_cool_lava", p, 2.0, 16)
  98. self.itemstring = ""
  99. self.object:remove()
  100. local node = minetest.get_node(lpos)
  101. lava_extras.spawn_particles(lpos, node)
  102. end,
  103. on_step = function(self, dtime, moveresult)
  104. builtin_item.on_step(self, dtime, moveresult)
  105. local is_falling = false
  106. local vel = self.object:get_velocity()
  107. -- Fix spurious error.
  108. -- Note: documentation explains this is caused by :remove() being called
  109. -- inside the original on_step() function; see just a few lines above, where
  110. -- it gets called.
  111. if not vel then
  112. return
  113. end
  114. if vel.y < -0.1 then
  115. is_falling = true
  116. self.need_lava_check = true
  117. --minetest.chat_send_all("# Server: Falling!")
  118. end
  119. if not is_falling and self.need_lava_check then
  120. --minetest.chat_send_all("# Server: Lava check!")
  121. local pos = self.object:get_pos()
  122. local node = minetest.get_node(pos)
  123. --minetest.chat_send_all("# Server: A=" .. node.name)
  124. if string.find(node.name, ":lava_") then
  125. self:melt_in_lava(vector_round(pos))
  126. return
  127. else
  128. local pb = vector_round({x=pos.x, y=pos.y+itemburn.get_fs(), z=pos.z})
  129. local node = minetest.get_node(pb)
  130. --minetest.chat_send_all("# Server: U=" .. node.name)
  131. if string.find(node.name, ":lava_") then
  132. self:melt_in_lava(pb)
  133. return
  134. end
  135. end
  136. self.need_lava_check = false
  137. end
  138. -- flammable, check for igniters
  139. self.ignite_timer = (self.ignite_timer or 0) - dtime
  140. if self.ignite_timer < 0 then
  141. self.ignite_timer = math_random(10, 100)/10
  142. local pos = self.object:get_pos()
  143. local node = minetest.get_node_or_nil(pos)
  144. if not node then
  145. return
  146. end
  147. -- Immediately burn up flammable items in lava
  148. if minetest.get_item_group(node.name, "lava") > 0 then
  149. self:melt_in_lava(vector_round(pos))
  150. else
  151. -- Check if sitting on top of lava.
  152. local pb = vector_round({x=pos.x, y=pos.y+itemburn.get_fs(), z=pos.z})
  153. local nb = minetest.get_node_or_nil(pb)
  154. if nb then
  155. local l = minetest.get_item_group(nb.name, "lava")
  156. if l > 0 then
  157. self:melt_in_lava(pb)
  158. return
  159. end
  160. end
  161. -- otherwise there'll be a chance based on its igniter value
  162. local burn_chance = (self.flammable or 1) * minetest.get_item_group(node.name, "igniter")
  163. if burn_chance > 0 and math_random(0, burn_chance) ~= 0 then
  164. self:burn_up()
  165. end
  166. end
  167. end
  168. end,
  169. on_punch = function(self, hitter)
  170. local inv = hitter:get_inventory()
  171. if not inv then
  172. return
  173. end
  174. -- Do not allow pickup of items inside fire.
  175. -- Do not allow pickup of moving items.
  176. do
  177. local nn = minetest.get_node(vector.round(self.object:get_pos())).name
  178. if minetest.get_item_group(nn, "fire") ~= 0 then
  179. return
  180. end
  181. local vel = self.object:get_velocity()
  182. if abs(vel.x) > 0.01 or abs(vel.y) > 0.01 or abs(vel.z) > 0.01 then
  183. return
  184. end
  185. end
  186. local clear = true
  187. if self.itemstring ~= "" then
  188. local stack = ItemStack(self.itemstring)
  189. local name = stack:get_name()
  190. local count = stack:get_count()
  191. local left
  192. local index
  193. local inserted = false
  194. local newstack
  195. for i=1, inv:get_size("main"), 1 do
  196. local s2 = inv:get_stack("main", i)
  197. local n2 = s2:get_name()
  198. local empty = s2:is_empty()
  199. if name == n2 or empty then
  200. if empty then
  201. local s3 = stack:take_item(math_min(stack:get_count(), stack:get_stack_max()))
  202. left = stack
  203. index = i
  204. inv:set_stack("main", i, s3)
  205. newstack = ItemStack(s3) -- A copy of the stack being added.
  206. inserted = true
  207. break
  208. elseif name == n2 and s2:get_free_space() > 0 then
  209. newstack = ItemStack(stack):take_item(math_min(s2:get_free_space(), stack:get_count())) -- A copy of the stack being added.
  210. left = s2:add_item(stack)
  211. index = i
  212. inv:set_stack("main", i, s2)
  213. inserted = true
  214. break
  215. end
  216. end
  217. end
  218. -- If something was added to the inv, we update the entity, but do not clear it.
  219. if left and not left:is_empty() then
  220. count = count - left:get_count()
  221. self:set_item(left)
  222. clear = false
  223. end
  224. -- If nothing was added to the inventory, we cannot remove the entity.
  225. if not inserted then
  226. clear = false
  227. end
  228. if inserted then
  229. minetest.log("action", hitter:get_player_name() .. " picks item-entity " ..
  230. stack:get_name() .. " " .. count .. " at " .. minetest.pos_to_string(vector_round(self.object:get_pos())))
  231. -- Execute player inventory callbacks.
  232. -- Note: inventory callbacks are called when player drops item (Q) so this
  233. -- implements the reciprocal.
  234. for _, func in ipairs(core.registered_on_player_inventory_actions) do
  235. func(hitter, "put", inv, {listname="main", index=index, stack=newstack})
  236. end
  237. end
  238. end
  239. if clear then
  240. self.itemstring = ""
  241. self.object:remove()
  242. end
  243. end,
  244. }
  245. -- set defined item as new __builtin:item, with the old one as fallback table
  246. setmetatable(item, builtin_item)
  247. minetest.register_entity(":__builtin:item", item)