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
Current Screenshot
Current Screenshot
Old Screenshot
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,
})
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)
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