385 lines
11 KiB
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
|
|
}) |