2018-09-12 22:15:04 +01:00

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)