init.lua_ 9.4 KB

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