1 local weather_channel = minetest.mod_channel_join("weather_type")
2 local weather_intake = minetest.mod_channel_join("weather_intake")
3 local weather_nodes_channel = minetest.mod_channel_join("weather_nodes")
6 weather_channel:send_all("")
7 weather_intake:send_all("")
8 weather_nodes_channel:send_all("")
12 local mod_storage = minetest.get_mod_storage()
14 weather_type = mod_storage:get_int("weather_type")
16 local path = minetest.get_modpath(minetest.get_current_modname())
17 dofile(path.."/commands.lua")
20 --this updates players skys since it cannot be done clientside
21 update_player_sky = function()
22 for _,player in ipairs(minetest.get_connected_players()) do
23 if weather_type ~= 0 then
30 dawn_horizon = "#808080",
32 fog_sun_tint = "#808080",
35 night_horizon="#808080"
37 player:set_sun({visible=false,sunrise_visible=false})
38 player:set_moon({visible=false})
39 player:set_stars({visible=false})
48 dawn_horizon = "#bac1f0",
52 night_horizon="#4090ff"
55 player:set_sun({visible=true,sunrise_visible=true})
56 player:set_moon({visible=true})
57 player:set_stars({visible=true})
62 --this tells the client mod to update the weather type
63 function_send_weather_type = function()
64 weather_channel:send_all(tostring(weather_type))
69 minetest.register_on_mods_loaded(function()
70 for name in pairs(minetest.registered_nodes) do
71 if name ~= "air" and name ~= "ignore" then
72 table.insert(all_nodes,name)
77 --this sends the client all nodes that weather can be on top of
80 --have the client send the server the ready signal
81 minetest.register_on_modchannel_message(function(channel_name, sender, message)
82 if channel_name == "weather_intake" then
83 --print("sending player weather")
84 --for some reason this variable assignment does not work outside the scope of this function
85 local all_nodes_serialized = minetest.serialize(all_nodes)
86 weather_nodes_channel:send_all(all_nodes_serialized)
87 function_send_weather_type()
93 minetest.register_on_joinplayer(function(player)
94 minetest.after(3,function()
95 local all_nodes_serialized = minetest.serialize(all_nodes)
96 weather_nodes_channel:send_all(all_nodes_serialized)
97 function_send_weather_type()
104 local area = vector.new(80,40,80)
107 local subber = vector.subtract
108 local adder = vector.add
110 local under_air = minetest.find_nodes_in_area_under_air
112 local round_it = vector.round
113 local randomize_number = math.random
114 local n_vec = vector.new
116 local get_light = minetest.get_node_light
117 local g_node = minetest.get_node
123 local r_nodes = minetest.registered_nodes
127 local mass_set = minetest.bulk_set_node
128 local inserter = table.insert
130 local get_table_size = table.getn
135 local function do_snow()
136 if weather_type == 1 then
137 for _,player in ipairs(minetest.get_connected_players()) do
139 --local t0 = os.clock()
141 pos = round_it(player:get_pos())
143 area = n_vec(40,40,40)
144 min = subber(pos, area)
145 max = adder(pos, area)
147 area_index = under_air(min, max, all_nodes)
151 --the highest value is always indexed last in minetest.find_nodes_in_area_under_air,
152 --so all that is needed is to iterate through it backwards and hook into the first
153 --y value on the x and y and ignore the rest
154 for key = get_table_size(area_index),1,-1 do
155 temp_pos = area_index[key]
156 if not spawn_table[temp_pos.x] then spawn_table[temp_pos.x] = {} end
157 if not spawn_table[temp_pos.x][temp_pos.z] then
158 spawn_table[temp_pos.x][temp_pos.z] = temp_pos.y
162 --save old method just in case useful or turns out it's faster after all
163 --for _,index in pairs(area_index) do
164 -- if not spawn_table[index.x] then spawn_table[index.x] = {} end
165 -- if not spawn_table[index.x][index.z] then
166 -- spawn_table[index.x][index.z] = index.y
167 -- elseif spawn_table[index.x][index.z] < index.y then
168 -- spawn_table[index.x][index.z] = index.y
172 --find the highest y value
175 for x,x_index in pairs(spawn_table) do
176 for z,y in pairs(x_index) do
177 if randomize_number(1100) >= 1098 then
178 lightlevel = get_light(n_vec(x,y+1,z), 0.5)
179 if lightlevel >= 14 then
180 --make it so buildable to nodes get replaced
181 node = g_node(n_vec(x,y,z)).name
183 buildable = def.buildable_to
184 walkable = def.walkable
185 liquid = (def.liquidtype ~= "none")
188 if not buildable and g_node(n_vec(x,y+1,z)).name ~= "weather:snow" and walkable == true then
189 inserter(bulk_list, n_vec(x,y+1,z))
190 elseif buildable == true and node ~= "weather:snow" then
191 inserter(bulk_list, n_vec(x,y,z))
193 elseif g_node(n_vec(x,y,z)).name == "main:water" then
194 inserter(ice_list, n_vec(x,y,z))
201 mass_set(bulk_list, {name="weather:snow"})
204 mass_set(ice_list, {name="main:ice"})
210 local chugent = math.ceil((os.clock() - t0) * 1000)
211 print("---------------------------------")
212 print ("Snow generation time " .. chugent .. " ms")
214 inserter(average, chugent)
216 --don't cause memory leak
217 if get_table_size(average) > 10 then
218 table.remove(average,1)
220 for _,i in ipairs(average) do
224 a = a / get_table_size(average)
225 print("average = "..a.."ms")
226 print("---------------------------------")
231 minetest.after(3, function()
235 minetest.register_on_mods_loaded(function()
236 minetest.after(0,function()
243 --this sets random weather
244 local initial_run = true
245 local function randomize_weather()
246 if not initial_run then
247 weather_type = math.random(0,weather_max)
248 mod_storage:set_int("weather_type", weather_type)
253 function_send_weather_type()
256 minetest.after((math.random(5,7)+math.random())*60, function()
261 minetest.register_on_mods_loaded(function()
262 if mod_storage:get_int("weather_initialized") == 0 then
263 mod_storage:set_int("weather_initialized",1)
264 weather_type = math.random(0,weather_max)
265 mod_storage:set_int("weather_type", weather_type)
271 minetest.register_on_shutdown(function()
272 mod_storage:set_int("weather_type", weather_type)
275 local snowball_throw = function(player)
276 local pos = player:get_pos()
277 pos.y = pos.y + 1.625
278 --let other players hear the noise too
279 minetest.sound_play("woosh",{to_player=player:get_player_name(), pitch = math.random(80,100)/100})
280 minetest.sound_play("woosh",{pos=pos, exclude_player = player:get_player_name(), pitch = math.random(80,100)/100})
281 local snowball = minetest.add_entity(pos,"weather:snowball")
283 local vel = player:get_player_velocity()
284 snowball:set_velocity(vector.add(vel,vector.multiply(player:get_look_dir(),20)))
285 snowball:get_luaentity().thrower = player:get_player_name()
291 minetest.register_node("weather:snow", {
292 description = "Snow",
293 tiles = {"snow_block.png"},
294 groups = {pathable = 1,snow = 1, falling_node=1},
295 sounds = main.woolSound(),
297 drawtype = "nodebox",
304 items = {"weather:snowball"},
307 items = {"weather:snowball"},
310 items = {"weather:snowball"},
313 items = {"weather:snowball"},
317 items = {"weather:snowball"},
325 {-8/16, -8/16, -8/16, 8/16, -6/16, 8/16},
330 minetest.register_node("weather:snow_block", {
331 description = "Snow",
332 tiles = {"snow_block.png"},
333 groups = {pathable = 1,snow = 1},
334 sounds = main.woolSound(),
339 items = {"weather:snowball"},
342 items = {"weather:snowball"},
345 items = {"weather:snowball"},
348 items = {"weather:snowball"},
352 items = {"weather:snowball"},
358 minetest.register_abm({
359 label = "snow and ice melt",
360 nodenames = {"weather:snow","main:ice"},
365 action = function(pos)
366 if weather_type ~= 1 then
367 minetest.remove_node(pos)
372 minetest.register_craftitem("weather:snowball", {
373 description = "Snowball",
374 inventory_image = "snowball.png",
377 on_place = function(itemstack, placer, pointed_thing)
378 local worked = snowball_throw(placer)
380 itemstack:take_item()
384 on_secondary_use = function(itemstack, user, pointed_thing)
385 local worked = snowball_throw(user)
387 itemstack:take_item()
395 snowball.initial_properties = {
398 collide_with_objects = false,
399 collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
401 visual_size = {x = 0.5, y = 0.5},
409 snowball.snowball = true
411 snowball.on_activate = function(self)
412 self.object:set_acceleration(vector.new(0,-9.81,0))
415 --make this as efficient as possible
416 --make it so you can hit one snowball with another
417 snowball.on_step = function(self, dtime)
418 local vel = self.object:get_velocity()
420 local pos = self.object:get_pos()
422 --hit object with the snowball
423 for _,object in ipairs(minetest.get_objects_inside_radius(pos, 1)) do
424 if (object:is_player() and object:get_hp() > 0 and object:get_player_name() ~= self.thrower) or (object:get_luaentity() and object:get_luaentity().mob == true and object ~= self.owner) then
425 object:punch(self.object, 2,
427 full_punch_interval=1.5,
428 damage_groups = {damage=0,fleshy=0},
435 if (self.oldvel and ((vel.x == 0 and self.oldvel.x ~= 0) or (vel.y == 0 and self.oldvel.y ~= 0) or (vel.z == 0 and self.oldvel.z ~= 0))) or hit == true then
436 --snowballs explode in the nether
437 if pos.y <= -10000 and pos.y >= -20000 then
441 minetest.sound_play("wool",{pos=pos, pitch = math.random(80,100)/100})
442 minetest.add_particlespawner({
444 -- Number of particles spawned over the time period `time`.
447 -- Lifespan of spawner in seconds.
448 -- If time is 0 spawner has infinite lifespan and spawns the `amount` on
449 -- a per-second basis.
453 minvel = {x=-2, y=3, z=-2},
454 maxvel = {x=2, y=5, z=2},
455 minacc = {x=0, y=-9.81, z=0},
456 maxacc = {x=0, y=-9.81, z=0},
461 -- The particles' properties are random values between the min and max
463 -- pos, velocity, acceleration, expirationtime, size
465 collisiondetection = true,
467 collision_removal = true,
469 object_collision = false,
471 texture = "snowflake_"..math.random(1,2)..".png",
480 minetest.register_entity("weather:snowball", snowball)