particles.lua 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. --[[
  2. # Particle Effect
  3. Use this effect to render downfall or similar visuals using particles.
  4. Expects a table as the parameter containing information for the spawner.
  5. All values for ParticleSpawner definitions are valid.
  6. See https://minetest.gitlab.io/minetest/definition-tables/#particlespawner-definition
  7. Furthermore, the following default values have been changed:
  8. - time <int> [0.5] (reduced time results in smoother position updates, but more lag)
  9. - collisiondetection <bool> [true]
  10. - collision_removal <bool> [true]
  11. - playername <string> [current player] (Set to empty string to show for everyone)
  12. - vertical <bool> [nil] (Unless explicitly set, particle facing rotation will be automatically set based on direction of velocity)
  13. The following optional values have been introduced for convenience:
  14. - boxsize <vector> [nil] (Overrides minpos and maxpos based on specified sizes per direction with the player in the center)
  15. - boxsize <number> [nil] (If set to a number, the resulting vector will have the specified size in all directions)
  16. - v_offset <int> [0] (Use in conjunctin with boxsize. Adds specified height to minpos and maxpos y-coordinates)
  17. - attach_to_player <bool> [false] (Overrides attached object with current player)
  18. The following optional values have been expanded with additional value types for convenience:
  19. - size <int> [nil] (Overrides both minsize and maxsize if set)
  20. - minvel <int> [nil] (Overrides minvel with a downward facing vector of specified length)
  21. - maxvel <int> [nil] (Overrides maxvel with a downward facing vector of specified length)
  22. - velocity <vector | int> [nil] (Overrides both minvel and maxvel if set)
  23. - minacc <int> [nil] (Overrides minacc with a downward facing vector of specified length)
  24. - maxacc <int> [nil] (Overrides maxacc with a downward facing vector of specified length)
  25. - acceleration <vector | int> [nil] (Overrides both minacc and maxacc if set)
  26. The following new behaviours have been introduced:
  27. - use_wind <bool> [true] (Adjusts velocity and position for current windspeed)
  28. - detach <bool> [false] (Unless enabled, considers positions as relative to current player as if spawner's position would be attached)
  29. - adjust_for_velocity <bool> [true] (Corrects position of particle spawner by player's movement speed. Only applicable if detach = false and not manually attached)
  30. ]]
  31. if not climate_mod.settings.particles then return end
  32. local EFFECT_NAME = "climate_api:particles"
  33. local CYCLE_LENGTH = climate_api.SHORT_CYCLE
  34. -- parse config by injecting default values and adding additional parameters
  35. local function parse_config(player, particles)
  36. -- override default values with more useful ones
  37. local defaults = {
  38. time = 0.5,
  39. collisiondetection = true,
  40. collision_removal = true,
  41. playername = player:get_player_name(),
  42. use_wind = true,
  43. attach_to_player = false,
  44. detach = false,
  45. adjust_for_velocity = true
  46. }
  47. -- inject missing default values into specified config
  48. local config = climate_api.utility.merge_tables(defaults, particles)
  49. -- scale particle amount based on mod config
  50. if particles.amount ~= nil then
  51. config.amount = particles.amount * climate_mod.settings.particle_count
  52. end
  53. -- restore default visibility if specified
  54. if particles.playername == "" then
  55. config.playername = nil
  56. end
  57. -- provide easier param for exptime
  58. if particles.expirationtime ~= nil then
  59. config.minexptime = particles.expirationtime
  60. config.maxexptime = particles.expirationtime
  61. config.expirationtime = nil
  62. end
  63. -- provide easier param for size
  64. if particles.size ~= nil then
  65. config.minsize = particles.size
  66. config.maxsize = particles.size
  67. config.size = nil
  68. end
  69. -- randomly select a texture when given a table
  70. if type(particles.texture) == "table" then
  71. config.texture = particles.texture[math.random(#particles.texture)]
  72. end
  73. if particles.pos ~= nil then
  74. config.minpos = particles.pos
  75. config.maxpos = particles.pos
  76. config.pos = nil
  77. end
  78. -- provide easier size based param for position
  79. if type(particles.boxsize) == "number" then
  80. particles.boxsize = {
  81. x = particles.boxsize,
  82. y = particles.boxsize,
  83. z = particles.boxsize
  84. }
  85. end
  86. if particles.boxsize ~= nil then
  87. local size_x = particles.boxsize.x or 0
  88. local size_y = particles.boxsize.y or 0
  89. local size_z = particles.boxsize.z or 0
  90. local v_offset = particles.v_offset or 0
  91. v_offset = v_offset + (size_y / 2)
  92. config.minpos = {
  93. x = -size_x / 2,
  94. y = v_offset - (size_y / 2),
  95. z = -size_z / 2
  96. }
  97. config.maxpos = {
  98. x = size_x / 2,
  99. y = v_offset + (size_y / 2),
  100. z = size_z / 2
  101. }
  102. config.size_x = nil
  103. config.size_y = nil
  104. config.size_z = nil
  105. config.v_offset = nil
  106. end
  107. -- provide easy param to define unanimous falling speed
  108. if particles.velocity ~= nil then
  109. particles.minvel = particles.velocity
  110. particles.maxvel = particles.velocity
  111. config.velocity = nil
  112. end
  113. if type(particles.minvel) == "number" then
  114. config.minvel = { x = 0, y = -particles.minvel, z = 0 }
  115. end
  116. if type(particles.maxvel) ~= nil then
  117. config.maxvel = { x = 0, y = -particles.maxvel, z = 0 }
  118. end
  119. -- provide easy param to define unanimous falling acceleration
  120. if particles.acceleration ~= nil then
  121. particles.minacc = particles.acceleration
  122. particles.maxacc = particles.acceleration
  123. config.acceleration = nil
  124. end
  125. if type(particles.minacc) == "number" then
  126. config.minacc = { x = 0, y = -particles.minacc, z = 0 }
  127. end
  128. if type(particles.maxacc) == "number" then
  129. config.maxacc = { x = 0, y = -particles.maxacc, z = 0 }
  130. end
  131. -- attach particles to current player if specified
  132. if config.attach_to_player then
  133. config.attached = player
  134. end
  135. config.attach_to_player = nil
  136. -- attach coordinates to player unless specified or already attached
  137. if (not config.detach) and config.attached == nil then
  138. local ppos = player:get_pos()
  139. config.minpos = vector.add(config.minpos, ppos)
  140. config.maxpos = vector.add(config.maxpos, ppos)
  141. -- correct spawn coordinates to adjust for player movement
  142. if config.adjust_for_velocity then
  143. local velocity = player:get_player_velocity()
  144. config.minpos = vector.add(config.minpos, velocity)
  145. config.maxpos = vector.add(config.maxpos, velocity)
  146. end
  147. end
  148. config.detach = nil
  149. config.adjust_for_velocity = nil
  150. -- move particles in wind direction
  151. if config.use_wind then
  152. local pos = vector.multiply(vector.add(config.minpos, config.maxpos), 0.5)
  153. local wind = climate_api.environment.get_wind(pos)
  154. -- adjust velocity to include wind
  155. config.minvel = vector.add(config.minvel, wind)
  156. config.maxvel = vector.add(config.maxvel, wind)
  157. -- adjust spawn position for better visibility
  158. local vel = vector.multiply(vector.add(config.minvel, config.maxvel), 0.5)
  159. local windpos = vector.multiply(
  160. vector.normalize(vel),
  161. -vector.length(wind)
  162. )
  163. config.minpos = vector.add(config.minpos, windpos)
  164. config.maxpos = vector.add(config.maxpos, windpos)
  165. end
  166. config.use_wind = nil
  167. -- if unspecified, use 2D or 3D rotation based on movement direction
  168. if particles.vertical == nil then
  169. local vel = vector.multiply(vector.add(config.minvel, config.maxvel), 0.5)
  170. config.vertical = math.abs(vector.normalize(vel).y) >= 0.6
  171. end
  172. return config
  173. end
  174. local function handle_effect(player_data)
  175. for playername, data in pairs(player_data) do
  176. local player = minetest.get_player_by_name(playername)
  177. for weather, value in pairs(data) do
  178. local config = parse_config(player, value)
  179. minetest.add_particlespawner(config)
  180. end
  181. end
  182. end
  183. climate_api.register_effect(EFFECT_NAME, handle_effect, "tick")
  184. climate_api.set_effect_cycle(EFFECT_NAME, CYCLE_LENGTH)