diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index b6fd71673..783f5031b 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -1103,3 +1103,8 @@ function mcl_util.is_it_christmas() return false end end + +function mcl_util.to_bool(val) + if not val then return false end + return true +end diff --git a/mods/ENVIRONMENT/mcl_weather/mod.conf b/mods/ENVIRONMENT/mcl_weather/mod.conf index 4f1102b7a..ee6f03407 100644 --- a/mods/ENVIRONMENT/mcl_weather/mod.conf +++ b/mods/ENVIRONMENT/mcl_weather/mod.conf @@ -1,5 +1,5 @@ name = mcl_weather author = xeranas description = Weather and sky handling: Rain, snow, thunderstorm, End and Nether ambience -depends = mcl_init, mcl_worlds +depends = mcl_init, mcl_worlds, mcl_playerinfo, mcl_util optional_depends = lightning diff --git a/mods/ENVIRONMENT/mcl_weather/skycolor.lua b/mods/ENVIRONMENT/mcl_weather/skycolor.lua index 87952845b..19cec9eac 100644 --- a/mods/ENVIRONMENT/mcl_weather/skycolor.lua +++ b/mods/ENVIRONMENT/mcl_weather/skycolor.lua @@ -1,15 +1,19 @@ -local mods_loaded = false +-- Constants +local modname = minetest.get_current_modname() +local modpath = minetest.get_modpath(modname) local NIGHT_VISION_RATIO = 0.45 -local MINIMUM_LIGHT_LEVEL = 0.2 +-- Settings +local minimum_update_interval = { 250e3 } -local water_color = "#3F76E4" - -local mg_name = minetest.get_mapgen_setting("mg_name") +-- Module state +local mods_loaded = false function mcl_weather.set_sky_box_clear(player, sky, fog) - local pos = player:get_pos() - if minetest.get_item_group(minetest.get_node(vector.new(pos.x,pos.y+1.5,pos.z)).name, "water") ~= 0 then return end + -- Make sure the player's head isn't in water before changing the skybox + local node_head = mcl_playerinfo[player:get_player_name()].node_head + if minetest.get_item_group(node_head, "water") ~= 0 then return end + local sc = { day_sky = "#7BA4FF", day_horizon = "#C0D8FF", @@ -38,8 +42,10 @@ function mcl_weather.set_sky_box_clear(player, sky, fog) end function mcl_weather.set_sky_color(player, def) - local pos = player:get_pos() - if minetest.get_item_group(minetest.get_node(vector.offset(pos, 0, 1.5, 0)).name, "water") ~= 0 then return end + -- Make sure the player's head isn't in water before changing the skybox + local node_head = mcl_playerinfo[player:get_player_name()].node_head + if minetest.get_item_group(node_head, "water") ~= 0 then return end + player:set_sky({ type = def.type, sky_color = def.sky_color, @@ -47,25 +53,7 @@ function mcl_weather.set_sky_color(player, def) }) end --- Function to work out light modifier at different times --- Noon is brightest, midnight is darkest, 0600 and 18000 is in the middle of this -local function get_light_modifier(time) - -- 0.1 = 0.2 - -- 0.4 = 0.8 - -- 0.5 = 1 - -- 0.6 = 0.8 - -- 0.9 = 0.2 - - local light_multiplier = time * 2 - if time > 0.5 then - light_multiplier = 2 * (1 - time) - else - light_multiplier = time / 0.5 - end - return light_multiplier -end - -mcl_weather.skycolor = { +local skycolor = { -- Should be activated before do any effect. active = true, @@ -85,319 +73,205 @@ mcl_weather.skycolor = { -- number of colors while constructing gradient of user given colors max_val = 1000, + NIGHT_VISION_RATIO = NIGHT_VISION_RATIO, + -- Table for tracking layer order layer_names = {}, - -- To layer to colors table - add_layer = function(layer_name, layer_color, instant_update) - mcl_weather.skycolor.colors[layer_name] = layer_color - table.insert(mcl_weather.skycolor.layer_names, layer_name) - mcl_weather.skycolor.force_update = true - end, - - current_layer_name = function() - return mcl_weather.skycolor.layer_names[#mcl_weather.skycolor.layer_names] - end, - - -- Retrieve layer from colors table - retrieve_layer = function() - local last_layer = mcl_weather.skycolor.current_layer_name() - return mcl_weather.skycolor.colors[last_layer] - end, - - -- Remove layer from colors table - remove_layer = function(layer_name) - for k, name in pairs(mcl_weather.skycolor.layer_names) do - if name == layer_name then - table.remove(mcl_weather.skycolor.layer_names, k) - mcl_weather.skycolor.force_update = true - return - end - end - end, - - -- Wrapper for updating day/night ratio that respects night vision - override_day_night_ratio = function(player, ratio) - local meta = player:get_meta() - local has_night_vision = meta:get_int("night_vision") == 1 - local has_darkness = meta:get_int("darkness") == 1 - local is_visited_shepherd = meta:get_int("mcl_shepherd:special") == 1 - local arg - if has_darkness and not is_visited_shepherd then - if has_night_vision then arg = 0.1 - else arg = 0 end - else - -- Apply night vision only for dark sky - local is_dark = minetest.get_timeofday() > 0.8 or minetest.get_timeofday() < 0.2 or mcl_weather.state ~= "none" - local pos = player:get_pos() - local dim = mcl_worlds.pos_to_dimension(pos) - if (has_night_vision or is_visited_shepherd) and is_dark and dim ~= "nether" and dim ~= "end" then - if ratio == nil then - arg = NIGHT_VISION_RATIO - else - arg = math.max(ratio, NIGHT_VISION_RATIO) - end - else - arg = ratio - end - end - player:override_day_night_ratio(arg) - end, - - -- Update sky color. If players not specified update sky for all players. - update_sky_color = function(players) - -- Override day/night ratio as well - players = mcl_weather.skycolor.utils.get_players(players) - for _, player in ipairs(players) do - local pos = player:get_pos() - local dim = mcl_worlds.pos_to_dimension(pos) - local has_weather = (mcl_worlds.has_weather(pos) and (mcl_weather.state == "snow" or mcl_weather.state =="rain" or mcl_weather.state == "thunder") and mcl_weather.has_snow(pos)) or ((mcl_weather.state =="rain" or mcl_weather.state == "thunder") and mcl_weather.has_rain(pos)) - local checkname = minetest.get_node(vector.new(pos.x,pos.y+1.5,pos.z)).name - if minetest.get_item_group(checkname, "water") ~= 0 then - local biome_index = minetest.get_biome_data(player:get_pos()).biome - local biome_name = minetest.get_biome_name(biome_index) - local biome = minetest.registered_biomes[biome_name] - if biome then water_color = biome._mcl_waterfogcolor end - if not biome then water_color = "#3F76E4" end - if checkname == "mclx_core:river_water_source" or checkname == "mclx_core:river_water_flowing" then water_color = "#0084FF" end - player:set_sky({ type = "regular", - sky_color = { - day_sky = water_color, - day_horizon = water_color, - dawn_sky = water_color, - dawn_horizon = water_color, - night_sky = water_color, - night_horizon = water_color, - indoors = water_color, - fog_sun_tint = water_color, - fog_moon_tint = water_color, - fog_tint_type = "custom" - }, - clouds = false, - }) - end - if dim == "overworld" then - local biomesky - local biomefog - if mg_name ~= "v6" and mg_name ~= "singlenode" then - local biome_index = minetest.get_biome_data(player:get_pos()).biome - local biome_name = minetest.get_biome_name(biome_index) - local biome = minetest.registered_biomes[biome_name] - if biome then - --minetest.log("action", string.format("Biome found for number: %s in biome: %s", tostring(biome_index), biome_name)) - biomesky = biome._mcl_skycolor - biomefog = biome._mcl_fogcolor - else - --minetest.log("action", string.format("No biome for number: %s in biome: %s", tostring(biome_index), biome_name)) - end - end - if (mcl_weather.state == "none") then - -- Clear weather - mcl_weather.set_sky_box_clear(player,biomesky,biomefog) - player:set_sun({visible = true, sunrise_visible = true}) - player:set_moon({visible = true}) - player:set_stars({visible = true}) - mcl_weather.skycolor.override_day_night_ratio(player, nil) - elseif not has_weather then - local day_color = mcl_weather.skycolor.get_sky_layer_color(0.15) - local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.27) - local night_color = mcl_weather.skycolor.get_sky_layer_color(0.1) - mcl_weather.set_sky_color(player, { - type = "regular", - sky_color = { - day_sky = day_color, - day_horizon = day_color, - dawn_sky = dawn_color, - dawn_horizon = dawn_color, - night_sky = night_color, - night_horizon = night_color, - }, - clouds = true, - }) - player:set_sun({visible = false, sunrise_visible = false}) - player:set_moon({visible = false}) - player:set_stars({visible = false}) - elseif has_weather then - -- Weather skies - local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5) - local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.75) - local night_color = mcl_weather.skycolor.get_sky_layer_color(0) - mcl_weather.set_sky_color(player, { - type = "regular", - sky_color = { - day_sky = day_color, - day_horizon = day_color, - dawn_sky = dawn_color, - dawn_horizon = dawn_color, - night_sky = night_color, - night_horizon = night_color, - }, - clouds = true, - }) - player:set_sun({visible = false, sunrise_visible = false}) - player:set_moon({visible = false}) - player:set_stars({visible = false}) - - local light_factor = mcl_weather.get_current_light_factor() - if mcl_weather.skycolor.current_layer_name() == "lightning" then - mcl_weather.skycolor.override_day_night_ratio(player, 1) - elseif light_factor then - local time = minetest.get_timeofday() - local light_multiplier = get_light_modifier(time) - local new_light = math.max(light_factor * light_multiplier, MINIMUM_LIGHT_LEVEL) - mcl_weather.skycolor.override_day_night_ratio(player, new_light) - else - mcl_weather.skycolor.override_day_night_ratio(player, nil) - end - end - elseif dim == "end" then - local biomesky = "#000000" - local biomefog = "#A080A0" - if mg_name ~= "v6" and mg_name ~= "singlenode" then - local biome_index = minetest.get_biome_data(player:get_pos()).biome - local biome_name = minetest.get_biome_name(biome_index) - local biome = minetest.registered_biomes[biome_name] - if biome then - --minetest.log("action", string.format("Biome found for number: %s in biome: %s", tostring(biome_index), biome_name)) - biomesky = biome._mcl_skycolor - biomefog = biome._mcl_fogcolor -- The End biomes seemingly don't use the fog colour, despite having this value according to the wiki. The sky colour is seemingly used for both sky and fog? - else - --minetest.log("action", string.format("No biome for number: %s in biome: %s", tostring(biome_index), biome_name)) - end - end - local t = "mcl_playerplus_end_sky.png" - player:set_sky({ type = "skybox", - base_color = biomesky, - textures = {t,t,t,t,t,t}, - clouds = false, - }) - player:set_sun({visible = false , sunrise_visible = false}) - player:set_moon({visible = false}) - player:set_stars({visible = false}) - mcl_weather.skycolor.override_day_night_ratio(player, 0.5) - elseif dim == "nether" then - local biomesky = "#6EB1FF" - local biomefog = "#330808" - if mg_name ~= "v6" and mg_name ~= "singlenode" then - local biome_index = minetest.get_biome_data(player:get_pos()).biome - local biome_name = minetest.get_biome_name(biome_index) - local biome = minetest.registered_biomes[biome_name] - if biome then - --minetest.log("action", string.format("Biome found for number: %s in biome: %s", tostring(biome_index), biome_name)) - biomesky = biome._mcl_skycolor -- The Nether biomes seemingly don't use the sky colour, despite having this value according to the wiki. The fog colour is used for both sky and fog. - biomefog = biome._mcl_fogcolor - else - --minetest.log("action", string.format("No biome for number: %s in biome: %s", tostring(biome_index), biome_name)) - end - end - mcl_weather.set_sky_color(player, { - type = "regular", - sky_color = { - day_sky = biomefog, - day_horizon = biomefog, - dawn_sky = biomefog, - dawn_horizon = biomefog, - night_sky = biomefog, - night_horizon = biomefog, - indoors = biomefog, - fog_sun_tint = biomefog, - fog_moon_tint = biomefog, - fog_tint_type = "custom" - }, - clouds = false, - }) - player:set_sun({visible = false , sunrise_visible = false}) - player:set_moon({visible = false}) - player:set_stars({visible = false}) - mcl_weather.skycolor.override_day_night_ratio(player, nil) - elseif dim == "void" then - player:set_sky({ type = "plain", - base_color = "#000000", - clouds = false, - }) - player:set_sun({visible = false, sunrise_visible = false}) - player:set_moon({visible = false}) - player:set_stars({visible = false}) - end - end - end, - - -- Returns current layer color in {r, g, b} format - get_sky_layer_color = function(timeofday) - if #mcl_weather.skycolor.layer_names == 0 then - return nil - end - - -- min timeofday value 0; max timeofday value 1. So sky color gradient range will be between 0 and 1 * mcl_weather.skycolor.max_val. - local rounded_time = math.floor(timeofday * mcl_weather.skycolor.max_val) - local color = mcl_weather.skycolor.utils.convert_to_rgb(mcl_weather.skycolor.min_val, mcl_weather.skycolor.max_val, rounded_time, mcl_weather.skycolor.retrieve_layer()) - return color - end, - - utils = { - convert_to_rgb = function(minval, maxval, current_val, colors) - local max_index = #colors - 1 - local val = (current_val-minval) / (maxval-minval) * max_index + 1.0 - local index1 = math.floor(val) - local index2 = math.min(math.floor(val)+1, max_index + 1) - local f = val - index1 - local c1 = colors[index1] - local c2 = colors[index2] - return {r=math.floor(c1.r + f*(c2.r - c1.r)), g=math.floor(c1.g + f*(c2.g-c1.g)), b=math.floor(c1.b + f*(c2.b - c1.b))} - end, - - -- Simply getter. Ether returns user given players list or get all connected players if none provided - get_players = function(players) - if players == nil or #players == 0 then - if mods_loaded then - players = minetest.get_connected_players() - elseif players == nil then - players = {} - end - end - return players - end, - - -- Returns first player sky color. I assume that all players are in same color layout. - get_current_bg_color = function() - local players = mcl_weather.skycolor.utils.get_players(nil) - if players[1] then - return players[1]:get_sky(true).sky_color - end - return nil - end - }, - + utils = {}, } +mcl_weather.skycolor = skycolor +local skycolor_utils = skycolor.utils + +-- Add layer to colors table +function skycolor.add_layer(layer_name, layer_color, instant_update) + skycolor.colors[layer_name] = layer_color + table.insert(skycolor.layer_names, layer_name) + skycolor.force_update = true +end + +function skycolor.current_layer_name() + return skycolor.layer_names[#skycolor.layer_names] +end + +-- Retrieve layer from colors table +function skycolor.retrieve_layer() + local last_layer = skycolor.current_layer_name() + return skycolor.colors[last_layer] +end + +-- Remove layer from colors table +function skycolor.remove_layer(layer_name) + for k, name in pairs(skycolor.layer_names) do + if name == layer_name then + table.remove(skycolor.layer_names, k) + skycolor.force_update = true + return + end + end +end + +-- Wrapper for updating day/night ratio that respects night vision +function skycolor.override_day_night_ratio(player, ratio) + player._skycolor_day_night_ratio = ratio + skycolor.update_player_sky_color(player) + player._skycolor_day_night_ratio = nil +end + +local skycolor_filters = {} +skycolor.filters = skycolor_filters +dofile(modpath.."/skycolor/water.lua") +dofile(modpath.."/skycolor/dimensions.lua") +dofile(modpath.."/skycolor/effects.lua") + +local function get_skycolor_info(player) + local player_name = player:get_player_name() + + local info = mcl_playerinfo[player_name] or {} + + local skycolor_data = info.skycolor + if not skycolor_data then + skycolor_data = {} + info.skycolor = skycolor_data + end + + return skycolor_data +end + +local water_sky = skycolor.water_sky +function skycolor.update_player_sky_color(player) + -- Don't update more than once every 250 milliseconds + local skycolor_data = get_skycolor_info(player) + local last_update = skycolor_data.last_update or 0 + local now_us = minetest.get_us_time() + if (now_us - last_update) < minimum_update_interval[1] then return end + skycolor_data.last_update = now_us + + local sky_data = { + day_night_ratio = player._skycolor_day_night_ratio + } + + for i = 1,#skycolor_filters do + skycolor_filters[i](player, sky_data) + end + + assert(sky_data.sky) + player:set_sky(sky_data.sky) + + if sky_data.sun then player:set_sun(sky_data.sun) end + if sky_data.moon then player:set_moon(sky_data.moon) end + if sky_data.stars then player:set_stars(sky_data.stars) end + player:override_day_night_ratio(sky_data.day_night_ratio) +end + +-- Update sky color. If players not specified update sky for all players. +function skycolor.update_sky_color(players) + -- Override day/night ratio as well + players = skycolor_utils.get_players(players) + local update = skycolor.update_player_sky_color + for _, player in ipairs(players) do + update(player) + end +end + +-- Returns current layer color in {r, g, b} format +function skycolor.get_sky_layer_color(timeofday) + if #skycolor.layer_names == 0 then + return nil + end + + -- min timeofday value 0; max timeofday value 1. So sky color gradient range will be between 0 and 1 * skycolor.max_val + local rounded_time = math.floor(timeofday * skycolor.max_val) + return skycolor_utils.convert_to_rgb( + skycolor.min_val, skycolor.max_val, + rounded_time, skycolor.retrieve_layer() + ) +end + +function skycolor_utils.convert_to_rgb(minval, maxval, current_val, colors) + -- Clamp current_val to valid range + current_val = math.min(minval, current_val) + current_val = math.max(maxval, current_val) + + -- Rescale current_val from a number between minval and maxval to a number between 1 and #colors + local scaled_value = (current_val - minval) / (maxval - minval) * (#colors - 1) + 1.0 + + -- Get the first color's values + local index1 = math.floor(scaled_value) + local color1 = colors[index1] + local frac1 = scaled_value - index1 + + -- Get the second color's values + local index2 = math.min(index1 + 1, #colors) -- clamp to maximum color index (will occur if index1 == #colors) + local frac2 = 1.0 - frac1 + local color2 = colors[index2] + + -- Interpolate between color1 and color2 + return { + r = math.floor(frac1 * color1.r + frac2 * color2.r), + g = math.floor(frac1 * color1.g + frac2 * color2.g), + b = math.floor(frac1 * color1.b + frac2 * color2.b), + } +end + +-- Simple getter. Either returns user given players list or get all connected players if none provided +function skycolor_utils.get_players(players) + if players == nil or #players == 0 then + if mods_loaded then + players = minetest.get_connected_players() + elseif players == nil then + players = {} + end + end + return players +end + +-- Returns the sky color of the first player, which is done assuming that all players are in same color layout. +function skycolor_utils.get_current_bg_color() + local players = skycolor_utils.get_players(nil) + if players[1] then + return players[1]:get_sky(true).sky_color + end + return nil +end local timer = 0 minetest.register_globalstep(function(dtime) - if mcl_weather.skycolor.active ~= true or #minetest.get_connected_players() == 0 then + if skycolor.active ~= true or #minetest.get_connected_players() == 0 then return end - if mcl_weather.skycolor.force_update then - mcl_weather.skycolor.update_sky_color() - mcl_weather.skycolor.force_update = false + if skycolor.force_update then + skycolor.update_sky_color() + skycolor.force_update = false return end -- regular updates based on iterval timer = timer + dtime; - if timer >= mcl_weather.skycolor.update_interval then - mcl_weather.skycolor.update_sky_color() + if timer >= skycolor.update_interval then + skycolor.update_sky_color() timer = 0 end - end) local function initsky(player) - if player.set_lighting then - player:set_lighting({ shadows = { intensity = tonumber(minetest.settings:get("mcl_default_shadow_intensity") or 0.33) } }) + player:set_lighting({ + shadows = { intensity = 0.33 }, + volumetric_light = { strength = 0.45 }, + exposure = { + luminance_min = -3.5, + luminance_max = -2.5, + exposure_correction = 0.35, + speed_dark_bright = 1500, + speed_bright_dark = 700, + }, + saturation = 1.1, + }) end - if (mcl_weather.skycolor.active) then + if (skycolor.active) then mcl_weather.skycolor.force_update = true end @@ -408,7 +282,7 @@ minetest.register_on_joinplayer(initsky) minetest.register_on_respawnplayer(initsky) mcl_worlds.register_on_dimension_change(function(player) - mcl_weather.skycolor.update_sky_color({player}) + skycolor.update_sky_color({player}) end) minetest.register_on_mods_loaded(function() diff --git a/mods/ENVIRONMENT/mcl_weather/skycolor/dimensions.lua b/mods/ENVIRONMENT/mcl_weather/skycolor/dimensions.lua new file mode 100644 index 000000000..fcf85a7e0 --- /dev/null +++ b/mods/ENVIRONMENT/mcl_weather/skycolor/dimensions.lua @@ -0,0 +1,184 @@ +local MINIMUM_LIGHT_LEVEL = 0.2 +local VALID_SNOW_WEATHER_STATES = { snow = true, rain = true, thunder = true } +local VALID_RAIN_WEATHER_STATES = { rain = true, thunder = true } +local mg_name = minetest.get_mapgen_setting("mg_name") + +local dimension_handlers = {} +mcl_weather.skycolor.dimension_handlers = dimension_handlers + +-- Function to work out light modifier at different times +-- Noon is brightest, midnight is darkest, 0600 and 1800 is in the middle of this +local function get_light_modifier(time) + -- 0.1 = 0.2 + -- 0.4 = 0.8 + -- 0.5 = 1 + -- 0.6 = 0.8 + -- 0.9 = 0.2 + + local light_multiplier = time * 2 + if time > 0.5 then + light_multiplier = 2 * (1 - time) + else + light_multiplier = time / 0.5 + end + return light_multiplier +end + +function dimension_handlers.overworld(player, sky_data) + local pos = player:get_pos() + + local biomesky + local biomefog + if mg_name ~= "v6" and mg_name ~= "singlenode" then + local biome_index = minetest.get_biome_data(player:get_pos()).biome + local biome_name = minetest.get_biome_name(biome_index) + local biome = minetest.registered_biomes[biome_name] + if biome then + biomesky = biome._mcl_skycolor + biomefog = biome._mcl_fogcolor + end + end + + -- Use overworld defaults + local day_color = mcl_weather.skycolor.get_sky_layer_color(0.15) + local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.27) + local night_color = mcl_weather.skycolor.get_sky_layer_color(0.1) + sky_data.sky = { + type = "regular", + sky_color = { + day_sky = day_color, + day_horizon = day_color, + dawn_sky = dawn_color, + dawn_horizon = dawn_color, + night_sky = night_color, + night_horizon = night_color, + }, + clouds = true, + } + sky_data.sun = {visible = true, sunrise_visible = true} + sky_data.moon = {visible = true} + sky_data.stars = {visible = true} + + if mcl_weather.state == "none" then + -- Clear weather + mcl_weather.set_sky_box_clear(player,biomesky,biomefog) + return + end + + -- Check if we currently have weather that affects the sky color + local has_weather = mcl_worlds.has_weather(pos) and ( + mcl_weather.has_snow(pos) and VALID_SNOW_WEATHER_STATES[mcl_weather.state] or + mcl_weather.has_rain(pos) and VALID_RAIN_WEATHER_STATES[mcl_weather.state] + ) + if has_weather then + -- Weather skies + local day_color = mcl_weather.skycolor.get_sky_layer_color(0.5) + local dawn_color = mcl_weather.skycolor.get_sky_layer_color(0.75) + local night_color = mcl_weather.skycolor.get_sky_layer_color(0) + sky_data.sky = { + type = "regular", + sky_color = { + day_sky = day_color, + day_horizon = day_color, + dawn_sky = dawn_color, + dawn_horizon = dawn_color, + night_sky = night_color, + night_horizon = night_color, + }, + clouds = true, + } + sky_data.sun = {visible = false, sunrise_visible = false} + sky_data.moon = {visible = false} + sky_data.stars = {visible = false} + + local light_factor = mcl_weather.get_current_light_factor() + if mcl_weather.skycolor.current_layer_name() == "lightning" then + sky_data.day_night_ratio = 1 + elseif light_factor then + local time = minetest.get_timeofday() + local light_multiplier = get_light_modifier(time) + local new_light = math.max(light_factor * light_multiplier, MINIMUM_LIGHT_LEVEL) + sky_data.day_night_ratio = new_light + end + end +end + +-- This can't be function dimension_handlers.end() due to lua syntax +dimension_handlers["end"] = function(player, sky_data) + local biomesky = "#000000" + local biomefog = "#A080A0" + if mg_name ~= "v6" and mg_name ~= "singlenode" then + local biome_index = minetest.get_biome_data(player:get_pos()).biome + local biome_name = minetest.get_biome_name(biome_index) + local biome = minetest.registered_biomes[biome_name] + if biome then + biomesky = biome._mcl_skycolor + biomefog = biome._mcl_fogcolor -- The End biomes seemingly don't use the fog colour, despite having this value according to the wiki. The sky colour is seemingly used for both sky and fog? + end + end + local t = "mcl_playerplus_end_sky.png" + sky_data.sky = { type = "skybox", + base_color = biomesky, + textures = {t,t,t,t,t,t}, + clouds = false, + } + sky_data.sun = {visible = false , sunrise_visible = false} + sky_data.moon = {visible = false} + sky_data.stars = {visible = false} + sky_data.day_night_ratio = 0.5 +end + +function dimension_handlers.nether(player, sky_data) + local biomesky = "#6EB1FF" + local biomefog = "#330808" + if mg_name ~= "v6" and mg_name ~= "singlenode" then + local biome_index = minetest.get_biome_data(player:get_pos()).biome + local biome_name = minetest.get_biome_name(biome_index) + local biome = minetest.registered_biomes[biome_name] + if biome then + -- The Nether biomes seemingly don't use the sky colour, despite having this value according to the wiki. + -- The fog colour is used for both sky and fog. + biomesky = biome._mcl_skycolor + biomefog = biome._mcl_fogcolor + end + end + sky_data.sky = { + type = "regular", + sky_color = { + day_sky = biomefog, + day_horizon = biomefog, + dawn_sky = biomefog, + dawn_horizon = biomefog, + night_sky = biomefog, + night_horizon = biomefog, + indoors = biomefog, + fog_sun_tint = biomefog, + fog_moon_tint = biomefog, + fog_tint_type = "custom" + }, + clouds = false, + } + sky_data.sun = {visible = false , sunrise_visible = false} + sky_data.moon = {visible = false} + sky_data.stars = {visible = false} +end + +function dimension_handlers.void(player, sky_data) + sky_data.sky = { type = "plain", + base_color = "#000000", + clouds = false, + } + sky_data.sun = {visible = false, sunrise_visible = false} + sky_data.moon = {visible = false} + sky_data.stars = {visible = false} +end + +local function dimension(player, sky_data) + local pos = player:get_pos() + local dim = mcl_worlds.pos_to_dimension(pos) + + local handler = dimension_handlers[dim] + if handler then return handler(player, sky_data) end +end +table.insert(mcl_weather.skycolor.filters, dimension) + diff --git a/mods/ENVIRONMENT/mcl_weather/skycolor/effects.lua b/mods/ENVIRONMENT/mcl_weather/skycolor/effects.lua new file mode 100644 index 000000000..ce90bb13a --- /dev/null +++ b/mods/ENVIRONMENT/mcl_weather/skycolor/effects.lua @@ -0,0 +1,56 @@ +local DIM_ALLOW_NIGHT_VISION = { + overworld = true, + void = true, +} + +local NIGHT_VISION_RATIO = mcl_weather.skycolor.NIGHT_VISION_RATIO +local effects_handlers = {} +local has_mcl_potions = mcl_util.to_bool(minetest.get_modpath("mcl_potions")) + +function effects_handlers.darkness(player, meta, effect, sky_data) + -- No darkness effect if is a visited shepherd + if meta:get_int("mcl_shepherd:special") == 1 then return end + + -- High stars + sky_data.stars = {visible = false} + + -- Minor visibility if the player has the night vision effect + if mcl_potions.has_effect(player, "night_vision") then + sky_data.day_night_ratio = 0.1 + else + sky_data.day_night_ratio = 0 + end +end + +function effects_handlers.night_vision(player, meta, effect, sky_data) + -- Apply night vision only for dark sky + if not (minetest.get_timeofday() > 0.8 or minetest.get_timeofday() < 0.2 or mcl_weather.state ~= "none") then return end + + -- Only some dimensions allow night vision + local pos = player:get_pos() + local dim = mcl_worlds.pos_to_dimension(pos) + if not DIM_ALLOW_NIGHT_VISION[dim] then return end + + -- Apply night vision + sky_data.day_night_ratio = math.max(sky_data.day_night_ratio or 0, NIGHT_VISION_RATIO) +end + +local function effects(player, sky_data) + if not has_mcl_potions then return end + + local meta = player:get_meta() + for name,effect in pairs(mcl_potions.registered_effects) do + local effect_data = mcl_potions.get_effect(player, name) + if effect_data then + local hook = effect.mcl_weather_skycolor or effects_handlers[name] + if hook then hook(player, meta, effect_data, sky_data) end + end + end + + -- Handle night vision for shepherd + if meta:get_int("mcl_shepherd:special") == 1 then + return effects_handlers.night_vision(player, meta, {}, sky_data) + end +end +table.insert(mcl_weather.skycolor.filters, effects) + diff --git a/mods/ENVIRONMENT/mcl_weather/skycolor/water.lua b/mods/ENVIRONMENT/mcl_weather/skycolor/water.lua new file mode 100644 index 000000000..ec14d8844 --- /dev/null +++ b/mods/ENVIRONMENT/mcl_weather/skycolor/water.lua @@ -0,0 +1,40 @@ + +local DEFAULT_WATER_COLOR = "#3F76E4" +local mg_name = minetest.get_mapgen_setting("mg_name") + +local function water_sky(player, sky_data) + local water_color = DEFAULT_WATER_COLOR + + local checkname = mcl_playerinfo[player:get_player_name()].node_head + if minetest.get_item_group(checkname, "water") == 0 then return end + + local pos = player:get_pos() + local biome = nil + if mg_name ~= "v6" and mg_name ~= "singlenode" then + local biome_index = minetest.get_biome_data(pos).biome + local biome_name = minetest.get_biome_name(biome_index) + biome = minetest.registered_biomes[biome_name] + end + if biome then water_color = biome._mcl_waterfogcolor end + if not biome then water_color = DEFAULT_WATER_COLOR end + + if checkname == "mclx_core:river_water_source" or checkname == "mclx_core:river_water_flowing" then water_color = "#0084FF" end + + sky_data.sky = { type = "regular", + sky_color = { + day_sky = water_color, + day_horizon = water_color, + dawn_sky = water_color, + dawn_horizon = water_color, + night_sky = water_color, + night_horizon = water_color, + indoors = water_color, + fog_sun_tint = water_color, + fog_moon_tint = water_color, + fog_tint_type = "custom" + }, + clouds = true, + } +end +table.insert(mcl_weather.skycolor.filters, water_sky) + diff --git a/mods/ITEMS/mcl_mobitems/init.lua b/mods/ITEMS/mcl_mobitems/init.lua index 4d92c2d43..a3155b13c 100644 --- a/mods/ITEMS/mcl_mobitems/init.lua +++ b/mods/ITEMS/mcl_mobitems/init.lua @@ -155,6 +155,7 @@ local function drink_milk_delayed(itemstack, player, pointed_thing) mcl_hunger.stop_poison(player) end mcl_potions._reset_effects(player) + mcl_weather.skycolor.update_player_sky_color(player) end -- Wrapper for handling mcl_hunger delayed eating diff --git a/mods/ITEMS/mcl_potions/functions.lua b/mods/ITEMS/mcl_potions/functions.lua index b59749d2a..b3aa232ba 100644 --- a/mods/ITEMS/mcl_potions/functions.lua +++ b/mods/ITEMS/mcl_potions/functions.lua @@ -492,7 +492,7 @@ mcl_potions.register_effect({ end, on_step = function(dtime, object, factor, duration) if object:get_meta():get_int("night_vision") ~= 1 then - local flash = EF.darkness[object].flash + local flash = EF.darkness[object].flash or 0 if flash < 0.2 then EF.darkness[object].flashdir = true elseif flash > 0.6 then EF.darkness[object].flashdir = false end flash = EF.darkness[object].flashdir and (flash + dtime) or (flash - dtime) diff --git a/mods/PLAYER/mcl_playerplus/init.lua b/mods/PLAYER/mcl_playerplus/init.lua index ad1143c9d..709d1d0fb 100644 --- a/mods/PLAYER/mcl_playerplus/init.lua +++ b/mods/PLAYER/mcl_playerplus/init.lua @@ -407,13 +407,23 @@ minetest.register_globalstep(function(dtime) set_bone_pos(player,"Body_Control", nil, vector.new(0, -player_vel_yaw + yaw, 0)) end - local underwater - if get_item_group(mcl_playerinfo[name].node_head, "water") ~= 0 and underwater ~= true then - mcl_weather.skycolor.update_sky_color() - local underwater = true - elseif get_item_group(mcl_playerinfo[name].node_head, "water") == 0 and underwater == true then - mcl_weather.skycolor.update_sky_color() - local underwater = false + local playerinfo = mcl_playerinfo[name] or {} + local plusinfo = playerinfo.mcl_playerplus + if not plusinfo then + plusinfo = {} + playerinfo.mcl_playerplus = plusinfo + end + + -- Only process if node_head changed + if plusinfo.old_node_head ~= playerinfo.node_head then + local node_head = playerinfo.node_head or "" + local old_node_head = plusinfo.old_node_head or "" + plusinfo.old_node_head = playerinfo.node_head + + -- Update skycolor if moving in or out of water + if (get_item_group(node_head, "water") == 0) ~= (get_item_group(old_node_head, "water") == 0) then + mcl_weather.skycolor.update_sky_color() + end end elytra.last_yaw = player:get_look_horizontal()