Fire: Allow placing only above flammable blocks
[oweals/minetest_game.git] / mods / fire / init.lua
1 -- minetest/fire/init.lua
2
3 -- Global namespace for functions
4
5 fire = {}
6
7
8 -- Register flame nodes
9
10 minetest.register_node("fire:basic_flame", {
11         drawtype = "firelike",
12         tiles = {
13                 {
14                         name = "fire_basic_flame_animated.png",
15                         animation = {
16                                 type = "vertical_frames",
17                                 aspect_w = 16,
18                                 aspect_h = 16,
19                                 length = 1
20                         },
21                 },
22         },
23         inventory_image = "fire_basic_flame.png",
24         paramtype = "light",
25         light_source = 14,
26         walkable = false,
27         buildable_to = true,
28         sunlight_propagates = true,
29         damage_per_second = 4,
30         groups = {igniter = 2, dig_immediate = 3, not_in_creative_inventory = 1},
31         drop = "",
32
33         on_construct = function(pos)
34                 minetest.after(0, fire.on_flame_add_at, pos)
35         end,
36
37         on_destruct = function(pos)
38                 minetest.after(0, fire.on_flame_remove_at, pos)
39         end,
40
41         on_blast = function()
42         end, -- unaffected by explosions
43 })
44
45 minetest.register_node("fire:permanent_flame", {
46         description = "Permanent Flame",
47         drawtype = "firelike",
48         tiles = {
49                 {
50                         name = "fire_basic_flame_animated.png",
51                         animation = {
52                                 type = "vertical_frames",
53                                 aspect_w = 16,
54                                 aspect_h = 16,
55                                 length = 1
56                         },
57                 },
58         },
59         inventory_image = "fire_basic_flame.png",
60         paramtype = "light",
61         light_source = 14,
62         walkable = false,
63         buildable_to = true,
64         sunlight_propagates = true,
65         damage_per_second = 4,
66         groups = {igniter = 2, dig_immediate = 3},
67         drop = "",
68
69         on_blast = function()
70         end,
71 })
72
73 minetest.register_tool("fire:flint_and_steel", {
74         description = "Flint and Steel",
75         inventory_image = "fire_flint_steel.png",
76         on_use = function(itemstack, user, pointed_thing)
77                 local player_name = user:get_player_name()
78                 local pt = pointed_thing
79
80                 if pt.type == "node" and minetest.get_node(pt.above).name == "air" then
81                         itemstack:add_wear(1000)
82                         local node_under = minetest.get_node(pt.under).name
83
84                         if minetest.get_node_group(node_under, "flammable") >= 1 then
85                                 if not minetest.is_protected(pt.above, player_name) then
86                                         minetest.set_node(pt.above, {name = "fire:basic_flame"})
87                                 else
88                                         minetest.chat_send_player(player_name, "This area is protected")
89                                 end
90                         end
91                 end
92
93                 if not minetest.setting_getbool("creative_mode") then
94                         return itemstack
95                 end
96         end
97 })
98
99 minetest.register_craft({
100         output = "fire:flint_and_steel",
101         recipe = {
102                 {"default:flint", "default:steel_ingot"}
103         }
104 })
105
106 -- Get sound area of position
107
108 fire.D = 6 -- size of sound areas
109
110 function fire.get_area_p0p1(pos)
111         local p0 = {
112                 x = math.floor(pos.x / fire.D) * fire.D,
113                 y = math.floor(pos.y / fire.D) * fire.D,
114                 z = math.floor(pos.z / fire.D) * fire.D,
115         }
116         local p1 = {
117                 x = p0.x + fire.D - 1,
118                 y = p0.y + fire.D - 1,
119                 z = p0.z + fire.D - 1
120         }
121         return p0, p1
122 end
123
124
125 -- Fire sounds table
126 -- key: position hash of low corner of area
127 -- value: {handle=sound handle, name=sound name}
128 fire.sounds = {}
129
130
131 -- Update fire sounds in sound area of position
132
133 function fire.update_sounds_around(pos)
134         local p0, p1 = fire.get_area_p0p1(pos)
135         local cp = {x = (p0.x + p1.x) / 2, y = (p0.y + p1.y) / 2, z = (p0.z + p1.z) / 2}
136         local flames_p = minetest.find_nodes_in_area(p0, p1, {"fire:basic_flame"})
137         --print("number of flames at "..minetest.pos_to_string(p0).."/"
138         --              ..minetest.pos_to_string(p1)..": "..#flames_p)
139         local should_have_sound = (#flames_p > 0)
140         local wanted_sound = nil
141         if #flames_p >= 9 then
142                 wanted_sound = {name = "fire_large", gain = 0.7}
143         elseif #flames_p > 0 then
144                 wanted_sound = {name = "fire_small", gain = 0.9}
145         end
146         local p0_hash = minetest.hash_node_position(p0)
147         local sound = fire.sounds[p0_hash]
148         if not sound then
149                 if should_have_sound then
150                         fire.sounds[p0_hash] = {
151                                 handle = minetest.sound_play(wanted_sound,
152                                         {pos = cp, max_hear_distance = 16, loop = true}),
153                                 name = wanted_sound.name,
154                         }
155                 end
156         else
157                 if not wanted_sound then
158                         minetest.sound_stop(sound.handle)
159                         fire.sounds[p0_hash] = nil
160                 elseif sound.name ~= wanted_sound.name then
161                         minetest.sound_stop(sound.handle)
162                         fire.sounds[p0_hash] = {
163                                 handle = minetest.sound_play(wanted_sound,
164                                         {pos = cp, max_hear_distance = 16, loop = true}),
165                                 name = wanted_sound.name,
166                         }
167                 end
168         end
169 end
170
171
172 -- Update fire sounds on flame node construct or destruct
173
174 function fire.on_flame_add_at(pos)
175         fire.update_sounds_around(pos)
176 end
177
178
179 function fire.on_flame_remove_at(pos)
180         fire.update_sounds_around(pos)
181 end
182
183
184 -- Return positions for flames around a burning node
185
186 function fire.find_pos_for_flame_around(pos)
187         return minetest.find_node_near(pos, 1, {"air"})
188 end
189
190
191 -- Detect nearby extinguishing nodes
192
193 function fire.flame_should_extinguish(pos)
194         return minetest.find_node_near(pos, 1, {"group:puts_out_fire"})
195 end
196
197
198 -- Extinguish all flames quickly with water, snow, ice
199
200 minetest.register_abm({
201         nodenames = {"fire:basic_flame", "fire:permanent_flame"},
202         neighbors = {"group:puts_out_fire"},
203         interval = 3,
204         chance = 1,
205         catch_up = false,
206         action = function(p0, node, _, _)
207                 minetest.remove_node(p0)
208                 minetest.sound_play("fire_extinguish_flame",
209                         {pos = p0, max_hear_distance = 16, gain = 0.25})
210         end,
211 })
212
213
214 -- Enable the following ABMs according to 'disable fire' setting
215
216 if minetest.setting_getbool("disable_fire") then
217
218         -- Remove basic flames only
219
220         minetest.register_abm({
221                 nodenames = {"fire:basic_flame"},
222                 interval = 7,
223                 chance = 1,
224                 catch_up = false,
225                 action = function(p0, node, _, _)
226                         minetest.remove_node(p0)
227                 end,
228         })
229
230 else
231
232         -- Ignite neighboring nodes, add basic flames
233
234         minetest.register_abm({
235                 nodenames = {"group:flammable"},
236                 neighbors = {"group:igniter"},
237                 interval = 7,
238                 chance = 12,
239                 catch_up = false,
240                 action = function(p0, node, _, _)
241                         -- If there is water or stuff like that around node, don't ignite
242                         if fire.flame_should_extinguish(p0) then
243                                 return
244                         end
245                         local p = fire.find_pos_for_flame_around(p0)
246                         if p then
247                                 minetest.set_node(p, {name = "fire:basic_flame"})
248                         end
249                 end,
250         })
251
252         -- Remove basic flames and flammable nodes
253
254         minetest.register_abm({
255                 nodenames = {"fire:basic_flame"},
256                 interval = 5,
257                 chance = 6,
258                 catch_up = false,
259                 action = function(p0, node, _, _)
260                         -- If there are no flammable nodes around flame, remove flame
261                         local p = minetest.find_node_near(p0, 1, {"group:flammable"})
262                         if not p then
263                                 minetest.remove_node(p0)
264                                 return
265                         end
266                         if math.random(1, 4) == 1 then
267                                 -- remove flammable nodes around flame
268                                 local node = minetest.get_node(p)
269                                 local def = minetest.registered_nodes[node.name]
270                                 if def.on_burn then
271                                         def.on_burn(p)
272                                 else
273                                         minetest.remove_node(p)
274                                         nodeupdate(p)
275                                 end
276                         end
277                 end,
278         })
279
280 end
281
282
283 -- Rarely ignite things from far
284
285 --[[ Currently disabled to reduce the chance of uncontrollable spreading
286         fires that disrupt servers. Also for less lua processing load.
287
288 minetest.register_abm({
289         nodenames = {"group:igniter"},
290         neighbors = {"air"},
291         interval = 5,
292         chance = 10,
293         action = function(p0, node, _, _)
294                 local reg = minetest.registered_nodes[node.name]
295                 if not reg or not reg.groups.igniter or reg.groups.igniter < 2 then
296                         return
297                 end
298                 local d = reg.groups.igniter
299                 local p = minetest.find_node_near(p0, d, {"group:flammable"})
300                 if p then
301                         -- If there is water or stuff like that around flame, don't ignite
302                         if fire.flame_should_extinguish(p) then
303                                 return
304                         end
305                         local p2 = fire.find_pos_for_flame_around(p)
306                         if p2 then
307                                 minetest.set_node(p2, {name = "fire:basic_flame"})
308                         end
309                 end
310         end,
311 })
312 --]]