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