]> git.lizzy.rs Git - Crafter.git/blob - mods/weather/init.lua
Snowballs now explode when they hit something in the nether
[Crafter.git] / mods / weather / init.lua
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")
4
5
6 weather_channel:send_all("")
7 weather_intake:send_all("")
8 weather_nodes_channel:send_all("")
9
10 local weather_max = 2
11
12 local mod_storage = minetest.get_mod_storage()
13
14 weather_type = mod_storage:get_int("weather_type")
15
16 local path = minetest.get_modpath(minetest.get_current_modname())
17 dofile(path.."/commands.lua")
18
19
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
24                         player:set_sky({
25                                 base_color="#808080",
26                                 type="plain",
27                                 clouds=false,
28                                 
29                                 day_sky = "#808080",
30                                 dawn_horizon = "#808080",
31                                 dawn_sky = "#808080",
32                                 fog_sun_tint = "#808080",
33                                 
34                                 night_sky="#808080",
35                                 night_horizon="#808080"
36                         })
37                         player:set_sun({visible=false,sunrise_visible=false})
38                         player:set_moon({visible=false})
39                         player:set_stars({visible=false})
40                 else
41                         player:set_sky({
42                                 base_color="#8cbafa",
43                                 type="regular",
44                                 clouds=true,
45                                 
46                                 day_sky = "#8cbafa",
47                                 
48                                 dawn_horizon = "#bac1f0",
49                                 dawn_sky = "#b4bafa",
50                                 
51                                 night_sky="#006aff",
52                                 night_horizon="#4090ff"
53                         })
54                         
55                         player:set_sun({visible=true,sunrise_visible=true})
56                         player:set_moon({visible=true})
57                         player:set_stars({visible=true})
58                 end
59         end
60 end
61
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))
65 end
66
67 --index all mods
68 local all_nodes = {}
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)
73                 end
74         end     
75 end)
76
77 --this sends the client all nodes that weather can be on top of
78 --(everything)
79
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()
88                 update_player_sky()
89         end
90 end)
91
92
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()
98                 update_player_sky()
99         end)
100 end)
101
102 --spawn snow nodes
103 local pos
104 local area = vector.new(80,40,80)
105 local min
106 local max
107 local subber = vector.subtract
108 local adder  = vector.add
109 local area_index
110 local under_air = minetest.find_nodes_in_area_under_air
111 local area
112 local round_it = vector.round
113 local randomize_number = math.random
114 local n_vec = vector.new
115 local lightlevel
116 local get_light = minetest.get_node_light
117 local g_node = minetest.get_node
118 local node
119 local def
120 local buildable
121 local walkable
122 local liquid
123 local r_nodes = minetest.registered_nodes
124 local bulk_list
125 local ice_list
126 local spawn_table
127 local mass_set = minetest.bulk_set_node
128 local inserter = table.insert
129 local temp_pos
130 local get_table_size = table.getn
131
132 --this is debug
133 --local average = {}
134
135 local function do_snow()
136         if weather_type == 1 then
137                 for _,player in ipairs(minetest.get_connected_players()) do
138                         --this is debug
139                         --local t0 = os.clock()
140                         
141                         pos = round_it(player:get_pos())
142                         
143                         area = n_vec(40,40,40)
144                         min = subber(pos, area)
145                         max = adder(pos, area)
146                         
147                         area_index = under_air(min, max, all_nodes)
148                         
149                         spawn_table = {}
150                         
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
159                                 end
160                         end
161                         
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
169                         --      end
170                         --end
171                         
172                         --find the highest y value
173                         bulk_list = {}
174                         ice_list = {}
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
182                                                         def = r_nodes[node]
183                                                         buildable = def.buildable_to
184                                                         walkable = def.walkable
185                                                         liquid = (def.liquidtype ~= "none")
186                                                         
187                                                         if not liquid then
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))
192                                                                 end
193                                                         elseif g_node(n_vec(x,y,z)).name == "main:water" then
194                                                                 inserter(ice_list, n_vec(x,y,z))
195                                                         end
196                                                 end
197                                         end
198                                 end
199                         end
200                         if bulk_list then
201                                 mass_set(bulk_list, {name="weather:snow"})
202                         end
203                         if ice_list then
204                                 mass_set(ice_list, {name="main:ice"})
205                         end
206                         
207                         
208                         --this is debug
209                         --[[
210                         local chugent = math.ceil((os.clock() - t0) * 1000)
211                         print("---------------------------------")
212                         print ("Snow generation time " .. chugent .. " ms")
213
214                         inserter(average, chugent)
215                         local a = 0
216                         --don't cause memory leak
217                         if get_table_size(average) > 10 then
218                                 table.remove(average,1)
219                         end
220                         for _,i in ipairs(average) do
221                                 a = a + i
222                         end
223                         print(dump(average))
224                         a = a / get_table_size(average)
225                         print("average = "..a.."ms")
226                         print("---------------------------------")
227                         ]]--
228                 end
229         end
230         
231         minetest.after(3, function()
232                 do_snow()
233         end)
234 end
235 minetest.register_on_mods_loaded(function()
236         minetest.after(0,function()
237                 do_snow()
238         end)
239 end)
240
241
242
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)
249         else
250                 initial_run = false
251         end
252
253         function_send_weather_type()
254         update_player_sky()
255
256         minetest.after((math.random(5,7)+math.random())*60, function()
257                 randomize_weather()
258         end)
259 end
260
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)
266         end
267
268         randomize_weather()
269 end)
270
271 minetest.register_on_shutdown(function()
272         mod_storage:set_int("weather_type", weather_type)
273 end)
274
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")
282         if snowball then
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()
286                 return(true)
287         end
288         return(false)
289 end
290
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(),
296     paramtype = "light",
297         drawtype = "nodebox",
298         walkable = false,
299         floodable = true,
300     drop = {
301                         max_items = 5,
302                         items= {
303                                 {
304                                         items = {"weather:snowball"},
305                                 },
306                                 {
307                                         items = {"weather:snowball"},
308                                 },
309                                 {
310                                         items = {"weather:snowball"},
311                                 },
312                                 {
313                                         items = {"weather:snowball"},
314                                 },
315                                 {
316                                         rarity = 5,
317                                         items = {"weather:snowball"},
318                                 },
319                         },
320                 },
321     buildable_to = true,
322     node_box = {
323                 type = "fixed",
324                 fixed = {
325                 {-8/16, -8/16, -8/16, 8/16, -6/16, 8/16},
326                 }
327         },
328 })
329
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(),
335     drop = {
336                         max_items = 5,
337                         items= {
338                                 {
339                                         items = {"weather:snowball"},
340                                 },
341                                 {
342                                         items = {"weather:snowball"},
343                                 },
344                                 {
345                                         items = {"weather:snowball"},
346                                 },
347                                 {
348                                         items = {"weather:snowball"},
349                                 },
350                                 {
351                                         rarity = 5,
352                                         items = {"weather:snowball"},
353                                 },
354                         },
355                 },
356 })
357
358 minetest.register_abm({
359         label = "snow and ice melt",
360         nodenames = {"weather:snow","main:ice"},
361         neighbors = {"air"},
362         interval = 3,
363         chance = 10,
364         catch_up = true,
365         action = function(pos)
366                 if weather_type ~= 1 then
367                         minetest.remove_node(pos)
368                 end
369         end,
370 })
371
372 minetest.register_craftitem("weather:snowball", {
373         description = "Snowball",
374         inventory_image = "snowball.png",
375         --stack_max = 1,
376         --range = 0,
377         on_place = function(itemstack, placer, pointed_thing)
378                 local worked = snowball_throw(placer)
379                 if worked then
380                         itemstack:take_item()
381                 end
382                 return(itemstack)
383         end,
384         on_secondary_use = function(itemstack, user, pointed_thing)
385                 local worked = snowball_throw(user)
386                 if worked then
387                         itemstack:take_item()
388                 end
389                 return(itemstack)
390         end,
391 })
392
393
394 snowball = {}
395 snowball.initial_properties = {
396         hp_max = 1,
397         physical = true,
398         collide_with_objects = false,
399         collisionbox = {-0.1, -0.1, -0.1, 0.1, 0.1, 0.1},
400         visual = "sprite",
401         visual_size = {x = 0.5, y = 0.5},
402         textures = {
403                 "snowball.png"
404         },
405         is_visible = true,
406         pointable = false,
407 }
408
409 snowball.snowball = true
410
411 snowball.on_activate = function(self)
412         self.object:set_acceleration(vector.new(0,-9.81,0))
413 end
414
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()
419         local hit = false
420         local pos = self.object:get_pos()
421         
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, 
426                                 {
427                                 full_punch_interval=1.5,
428                                 damage_groups = {damage=0,fleshy=0},
429                         })
430                         hit = true
431                         break
432                 end
433         end
434         
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
438                         self.object:remove()
439                         tnt(pos,4)
440                 else
441                         minetest.sound_play("wool",{pos=pos, pitch = math.random(80,100)/100})
442                         minetest.add_particlespawner({
443                                 amount = 20,
444                                 -- Number of particles spawned over the time period `time`.
445
446                                 time = 0.001,
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.
450
451                                 minpos = pos,
452                                 maxpos = pos,
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},
457                                 minexptime = 1,
458                                 maxexptime = 3,
459                                 minsize = 1,
460                                 maxsize = 1,
461                                 -- The particles' properties are random values between the min and max
462                                 -- values.
463                                 -- pos, velocity, acceleration, expirationtime, size
464
465                                 collisiondetection = true,
466
467                                 collision_removal = true,
468
469                                 object_collision = false,
470
471                                 texture = "snowflake_"..math.random(1,2)..".png",
472
473                         })
474                         self.object:remove()
475                 end
476         end
477         
478         self.oldvel = vel
479 end
480 minetest.register_entity("weather:snowball", snowball)