Mineclonia/mods/ENVIRONMENT/mcl_weather/init.lua

385 lines
11 KiB
Lua

local S = minetest.get_translator("mcl_weather")
mcl_weather = {}
mcl_weather.allow_abm = true
mcl_weather.current = "none"
mcl_weather.state = "none"
mcl_weather.current_endtime = 0
mcl_weather.next = nil
mcl_weather.registered_weathers = {}
mcl_weather.min_duration = 600
mcl_weather.max_duration = 9000
local players = {}
players.particlespawners={}
players.soundhandler={}
players.weatheractive = {}
players.weather = {}
local interval=1
local weathercycle_active=false
function mcl_weather.register_weather(name,def)
mcl_weather.registered_weathers[name] = def
end
mcl_weather.register_weather("none",{
min_duration = mcl_weather.min_duration,
max_duration = mcl_weather.max_duration,
transitions = {
[50] = "rain",
[100] = "snow"
}
})
local modpath=minetest.get_modpath(minetest.get_current_modname())
dofile(modpath.."/skycolor.lua")
dofile(modpath.."/snow.lua")
dofile(modpath.."/rain.lua")
dofile(modpath.."/nether_dust.lua")
dofile(modpath.."/thunder.lua")
function mcl_weather.get_weatherdef(name)
if mcl_weather.registered_weathers[name] then
return mcl_weather.registered_weathers[name]
end
return mcl_weather.registered_weathers["none"]
end
function mcl_weather.get_weather()
return mcl_weather.current
end
function mcl_weather.get_current_light_factor()
local lf = mcl_weather.get_weatherdef(mcl_weather.current).light_factor
if mcl_weather.current == "none" or not lf then
return nil
else
return lf
end
end
-- Returns true if pos is outdoor.
-- Outdoor is defined as any node in the Overworld under open sky.
-- FIXME: Nodes below glass also count as “outdoor”, this should not be the case.
function mcl_weather.is_outdoor(pos)
local cpos = {x=pos.x, y=pos.y+1, z=pos.z}
local dim = mcl_worlds.pos_to_dimension(cpos)
if minetest.get_node_light(cpos, 0.5) == 15 and dim == "overworld" then
return true
end
return false
end
-- checks if player is undewater. This is needed in order to
-- turn off weather particles generation.
function mcl_weather.is_underwater(player)
local ppos=player:get_pos()
local offset = player:get_eye_offset()
local player_eye_pos = {x = ppos.x + offset.x,
y = ppos.y + offset.y + 1.5,
z = ppos.z + offset.z}
local node_level = minetest.get_node_level(player_eye_pos)
if node_level == 8 or node_level == 7 then
return true
end
return false
end
function mcl_weather.player_has_weather(player)
local pos=player:get_pos()
if mcl_weather.is_outdoor(pos) and not mcl_weather.is_underwater(player) then
return true
end
return false
end
function mcl_weather.add_sound(name,sound)
players.soundhandler[name] = minetest.sound_play(sound, {
to_player = name,
loop = true,
})
end
function mcl_weather.remove_sound(name)
if players.soundhandler[name] then
minetest.sound_fade(players.soundhandler[name], -0.5, 0.0)
end
end
function mcl_weather.add_particlespawners_player(name,spawners)
if not spawners then return end
local player = minetest.get_player_by_name(name)
if not player then return end
if players.particlespawners[name] ~= nil then return end
players.particlespawners[name]={}
for k,v in ipairs(spawners) do
v.playername=name
v.attached=player
table.insert(players.particlespawners[name],minetest.add_particlespawner(v))
end
end
function mcl_weather.delete_particlespawners_player(name)
if players.particlespawners[name] == nil then
players.particlespawners[name]={}
end
for k,v in ipairs(players.particlespawners[name]) do
minetest.delete_particlespawner(v)
end
players.particlespawners[name]=nil
end
local function doplayers(func,players)
players = players or minetest.get_connected_players()
for k,v in ipairs(players) do
func(v:get_player_name(),v)
end
end
function mcl_weather.get_next_weather()
local current=mcl_weather.get_weatherdef(mcl_weather.current)
local random_roll = math.random(0,100)
local new_weather
if current and current.transitions then
for v, weather in pairs(current.transitions) do
if random_roll < v then
new_weather=weather
break
end
end
end
if not new_weather then new_weather = "none" end
return new_weather
end
function mcl_weather.start_weather_player(name,def)
local player=minetest.get_player_by_name(name)
mcl_weather.add_particlespawners_player(name,def.particlespawners)
if def.skycolor then
local player=minetest.get_player_by_name(name)
player:set_clouds({color=def.skycolor})
end
if def.sound then
mcl_weather.add_sound(name,def.sound)
end
players.weather[name]=def
players.weatheractive[name] = true
end
function mcl_weather.stop_weather_player(name,def)
mcl_weather.delete_particlespawners_player(name)
if def.skycolor then
local player=minetest.get_player_by_name(name)
player:set_clouds({color="#FFF0F0E5"})
end
mcl_weather.remove_sound(name,def.sound)
end
function mcl_weather.start_weather(def)
if def.start then def.start() end
if def.skylayer then
mcl_weather.skycolor.add_layer("weather",def.skylayer)
mcl_weather.skycolor.update_sky_color()
end
doplayers(function(name)
if def.start_player == nil or not def.start_player(name) then
mcl_weather.start_weather_player(name,def)
players.weather[name]=mcl_weather.current
end
end)
end
function mcl_weather.stop_weather(def)
if def.clear then def.clear() end
if def.clear_player then doplayers(def.clear_player) end
doplayers(function(name)
mcl_weather.stop_weather_player(name,def)
end)
if def.skylayer then
mcl_weather.skycolor.remove_layer("weather")
mcl_weather.skycolor.force_update=true
mcl_weather.skycolor.update_sky_color()
end
end
function mcl_weather.change(new_weather,force)
if new_weather == mcl_weather.current then return end
local def=mcl_weather.get_weatherdef(new_weather)
local old=mcl_weather.get_weatherdef(mcl_weather.current)
if not def then return end
if force or minetest.settings:get_bool("mcl_doWeatherCycle") then
mcl_weather.stop_weather(old)
mcl_weather.start_weather(def)
mcl_weather.current = new_weather
mcl_weather.state = new_weather
end
end
function mcl_weather.seasonal_change()
local new=mcl_weather.get_next_weather()
local def=mcl_weather.get_weatherdef(new)
local duration = math.random(def.min_duration,def.max_duration)
mcl_weather.current_endtime = os.time() + duration
minetest.log("action", "[mcl_weather] Weather changed from " .. mcl_weather.current .. " to " .. new .. " Duration: "..duration)
mcl_weather.change(new)
minetest.after(duration,mcl_weather.seasonal_change)
end
minetest.register_on_mods_loaded(function()
minetest.after(5,mcl_weather.seasonal_change)
end)
function mcl_weather.change_player(name,new)
if players.weather[name] ~= new then
local nd = mcl_weather.get_weatherdef(new)
local od=mcl_weather.get_weatherdef(mcl_weather.current)
if players.weather[name] ~= nil then
minetest.log("action", "[mcl_weather] Weather for ".. name .." changed from "..players.weather[name].. " to "..new)
od=mcl_weather.get_weatherdef(players.weather[name])
else
minetest.log("action", "[mcl_weather] Weather for ".. name .." changed from "..mcl_weather.current.. " to "..new)
end
if nd then
mcl_weather.stop_weather_player(name,od)
mcl_weather.start_weather_player(name,nd)
players.weather[name] = new
end
end
end
function mcl_weather.tick()
doplayers(function(name,player)
local pos=player:get_pos()
local cdef=mcl_weather.get_weatherdef(mcl_weather.current)
if type(cdef.change_at_pos) == "function" then --switch to returned weather in at_pos conditions
local cap=cdef.change_at_pos(pos)
if cap then
mcl_weather.change_player(name,cap)
else
mcl_weather.change_player(name,mcl_weather.current)
end
end
if players.weatheractive[name] then --turn off weather indoows/underwater
if not mcl_weather.player_has_weather(player) then
mcl_weather.stop_weather_player(name,cdef)
players.weatheractive[name] = false
end
if type(cdef.at_pos) == "function" and cdef.at_pos(pos) then
mcl_weather.stop_weather_player(name,cdef)
players.weatheractive[name] = false
end
else
if mcl_weather.player_has_weather(player) then
mcl_weather.start_weather_player(name,cdef)
players.weather[name]=mcl_weather.current
players.weatheractive[name] = true
end
if type(cdef.at_pos) == "function" and not cdef.at_pos(pos) then
mcl_weather.start_weather_player(name,cdef)
players.weatheractive[name] = false
end
end
end)
minetest.after(interval,mcl_weather.tick)
end
mcl_weather.tick()
mcl_worlds.register_on_dimension_change(function(player, dimension)
if mcl_worlds.has_weather(player:get_pos()) then
mcl_weather.start_weather_player(player:get_player_name(),mcl_weather.get_weatherdef(mcl_weather.current))
else
mcl_weather.stop_weather_player(player:get_player_name(),mcl_weather.get_weatherdef(mcl_weather.current))
end
end)
minetest.register_on_joinplayer(function(player)
mcl_weather.start_weather_player(player:get_player_name(),mcl_weather.get_weatherdef(mcl_weather.current))
end)
minetest.register_on_leaveplayer(function(player)
cla_weather.stop_weather_player(player:get_player_name())
end)
minetest.register_privilege("weather_manager", {
description = S("Gives ability to control weather"),
give_to_singleplayer = false
})
-- Weather command definition. Set
minetest.register_chatcommand("weather", {
params = "(clear | rain | snow | thunder) [<duration>]",
description = S("Changes the weather to the specified parameter."),
privs = {weather_manager = true},
func = function(name, param)
if (param == "") then
return false, S("Error: No weather specified.")
end
local player=minetest.get_player_by_name(name)
local new_weather, duration
local parse1, parse2 = string.match(param, "(%w+) ?(%d*)")
if parse1 then
if parse1 == "clear" then
new_weather = "none"
else
new_weather = parse1
end
else
return false, S("Error: Invalid parameters.")
end
if parse2 then
if type(tonumber(parse2)) == "number" then
duration = tonumber(parse2)
if duration < 1 then
return false, S("Error: Duration can't be less than 1 second.")
end
end
end
local def=mcl_weather.get_weatherdef(param)
local old_weather=mcl_weather.current
if def then
if duration then
def.min_duration=duration
def.max_duration=duration
end
mcl_weather.change(param,true)
minetest.log("action", "[mcl_weather] " .. name .. " changed the weather from " .. old_weather .. " to " .. param)
return true
end
return false, S("Error: Invalid weather specified. Use “clear”, “rain”, “snow” or “thunder”.")
end
})
minetest.register_chatcommand("biome", {
params = "",
description = S("Gets current biome"),
privs = {weather_manager = true},
func = function(name)
local player=minetest.get_player_by_name(name)
local biome=minetest.get_biome_data(player:get_pos())
local bname = minetest.get_biome_name(biome.biome)
minetest.chat_send_player(name,bname.. " " ..dump(biome))
end
})
minetest.register_chatcommand("toggledownfall", {
params = "",
description = S("Toggles between clear weather and weather with downfall (randomly rain, thunderstorm or snow)"),
privs = {weather_manager = true},
func = function(name, param)
-- Currently rain/thunder/snow: Set weather to clear
if mcl_weather.state ~= "none" then
mcl_weather.change("none",true)
-- Currently clear: Set weather randomly to rain/thunder/snow
else
local new = { "rain", "thunder", "snow" }
local r = math.random(1, #new)
return mcl_weather.change(new[r],true)
end
end
})