[POC] Weather

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

[POC] Weather

by jordan4ibanez » Post

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

Re: [POC] Weather

by proller » Post


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

Re: [POC] Weather

by jordan4ibanez » Post

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 » Post

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: 1003
Joined: Wed Sep 24, 2014 17:47
GitHub: NathanSalapat
IRC: NathanS21
In-game: NathanS21
Location: Bigsby Texas
Contact:

Re: [POC] Weather

by Nathan.S » Post

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: 161
Joined: Fri Feb 05, 2016 11:06

Re: [POC] Weather

by xeranas » Post

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: 2131
Joined: Fri Jan 16, 2015 07:31
GitHub: sofar
IRC: sofar
In-game: sofar

Re: [POC] Weather

by sofar » Post

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: 548
Joined: Tue Jun 16, 2015 16:23

Re: [POC] Weather

by amadin » Post

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.

Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests