[POC] Weather

User avatar
jordan4ibanez
Member
 
Posts: 1895
Joined: Tue Sep 27, 2011 18:44
Location: Rhode Island, USA
GitHub: jordan4ibanez
IRC: jordan4ibanez
In-game: jordan4ibanez

[POC] Weather

by jordan4ibanez » Tue Feb 09, 2016 02:11

This is a proof of concept, physical weather system. Currently only does snow in singleplayer, I wouldn't use this on a server.

Snow falls using the player's position, the overcast is an entity following the player, the snow is simulated by dropping invisible entities and creating snow when they stop, or adding onto existing

Rain does the same thing, making puddles instead.

Snow storms are just crazy.

Current Video

Old Video

Old Video

Current Screenshot
Image

Current Screenshot
Image

Current Screenshot
Image

Old Screenshot
Image


Download

License: WTFPL

Code

init.lua
Code: Select all
--License: WTFPL
--[[
force load map blocks around players and drop force loaded entites to simulate weather?
possible wind direction for particles?
]]--
local modpath = minetest.get_modpath("weather")

weather               = {}
weather.clouds        = {}
weather.sounds        = {}
--weather variables
weather.snow          = false
weather.snow_velocity = 0.2
weather.rain          = false
weather.rain_velocity = 5
weather.timer         = 0

--testing
------------------------------------
weather.update_timer  = 0.1
weather.snow_updates  = 3
weather.weatherdensity = 10 --100
-------------------------------------



weather.cloudsize     = 500

weather.weatherdistance = 15
weather.cloud_height  = 10
weather.snow_layers   = 8

dofile(modpath.."/snow.lua")
dofile(modpath.."/rain.lua")

--update the cloud position around the player
function weather.update_player_weather()
   for _,player in ipairs(minetest.get_connected_players()) do
      local name = player:get_player_name()
      local pos  = player:getpos()   
      pos.y = pos.y + weather.cloud_height
      --add snow
      --add particles if cloud
      if weather.clouds[name] ~= nil then         
         if weather.weather == "snow" or weather.weather == "snowstorm" then
            weather.clouds[name]:moveto(pos)
            --add in snow entities
            if weather.weather == "snow" then
               for i = 1,weather.snow_updates do
                  minetest.add_entity({x=math.floor(pos.x + 0.5)+math.random(weather.weatherdistance * -1,weather.weatherdistance),y=pos.y,z=math.floor(pos.z + 0.5)+math.random(weather.weatherdistance * -1,weather.weatherdistance)}, "weather:snow_entity")
               end
               minetest.add_particlespawner({
                 amount = weather.weatherdensity,
                 time = weather.update_timer,
                 minpos = {x=pos.x-weather.weatherdistance, y=pos.y-0.5, z=pos.z-weather.weatherdistance},
                 maxpos = {x=pos.x+weather.weatherdistance, y=pos.y-0.5, z=pos.z+weather.weatherdistance},
                 minvel = {x=0, y=-2, z=0},
                 maxvel = {x=0, y=-3, z=0},
                 minacc = {x=-1, y=0, z=-1},
                 maxacc = {x=1,  y=0, z=1},
                 minexptime = 3,
                 maxexptime = 5,
                 minsize = 1,
                 maxsize = 1,
                 collisiondetection = true,
                 vertical = true,
                 texture = "snowflake.png",
               })
            elseif weather.weather == "snowstorm" then
               for i = 1,weather.snow_updates*3 do
                  minetest.add_entity({x=math.floor(pos.x + 0.5)+math.random(weather.weatherdistance * -1,weather.weatherdistance),y=pos.y,z=math.floor(pos.z + 0.5)+math.random(weather.weatherdistance * -1,weather.weatherdistance)}, "weather:snow_entity")
               end
               minetest.add_particlespawner({
                 amount = weather.weatherdensity*2,
                 time = weather.update_timer,
                 minpos = {x=pos.x-weather.weatherdistance, y=pos.y-0.5, z=pos.z-weather.weatherdistance},
                 maxpos = {x=pos.x+weather.weatherdistance, y=pos.y-0.5, z=pos.z+weather.weatherdistance},
                 minvel = {x=-3, y=-12, z=-3},
                 maxvel = {x=3, y=-15, z=3},
                 minacc = {x=-2, y=0, z=-2},
                 maxacc = {x=2,  y=0, z=2},
                 minexptime = 1,
                 maxexptime = 1,
                 minsize = 3,
                 maxsize = 4,
                 collisiondetection = true,
                 vertical = true,
                 texture = "snowflake.png",
               })
            end

         elseif weather.weather == "rain" then
            weather.clouds[name]:moveto(pos)
            --add in rain entities
            for i = 1,weather.snow_updates do
               minetest.add_entity({x=math.floor(pos.x + 0.5)+math.random(weather.weatherdistance * -1,weather.weatherdistance),y=pos.y,z=math.floor(pos.z + 0.5)+math.random(weather.weatherdistance * -1,weather.weatherdistance)}, "weather:rain_entity")
            end         
            minetest.add_particlespawner({
              amount = weather.weatherdensity,
              time = weather.update_timer,
              minpos = {x=pos.x-weather.weatherdistance, y=pos.y-0.5, z=pos.z-weather.weatherdistance},
              maxpos = {x=pos.x+weather.weatherdistance, y=pos.y-0.5, z=pos.z+weather.weatherdistance},
              minvel = {x=0, y=-10, z=0},
              maxvel = {x=0, y=-12, z=0},
              minacc = {x=0, y=0, z=0},
              maxacc = {x=0, y=0, z=0},
              minexptime = 1,
              maxexptime = 2,
              minsize = 1,
              maxsize = 1,
              collisiondetection = true,
              vertical = true,
              texture = "raindrop.png",
            })
         else
            if weather.clouds[name] then
               weather.clouds[name]:remove()
            end
         end

      end
   end
end
--update the weather on weather step
minetest.register_globalstep(function(dtime)
   if weather.weather ~= nil then
      weather.timer = weather.timer + dtime
      if weather.update_timer <= weather.timer then
         weather.update_player_weather()--
         weather.timer = 0
      end
   end
end)
--this controls the particles of the clouds
function cloud_particles(pos,object)
   if object:get_luaentity().clouding == nil then
      object:get_luaentity().clouding = true
         minetest.add_particlespawner({
           amount = weather.weatherdensity,
           time = 2,
           minpos = {x=pos.x-weather.weatherdistance, y=pos.y-0.5, z=pos.z-weather.weatherdistance},
           maxpos = {x=pos.x+weather.weatherdistance, y=pos.y-0.5, z=pos.z+weather.weatherdistance},
           minvel = {x=0, y=-1, z=0},
           maxvel = {x=0, y=-2, z=0},
           minacc = {x=0, y=-1, z=0},
           maxacc = {x=0, y=-2, z=0},
           minexptime = 5,
           maxexptime = 10,
           minsize = 1,
           maxsize = 1,
           collisiondetection = true,
           vertical = true,
           texture = "snowflake.png",
         })
   end
end


--cloud definition
local cloud = {
   collide_with_objects = false,
   physical = false,
   collisionbox = {0,0,0,0,0,0},
   visual_size = {x=weather.cloudsize, y=1},
   visual = "cube",
   textures = {"stormcloud.png","stormcloud.png","stormcloud.png","stormcloud.png","stormcloud.png","stormcloud.png"},
}
function cloud.on_activate(self, staticdata, dtime_s)
   self.object:set_armor_groups({immortal = 1})
   minetest.after(0, function()
      if self.player == nil then
         self.object:remove()
      end
   end)
end
function cloud.on_step(self, dtime)
   cloud_particles(self.object:getpos(),self.object)
end
minetest.register_entity("weather:cloud", cloud)



--add clouds - figure out how to make clouds merge or be at a certain height or something
function weather.add_clouds(name)
   local player = minetest.get_player_by_name(name)
   local pos = player:getpos()
   pos.y = pos.y + weather.cloud_height
   local currentcloud = minetest.add_entity(pos, "weather:cloud")
   weather.clouds[name] = currentcloud
   currentcloud:get_luaentity().player = name
end

minetest.register_chatcommand("weather", {
   params = "<weather>",
   description = "Send text to chat",
   --privs = {talk = true},
   func = function( name , param)
      --snow
      if param == "snow" then
         if weather.sounds[name] ~= nil then
            minetest.sound_stop(weather.sounds[name])
         end
         if weather.clouds[name] ~= nil then
            weather.clouds[name]:remove()
            weather.clouds[name] = nil
         end
         weather.add_clouds(name)
         weather.weather = "snow"
         minetest.get_player_by_name(name):set_physics_override({
            speed = 0.6 -- set gravity to 10% of its original value
                       -- (0.1 * 9.81)
         })
      --snow storm
      elseif param == "snowstorm" then
         if weather.sounds[name] ~= nil then
            minetest.sound_stop(weather.sounds[name])
         end
         if weather.clouds[name] ~= nil then
            weather.clouds[name]:remove()
            weather.clouds[name] = nil
         end
         weather.add_clouds(name)
         weather.weather = "snowstorm"
         minetest.get_player_by_name(name):set_physics_override({
            speed = 0.3 -- set gravity to 10% of its original value
                       -- (0.1 * 9.81)
         })
         local sound = minetest.sound_play("stormwind", {
            to_player = name,
            gain = 0.5,
            loop = true,
         })
         weather.sounds[name] = sound
      --rain
      elseif param == "rain" then
         if weather.sounds[name] ~= nil then
            minetest.sound_stop(weather.sounds[name])
         end
         if weather.clouds[name] ~= nil then
            weather.clouds[name]:remove()
            weather.clouds[name] = nil
         end
         weather.add_clouds(name)
         weather.weather = "rain"
         local sound = minetest.sound_play("rain", {
            to_player = name,
            gain = 0.1,
            loop = true,
         })
         weather.sounds[name] = sound
      elseif param == "stop" then
         if weather.sounds[name] ~= nil then
            minetest.sound_stop(weather.sounds[name])
         end
         if weather.clouds[name] ~= nil then
            weather.clouds[name]:remove()
            weather.clouds[name] = nil
         end
         weather.weather = nil
         minetest.get_player_by_name(name):set_physics_override({
            speed = 1 -- set gravity to 10% of its original value
                       -- (0.1 * 9.81)
         })
      else
         minetest.chat_send_player(name, param.." is not a defined atmospheric condition.")
      end
      --return true, "Text was sent successfully"
   end,
})


snow.lua

Code: Select all
--snow entity definition
local snow_entity = {
   collide_with_objects = false,
   physical = true,
   collisionbox = {0,0,0,0,0,0}, --debug {-0.5,-0.1,-0.5,0.5,0.1,0.5}
   
   --visual_size = {x=1, y=0.2},
   --visual = "cube",
   --textures = {"stormcloud.png","stormcloud.png","stormcloud.png","stormcloud.png","stormcloud.png","stormcloud.png"},
   is_visible = false,
}
function snow_entity.on_activate(self, staticdata, dtime_s)
   self.object:set_armor_groups({immortal = 1})
   self.object:setvelocity({x=0,y=-16,z=0})
end
function snow_entity.on_step(self)
   local vel = self.object:getvelocity()
   local pos = self.object:getpos()
   if vel.y == 0 then
      if minetest.get_node(pos).name == "air" or weather.weather == "snowstorm" then
         add_snow(pos)
      end
      self.object:remove()
   end
end
minetest.register_entity("weather:snow_entity", snow_entity)

--create snow on ground
function add_snow(pos)
   local node  = minetest.get_node(pos).name
   local layer = minetest.get_item_group(node, "snow")
   if layer == 0 then
      minetest.set_node(pos, {name="weather:snow_1"})
   elseif layer < weather.snow_layers then
      minetest.set_node(pos, {name="weather:snow_"..layer+1})
   end
end

--generate multiple layers of snow
for i = 1, weather.snow_layers do
   local buildto
   if i < weather.snow_layers then
      buildto = true
   else
      buildto = false
   end
   minetest.register_node("weather:snow_"..i, {
      -------
      description = "Snow",
      tiles = {"default_snow.png"},
      groups = {snow=i,crumbly = 3},
      paramtype = "light",
      buildable_to = buildto,
      --walkable = false,
      --climbable = true,
      drop = "weather:snowball "..i, --however many layers is however many snowballs
      --sounds = default.node_sound_dirt_defaults({
      --   footstep = {name = "default_snow_footstep", gain = 0.25},
      --   dug = {name = "default_snow_footstep", gain = 0.75},
      --}),
      ------
      drawtype = "nodebox",
      paramtype = "light",
      node_box = {
         type = "fixed",
         fixed = {
            {-0.5, -0.5, -0.5, 0.5, -0.5+((1/weather.snow_layers)*i), 0.5},
         },
      }
   })
end

minetest.register_craftitem("weather:snowball", {
   description = "Snowball",
   inventory_image = "default_snowball.png",
   on_place = function(itemstack, placer, pointed_thing)
      --gravity based snow addition
      local layer = minetest.get_item_group(minetest.get_node(pointed_thing.under).name , "snow")
      local abovelayer = minetest.get_item_group(minetest.get_node(pointed_thing.above).name , "snow")
      if layer > 0 and layer < weather.snow_layers then
         minetest.set_node(pointed_thing.under, {name="weather:snow_"..layer+1})
         itemstack:take_item()
      elseif abovelayer > 0 and abovelayer < weather.snow_layers then
         minetest.set_node(pointed_thing.above, {name="weather:snow_"..abovelayer+1})
         itemstack:take_item()
      else
         local nodeunder = minetest.get_node({x=pointed_thing.above.x,y= pointed_thing.above.y-1,z= pointed_thing.above.z}).name

         if minetest.registered_nodes[nodeunder].walkable == true then
            if minetest.get_item_group(nodeunder , "snow") == weather.snow_layers or minetest.get_item_group(nodeunder , "snow") == 0 then
               local before = minetest.get_node(pointed_thing.above).name
               minetest.place_node(pointed_thing.above, {name="weather:snow_1"})         
               local after = minetest.get_node(pointed_thing.above).name
               
               if before ~= after then
                  itemstack:take_item()
               end
            end
         end
         
      end
      return itemstack
   end,
   --throw the snowball
   on_drop = function(itemstack, dropper, pos)
      local v = dropper:get_look_dir()
      local p = {x=pos.x, y=pos.y+1.2, z=pos.z}
      local obj = minetest.add_entity(p, "weather:snowball_entity")
      
      if obj then
         v.x = v.x*10
         v.y = v.y*6 + 2
         v.z = v.z*10
         obj:setvelocity(v)
      end

      itemstack:take_item()
      return itemstack
   end,
})
--snowball entity definition
local snowball_entity = {
   collide_with_objects = false,
   physical = true,
   collisionbox = {-0.1,-0.1,-0.1,0.1,0.1,0.1}, --debug {-0.5,-0.1,-0.5,0.5,0.1,0.5}
   
   visual_size = {x=0.2, y=0.2},
   visual = "cube",
   textures = {"stormcloud.png","stormcloud.png","stormcloud.png","stormcloud.png","stormcloud.png","stormcloud.png"},
   is_visible = true,
}
function snowball_entity.on_activate(self, staticdata, dtime_s)
   self.object:set_armor_groups({immortal = 1})
   self.object:setacceleration({x=0,y=-10,z=0})
end
function snowball_entity.on_step(self)
   local vel = self.object:getvelocity()
   local pos = self.object:getpos()
   if vel.y == 0 then
      add_snow(pos)   
      self.object:remove()
      minetest.add_particlespawner({
        amount = 10,
        time = 0.1,
        minpos = {x=math.floor(pos.x + 0.5)-0.5, y=pos.y+0.1, z=math.floor(pos.z + 0.5)-0.5},
        maxpos = {x=math.floor(pos.x + 0.5)+0.5, y=pos.y+0.1, z=math.floor(pos.z + 0.5)+0.5},
        minvel = {x=-0.1, y=0.1, z=-0.1},
        maxvel = {x=0.1, y=0.1, z=0.1},
        minacc = {x=0, y=0, z=0},
        maxacc = {x=0, y=0, z=0},
        minexptime = 1,
        maxexptime = 1,
        minsize = 1,
        maxsize = 1,
        collisiondetection = false,
        vertical = false,
        texture = "snowflake.png",
      })
    minetest.sound_play("snowfluff", {
      pos = pos,
      gain = 0.4,
      })
   end
end
minetest.register_entity("weather:snowball_entity", snowball_entity)


rain.lua
Code: Select all
--rain entity definition
local rain_entity = {
   collide_with_objects = false,
   physical = true,
   collisionbox = {0,0,0,0,0,0}, --debug {-0.5,-0.5,-0.5,0.5,0.5,0.5}
   
   --visual_size = {x=1, y=1},
  -- visual = "cube",
   --textures = {"default_water.png","default_water.png","default_water.png","default_water.png","default_water.png","default_water.png"},
   is_visible = false,
}
function rain_entity.on_activate(self, staticdata, dtime_s)
   self.object:set_armor_groups({immortal = 1})
   self.object:setvelocity({x=0,y=-16,z=0})
end
function rain_entity.on_step(self)
   local vel = self.object:getvelocity()
   local pos = self.object:getpos()
   if vel.y == 0 then
      add_rain(pos)
      self.object:remove()
   end
end
minetest.register_entity("weather:rain_entity", rain_entity)

--create water on ground
function add_rain(pos)
   minetest.set_node(pos, {name="default:water_flowing", param2=1})
end
Last edited by jordan4ibanez on Wed Feb 10, 2016 23:45, edited 3 times in total.
I've been gone for a long time
 

proller
Member
 
Posts: 186
Joined: Sat Jan 26, 2013 15:22
 

User avatar
jordan4ibanez
Member
 
Posts: 1895
Joined: Tue Sep 27, 2011 18:44
Location: Rhode Island, USA
GitHub: jordan4ibanez
IRC: jordan4ibanez
In-game: jordan4ibanez

Re: [POC] Weather

by jordan4ibanez » Wed Feb 10, 2016 23:42


That's pretty cool. This mod has a different method of doing it though.
I've been gone for a long time
 

User avatar
mahmutelmas06
Member
 
Posts: 367
Joined: Mon Mar 02, 2015 13:10
GitHub: mahmutelmas06
IRC: mahmutelmas06
In-game: masum

Re: [POC] Weather

by mahmutelmas06 » Wed Feb 10, 2016 23:56

I think snow slows down too much.
And rain with lightning would be nice

viewtopic.php?f=9&t=13886&p=204524#p204524

I wonder how the performance differs from other mods like mymonths
My Mods:

Beverage
 

User avatar
Nathan.S
Member
 
Posts: 919
Joined: Wed Sep 24, 2014 17:47
Location: Bigsby Texas
GitHub: NathanSalapat
IRC: NathanS21
In-game: NathanS21

Re: [POC] Weather

by Nathan.S » Thu Feb 11, 2016 04:41

Mymonths doesn't have any particle collisions, to my knowledge so it is probably a tax faster that way, but the snow and water don't accumulate like they do here. True you can get little water puddles or patches of snow, but that is all random from an abm.
I record Minetest videos, Mod reviews, Modding tutorials, and Lets plays.
Check out my website, and brand new Minetest Modding Course
 

User avatar
xeranas
Member
 
Posts: 155
Joined: Fri Feb 05, 2016 11:06

Re: [POC] Weather

by xeranas » Thu Feb 11, 2016 07:47

Cool! I should come to WIP mods section more often :)

Performance is better than I expected (at least on non-gamming laptop it's still playable for singleplayer).

I would not add physics restrictions (gravity changes) for snow weather since few snowflakes is not big deal. However for snowstorm its different story slowing movement is good idea, though It would be more fun if movement restriction would be added just for one direction (randomly selected by storm) it would more realist wind imitation. E.g. if you go against storm direction you would be movement penalty otherwise you would get sligtly speed bonus (your movement speed boosted by wind).

To saturate difference between snow and snowstorm it would be good idea to change snowstorm snowflake texture (maybe more blurred). Snowstorm particles still need more chaos/aggression feeling.

To separate snow from snowstorm weather is good step, I would do same for rain (unless I missed rainstorm).
I would consider to remove snow/puddles generation for non-storm weather variations. That would add additional differences between e.g. snow and snowstorm weather and also would make snow weather ligther on performance, so people who have low end pc could disable storms and still have some weather variations - just a thought.

Issues caught by eye:
rain puddles destroys walkable entities (rails, saplings and etc)
rain puddles appears on existing full water blocks (not sure if it was intentionally)

Recently I myself did some tweaks for weather-pack fork so I feel your pain not to able add as many partiples as you want due performace cost :)
 

sofar
Developer
 
Posts: 2075
Joined: Fri Jan 16, 2015 07:31
GitHub: sofar
IRC: sofar
In-game: sofar

Re: [POC] Weather

by sofar » Fri Feb 12, 2016 20:26

Maybe you can integrate my lightning code with your rainstorm? Try and post this on github instead of dropbox, so people can make pull requests, clone & collaborate easier...
 

amadin
Member
 
Posts: 544
Joined: Tue Jun 16, 2015 16:23

Re: [POC] Weather

by amadin » Tue Feb 16, 2016 16:48

Can this mod raining only for non-snow areas but snow falling only for snow areas? I need it for public server and players must see the same weather in one place.
 


Return to WIP Mods



Who is online

Users browsing this forum: No registered users and 4 guests