particles.lua 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  1. particles = particles or {}
  2. particles.modpath = minetest.get_modpath("ambiance")
  3. -- Food crumbs when player eats something.
  4. function ambiance.particles_eat_item(user, name)
  5. local def = minetest.registered_items[name]
  6. if not def then
  7. return
  8. end
  9. local texture = ""
  10. if def.inventory_image then
  11. texture = def.inventory_image
  12. elseif def.wield_image then
  13. texture = def.wield_image
  14. end
  15. if texture == "" then
  16. return
  17. end
  18. local rnd = function(u, l)
  19. return math.random(u*10, l*10)/10
  20. end
  21. local pos = user:get_pos()
  22. local yaw = user:get_look_horizontal()
  23. local forward = vector.multiply(minetest.yaw_to_dir(yaw), 0.3)
  24. local push = vector.multiply(minetest.yaw_to_dir(yaw), 1.5)
  25. pos = vector.add(pos, forward)
  26. local particles = {
  27. amount = math.random(2, 5),
  28. time = 0.2,
  29. minpos = vector.add(pos, {x=-0.1, y=1.1, z=-0.1}),
  30. maxpos = vector.add(pos, {x=0.1, y=1.6, z=0.1}),
  31. minvel = vector.add(vector.new(-0.3, 0.3, -0.3), push),
  32. maxvel = vector.add(vector.new(0.3, 0.6, 0.3), push),
  33. minacc = vector.new(0.0, -8.0, 0.0),
  34. maxacc = vector.new(0.0, -8.0, 0.0),
  35. minexptime = 0.5,
  36. maxexptime = 2,
  37. minsize = 0.8,
  38. maxsize = 1.3,
  39. collisiondetection = true,
  40. collision_removal = true,
  41. vertical = false,
  42. texture = texture,
  43. }
  44. minetest.add_particlespawner(particles)
  45. end
  46. -- Particles when player is entirely submerged.
  47. function ambiance.particles_underwater(pos)
  48. local rnd = function(u, l)
  49. return math.random(u*10, l*10)/10
  50. end
  51. local particles = {
  52. amount = math.random(5, 10),
  53. time = 1.0,
  54. minpos = vector.add(pos, -0.5),
  55. maxpos = vector.add(pos, 0.5),
  56. minvel = vector.new(0.5, 0.5, 0.5),
  57. maxvel = vector.new(-0.5, -0.5, -0.5),
  58. minacc = vector.new(0.1, 0.1, 0.1),
  59. maxacc = vector.new(-0.1, -0.1, -0.1),
  60. minexptime = 0.5,
  61. maxexptime = 2,
  62. minsize = 0.1,
  63. maxsize = 1.0,
  64. collisiondetection = false,
  65. collision_removal = false,
  66. vertical = false,
  67. texture = "default_water.png",
  68. }
  69. minetest.add_particlespawner(particles)
  70. end
  71. -- Swimming particles when player is swimming on water surface.
  72. function ambiance.particles_swimming(pos)
  73. local rnd = function(u, l)
  74. return math.random(u*10, l*10)/10
  75. end
  76. local bubbles = {
  77. amount = math.random(5, 10),
  78. time = 1.0,
  79. minpos = vector.add(pos, -0.5),
  80. maxpos = vector.add(pos, 0.5),
  81. minvel = vector.new(-0.5, 0.0, -0.5),
  82. maxvel = vector.new(0.5, 0.5, 0.5),
  83. minacc = vector.new(0.0, -6, 0.0),
  84. maxacc = vector.new(0.0, -10, 0.0),
  85. minexptime = 0.5,
  86. maxexptime = 2,
  87. minsize = 0.5,
  88. maxsize = 1.5,
  89. collisiondetection = false,
  90. collision_removal = false,
  91. vertical = false,
  92. texture = "bubble.png",
  93. }
  94. minetest.add_particlespawner(bubbles)
  95. local splash = {
  96. amount = math.random(5, 10),
  97. time = 1.0,
  98. minpos = vector.add(pos, -0.5),
  99. maxpos = vector.add(pos, 0.5),
  100. minvel = vector.new(-0.5, 0.0, -0.5),
  101. maxvel = vector.new(0.5, 0.5, 0.5),
  102. minacc = vector.new(0.0, -6, 0.0),
  103. maxacc = vector.new(0.0, -10, 0.0),
  104. minexptime = 0.5,
  105. maxexptime = 2,
  106. minsize = 0.1,
  107. maxsize = 1.0,
  108. collisiondetection = false,
  109. collision_removal = false,
  110. vertical = false,
  111. texture = "default_water.png",
  112. }
  113. minetest.add_particlespawner(splash)
  114. end
  115. local function get_texture(node)
  116. local texture = ""
  117. local def = minetest.registered_items[node.name]
  118. if def then
  119. if def.tiles and def.tiles[1] then
  120. if type(def.tiles[1]) == "string" then
  121. texture = def.tiles[1]
  122. end
  123. end
  124. end
  125. -- If no texture could be found, calling function should abort.
  126. return texture
  127. end
  128. function ambiance.particles_on_dig(pos, node)
  129. local texture = get_texture(node)
  130. if texture == "" then
  131. return
  132. end
  133. local particles = {
  134. amount = math.random(5, 10),
  135. time = 0.1,
  136. minpos = vector.add(pos, -0.49),
  137. maxpos = vector.add(pos, 0.49),
  138. minvel = {x=0, y=1, z=0},
  139. maxvel = {x=0, y=2, z=0},
  140. minacc = {x=0, y=-10, z=0},
  141. maxacc = {x=0, y=-10, z=0},
  142. minexptime = 0.5,
  143. maxexptime = 1,
  144. minsize = 1,
  145. maxsize = 1,
  146. collisiondetection = true,
  147. collision_removal = false,
  148. vertical = false,
  149. texture = texture,
  150. }
  151. minetest.add_particlespawner(particles)
  152. end
  153. function ambiance.particles_on_punch(pos, node)
  154. local texture = get_texture(node)
  155. if texture == "" then
  156. return
  157. end
  158. local particles = {
  159. amount = math.random(1, 5),
  160. time = 0.1,
  161. minpos = vector.add(pos, -0.49),
  162. maxpos = vector.add(pos, 0.49),
  163. minvel = {x=-0.3, y=0, z=-0.3},
  164. maxvel = {x=0.3, y=0, z=0.3},
  165. minacc = {x=0, y=-10, z=0},
  166. maxacc = {x=0, y=-10, z=0},
  167. minexptime = 0.5,
  168. maxexptime = 1,
  169. minsize = 1,
  170. maxsize = 1,
  171. collisiondetection = true,
  172. collision_removal = false,
  173. vertical = false,
  174. texture = texture,
  175. }
  176. minetest.add_particlespawner(particles)
  177. end
  178. function ambiance.particles_on_place(pos, node)
  179. local texture = get_texture(node)
  180. if texture == "" then
  181. return
  182. end
  183. local particles = {
  184. amount = math.random(5, 10),
  185. time = 0.1,
  186. minpos = vector.add(pos, -0.49),
  187. maxpos = vector.add(pos, 0.49),
  188. minvel = {x=-0.3, y=0, z=-0.3},
  189. maxvel = {x=0.3, y=0, z=0.3},
  190. minacc = {x=0, y=-10, z=0},
  191. maxacc = {x=0, y=-10, z=0},
  192. minexptime = 0.5,
  193. maxexptime = 1,
  194. minsize = 1,
  195. maxsize = 1,
  196. collisiondetection = true,
  197. collision_removal = false,
  198. vertical = false,
  199. texture = texture,
  200. }
  201. minetest.add_particlespawner(particles)
  202. end
  203. local function player_nearby(pos)
  204. local players = minetest.get_connected_players()
  205. for k, v in ipairs(players) do
  206. if vector.distance(pos, v:get_pos()) < 16 then
  207. return true
  208. end
  209. end
  210. end
  211. function ambiance.flamespawner(self, dtime)
  212. self.st = (self.st or 0) - dtime
  213. self.ct = (self.ct or 0) - dtime
  214. self.nt = (self.nt or 0) - dtime
  215. -- Remove spawner if no flame here.
  216. if self.nt < 0 then
  217. self.nt = math.random(10, 60)
  218. local pos = self.object:get_pos()
  219. local nn = minetest.get_node(pos).name
  220. if not string.find(nn, "^fire:") and not string.find(nn, "^maptools:") then
  221. self.object:remove()
  222. return
  223. end
  224. end
  225. -- Check every so often if a player is nearby.
  226. if self.ct < 0 then
  227. local pos = self.object:get_pos()
  228. self.good = player_nearby(pos)
  229. self.ct = math.random(2, 8)
  230. end
  231. if self.st < 0 then
  232. local rnd = function(u, l)
  233. return math.random(u*10, l*10)/10
  234. end
  235. self.st = rnd(0.5, 1.0)
  236. -- Spawn particle only if player nearby.
  237. if self.good then
  238. local pos = self.object:get_pos()
  239. local particle = {
  240. pos = {x=pos.x+rnd(-0.5, 0.5), y=pos.y+rnd(0.0, 0.5), z=pos.z+rnd(-0.5, 0.5)},
  241. velocity = {x=rnd(-0.1, 0.1), y=rnd(0.1, 0.7), z=rnd(-0.1, 0.1)},
  242. acceleration = {x=rnd(-0.1, 0.1), y=rnd(0.1, 0.7), z=rnd(-0.1, 0.1)},
  243. expirationtime = rnd(0.1, 2.0),
  244. size = rnd(0.3, 0.7),
  245. collisiondetection = false,
  246. collision_removal = false,
  247. vertical = false,
  248. texture = "particles.png",
  249. animation = {
  250. type = "vertical_frames",
  251. aspect_w = 1,
  252. aspect_h = 1,
  253. length = 1.0,
  254. },
  255. glow = rnd(10, 15),
  256. }
  257. if math.random(1, 6) == 1 then
  258. particle.texture = "smoke_particles.png"
  259. particle.animation = {
  260. type = "vertical_frames",
  261. aspect_w = 4,
  262. aspect_h = 4,
  263. length = 1.0,
  264. }
  265. particle.size = rnd(0.8, 1.1)
  266. particle.velocity.y = particle.velocity.y + rnd(0.2, 0.4)
  267. particle.pos.y = pos.y + rnd(0.4, 0.6)
  268. end
  269. minetest.add_particle(particle)
  270. -- Occasionally play flame sound.
  271. if math.random(1, 10) == 1 then
  272. ambiance.sound_play("fire_small", pos, 0.3, 16)
  273. end
  274. end
  275. end
  276. end
  277. function particles.add_flame_spawner(pos)
  278. minetest.add_entity(pos, "particles:flamespawner")
  279. end
  280. function particles.del_flame_spawner(pos)
  281. local ents = minetest.get_objects_inside_radius(pos, 0.6)
  282. if ents then
  283. for k, obj in ipairs(ents) do
  284. if obj and obj:get_luaentity() and obj:get_luaentity().name == "particles:flamespawner" then
  285. obj:remove()
  286. end
  287. end
  288. end
  289. end
  290. function particles.restore_flame_spawner(pos)
  291. particles.del_flame_spawner(pos)
  292. particles.add_flame_spawner(pos)
  293. end
  294. if not particles.run_once then
  295. -- File is reloadable.
  296. local c = "particles:core"
  297. local f = particles.modpath .. "/particles.lua"
  298. reload.register_file(c, f, false)
  299. -- Torches no longer spawn particles, this creates too many entities and network packets.
  300. -- The entity definition now only exists to delete existing torchspawners from the world.
  301. local torchspawner = {
  302. visual = "wielditem",
  303. visual_size = {x=0, y=0},
  304. collisionbox = {0, 0, 0, 0, 0, 0},
  305. physical = false,
  306. textures = {"air"},
  307. is_visible = false,
  308. on_activate = function(self, staticdata, dtime_s)
  309. self.object:remove()
  310. end,
  311. on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
  312. end,
  313. on_death = function(self, killer)
  314. end,
  315. on_rightclick = function(self, clicker)
  316. end,
  317. get_staticdata = function(self)
  318. return ""
  319. end,
  320. on_step = function(self, dtime)
  321. self.object:remove()
  322. end,
  323. }
  324. minetest.register_entity(":particles:torchspawner", torchspawner)
  325. local flamespawner = {
  326. visual = "wielditem",
  327. visual_size = {x=0, y=0},
  328. collisionbox = {0, 0, 0, 0, 0, 0},
  329. physical = false,
  330. textures = {"air"},
  331. is_visible = false,
  332. on_activate = function(self, staticdata, dtime_s)
  333. end,
  334. on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir)
  335. end,
  336. on_death = function(self, killer)
  337. end,
  338. on_rightclick = function(self, clicker)
  339. end,
  340. get_staticdata = function(self)
  341. return ""
  342. end,
  343. on_step = function(self, dtime)
  344. return ambiance.flamespawner(self, dtime)
  345. end,
  346. }
  347. minetest.register_entity(":particles:flamespawner", flamespawner)
  348. particles.run_once = true
  349. end