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