Rain now also extinguishes neighbor tiles
[MineClone/MineClone2.git] / mods / ENVIRONMENT / mcl_weather / rain.lua
blob0dde0d2d8b89d1b602b660deaca31d309368a5a3
1 local PARTICLES_COUNT_RAIN = 30
2 local PARTICLES_COUNT_THUNDER = 45
4 mcl_weather.rain = {
5 -- max rain particles created at time
6 particles_count = PARTICLES_COUNT_RAIN,
8 -- flag to turn on/off extinguish fire for rain
9 extinguish_fire = true,
11 -- flag useful when mixing weathers
12 raining = false,
14 -- keeping last timeofday value (rounded).
15 -- Defaulted to non-existing value for initial comparing.
16 sky_last_update = -1,
18 init_done = false,
21 mcl_weather.rain.sound_handler = function(player)
22 return minetest.sound_play("weather_rain", {
23 to_player = player:get_player_name(),
24 loop = true,
26 end
28 -- set skybox based on time (uses skycolor api)
29 mcl_weather.rain.set_sky_box = function()
30 if mcl_weather.state == "rain" then
31 mcl_weather.skycolor.add_layer(
32 "weather-pack-rain-sky",
33 {{r=0, g=0, b=0},
34 {r=85, g=86, b=98},
35 {r=135, g=135, b=151},
36 {r=85, g=86, b=98},
37 {r=0, g=0, b=0}})
38 mcl_weather.skycolor.active = true
39 for _, player in pairs(minetest.get_connected_players()) do
40 player:set_clouds({color="#5D5D5FE8"})
41 end
42 end
43 end
45 -- creating manually parctiles instead of particles spawner because of easier to control
46 -- spawn position.
47 mcl_weather.rain.add_rain_particles = function(player)
49 mcl_weather.rain.last_rp_count = 0
50 for i=mcl_weather.rain.particles_count, 1,-1 do
51 local random_pos_x, random_pos_y, random_pos_z = mcl_weather.get_random_pos_by_player_look_dir(player)
52 if mcl_weather.is_outdoor({x=random_pos_x, y=random_pos_y, z=random_pos_z}) then
53 mcl_weather.rain.last_rp_count = mcl_weather.rain.last_rp_count + 1
54 minetest.add_particle({
55 pos = {x=random_pos_x, y=random_pos_y, z=random_pos_z},
56 velocity = {x=0, y=-10, z=0},
57 acceleration = {x=0, y=-30, z=0},
58 expirationtime = 1.0,
59 size = math.random(0.5, 3),
60 collisiondetection = true,
61 collision_removal = true,
62 vertical = true,
63 texture = mcl_weather.rain.get_texture(),
64 playername = player:get_player_name()
66 end
67 end
68 end
70 -- Simple random texture getter
71 mcl_weather.rain.get_texture = function()
72 local texture_name
73 local random_number = math.random()
74 if random_number > 0.33 then
75 texture_name = "weather_pack_rain_raindrop_1.png"
76 elseif random_number > 0.66 then
77 texture_name = "weather_pack_rain_raindrop_2.png"
78 else
79 texture_name = "weather_pack_rain_raindrop_3.png"
80 end
81 return texture_name;
82 end
84 -- register player for rain weather.
85 -- basically needs for origin sky reference and rain sound controls.
86 mcl_weather.rain.add_player = function(player)
87 if mcl_weather.players[player:get_player_name()] == nil then
88 local player_meta = {}
89 player_meta.origin_sky = {player:get_sky()}
90 mcl_weather.players[player:get_player_name()] = player_meta
91 end
92 end
94 -- remove player from player list effected by rain.
95 -- be sure to remove sound before removing player otherwise soundhandler reference will be lost.
96 mcl_weather.rain.remove_player = function(player)
97 local player_meta = mcl_weather.players[player:get_player_name()]
98 if player_meta ~= nil and player_meta.origin_sky ~= nil then
99 player:set_clouds({color="#FFF0F0E5"})
100 mcl_weather.players[player:get_player_name()] = nil
104 mcl_worlds.register_on_dimension_change(function(player, dimension)
105 if dimension ~= "overworld" and dimension ~= "void" then
106 mcl_weather.rain.remove_sound(player)
107 mcl_weather.rain.remove_player(player)
108 elseif dimension == "overworld" then
109 mcl_weather.rain.update_sound(player)
110 if mcl_weather.rain.raining then
111 mcl_weather.rain.add_rain_particles(player)
112 mcl_weather.rain.add_player(player)
115 end)
117 -- adds and removes rain sound depending how much rain particles around player currently exist.
118 -- have few seconds delay before each check to avoid on/off sound too often
119 -- when player stay on 'edge' where sound should play and stop depending from random raindrop appearance.
120 mcl_weather.rain.update_sound = function(player)
121 local player_meta = mcl_weather.players[player:get_player_name()]
122 if player_meta ~= nil then
123 if player_meta.sound_updated ~= nil and player_meta.sound_updated + 5 > minetest.get_gametime() then
124 return false
127 if player_meta.sound_handler ~= nil then
128 if mcl_weather.rain.last_rp_count == 0 then
129 minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0)
130 player_meta.sound_handler = nil
132 elseif mcl_weather.rain.last_rp_count > 0 then
133 player_meta.sound_handler = mcl_weather.rain.sound_handler(player)
136 player_meta.sound_updated = minetest.get_gametime()
140 -- rain sound removed from player.
141 mcl_weather.rain.remove_sound = function(player)
142 local player_meta = mcl_weather.players[player:get_player_name()]
143 if player_meta ~= nil and player_meta.sound_handler ~= nil then
144 minetest.sound_fade(player_meta.sound_handler, -0.5, 0.0)
145 player_meta.sound_handler = nil
146 player_meta.sound_updated = nil
150 -- callback function for removing rain
151 mcl_weather.rain.clear = function()
152 mcl_weather.rain.raining = false
153 mcl_weather.rain.sky_last_update = -1
154 mcl_weather.rain.init_done = false
155 mcl_weather.rain.set_particles_mode("rain")
156 mcl_weather.skycolor.remove_layer("weather-pack-rain-sky")
157 for _, player in ipairs(minetest.get_connected_players()) do
158 mcl_weather.rain.remove_sound(player)
159 mcl_weather.rain.remove_player(player)
163 minetest.register_globalstep(function(dtime)
164 if mcl_weather.state ~= "rain" then
165 return false
168 mcl_weather.rain.make_weather()
169 end)
171 mcl_weather.rain.make_weather = function()
172 if mcl_weather.rain.init_done == false then
173 mcl_weather.rain.raining = true
174 mcl_weather.rain.set_sky_box()
175 mcl_weather.rain.set_particles_mode(mcl_weather.mode)
176 mcl_weather.rain.init_done = true
179 for _, player in ipairs(minetest.get_connected_players()) do
180 if (mcl_weather.is_underwater(player) or not mcl_worlds.has_weather(player:get_pos())) then
181 mcl_weather.rain.remove_sound(player)
182 return false
184 mcl_weather.rain.add_player(player)
185 mcl_weather.rain.add_rain_particles(player)
186 mcl_weather.rain.update_sound(player)
190 -- Switch the number of raindrops: "thunder" for many raindrops, otherwise for normal raindrops
191 mcl_weather.rain.set_particles_mode = function(mode)
192 if mode == "thunder" then
193 mcl_weather.rain.particles_count = PARTICLES_COUNT_THUNDER
194 else
195 mcl_weather.rain.particles_count = PARTICLES_COUNT_RAIN
199 if mcl_weather.allow_abm then
200 -- ABM for extinguish fire
201 minetest.register_abm({
202 label = "Rain extinguishes fire",
203 nodenames = {"mcl_fire:fire"},
204 interval = 2.0,
205 chance = 2,
206 action = function(pos, node, active_object_count, active_object_count_wider)
207 -- Fire is extinguished if in rain or one of 4 neighbors is in rain
208 if mcl_weather.rain.raining and mcl_weather.rain.extinguish_fire then
209 local around = {
210 { x = 0, y = 0, z = 0 },
211 { x = -1, y = 0, z = 0 },
212 { x = 1, y = 0, z = 0 },
213 { x = 0, y = 0, z = -1 },
214 { x = 0, y = 0, z = 1 },
216 for a=1, #around do
217 local apos = vector.add(pos, around[a])
218 if mcl_weather.is_outdoor(apos) then
219 minetest.remove_node(pos)
220 minetest.sound_play("fire_extinguish_flame", {pos = pos, max_hear_distance = 8, gain = 0.1}, true)
221 return
225 end,
228 -- Slowly fill up cauldrons
229 minetest.register_abm({
230 label = "Rain fills cauldrons with water",
231 nodenames = {"mcl_cauldrons:cauldron", "mcl_cauldrons:cauldron_1", "mcl_cauldrons:cauldron_2"},
232 interval = 56.0,
233 chance = 1,
234 action = function(pos, node, active_object_count, active_object_count_wider)
235 -- Rain is equivalent to a water bottle
236 if mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
237 if node.name == "mcl_cauldrons:cauldron" then
238 minetest.set_node(pos, {name="mcl_cauldrons:cauldron_1"})
239 elseif node.name == "mcl_cauldrons:cauldron_1" then
240 minetest.set_node(pos, {name="mcl_cauldrons:cauldron_2"})
241 elseif node.name == "mcl_cauldrons:cauldron_2" then
242 minetest.set_node(pos, {name="mcl_cauldrons:cauldron_3"})
243 elseif node.name == "mcl_cauldrons:cauldron_1r" then
244 minetest.set_node(pos, {name="mcl_cauldrons:cauldron_2r"})
245 elseif node.name == "mcl_cauldrons:cauldron_2r" then
246 minetest.set_node(pos, {name="mcl_cauldrons:cauldron_3r"})
252 -- Wetten the soil
253 minetest.register_abm({
254 label = "Rain hydrates farmland",
255 nodenames = {"mcl_farming:soil"},
256 interval = 22.0,
257 chance = 3,
258 action = function(pos, node, active_object_count, active_object_count_wider)
259 if mcl_weather.rain.raining and mcl_weather.is_outdoor(pos) then
260 if node.name == "mcl_farming:soil" then
261 minetest.set_node(pos, {name="mcl_farming:soil_wet"})
268 if mcl_weather.reg_weathers.rain == nil then
269 mcl_weather.reg_weathers.rain = {
270 clear = mcl_weather.rain.clear,
271 light_factor = 0.6,
272 -- 10min - 20min
273 min_duration = 600,
274 max_duration = 1200,
275 transitions = {
276 [65] = "none",
277 [70] = "snow",
278 [100] = "thunder",