Move all common mods back to minetest_game
[oweals/minetest_game.git] / mods / fire / init.lua
1 -- minetest/fire/init.lua
2
3 minetest.register_node("fire:basic_flame", {
4         description = "Fire",
5         drawtype = "plantlike",
6         tiles = {{
7                 name="fire_basic_flame_animated.png",
8                 animation={type="vertical_frames", aspect_w=16, aspect_h=16, length=1},
9         }},
10         inventory_image = "fire_basic_flame.png",
11         light_source = 14,
12         groups = {igniter=2,dig_immediate=3},
13         drop = '',
14         walkable = false,
15         buildable_to = true,
16         damage_per_second = 4,
17         
18         after_place_node = function(pos, placer)
19                 fire.on_flame_add_at(pos)
20         end,
21         
22         after_dig_node = function(pos, oldnode, oldmetadata, digger)
23                 fire.on_flame_remove_at(pos)
24         end,
25 })
26
27 fire = {}
28 fire.D = 6
29 -- key: position hash of low corner of area
30 -- value: {handle=sound handle, name=sound name}
31 fire.sounds = {}
32
33 function fire.get_area_p0p1(pos)
34         local p0 = {
35                 x=math.floor(pos.x/fire.D)*fire.D,
36                 y=math.floor(pos.y/fire.D)*fire.D,
37                 z=math.floor(pos.z/fire.D)*fire.D,
38         }
39         local p1 = {
40                 x=p0.x+fire.D-1,
41                 y=p0.y+fire.D-1,
42                 z=p0.z+fire.D-1
43         }
44         return p0, p1
45 end
46
47 function fire.update_sounds_around(pos)
48         local p0, p1 = fire.get_area_p0p1(pos)
49         local cp = {x=(p0.x+p1.x)/2, y=(p0.y+p1.y)/2, z=(p0.z+p1.z)/2}
50         local flames_p = minetest.env:find_nodes_in_area(p0, p1, {"fire:basic_flame"})
51         --print("number of flames at "..minetest.pos_to_string(p0).."/"
52         --              ..minetest.pos_to_string(p1)..": "..#flames_p)
53         local should_have_sound = (#flames_p > 0)
54         local wanted_sound = nil
55         if #flames_p >= 9 then
56                 wanted_sound = {name="fire_large", gain=1.5}
57         elseif #flames_p > 0 then
58                 wanted_sound = {name="fire_small", gain=1.5}
59         end
60         local p0_hash = minetest.hash_node_position(p0)
61         local sound = fire.sounds[p0_hash]
62         if not sound then
63                 if should_have_sound then
64                         fire.sounds[p0_hash] = {
65                                 handle = minetest.sound_play(wanted_sound, {pos=cp, loop=true}),
66                                 name = wanted_sound.name,
67                         }
68                 end
69         else
70                 if not wanted_sound then
71                         minetest.sound_stop(sound.handle)
72                         fire.sounds[p0_hash] = nil
73                 elseif sound.name ~= wanted_sound.name then
74                         minetest.sound_stop(sound.handle)
75                         fire.sounds[p0_hash] = {
76                                 handle = minetest.sound_play(wanted_sound, {pos=cp, loop=true}),
77                                 name = wanted_sound.name,
78                         }
79                 end
80         end
81 end
82
83 function fire.on_flame_add_at(pos)
84         --print("flame added at "..minetest.pos_to_string(pos))
85         fire.update_sounds_around(pos)
86 end
87
88 function fire.on_flame_remove_at(pos)
89         --print("flame removed at "..minetest.pos_to_string(pos))
90         fire.update_sounds_around(pos)
91 end
92
93 function fire.find_pos_for_flame_around(pos)
94         return minetest.env:find_node_near(pos, 1, {"air"})
95 end
96
97 function fire.flame_should_extinguish(pos)
98         if minetest.setting_getbool("disable_fire") then return true end
99         --return minetest.env:find_node_near(pos, 1, {"group:puts_out_fire"})
100         local p0 = {x=pos.x-2, y=pos.y, z=pos.z-2}
101         local p1 = {x=pos.x+2, y=pos.y, z=pos.z+2}
102         local ps = minetest.env:find_nodes_in_area(p0, p1, {"group:puts_out_fire"})
103         return (#ps ~= 0)
104 end
105
106 -- Ignite neighboring nodes
107 minetest.register_abm({
108         nodenames = {"group:flammable"},
109         neighbors = {"group:igniter"},
110         interval = 1,
111         chance = 2,
112         action = function(p0, node, _, _)
113                 -- If there is water or stuff like that around flame, don't ignite
114                 if fire.flame_should_extinguish(p0) then
115                         return
116                 end
117                 local p = fire.find_pos_for_flame_around(p0)
118                 if p then
119                         minetest.env:set_node(p, {name="fire:basic_flame"})
120                         fire.on_flame_add_at(p)
121                 end
122         end,
123 })
124
125 -- Rarely ignite things from far
126 minetest.register_abm({
127         nodenames = {"group:igniter"},
128         neighbors = {"air"},
129         interval = 2,
130         chance = 10,
131         action = function(p0, node, _, _)
132                 local reg = minetest.registered_nodes[node.name]
133                 if not reg or not reg.groups.igniter or reg.groups.igniter < 2 then
134                         return
135                 end
136                 local d = reg.groups.igniter
137                 local p = minetest.env:find_node_near(p0, d, {"group:flammable"})
138                 if p then
139                         -- If there is water or stuff like that around flame, don't ignite
140                         if fire.flame_should_extinguish(p) then
141                                 return
142                         end
143                         local p2 = fire.find_pos_for_flame_around(p)
144                         if p2 then
145                                 minetest.env:set_node(p2, {name="fire:basic_flame"})
146                                 fire.on_flame_add_at(p2)
147                         end
148                 end
149         end,
150 })
151
152 -- Remove flammable nodes and flame
153 minetest.register_abm({
154         nodenames = {"fire:basic_flame"},
155         interval = 1,
156         chance = 2,
157         action = function(p0, node, _, _)
158                 -- If there is water or stuff like that around flame, remove flame
159                 if fire.flame_should_extinguish(p0) then
160                         minetest.env:remove_node(p0)
161                         fire.on_flame_remove_at(p0)
162                         return
163                 end
164                 -- Make the following things rarer
165                 if math.random(1,3) == 1 then
166                         return
167                 end
168                 -- If there are no flammable nodes around flame, remove flame
169                 if not minetest.env:find_node_near(p0, 1, {"group:flammable"}) then
170                         minetest.env:remove_node(p0)
171                         fire.on_flame_remove_at(p0)
172                         return
173                 end
174                 if math.random(1,4) == 1 then
175                         -- remove a flammable node around flame
176                         local p = minetest.env:find_node_near(p0, 1, {"group:flammable"})
177                         if p then
178                                 -- If there is water or stuff like that around flame, don't remove
179                                 if fire.flame_should_extinguish(p0) then
180                                         return
181                                 end
182                                 minetest.env:remove_node(p)
183                                 nodeupdate(p)
184                         end
185                 else
186                         -- remove flame
187                         minetest.env:remove_node(p0)
188                         fire.on_flame_remove_at(p0)
189                 end
190         end,
191 })
192