--
-- Weather mod
-- By Kaadmy, for Pixture
--

local S = minetest.get_translator("weather")

weather = {}
weather.weather = "clear"
weather.types = {"storm", "snowstorm", "clear"}

local function addvec(v1, v2)
   return {x = v1.x + v2.x, y = v1.y + v2.y, z = v1.z + v2.z}
end

local snow_enable = minetest.settings:get_bool("weather_snow_enable") or false

local weather_soundspec=nil
local mapseed = minetest.get_mapgen_setting("seed")
local weather_pr=PseudoRandom(mapseed + 2387)

local sound_min_height = -20 -- Below -20m you can't hear weather

local default_cloud_state = nil

local function play_sound()
   if weather_soundspec ~= nil then
      minetest.sound_stop(weather_soundspec)
   end

   if weather.weather == "storm" then
      for _, player in ipairs(minetest.get_connected_players()) do
         if player:get_pos().y > sound_min_height then
            weather_soundspec = minetest.sound_play(
               {
               name = "weather_storm",
               to_player = player:get_player_name()
               }
            )
         end
      end

      minetest.after(18, play_sound)
      return
   elseif weather.weather == "snowstorm" then
      for _, player in ipairs(minetest.get_connected_players()) do
         if player:get_pos().y > sound_min_height then
            weather_soundspec = minetest.sound_play(
               {
                  name = "weather_snowstorm",
                  to_player = player:get_player_name()
               }
            )
         end
      end

      minetest.after(7, play_sound)
      return
   end

   minetest.after(3, play_sound)
end

local function setweather_type(type)
   local valid = false
   for i = 1, #weather.types do
      if weather.types[i] == type then
	 valid = true
      end
   end
   if valid then
      weather.weather = type
      play_sound()
      return true
   else
      return false
   end
end

minetest.register_globalstep(
   function(dtime)
      if minetest.settings:get_bool("weather_enable") then
	 if weather_pr:next(0, 5000) < 1 then
	    local weathertype = weather_pr:next(0, 19)

	    -- on avg., every 1800 frames the weather.weather will change to one of:
	    -- 13/20 chance of clear weather
	    -- 6/20 chance or stormy weather
	    -- 1/20 chance of snowstorm weather

	    if weathertype < 13 then
	       weather.weather = "clear"
	    elseif weathertype < 19 then
	       weather.weather = "storm"
	    elseif weathertype < 20 and snow_enable then
	       weather.weather = "snowstorm"
	    end
	 end
      end

      local light = (minetest.get_timeofday() * 2)

      if light > 1 then
	 light = 1 - (light - 1)
      end

      light = (light * 0.5) + 0.15

      local skycol = math.floor(light * 190)

      for _, player in ipairs(minetest.get_connected_players()) do
	 if weather.weather == "storm" or weather.weather == "snowstorm" then
	    player:set_sky({r = skycol, g = skycol, b = skycol * 1.2}, "plain", {}, true)

            if default_cloud_state == nil then
               default_cloud_state = player:get_clouds()
            end

            player:set_clouds({
                  density = 0.5,
                  color = "#a0a0a0f0",
                  ambient = "#000000",
                  height = 100,
                  thickness = 40,
                  speed = {x = -2, y = 1},
            })

	    player:override_day_night_ratio(light)
	 else
	    player:set_sky(nil, "regular", {}, true)

            if default_cloud_state ~= nil then
               player:set_clouds(default_cloud_state)
            end

	    player:override_day_night_ratio(nil)
	 end

	 local p=player:get_pos()

	 if weather.weather == "storm" then
	    if minetest.get_node_light({x=p.x, y=p.y+15, z=p.z}, 0.5) == 15 then
	       local minpos = addvec(player:get_pos(), {x = -15, y = 15, z = -15})
	       local maxpos = addvec(player:get_pos(), {x = 15, y = 10, z = 15})
	       minetest.add_particlespawner(
		  {
		     amount = 30,
		     time = 0.5,
		     minpos = minpos,
		     maxpos = maxpos,
		     minvel = {x = 0, y = -20, z = 0},
		     maxvel = {x = 0, y = -20, z = 0},
		     minexptime = 0.9,
		     maxexptime = 1.1,
		     minsize = 2,
		     maxsize = 3,
		     collisiondetection = true,
		     collision_removal = true,
		     vertical = true,
		     texture = "weather_rain.png",
		     playername = player:get_player_name()
		  }
	       )
	    end
	 elseif weather.weather == "snowstorm" then
	    if math.random(0, 6000*dtime) <= 1 then
	       local hp = player:get_hp()

	       if minetest.get_node_light(p) == 15 then
		  player:set_hp(hp-1)
	       end
	    end

	    if minetest.get_node_light({x=p.x, y=p.y+15, z=p.z}, 0.5) == 15 then
	       local minpos = addvec(player:get_pos(), {x = -30, y = 20, z = -30})
	       local maxpos = addvec(player:get_pos(), {x = 30, y = 15, z = 30})
	       local vel = {x = 16.0, y = -8, z = 13.0}
	       local acc = {x = -16.0, y = -8, z = -13.0}
	       minetest.add_particlespawner(
		  {
		     amount = 8,
		     time = 0.4,
		     minpos = minpos,
		     maxpos = maxpos,
		     minvel = {x=-vel.x, y=vel.y, z=-vel.z},
		     maxvel = vel,
		     minacc = acc,
		     maxacc = acc,
		     minexptime = 1.0,
		     maxexptime = 1.4,
		     minsize = 3,
		     maxsize = 4,
		     collisiondetection = true,
		     collision_removal = true,
		     vertical = false,
		     texture = "weather_snowflake.png",
		     playername = player:get_player_name()
		  }
	       )
	    end
	 end
      end
   end
)

minetest.register_privilege(
   "weather",
   {
      description = S("Can change the weather using the /weather command"),
      give_to_singleplayer = false
})

minetest.register_chatcommand(
   "weather",
   {
      params = "storm | snowstorm | clear",
      description = S("Change the weather"),
      privs = {weather = true},
      func = function(name, param)
         local weather_set = setweather_type(param)
         if not weather_set then
             return false, S("Incorrect weather. Valid weathers are “storm”, “snowstorm” and “clear”.")
         else
             return true, S("Weather changed.")
         end
      end
})

setweather_type("clear")

default.log("mod:weather", "loaded")
