Replace deprecated function calls
[oweals/minetest_game.git] / mods / fire / init.lua
1 -- Global namespace for functions
2
3 fire = {}
4
5
6 --
7 -- Items
8 --
9
10 -- Flame nodes
11
12 minetest.register_node("fire:basic_flame", {
13         drawtype = "firelike",
14         tiles = {
15                 {
16                         name = "fire_basic_flame_animated.png",
17                         animation = {
18                                 type = "vertical_frames",
19                                 aspect_w = 16,
20                                 aspect_h = 16,
21                                 length = 1
22                         },
23                 },
24         },
25         inventory_image = "fire_basic_flame.png",
26         paramtype = "light",
27         light_source = 13,
28         walkable = false,
29         buildable_to = true,
30         sunlight_propagates = true,
31         damage_per_second = 4,
32         groups = {igniter = 2, dig_immediate = 3, not_in_creative_inventory = 1},
33         on_timer = function(pos)
34                 local f = minetest.find_node_near(pos, 1, {"group:flammable"})
35                 if not f then
36                         minetest.remove_node(pos)
37                         return
38                 end
39                 -- Restart timer
40                 return true
41         end,
42         drop = "",
43
44         on_construct = function(pos)
45                 minetest.get_node_timer(pos):start(math.random(30, 60))
46         end,
47 })
48
49 minetest.register_node("fire:permanent_flame", {
50         description = "Permanent Flame",
51         drawtype = "firelike",
52         tiles = {
53                 {
54                         name = "fire_basic_flame_animated.png",
55                         animation = {
56                                 type = "vertical_frames",
57                                 aspect_w = 16,
58                                 aspect_h = 16,
59                                 length = 1
60                         },
61                 },
62         },
63         inventory_image = "fire_basic_flame.png",
64         paramtype = "light",
65         light_source = 13,
66         walkable = false,
67         buildable_to = true,
68         sunlight_propagates = true,
69         damage_per_second = 4,
70         groups = {igniter = 2, dig_immediate = 3},
71         drop = "",
72 })
73
74
75 -- Flint and steel
76
77 minetest.register_tool("fire:flint_and_steel", {
78         description = "Flint and Steel",
79         inventory_image = "fire_flint_steel.png",
80         sound = {breaks = "default_tool_breaks"},
81
82         on_use = function(itemstack, user, pointed_thing)
83                 local sound_pos = pointed_thing.above or user:get_pos()
84                 minetest.sound_play(
85                         "fire_flint_and_steel",
86                         {pos = sound_pos, gain = 0.5, max_hear_distance = 8}
87                 )
88                 local player_name = user:get_player_name()
89                 if pointed_thing.type == "node" then
90                         local node_under = minetest.get_node(pointed_thing.under).name
91                         local nodedef = minetest.registered_nodes[node_under]
92                         if not nodedef then
93                                 return
94                         end
95                         if minetest.is_protected(pointed_thing.under, player_name) then
96                                 minetest.chat_send_player(player_name, "This area is protected")
97                                 return
98                         end
99                         if nodedef.on_ignite then
100                                 nodedef.on_ignite(pointed_thing.under, user)
101                         elseif minetest.get_item_group(node_under, "flammable") >= 1
102                                         and minetest.get_node(pointed_thing.above).name == "air" then
103                                 minetest.set_node(pointed_thing.above, {name = "fire:basic_flame"})
104                         end
105                 end
106                 if not (creative and creative.is_enabled_for
107                                 and creative.is_enabled_for(player_name)) then
108                         -- Wear tool
109                         local wdef = itemstack:get_definition()
110                         itemstack:add_wear(1000)
111                         -- Tool break sound
112                         if itemstack:get_count() == 0 and wdef.sound and wdef.sound.breaks then
113                                 minetest.sound_play(wdef.sound.breaks, {pos = sound_pos, gain = 0.5})
114                         end
115                         return itemstack
116                 end
117         end
118 })
119
120 minetest.register_craft({
121         output = "fire:flint_and_steel",
122         recipe = {
123                 {"default:flint", "default:steel_ingot"}
124         }
125 })
126
127
128 -- Override coalblock to enable permanent flame above
129 -- Coalblock is non-flammable to avoid unwanted basic_flame nodes
130
131 minetest.override_item("default:coalblock", {
132         after_destruct = function(pos, oldnode)
133                 pos.y = pos.y + 1
134                 if minetest.get_node(pos).name == "fire:permanent_flame" then
135                         minetest.remove_node(pos)
136                 end
137         end,
138         on_ignite = function(pos, igniter)
139                 local flame_pos = {x = pos.x, y = pos.y + 1, z = pos.z}
140                 if minetest.get_node(flame_pos).name == "air" then
141                         minetest.set_node(flame_pos, {name = "fire:permanent_flame"})
142                 end
143         end,
144 })
145
146
147 --
148 -- Sound
149 --
150
151 local flame_sound = minetest.settings:get_bool("flame_sound")
152 if flame_sound == nil then
153         -- Enable if no setting present
154         flame_sound = true
155 end
156
157 if flame_sound then
158
159         local handles = {}
160         local timer = 0
161
162         -- Parameters
163
164         local radius = 8 -- Flame node search radius around player
165         local cycle = 3 -- Cycle time for sound updates
166
167         -- Update sound for player
168
169         function fire.update_player_sound(player)
170                 local player_name = player:get_player_name()
171                 -- Search for flame nodes in radius around player
172                 local ppos = player:get_pos()
173                 local areamin = vector.subtract(ppos, radius)
174                 local areamax = vector.add(ppos, radius)
175                 local fpos, num = minetest.find_nodes_in_area(
176                         areamin,
177                         areamax,
178                         {"fire:basic_flame", "fire:permanent_flame"}
179                 )
180                 -- Total number of flames in radius
181                 local flames = (num["fire:basic_flame"] or 0) +
182                         (num["fire:permanent_flame"] or 0)
183                 -- Stop previous sound
184                 if handles[player_name] then
185                         minetest.sound_stop(handles[player_name])
186                         handles[player_name] = nil
187                 end
188                 -- If flames
189                 if flames > 0 then
190                         -- Find centre of flame positions
191                         local fposmid = fpos[1]
192                         -- If more than 1 flame
193                         if #fpos > 1 then
194                                 local fposmin = areamax
195                                 local fposmax = areamin
196                                 for i = 1, #fpos do
197                                         local fposi = fpos[i]
198                                         if fposi.x > fposmax.x then
199                                                 fposmax.x = fposi.x
200                                         end
201                                         if fposi.y > fposmax.y then
202                                                 fposmax.y = fposi.y
203                                         end
204                                         if fposi.z > fposmax.z then
205                                                 fposmax.z = fposi.z
206                                         end
207                                         if fposi.x < fposmin.x then
208                                                 fposmin.x = fposi.x
209                                         end
210                                         if fposi.y < fposmin.y then
211                                                 fposmin.y = fposi.y
212                                         end
213                                         if fposi.z < fposmin.z then
214                                                 fposmin.z = fposi.z
215                                         end
216                                 end
217                                 fposmid = vector.divide(vector.add(fposmin, fposmax), 2)
218                         end
219                         -- Play sound
220                         local handle = minetest.sound_play(
221                                 "fire_fire",
222                                 {
223                                         pos = fposmid,
224                                         to_player = player_name,
225                                         gain = math.min(0.06 * (1 + flames * 0.125), 0.18),
226                                         max_hear_distance = 32,
227                                         loop = true, -- In case of lag
228                                 }
229                         )
230                         -- Store sound handle for this player
231                         if handle then
232                                 handles[player_name] = handle
233                         end
234                 end
235         end
236
237         -- Cycle for updating players sounds
238
239         minetest.register_globalstep(function(dtime)
240                 timer = timer + dtime
241                 if timer < cycle then
242                         return
243                 end
244
245                 timer = 0
246                 local players = minetest.get_connected_players()
247                 for n = 1, #players do
248                         fire.update_player_sound(players[n])
249                 end
250         end)
251
252         -- Stop sound and clear handle on player leave
253
254         minetest.register_on_leaveplayer(function(player)
255                 local player_name = player:get_player_name()
256                 if handles[player_name] then
257                         minetest.sound_stop(handles[player_name])
258                         handles[player_name] = nil
259                 end
260         end)
261 end
262
263
264 -- Deprecated function kept temporarily to avoid crashes if mod fire nodes call it
265
266 function fire.update_sounds_around(pos)
267 end
268
269
270 --
271 -- ABMs
272 --
273
274 -- Extinguish all flames quickly with water, snow, ice
275
276 minetest.register_abm({
277         label = "Extinguish flame",
278         nodenames = {"fire:basic_flame", "fire:permanent_flame"},
279         neighbors = {"group:puts_out_fire"},
280         interval = 3,
281         chance = 1,
282         catch_up = false,
283         action = function(pos, node, active_object_count, active_object_count_wider)
284                 minetest.remove_node(pos)
285                 minetest.sound_play("fire_extinguish_flame",
286                         {pos = pos, max_hear_distance = 16, gain = 0.15})
287         end,
288 })
289
290
291 -- Enable the following ABMs according to 'enable fire' setting
292
293 local fire_enabled = minetest.settings:get_bool("enable_fire")
294 if fire_enabled == nil then
295         -- enable_fire setting not specified, check for disable_fire
296         local fire_disabled = minetest.settings:get_bool("disable_fire")
297         if fire_disabled == nil then
298                 -- Neither setting specified, check whether singleplayer
299                 fire_enabled = minetest.is_singleplayer()
300         else
301                 fire_enabled = not fire_disabled
302         end
303 end
304
305 if not fire_enabled then
306
307         -- Remove basic flames only if fire disabled
308
309         minetest.register_abm({
310                 label = "Remove disabled fire",
311                 nodenames = {"fire:basic_flame"},
312                 interval = 7,
313                 chance = 1,
314                 catch_up = false,
315                 action = minetest.remove_node,
316         })
317
318 else -- Fire enabled
319
320         -- Ignite neighboring nodes, add basic flames
321
322         minetest.register_abm({
323                 label = "Ignite flame",
324                 nodenames = {"group:flammable"},
325                 neighbors = {"group:igniter"},
326                 interval = 7,
327                 chance = 12,
328                 catch_up = false,
329                 action = function(pos, node, active_object_count, active_object_count_wider)
330                         -- If there is water or stuff like that around node, don't ignite
331                         if minetest.find_node_near(pos, 1, {"group:puts_out_fire"}) then
332                                 return
333                         end
334                         local p = minetest.find_node_near(pos, 1, {"air"})
335                         if p then
336                                 minetest.set_node(p, {name = "fire:basic_flame"})
337                         end
338                 end,
339         })
340
341         -- Remove flammable nodes around basic flame
342
343         minetest.register_abm({
344                 label = "Remove flammable nodes",
345                 nodenames = {"fire:basic_flame"},
346                 neighbors = "group:flammable",
347                 interval = 5,
348                 chance = 18,
349                 catch_up = false,
350                 action = function(pos, node, active_object_count, active_object_count_wider)
351                         local p = minetest.find_node_near(pos, 1, {"group:flammable"})
352                         if p then
353                                 local flammable_node = minetest.get_node(p)
354                                 local def = minetest.registered_nodes[flammable_node.name]
355                                 if def.on_burn then
356                                         def.on_burn(p)
357                                 else
358                                         minetest.remove_node(p)
359                                         minetest.check_for_falling(p)
360                                 end
361                         end
362                 end,
363         })
364
365 end