591 lines
16 KiB
Lua
591 lines
16 KiB
Lua
-- atmos, control of skies, weather, and various other visual tasks
|
|
|
|
atmos = {}
|
|
|
|
minetest.register_chatcommand("weather", {
|
|
|
|
description = "debugs the current atmos based weather system",
|
|
param = "use a number to select the weather type.",
|
|
func = function(name, param)
|
|
|
|
if not minetest.check_player_privs(name, "server") then
|
|
return false, "You are not allowed to control the weather, you scrub. \n \n This incident WILL be reported."
|
|
end
|
|
|
|
atmos.current_weather = 0 + param -- somehow it performs tonumber() on it's own?
|
|
|
|
return true, "Current weather updated."
|
|
end,
|
|
|
|
})
|
|
|
|
--
|
|
|
|
--atmos.weather_type = 1
|
|
atmos.current_weather = 3
|
|
|
|
--[[
|
|
|
|
atmos.fog_colour = {}
|
|
atmos.weather_sky_type = {}
|
|
atmos.weather_light_level = {}
|
|
|
|
atmos.weather_clouds = {}
|
|
atmos.weather_cloud_colour = {}
|
|
atmos.weather_cloud_thicc = {}
|
|
atmos.weather_cloud_height = {}
|
|
|
|
|
|
|
|
atmos.weather_light_level[1] = nil -- default skies
|
|
atmos.weather_light_level[5] = 0.8 -- cloudy
|
|
atmos.weather_light_level[8] = 0.175 -- rain, night
|
|
|
|
-- cloud settings
|
|
|
|
atmos.weather_clouds[1] = 0.275 -- light
|
|
atmos.weather_clouds[2] = 0.4 -- default
|
|
atmos.weather_clouds[3] = 0.5 -- large
|
|
atmos.weather_clouds[4] = 0.65 -- rain, snow, thunder
|
|
|
|
atmos.weather_cloud_colour[1] = "#f0f0f0e5" -- default
|
|
atmos.weather_cloud_colour[2] = "#9b9b9bff" -- cloudy
|
|
atmos.weather_cloud_colour[3] = "#797979ff" -- cloudy, night
|
|
atmos.weather_cloud_colour[4] = "#93a2b3ff" -- rain
|
|
atmos.weather_cloud_colour[5] = "#6b6b6bff" -- rain, night
|
|
|
|
atmos.weather_cloud_thicc[1] = 8 -- thin
|
|
atmos.weather_cloud_thicc[2] = 16 -- default
|
|
atmos.weather_cloud_thicc[3] = 32 -- large
|
|
atmos.weather_cloud_thicc[4] = 96/2 -- rain
|
|
|
|
atmos.weather_cloud_height[1] = 120 -- default
|
|
atmos.weather_cloud_height[2] = 110 -- rain, snow, thunder
|
|
|
|
]]--
|
|
|
|
-- load data into atmos2 from .atm configuration files:
|
|
|
|
local atmos_clear_weather = {}
|
|
local atmos_cloudy_weather = {}
|
|
|
|
local storage = minetest.get_modpath("atmos").."/skybox/"
|
|
local val = 0
|
|
|
|
for line in io.lines(storage.."skybox_clear_gradient.atm") do
|
|
|
|
atmos_clear_weather[val] = minetest.deserialize(line)
|
|
|
|
val = val + 1
|
|
|
|
end
|
|
|
|
val = 0
|
|
|
|
-- load data for cloudy / rainy / hail environments:
|
|
|
|
for line in io.lines(storage.."skybox_cloud_gradient.atm") do
|
|
|
|
|
|
|
|
end
|
|
|
|
local function atmos_ratio(current, next, ctime2)
|
|
|
|
if current < next then -- check if we're darker than the next skybox frame
|
|
|
|
local ratio = (next - current) * ctime2
|
|
return (current + ratio)
|
|
|
|
else -- we darken instead, this repeats for the next two if, else statements
|
|
|
|
local ratio = (current - next) * ctime2
|
|
return (current - ratio)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
function atmos.set_skybox_clear(player, weather_fade)
|
|
|
|
local ctime = minetest.get_timeofday() * 100
|
|
|
|
-- figure out our multiplier since get_timeofday returns 0->1, we can use the sig figs as a 0-100 percentage multiplier
|
|
|
|
-- contributed by @rubenwardy
|
|
|
|
local ctime2 = math.floor((ctime - math.floor(ctime)) * 100) / 100
|
|
|
|
if ctime2 == 0 then ctime2 = 0.01 end -- anti sudden skybox change syndrome
|
|
|
|
local fade_factor = math.floor(255 * ctime2)
|
|
|
|
ctime = math.floor(ctime) -- remove the sig figs, since we're accessing table points
|
|
|
|
-- assemble the skyboxes to fade neatly
|
|
|
|
local side_string_clear = "(atmos_sky.png^[multiply:".. atmos_clear_weather[ctime].bottom .. ")^" .. "(atmos_sky_top.png^[multiply:" .. atmos_clear_weather[ctime].top .. ")"
|
|
local side_string_new_clear = "(atmos_sky.png^[multiply:".. atmos_clear_weather[ctime+1].bottom .. ")^" .. "(atmos_sky_top.png^[multiply:" .. atmos_clear_weather[ctime+1].top .. ")"
|
|
|
|
local sky_top_clear = "(atmos_sky.png^[multiply:".. atmos_clear_weather[ctime].bottom .. ")^(atmos_sky_top_radial.png^[multiply:".. atmos_clear_weather[ctime].top .. ")"
|
|
local sky_top_new_clear = "(atmos_sky.png^[multiply:".. atmos_clear_weather[ctime+1].bottom .. ")^(atmos_sky_top_radial.png^[multiply:".. atmos_clear_weather[ctime+1].top .. ")"
|
|
|
|
local sky_bottom_clear = "(atmos_sky.png^[multiply:".. atmos_clear_weather[ctime].bottom .. ")"
|
|
local sky_bottom_new_clear = "(atmos_sky.png^[multiply:".. atmos_clear_weather[ctime+1].bottom .. ")"
|
|
|
|
-- let's convert the base colour to convert it into our transitioning fog colour:
|
|
|
|
local fog = {}
|
|
|
|
fog.current = {} -- we need two tables for comparing, as any matching pairs of hex will be skipped.
|
|
fog.next = {}
|
|
fog.result = {}
|
|
|
|
fog.current.red = 0
|
|
fog.current.grn = 0
|
|
fog.current.blu = 0
|
|
|
|
fog.next.red = 0
|
|
fog.next.grn = 0
|
|
fog.next.blu = 0
|
|
|
|
fog.result.red = 0
|
|
fog.result.grn = 0
|
|
fog.result.blu = 0
|
|
|
|
-- convert our hex into compatible minetest.rgba components:
|
|
-- we need these to make our lives easier when it comes to uh, things.
|
|
|
|
fog.current.red = tonumber("0x" .. atmos_clear_weather[ctime].base:sub(2,3))
|
|
fog.current.grn = tonumber("0x" .. atmos_clear_weather[ctime].base:sub(4,5))
|
|
fog.current.blu = tonumber("0x" .. atmos_clear_weather[ctime].base:sub(6,7))
|
|
|
|
fog.next.red = tonumber("0x" .. atmos_clear_weather[ctime+1].base:sub(2,3))
|
|
fog.next.grn = tonumber("0x" .. atmos_clear_weather[ctime+1].base:sub(4,5))
|
|
fog.next.blu = tonumber("0x" .. atmos_clear_weather[ctime+1].base:sub(6,7))
|
|
|
|
if atmos_clear_weather[ctime].base ~= atmos_clear_weather[ctime+1].base then
|
|
|
|
-- we compare colours the same way we do it for the light level
|
|
|
|
fog.result.red = atmos_ratio(fog.current.red, fog.next.red, ctime2)
|
|
fog.result.grn = atmos_ratio(fog.current.grn, fog.next.grn, ctime2)
|
|
fog.result.blu = atmos_ratio(fog.current.blu, fog.next.blu, ctime2)
|
|
|
|
else
|
|
|
|
fog.result.red = fog.current.red
|
|
fog.result.grn = fog.current.grn
|
|
fog.result.blu = fog.current.blu
|
|
|
|
end
|
|
|
|
if atmos_clear_weather[ctime].bottom == atmos_clear_weather[ctime+1].bottom then -- prevent more leakage
|
|
if atmos_clear_weather[ctime].top == atmos_clear_weather[ctime+1].top then
|
|
fade_factor = 0
|
|
end
|
|
end
|
|
|
|
player:set_sky(minetest.rgba(fog.result.red, fog.result.grn, fog.result.blu), "skybox", {
|
|
|
|
sky_top_clear .. "^(" .. sky_top_new_clear .. "^[opacity:" .. fade_factor .. ")",
|
|
sky_bottom_clear .. "^(" .. sky_bottom_new_clear .. "^[opacity:" .. fade_factor .. ")",
|
|
|
|
side_string_clear .. "^(" .. side_string_new_clear .. "^[opacity:" .. fade_factor .. ")",
|
|
side_string_clear .. "^(" .. side_string_new_clear .. "^[opacity:" .. fade_factor .. ")",
|
|
side_string_clear .. "^(" .. side_string_new_clear .. "^[opacity:" .. fade_factor .. ")",
|
|
side_string_clear .. "^(" .. side_string_new_clear .. "^[opacity:" .. fade_factor .. ")"
|
|
|
|
}, true)
|
|
|
|
local light_ratio = 0
|
|
local light_level = 0
|
|
|
|
if atmos_clear_weather[ctime].light == atmos_clear_weather[ctime+1].light then -- we do nothing, because there's nothing worth doing
|
|
|
|
light_level = atmos_clear_weather[ctime].light
|
|
|
|
else -- we do the light to dark fade
|
|
|
|
light_level = atmos_ratio(atmos_clear_weather[ctime].light, atmos_clear_weather[ctime+1].light, ctime2)
|
|
|
|
end
|
|
|
|
if light_level > 1 then light_level = 1 end -- sanity checks, going over 1 makes it dark again
|
|
if light_level < 0 then light_level = 0 end -- going under 0 makes it bright again
|
|
|
|
player:override_day_night_ratio(light_level)
|
|
|
|
end
|
|
|
|
local atmos_crossfade = 0
|
|
local atmos_start_fade = false
|
|
|
|
function atmos.sync_skybox()
|
|
|
|
|
|
|
|
-- sync skyboxes to all players connected to the server.
|
|
|
|
for _, player in ipairs(minetest.get_connected_players()) do
|
|
|
|
if atmos_start_fade then
|
|
atmos_crossfade = atmos_crossfade + 1
|
|
end
|
|
|
|
-- do not sync the current weather to players under -32 depth.
|
|
|
|
if player:get_pos().y <= -32 then
|
|
|
|
player:set_sky("#000000", "plain", false)
|
|
|
|
player:override_day_night_ratio(0)
|
|
|
|
elseif player:get_pos().y > 10000 then
|
|
|
|
-- change to low orbit skybox here
|
|
|
|
else
|
|
|
|
-- sync weather to players that are above -32, lightning effects (and flashes) only affects players above -16
|
|
|
|
--atmos.set_skybox(player)
|
|
|
|
-- move clouds here to enable realtime cloud changes
|
|
|
|
|
|
player:set_clouds({
|
|
|
|
density = 0.4,
|
|
color = "#fff0f0e5",
|
|
thickness = 16,
|
|
height = 210,
|
|
|
|
})
|
|
|
|
end
|
|
|
|
end
|
|
|
|
if atmos_crossfade == 20 then
|
|
atmos_crossfade = 0
|
|
atmos_start_fade = false
|
|
end
|
|
|
|
minetest.after(0.1, atmos.sync_skybox)
|
|
end
|
|
|
|
-- table of weathers; even numbers above 5 are night time version (but not this table)
|
|
|
|
-- 1 = cloudless (uses default dynamic skybox)
|
|
-- 2 = some clouds (uses default dynamic skybox)
|
|
-- 3 = default (uses default dynamic skybox)
|
|
-- 4 = somewhat cloudy, not overcast enough (uses default dynamic skybox)
|
|
-- 5 = cloudy, overcast?
|
|
-- 6 = raining (and snow in colder areas)
|
|
-- 7 = thunderstorm
|
|
-- 8 = snowing in all biomes exc. the desert
|
|
-- 9 = hailstorm
|
|
|
|
|
|
|
|
function atmos.weatherchange()
|
|
|
|
local rand = math.random(0, 1)
|
|
|
|
if rand == 0 then rand = -1 end
|
|
|
|
local cw = atmos.current_weather
|
|
|
|
if atmos.current_weather == 6 or atmos.current_weather == 7 or atmos.current_weather == 8 or atmos.current_weather == 9 and math.random(1,5) < 5 then
|
|
|
|
atmos.current_weather = 5
|
|
|
|
elseif atmos.current_weather + rand == 6 and atmos.current_weather == 5 then
|
|
|
|
if hudclock.month == 1 or hudclock.month == 11 or hudclock.month == 12 then --is it winter months?
|
|
|
|
if math.random(1,8) == 1 then --hail
|
|
|
|
atmos.current_weather = 9
|
|
|
|
elseif math.random(1,3) == 1 then --snow in cool and cold areas
|
|
|
|
atmos.current_weather = 8
|
|
|
|
end
|
|
|
|
elseif hudclock.month == 5 or hudclock.month == 6 or hudclock.month == 7 then --is it summer?
|
|
|
|
if math.random(1,7) == 1 then -- thunder + rain
|
|
|
|
atmos.current_weather = 7
|
|
|
|
elseif math.random(1,3) == 1 then -- rain
|
|
|
|
atmos.current_weather = 6
|
|
|
|
end
|
|
|
|
else -- other seasons just have rain.
|
|
|
|
if math.random(1,3) == 1 then -- we rain, else if it's not the winter months, or summer thunder
|
|
|
|
atmos.current_weather = 6
|
|
|
|
end
|
|
|
|
end
|
|
|
|
elseif atmos.current_weather < 5 then
|
|
|
|
atmos.current_weather = atmos.current_weather + rand
|
|
|
|
if atmos.current_weather == 0 then
|
|
|
|
atmos.current_weather = 1
|
|
|
|
end
|
|
|
|
end
|
|
|
|
minetest.after(60+math.random(1,59)*math.random(5,15), atmos.weatherchange)
|
|
end
|
|
|
|
function atmos.thunderstrike()
|
|
|
|
if atmos.get_weather_skybox() == 9 or atmos.get_weather_skybox() == 10 then
|
|
|
|
--lightning.weather_type = atmos.weather_type
|
|
|
|
-- doing the above doesn't seem to work, so i'll ignore it for now.
|
|
|
|
lightning.strike()
|
|
|
|
end
|
|
|
|
minetest.after(math.random(43, 156), atmos.thunderstrike)
|
|
|
|
end
|
|
|
|
minetest.after(1, atmos.sync_skybox)
|
|
--minetest.after(60+math.random(1,59)*math.random(5,15), atmos.weatherchange)
|
|
--minetest.after(math.random(43, 156), atmos.thunderstrike)
|
|
|
|
lightning.light_level = atmos.weather_light_level
|
|
|
|
-- abm to remove fires when it's raining, snowing or hailing?
|
|
|
|
-- logic to support taking damage when either too cold or too hot
|
|
|
|
hb.register_hudbar("overheat",
|
|
0xFFFFFF,
|
|
"Overheat",
|
|
{bar = "atmos_heatstroke_bar.png", icon = "atmos_heatstroke_icon.png", bgicon = "atmos_heatstroke_icon.png"},
|
|
0,
|
|
100,
|
|
false
|
|
)
|
|
|
|
hb.register_hudbar("frostbite",
|
|
0xFFFFFF,
|
|
"Frostbite",
|
|
{bar = "atmos_frostbite_bar.png", icon = "atmos_frostbite_icon.png", bgicon = "atmos_frostbite_icon.png"},
|
|
0,
|
|
100,
|
|
false
|
|
)
|
|
|
|
minetest.register_on_joinplayer(function(player)
|
|
|
|
hb.init_hudbar(player, "overheat")
|
|
hb.init_hudbar(player, "frostbite")
|
|
|
|
local meta = player:get_meta()
|
|
|
|
if meta:get_int("overheat") == "" then
|
|
|
|
meta:set_int("overheat", 0)
|
|
|
|
end
|
|
|
|
if meta:get_int("frostbite") == "" then
|
|
|
|
meta:set_int("frostbite", 0)
|
|
|
|
end
|
|
|
|
local frosty = meta:get_int("frostbite")
|
|
local toasty = meta:get_int("overheat")
|
|
|
|
hb.change_hudbar(player, "overheat", toasty)
|
|
hb.change_hudbar(player, "frostbite", frosty)
|
|
|
|
end)
|
|
|
|
|
|
local function local_area_stamina()
|
|
|
|
for _, player in ipairs(minetest.get_connected_players()) do
|
|
|
|
if player:is_player() == false then --uh skip? in case of we dont have players active, eg starting a server
|
|
|
|
else
|
|
|
|
|
|
local heat, humid, latch = mcore.get_heat_humidity(player)
|
|
|
|
-- if the local temp is more than -15 C then decrement frostbite every now and then, if the heatstroke bar is not at 100,
|
|
-- then start replenishing it
|
|
-- if the local temp is less than +35 C then decrement heatstroke every now and then, if the frostbite bar is not at 100,
|
|
-- then start replenishing it
|
|
-- if not under or over those values, slowly restore the bar to 0.
|
|
-- environmental timer is 15 seconds
|
|
|
|
local meta = player:get_meta()
|
|
|
|
local frosty = meta:get_int("frostbite") -- nice combo into uppercut, just wait for the kahn.
|
|
local toasty = meta:get_int("overheat")
|
|
|
|
if heat < -15 then -- do frostbite bar
|
|
|
|
if toasty > 0 then
|
|
|
|
meta:set_int("overheat", toasty - 1)
|
|
|
|
else
|
|
|
|
meta:set_int("frostbite", frosty + 1)
|
|
|
|
end
|
|
|
|
elseif heat > 35 then -- do the overheat bar
|
|
|
|
if frosty > 0 then
|
|
|
|
meta:set_int("frostbite", frosty - 1)
|
|
|
|
else
|
|
|
|
meta:set_int("overheat", toasty + 1)
|
|
|
|
end
|
|
|
|
else -- otherwise, let's cool off and remove frostbite
|
|
|
|
frosty = meta:get_int("frostbite")
|
|
toasty = meta:get_int("overheat")
|
|
|
|
frosty = frosty - 1
|
|
toasty = toasty - 1
|
|
|
|
if toasty > 100 then toasty = 100 end
|
|
if toasty < 0 then toasty = 0 end
|
|
|
|
if frosty > 100 then frosty = 100 end
|
|
if frosty < 0 then frosty = 0 end
|
|
|
|
meta:set_int("overheat", toasty)
|
|
meta:set_int("frostbite", frosty)
|
|
|
|
end
|
|
|
|
frosty = meta:get_int("frostbite")
|
|
toasty = meta:get_int("overheat")
|
|
|
|
hb.change_hudbar(player, "overheat", toasty) -- deal with our hudbars
|
|
hb.change_hudbar(player, "frostbite", frosty)
|
|
|
|
if frosty > 94 then -- we do damage in this order because while 94 is higher than 79, the 15hp would never activate.
|
|
|
|
player:set_hp(player:get_hp() - 15, "atmos_frostbite")
|
|
|
|
elseif frosty > 89 then
|
|
|
|
player:set_hp(player:get_hp() - 5, "atmos_frostbite")
|
|
|
|
elseif frosty > 79 then
|
|
|
|
player:set_hp(player:get_hp() - 2, "atmos_frostbite") -- do 1 hearts worth of damage
|
|
|
|
end
|
|
|
|
if toasty > 94 then -- read the above comment on L634
|
|
|
|
player:set_hp(player:get_hp() - 15, "atmos_overheat")
|
|
|
|
elseif toasty > 89 then
|
|
|
|
player:set_hp(player:get_hp() - 5, "atmos_overheat")
|
|
|
|
elseif toasty > 79 then
|
|
|
|
player:set_hp(player:get_hp() - 2, "atmos_overheat")
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
minetest.after(math.random(5, 15), local_area_stamina) -- restart the loop at a random time to simulate reality. (not really)
|
|
|
|
end
|
|
|
|
local_area_stamina()
|
|
|
|
minetest.register_chatcommand("frosty", { -- admin commands to debug the values for testing
|
|
|
|
description = "debugs the current frostbite level",
|
|
param = "use 0-100 to set frostbite level.",
|
|
func = function(name, param)
|
|
|
|
if not minetest.check_player_privs(name, "server") then
|
|
return false, "You are not allowed to be more or less frosty, you scrub. \n \n This incident WILL be reported."
|
|
end
|
|
|
|
local player = minetest.get_player_by_name(name)
|
|
|
|
player:get_meta():set_int("frostbite", tonumber(param))
|
|
|
|
hb.change_hudbar(player, "frostbite", tonumber(param))
|
|
|
|
return true, "Current frostbite levels updated."
|
|
end,
|
|
|
|
})
|
|
|
|
minetest.register_chatcommand("toasty", {
|
|
|
|
description = "debugs the current overheat level",
|
|
param = "use 0-100 to set overheat level.",
|
|
func = function(name, param)
|
|
|
|
if not minetest.check_player_privs(name, "server") then
|
|
return false, "You are not allowed to be more or less toasty, you scrub. \n \n This incident WILL be reported."
|
|
end
|
|
|
|
local player = minetest.get_player_by_name(name)
|
|
|
|
player:get_meta():set_int("overheat", tonumber(param))
|
|
|
|
hb.change_hudbar(player, "overheat", tonumber(param))
|
|
|
|
return true, "Current overheat levels updated."
|
|
end,
|
|
|
|
})
|
|
|
|
-- handle dying so that values are set to 1/4 of what they were when the player dies
|
|
|
|
minetest.register_on_dieplayer(function(player)
|
|
|
|
local meta = player:get_meta()
|
|
|
|
local frosty = meta:get_int("frostbite")
|
|
local toasty = meta:get_int("overheat")
|
|
|
|
meta:set_int("overheat", math.floor(toasty / 4))
|
|
meta:set_int("frostbite", math.floor(frosty / 4))
|
|
|
|
end) |