Address review comments

This commit is contained in:
teknomunk 2024-06-12 20:07:32 -05:00
parent f2a638f8e9
commit 820848fb2e
5 changed files with 323 additions and 291 deletions

@ -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
optional_depends = lightning

@ -1,16 +1,16 @@
-- Constants
local modname = minetest.get_current_modname()
local modpath = minetest.get_modpath(modname)
local mod = mcl_weather
local NIGHT_VISION_RATIO = 0.45
local MINIMUM_LIGHT_LEVEL = 0.2
local DEFAULT_WATER_COLOR = "#3F76E4"
-- Module state
local mods_loaded = false
local water_color = DEFAULT_WATER_COLOR
local mg_name = minetest.get_mapgen_setting("mg_name")
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
if minetest.get_item_group( mcl_playerinfo[player:get_player_name()].node_head, "water") ~= 0 then return end
local sc = {
day_sky = "#7BA4FF",
day_horizon = "#C0D8FF",
@ -48,24 +48,6 @@ 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
local skycolor = {
-- Should be activated before do any effect.
active = true,
@ -94,7 +76,7 @@ local skycolor = {
mcl_weather.skycolor = skycolor
local skycolor_utils = skycolor.utils
-- To layer to colors table
-- Add layer to colors table
function skycolor.add_layer(layer_name, layer_color, instant_update)
mcl_weather.skycolor.colors[layer_name] = layer_color
table.insert(mcl_weather.skycolor.layer_names, layer_name)
@ -129,269 +111,21 @@ function skycolor.override_day_night_ratio(player, ratio)
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")
function water_sky(player, sky_data)
local pos = player:get_pos()
local water_color = DEFAULT_WATER_COLOR
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 return end
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
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 = false,
}
end
local dimension_handlers = {}
function dimension_handlers.overworld(player, sky_data)
local pos = player:get_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 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)
sky_data.sun = {visible = true, sunrise_visible = true}
sky_data.moon = {visible = true}
sky_data.stars = {visible = true}
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)
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}
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)
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
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
--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"
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
--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
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
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
local effects_handlers = {}
function effects_handlers.darkness(player, meta, effect, sky_data)
-- No darkness effect if visited by 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
local DIM_ALLOW_NIGHT_VISION = {
overworld = true,
void = true,
}
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 has_mcl_potions = false
local function effects(player, sky_data)
if not has_mcl_potions then
if not minetest.get_modpath("mcl_potions") then return end
has_mcl_potions = true
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 shepheard
if meta:get_int("mcl_shepherd:special") == 1 then
return effects_handlers.night_vision(player, meta, {}, sky_data)
end
end
local water_sky = skycolor.water_sky
function skycolor.update_player_sky_color(player)
local sky_data = {
day_night_ratio = player._skycolor_day_night_ratio
}
water_sky(player, sky_data)
dimension(player, sky_data)
effects(player, sky_data)
for i = 1,#skycolor_filters do
skycolor_filters[i](player, sky_data)
end
if sky_data.sky then player:set_sky(sky_data.sky) end
if sky_data.sun then player:set_sun(sky_data.sun) end
@ -423,17 +157,32 @@ function skycolor.get_sky_layer_color(timeofday)
end
function skycolor_utils.convert_to_rgb(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))}
-- 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 - fraction1
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
-- Simply getter. Ether returns user given players list or get all connected players if none provided
-- 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
@ -445,7 +194,7 @@ function skycolor_utils.get_players(players)
return players
end
-- Returns first player sky color. I assume that all players are in same color layout.
-- 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 = mcl_weather.skycolor.utils.get_players(nil)
if players[1] then

@ -0,0 +1,190 @@
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 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 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
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
--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)
sky_data.sun = {visible = true, sunrise_visible = true}
sky_data.moon = {visible = true}
sky_data.stars = {visible = true}
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
return
end
-- No weather that affects the sky color, use default values
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 = false, sunrise_visible = false}
sky_data.moon = {visible = false}
sky_data.stars = {visible = false}
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)

@ -0,0 +1,57 @@
local DIM_ALLOW_NIGHT_VISION = {
overworld = true,
void = true,
}
local effects_handlers = {}
local has_mcl_potions = false
function effects_handlers.darkness(player, meta, effect, sky_data)
-- No darkness effect if visited by 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
if not minetest.get_modpath("mcl_potions") then return end
has_mcl_potions = true
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)

@ -0,0 +1,36 @@
local DEFAULT_WATER_COLOR = "#3F76E4"
local function water_sky(player, sky_data)
local pos = player:get_pos()
local water_color = DEFAULT_WATER_COLOR
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 return end
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 = 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 = false,
}
end
table.insert(mcl_weather.skycolor.filters, water_sky)