init.lua 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  1. -- Minetest: builtin/item_entity.lua
  2. -- override ice to make slippery for 0.4.16
  3. minetest.override_item("default:ice", {
  4. groups = {cracky = 3, puts_out_fire = 1, cools_lava = 1, slippery = 3}})
  5. function core.spawn_item(pos, item)
  6. local stack = ItemStack(item)
  7. local obj = core.add_entity(pos, "__builtin:item")
  8. if obj then
  9. obj:get_luaentity():set_item(stack:to_string())
  10. end
  11. return obj
  12. end
  13. -- If item_entity_ttl is not set, enity will have default life time
  14. -- Setting it to -1 disables the feature
  15. local time_to_live = tonumber(core.settings:get("item_entity_ttl")) or 900
  16. local gravity = tonumber(core.settings:get("movement_gravity")) or 9.81
  17. local destroy_item = core.settings:get_bool("destroy_item") ~= false
  18. -- water flow functions by QwertyMine3, edited by TenPlus1
  19. local function to_unit_vector(dir_vector)
  20. local inv_roots = {
  21. [0] = 1,
  22. [1] = 1,
  23. [2] = 0.70710678118655,
  24. [4] = 0.5,
  25. [5] = 0.44721359549996,
  26. [8] = 0.35355339059327
  27. }
  28. local sum = dir_vector.x * dir_vector.x + dir_vector.z * dir_vector.z
  29. return {
  30. x = dir_vector.x * inv_roots[sum],
  31. y = dir_vector.y,
  32. z = dir_vector.z * inv_roots[sum]
  33. }
  34. end
  35. local function node_ok(pos)
  36. local node = minetest.get_node_or_nil(pos)
  37. if node and minetest.registered_nodes[node.name] then
  38. return node
  39. end
  40. return minetest.registered_nodes["default:dirt"]
  41. end
  42. local function quick_flow_logic(node, pos_testing, direction)
  43. local node_testing = node_ok(pos_testing)
  44. if minetest.registered_nodes[node_testing.name].liquidtype ~= "flowing"
  45. and minetest.registered_nodes[node_testing.name].liquidtype ~= "source" then
  46. return 0
  47. end
  48. local param2_testing = node_testing.param2
  49. if param2_testing < node.param2 then
  50. if (node.param2 - param2_testing) > 6 then
  51. return -direction
  52. else
  53. return direction
  54. end
  55. elseif param2_testing > node.param2 then
  56. if (param2_testing - node.param2) > 6 then
  57. return direction
  58. else
  59. return -direction
  60. end
  61. end
  62. return 0
  63. end
  64. local function quick_flow(pos, node)
  65. if not minetest.registered_nodes[node.name].groups.liquid then
  66. return {x = 0, y = 0, z = 0}
  67. end
  68. local x, z = 0, 0
  69. x = x + quick_flow_logic(node, {x = pos.x - 1, y = pos.y, z = pos.z},-1)
  70. x = x + quick_flow_logic(node, {x = pos.x + 1, y = pos.y, z = pos.z}, 1)
  71. z = z + quick_flow_logic(node, {x = pos.x, y = pos.y, z = pos.z - 1},-1)
  72. z = z + quick_flow_logic(node, {x = pos.x, y = pos.y, z = pos.z + 1}, 1)
  73. return to_unit_vector({x = x, y = 0, z = z})
  74. end
  75. -- END water flow functions
  76. -- particle effects for when item is destroyed
  77. local function add_effects(pos)
  78. minetest.add_particlespawner({
  79. amount = 1,
  80. time = 0.25,
  81. minpos = pos,
  82. maxpos = pos,
  83. minvel = {x = -1, y = 2, z = -1},
  84. maxvel = {x = 1, y = 4, z = 1},
  85. minacc = {x = 0, y = 0, z = 0},
  86. maxacc = {x = 0, y = 0, z = 0},
  87. minexptime = 1,
  88. maxexptime = 3,
  89. minsize = 1,
  90. maxsize = 4,
  91. texture = "tnt_smoke.png",
  92. })
  93. end
  94. core.register_entity(":__builtin:item", {
  95. initial_properties = {
  96. hp_max = 1,
  97. physical = true,
  98. collide_with_objects = false,
  99. collisionbox = {-0.3, -0.3, -0.3, 0.3, 0.3, 0.3},
  100. visual = "wielditem",
  101. visual_size = {x = 0.4, y = 0.4},
  102. textures = {""},
  103. spritediv = {x = 1, y = 1},
  104. initial_sprite_basepos = {x = 0, y = 0},
  105. is_visible = false,
  106. infotext = "",
  107. },
  108. itemstring = "",
  109. moving_state = true,
  110. slippery_state = false,
  111. age = 0,
  112. set_item = function(self, item)
  113. local stack = ItemStack(item or self.itemstring)
  114. self.itemstring = stack:to_string()
  115. if self.itemstring == "" then
  116. return
  117. end
  118. local itemname = stack:is_known() and stack:get_name() or "unknown"
  119. local max_count = stack:get_stack_max()
  120. local count = math.min(stack:get_count(), max_count)
  121. local size = 0.2 + 0.1 * (count / max_count) ^ (1 / 3)
  122. local coll_height = size * 0.75
  123. self.object:set_properties({
  124. is_visible = true,
  125. visual = "wielditem",
  126. textures = {itemname},
  127. visual_size = {x = size, y = size},
  128. collisionbox = {-size, -coll_height, -size,
  129. size, coll_height, size},
  130. selectionbox = {-size, -size, -size, size, size, size},
  131. automatic_rotate = 0.314 / size,
  132. wield_item = self.itemstring,
  133. infotext = core.registered_items[itemname].description
  134. })
  135. end,
  136. get_staticdata = function(self)
  137. return core.serialize({
  138. itemstring = self.itemstring,
  139. age = self.age,
  140. dropped_by = self.dropped_by
  141. })
  142. end,
  143. on_activate = function(self, staticdata, dtime_s)
  144. if string.sub(staticdata, 1, string.len("return")) == "return" then
  145. local data = core.deserialize(staticdata)
  146. if data and type(data) == "table" then
  147. self.itemstring = data.itemstring
  148. self.age = (data.age or 0) + dtime_s
  149. self.dropped_by = data.dropped_by
  150. end
  151. else
  152. self.itemstring = staticdata
  153. end
  154. self.object:set_armor_groups({immortal = 1})
  155. self.object:set_velocity({x = 0, y = 2, z = 0})
  156. self.object:set_acceleration({x = 0, y = -gravity, z = 0})
  157. self:set_item()
  158. end,
  159. try_merge_with = function(self, own_stack, object, entity)
  160. if self.age == entity.age then
  161. return false -- Can not merge with itself
  162. end
  163. local stack = ItemStack(entity.itemstring)
  164. local name = stack:get_name()
  165. if own_stack:get_name() ~= name
  166. or own_stack:get_meta() ~= stack:get_meta()
  167. or own_stack:get_wear() ~= stack:get_wear()
  168. or own_stack:get_free_space() == 0 then
  169. return false -- Can not merge different or full stack
  170. end
  171. local count = own_stack:get_count()
  172. local total_count = stack:get_count() + count
  173. local max_count = stack:get_stack_max()
  174. if total_count > max_count then
  175. return false
  176. end
  177. -- Merge the remote stack into this one
  178. local pos = object:get_pos()
  179. pos.y = pos.y + ((total_count - count) / max_count) * 0.15
  180. self.object:move_to(pos)
  181. self.age = 0 -- Handle as new entity
  182. own_stack:set_count(total_count)
  183. self:set_item(own_stack)
  184. entity.itemstring = ""
  185. object:remove()
  186. return true
  187. end,
  188. on_step = function(self, dtime)
  189. local pos = self.object:get_pos()
  190. self.age = self.age + dtime
  191. if time_to_live > 0 and self.age > time_to_live then
  192. self.itemstring = ""
  193. self.object:remove()
  194. add_effects(pos)
  195. return
  196. end
  197. -- get nodes every 1/4 second
  198. self.timer = (self.timer or 0) + dtime
  199. if self.timer > 0.25 or not self.node_inside then
  200. self.node_inside = minetest.get_node_or_nil(pos)
  201. self.def_inside = self.node_inside
  202. and core.registered_nodes[self.node_inside.name]
  203. self.node_under = minetest.get_node_or_nil({
  204. x = pos.x,
  205. y = pos.y + self.object:get_properties().collisionbox[2] - 0.05,
  206. z = pos.z
  207. })
  208. self.def_under = self.node_under
  209. and core.registered_nodes[self.node_under.name]
  210. self.timer = 0
  211. end
  212. local node = self.node_inside
  213. -- Delete in 'ignore' nodes
  214. if node and node.name == "ignore" then
  215. self.itemstring = ""
  216. self.object:remove()
  217. return
  218. end
  219. -- do custom step function
  220. local name = ItemStack(self.itemstring):get_name() or ""
  221. local custom = core.registered_items[name]
  222. and core.registered_items[name].dropped_step
  223. if custom and custom(self, pos, dtime) == false then
  224. return -- skip further checks if false
  225. end
  226. local vel = self.object:get_velocity()
  227. local def = self.def_inside
  228. local is_slippery = false
  229. local is_moving = (def and not def.walkable) or
  230. vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
  231. -- destroy item when dropped into lava (if enabled)
  232. if destroy_item and def and def.groups and def.groups.lava then
  233. minetest.sound_play("builtin_item_lava", {
  234. pos = pos,
  235. max_hear_distance = 6,
  236. gain = 0.5
  237. })
  238. self.itemstring = ""
  239. self.object:remove()
  240. add_effects(pos)
  241. return
  242. end
  243. -- water flowing
  244. if def and def.liquidtype == "flowing" then
  245. local vec = quick_flow(pos, node)
  246. local v = self.object:get_velocity()
  247. self.object:set_velocity({x = vec.x, y = v.y, z = vec.z})
  248. return
  249. end
  250. -- item inside block, move to vacant space
  251. if def and not def.liquid
  252. and node.name ~= "air"
  253. and def.drawtype == "normal" then
  254. local npos = minetest.find_node_near(pos, 1, "air")
  255. if npos then
  256. self.object:moveto(npos)
  257. end
  258. self.node_inside = nil -- force get_node
  259. return
  260. end
  261. node = self.node_under
  262. def = self.def_under
  263. if def and def.walkable then
  264. local slippery = core.get_item_group(node.name, "slippery")
  265. is_slippery = slippery ~= 0
  266. if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
  267. -- Horizontal deceleration
  268. local slip_factor = 4.0 / (slippery + 4)
  269. self.object:set_acceleration({
  270. x = -vel.x * slip_factor,
  271. y = 0,
  272. z = -vel.z * slip_factor
  273. })
  274. elseif vel.y == 0 then
  275. is_moving = false
  276. end
  277. end
  278. if self.moving_state == is_moving
  279. and self.slippery_state == is_slippery then
  280. return -- No further updates until moving state changes
  281. end
  282. self.moving_state = is_moving
  283. self.slippery_state = is_slippery
  284. if is_moving then
  285. self.object:set_acceleration({x = 0, y = -gravity, z = 0})
  286. else
  287. self.object:set_acceleration({x = 0, y = 0, z = 0})
  288. self.object:set_velocity({x = 0, y = 0, z = 0})
  289. end
  290. --Only collect items if not moving
  291. if is_moving then
  292. return
  293. end
  294. -- Collect the items around to merge with
  295. local own_stack = ItemStack(self.itemstring)
  296. if own_stack:get_free_space() == 0 then
  297. return
  298. end
  299. local objects = core.get_objects_inside_radius(pos, 1.0)
  300. for k, obj in pairs(objects) do
  301. local entity = obj:get_luaentity()
  302. if entity and entity.name == "__builtin:item" then
  303. if self:try_merge_with(own_stack, obj, entity) then
  304. own_stack = ItemStack(self.itemstring)
  305. if own_stack:get_free_space() == 0 then
  306. return
  307. end
  308. end
  309. end
  310. end
  311. end,
  312. on_punch = function(self, hitter)
  313. local inv = hitter:get_inventory()
  314. if inv and self.itemstring ~= "" then
  315. local left = inv:add_item("main", self.itemstring)
  316. if left and not left:is_empty() then
  317. self:set_item(left)
  318. return
  319. end
  320. end
  321. self.itemstring = ""
  322. self.object:remove()
  323. end,
  324. })