diff --git a/game_api.txt b/game_api.txt index 8d3653d5..12cbdfde 100644 --- a/game_api.txt +++ b/game_api.txt @@ -1147,3 +1147,36 @@ the log. that log corresponding actions * `def` See [Node definition] * `name` Description of the node in the log message + + +Weather API +------- + +The weather API allows you to enable / disable weather effects programmatically +and also to modify weather effects before they are applied to the player. + +`weather.is_enabled()` + +* Returns true if weather mod is enabled or false if disabled +* Will return false regardless of whether the mod has been disabled via the settings or via `set_enabled`. +* Will return true even if selected worldgen doesn't support cloud updates. This is the case for v6 and singlenode types. + +`weather.set_enabled(enable)` + +* `enable` Boolean. Set to `true` to enable weather globally or `false` to disable it. +* Setting this to `false` is only advised if you want to disable the weather effects __permanently__ and for every player. + Re-enabling effects later on will work but might conflict with other mods doing the same. + Override `weather.on_update` if you want to disable temporarily or need a higher level of control. +* If the `enable_weather` setting is set to `false` in minetest.conf then this function will __not__ do nothing. + +`weather.on_update(player, overrides)` + +* Override this function to manipulate weather effects before they are applied to the player. +* Calling this function on its own will do nothing. You have to override it in order to modify its behavior. +* `player` The player entity that is about to be updated +* `overrides` Defines which changes are about to be made. It is a table consisting of the following values: + * `overrides.clouds` A table (or `nil`) with cloud data following the same format as used for `player:set_clouds()`. + * `overrides.lighting` A table (or `nil`) with lighting data following the same format as used for `player:set_lighting()`. +* This function is expected to return a table in the same format as `overrides`. + When overriding this function, the return value will be applied to the player instead of the original values. + Setting `clouds` or `lighting` to `nil` will __prevent__ that table from getting updated. \ No newline at end of file diff --git a/mods/weather/init.lua b/mods/weather/init.lua index 6daa050c..addf0132 100644 --- a/mods/weather/init.lua +++ b/mods/weather/init.lua @@ -1,10 +1,10 @@ -- Check whether mod should be active local mg_name = minetest.get_mapgen_setting("mg_name") -local mod_enabled = minetest.settings:get_bool("enable_weather", true) -local randomize_clouds = mod_enabled and mg_name ~= "v6" and mg_name ~= "singlenode" +local settings_enabled = minetest.settings:get_bool("enable_weather", true) +local randomize_clouds = mg_name ~= "v6" and mg_name ~= "singlenode" +-- global variable for API (see end of file) weather = {} -local playerset = {} -- Parameters local TSCALE = 600 -- Time scale of noise variation in seconds @@ -70,11 +70,14 @@ local t_offset = math.random(0, 300000) -- set a default shadow intensity for mgv6 and singlenode local function set_default_lighting(players) - for playername, _ in pairs(players) do - local player = minetest.get_player_by_name(playername) - player:set_lighting({ + for _, player in ipairs(players) do + local lighting = { shadows = { intensity = 0.33 } - }) + } + local overrides = weather.on_update(player, { lighting }) + if overrides.lighting ~= nil then + player:set_lighting(overrides.lighting) + end end end @@ -98,8 +101,7 @@ local function update_weather(players) local n_speedx = nobj_speedx:get_2d({x = time, y = 0}) -- -1 to 1 local n_speedz = nobj_speedz:get_2d({x = time, y = 0}) -- -1 to 1 - for playername, _ in pairs(players) do - local player = minetest.get_player_by_name(playername) + for _, player in ipairs(players) do -- Fallback to mid-value 50 for very old worlds local humid = minetest.get_humidity(player:get_pos()) or 50 -- Default and classic density value is 0.4, make this happen @@ -109,7 +111,7 @@ local function update_weather(players) -- density_max = 1.35 at humid = 100. local density_max = 0.8 + ((humid - 50) / 50) * 0.55 local density = rangelim(density_max, 0.2, 1.0) * n_density - player:set_clouds({ + local clouds = { -- Range limit density_max to always have occasional -- small scattered clouds at extreme low humidity. density = density, @@ -117,84 +119,82 @@ local function update_weather(players) rangelim(32 * humid / 100, 8, 32) * n_thickness ), 2), speed = {x = n_speedx * 4, z = n_speedz * 4}, - }) + } -- now adjust the shadow intensity - player:set_lighting({ + local lighting = { shadows = { intensity = 0.7 * (1 - density) } - }) + } + -- check for API overrides and then apply + local overrides = weather.on_update(player, { clouds, lighting }) + if overrides ~= nil and overrides.clouds ~= nil then + player:set_clouds(overrides.clouds) + end + if overrides ~= nil and overrides.lighting ~= nil then + player:set_lighting(overrides.lighting) + end end end -local function purge_effects(player) - -- only reset potentially touched table values - if randomize_clouds then - player:set_clouds({ - density = 0.4, - thickness = 16, - speed = { x = 0, z = -2 } - }) +-- Update clouds periodically for all players + +local timer = nil +local function cyclic_update() + local players = minetest.get_connected_players() + update_weather(players) + timer = minetest.after(CYCLE, cyclic_update) +end +timer = minetest.after(0, cyclic_update) + + +local function purge_effects() + -- stop timer for active cycle + if timer ~= nil then + timer:cancel() + timer = nil end - player:set_lighting({ - shadows = { intensity = 0 } - }) + -- reset changed player tables to default values + for _, player in ipairs(minetest.get_connected_players()) do + if randomize_clouds then + player:set_clouds({}) + end + -- NOTE: set_lighting doesn't allow empty table to reset + player:set_lighting({ + shadows = { + intensity = 0 + } + }) + end end --- Define API hooks - --- override to set state for newly joined players -weather.enable_on_join = true - --- returns bool for whether weather is active --- returns nil if player is offline or weather is disabled -weather.get_enabled = function(player) - if not mod_enabled or not player then - return - end - local playername = player:get_player_name() - return playerset[playername] or false -end - --- override weather generation for individual player -weather.set_enabled = function(player, enable) - local playername = player:get_player_name() - enable = enable or nil - if not mod_enabled or enable == playerset[playername] then - return - end - playerset[playername] = enable - if enable then - update_weather({ [playername] = true }) - else - purge_effects(player) - end -end - --- End API hooks - --- skip event registration if mod is disabled -if not mod_enabled then - return -end - -if randomize_clouds then - local function cyclic_update() - update_weather(playerset) - minetest.after(CYCLE, cyclic_update) - end - minetest.after(0, cyclic_update) -end -- Update on player join to instantly alter clouds from the default minetest.register_on_joinplayer(function(player) - if weather.enable_on_join then - local playername = player:get_player_name() - playerset[playername] = true - update_weather({ [playername] = true }) + if weather.is_enabled() then + update_weather({ player }) end end) -minetest.register_on_leaveplayer(function(player) - local playername = player:get_player_name() - playerset[playername] = nil -end) +-- Define API functions + +local is_enabled = true +weather.is_enabled = function() + return settings_enabled and is_enabled +end + +weather.set_enabled = function(enable) + if enable and not is_enabled then + -- restart stopped update cycle + cyclic_update() + elseif not enable and is_enabled then + -- stop update cycle and remove effects + purge_effects() + end + is_enabled = enable +end + +weather.on_update = function(player, overrides) + return overrides +end + +-- End API definition