weather = { -- weather states, 'none' is default, other states depends from active mods state = "none", -- player list for saving player meta info players = {}, -- time when weather should be re-calculated next_check = 0, -- default weather recalculation interval check_interval = 300, -- weather min duration min_duration = 240, -- weather max duration max_duration = 3600, -- weather calculated end time end_time = nil, -- registered weathers reg_weathers = {}, -- automaticly calculates intervals and swap weathers auto_mode = true } weather.get_rand_end_time = function(min_duration, max_duration) if min_duration ~= nil and max_duration ~= nil then return os.time() + math.random(min_duration, max_duration); else return os.time() + math.random(weather.min_duration, weather.max_duration); end end -- checks if player is undewater. This is needed in order to -- turn off weather particles generation. function is_underwater(player) local ppos = player:getpos() local offset = player:get_eye_offset() local player_eye_pos = {x = ppos.x + offset.x, y = ppos.y + offset.y + 1.5, z = ppos.z + offset.z} if minetest.get_node_level(player_eye_pos) == 8 then return true end return false end -- returns random number between a and b. function random_pos(a, b) if (a > b) then return math.random(b, a); end return math.random(a, b); end -- trying to locate position for particles by player look direction for performance reason. -- it is costly to generate many particles around player so goal is focus mainly on front view. function get_random_pos_by_player_look_dir(player) local look_dir = player:get_look_dir() local player_pos = player:getpos() local random_pos_x = 0 local random_pos_y = 0 local random_pos_z = 0 if look_dir.x > 0 then if look_dir.z > 0 then random_pos_x = math.random() + math.random(player_pos.x - 2.5, player_pos.x + 10) random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 10) else random_pos_x = math.random() + math.random(player_pos.x - 2.5, player_pos.x + 10) random_pos_z = math.random() + math.random(player_pos.z - 10, player_pos.z + 2.5) end else if look_dir.z > 0 then random_pos_x = math.random() + math.random(player_pos.x - 10, player_pos.x + 2.5) random_pos_z = math.random() + math.random(player_pos.z - 2.5, player_pos.z + 10) else random_pos_x = math.random() + math.random(player_pos.x - 10, player_pos.x + 2.5) random_pos_z = math.random() + math.random(player_pos.z - 10, player_pos.z + 2.5) end end random_pos_y = math.random() + random_pos(player_pos.y + 1, player_pos.y + 7) return random_pos_x, random_pos_y, random_pos_z end minetest.register_globalstep(function(dtime) if weather.auto_mode == false then return 0 end -- recalculate weather only when there aren't currently any if (weather.state ~= "none") then if (weather.end_time ~= nil and weather.end_time <= os.time()) then weather.reg_weathers[weather.state].clear() weather.state = "none" end elseif (weather.next_check <= os.time()) then for weather_name, weather_meta in pairs(weather.reg_weathers) do weather.set_random_weather(weather_name, weather_meta) end -- fallback next_check set, weather 'none' will be. weather.next_check = os.time() + weather.check_interval end end) -- sets random weather (which could be 'regular' (no weather)). weather.set_random_weather = function(weather_name, weather_meta) if weather.next_check > os.time() then return 0 end if (weather_meta ~= nil and weather_meta.chance ~= nil) then local random_roll = math.random(0,100) if (random_roll <= weather_meta.chance) then weather.state = weather_name weather.end_time = weather.get_rand_end_time(weather_meta.min_duration, weather_meta.max_duration) weather.next_check = os.time() + weather.check_interval end end end minetest.register_privilege("weather_manager", { description = "Gives ability to control weather", give_to_singleplayer = false }) -- Weather command definition. Set minetest.register_chatcommand("set_weather", { params = "", description = "Changes weather by given param, parameter none will remove weather.", privs = {weather_manager = true}, func = function(name, param) if (param == "none") then if (weather.state ~= nil and weather.reg_weathers[weather.state] ~= nil) then weather.reg_weathers[weather.state].clear() weather.state = param end weather.state = "none" end if (weather.reg_weathers ~= nil and weather.reg_weathers[param] ~= nil) then if (weather.state ~= nil and weather.state ~= "none" and weather.reg_weathers[weather.state] ~= nil) then weather.reg_weathers[weather.state].clear() end weather.state = param end end }) -- Overrides nodes 'sunlight_propagates' attribute for efficient indoor check (e.g. for glass roof). -- Controlled from minetest.conf setting and by default it is disabled. -- To enable set weather_allow_override_nodes to true. -- Only new nodes will be effected (glass roof needs to be rebuilded). if minetest.setting_getbool("weather_allow_override_nodes") then if minetest.registered_nodes["default:glass"] then minetest.override_item("default:glass", {sunlight_propagates = false}) end if minetest.registered_nodes["default:meselamp"] then minetest.override_item("default:meselamp", {sunlight_propagates = false}) end end