Replace getpos() with get_pos()
[MineClone/MineClone2.git] / mods / ITEMS / mcl_fire / init.lua
blobc8c12ea36541a3ab20a2b087f7c7c02ce78bd001
1 -- Global namespace for functions
3 mcl_fire = {}
6 --
7 -- Items
8 --
10 -- Flame nodes
12 local fire_help = "Fire is a damaging and destructive but short-lived kind of block. It will destroy and spread towards near flammable blocks, but fire will disappear when there is nothing to burn left. It will be extinguished by nearby water and rain. Fire can be destroyed safely by punching it, but it is hurtful if you stand directly in it. If a fire is started above netherrack or a magma block, it will immediately turn into an eternal fire."
13 local eternal_fire_help = "Eternal fire is a damaging and destructive block. It will create fire around it when flammable blocks are nearby. Eternal fire can be extinguished by punches and nearby water blocks. Other than (normal) fire, eternal fire does not get extinguished on its own and also continues to burn under rain. Punching eternal fire is safe, but it hurts if you stand inside."
15 minetest.register_node("mcl_fire:fire", {
16 description = "Fire",
17 _doc_items_longdesc = fire_help,
18 drawtype = "firelike",
19 tiles = {
21 name = "fire_basic_flame_animated.png",
22 animation = {
23 type = "vertical_frames",
24 aspect_w = 16,
25 aspect_h = 16,
26 length = 1
30 inventory_image = "fire_basic_flame.png",
31 paramtype = "light",
32 -- Real light level: 15 (but Minetest caps at 14)
33 light_source = 14,
34 walkable = false,
35 buildable_to = true,
36 sunlight_propagates = true,
37 damage_per_second = 1,
38 groups = {fire = 1, dig_immediate = 3, not_in_creative_inventory = 1, dig_by_piston=1},
39 floodable = true,
40 on_flood = function(pos, oldnode, newnode)
41 if minetest.get_item_group(newnode.name, "water") ~= 0 then
42 minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16})
43 end
44 end,
45 on_timer = function(pos)
46 local airs = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y+4, z=pos.z+1}, {"air"})
47 if #airs == 0 then
48 minetest.remove_node(pos)
49 return
50 end
51 local burned = false
52 if math.random(1,2) == 1 then
53 while #airs > 0 do
54 local r = math.random(1, #airs)
55 if minetest.find_node_near(airs[r], 1, {"group:flammable"}) then
56 minetest.set_node(airs[r], {name="mcl_fire:fire"})
57 burned = true
58 break
59 else
60 table.remove(airs, r)
61 end
62 end
63 end
64 if not burned then
65 if math.random(1,3) == 1 then
66 minetest.remove_node(pos)
67 return
68 end
69 end
70 -- Restart timer
71 minetest.get_node_timer(pos):start(math.random(3, 7))
72 end,
73 drop = "",
74 sounds = {},
75 -- Turn into eternal fire on special blocks, light Nether portal (if possible), start burning timer
76 on_construct = function(pos)
77 local bpos = {x=pos.x, y=pos.y-1, z=pos.z}
78 local under = minetest.get_node(bpos).name
80 local dim = mcl_worlds.pos_to_dimension(bpos)
81 if under == "mcl_nether:magma" or under == "mcl_nether:netherrack" or (under == "mcl_core:bedrock" and dim == "end") then
82 minetest.swap_node(pos, {name = "mcl_fire:eternal_fire"})
83 end
85 if minetest.get_modpath("mcl_portals") then
86 mcl_portals.light_nether_portal(pos)
87 end
89 minetest.get_node_timer(pos):start(math.random(3, 7))
90 end,
91 _mcl_blast_resistance = 0,
94 minetest.register_node("mcl_fire:eternal_fire", {
95 description = "Eternal Fire",
96 _doc_items_longdesc = eternal_fire_help,
97 drawtype = "firelike",
98 tiles = {
100 name = "fire_basic_flame_animated.png",
101 animation = {
102 type = "vertical_frames",
103 aspect_w = 16,
104 aspect_h = 16,
105 length = 1
109 inventory_image = "fire_basic_flame.png",
110 paramtype = "light",
111 -- Real light level: 15 (but Minetest caps at 14)
112 light_source = 14,
113 walkable = false,
114 buildable_to = true,
115 sunlight_propagates = true,
116 damage_per_second = 1,
117 groups = {fire = 1, dig_immediate = 3, not_in_creative_inventory = 1, dig_by_piston = 1},
118 floodable = true,
119 on_flood = function(pos, oldnode, newnode)
120 if minetest.get_item_group(newnode.name, "water") ~= 0 then
121 minetest.sound_play("fire_extinguish_flame", {pos = pos, gain = 0.25, max_hear_distance = 16})
123 end,
124 on_timer = function(pos)
125 local airs = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y-1, z=pos.z-1}, {x=pos.x+1, y=pos.y+4, z=pos.z+1}, {"air"})
126 while #airs > 0 do
127 local r = math.random(1, #airs)
128 if minetest.find_node_near(airs[r], 1, {"group:flammable"}) then
129 minetest.set_node(airs[r], {name="mcl_fire:fire"})
130 break
131 else
132 table.remove(airs, r)
135 -- Restart timer
136 minetest.get_node_timer(pos):start(math.random(3, 7))
137 end,
138 -- Start burning timer and light Nether portal (if possible)
139 on_construct = function(pos)
140 minetest.get_node_timer(pos):start(math.random(3, 7))
142 if minetest.get_modpath("mcl_portals") then
143 mcl_portals.light_nether_portal(pos)
145 end,
146 sounds = {},
147 drop = "",
148 _mcl_blast_resistance = 0,
151 -- Also make lava set fire to air blocks above
152 minetest.override_item("mcl_core:lava_source", {
153 on_timer = function(pos)
154 local function try_ignite(airs)
155 while #airs > 0 do
156 local r = math.random(1, #airs)
157 if minetest.find_node_near(airs[r], 1, {"group:flammable", "group:flammable_lava"}) then
158 minetest.set_node(airs[r], {name="mcl_fire:fire"})
159 return true
160 else
161 table.remove(airs, r)
164 return false
166 local airs1 = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y+1, z=pos.z-1}, {x=pos.x+1, y=pos.y+1, z=pos.z+1}, {"air"})
167 local ok = try_ignite(airs1)
168 if not ok then
169 local airs2 = minetest.find_nodes_in_area({x=pos.x-2, y=pos.y+2, z=pos.z-2}, {x=pos.x+2, y=pos.y+2, z=pos.z+2}, {"air"})
170 try_ignite(airs2)
173 -- Restart timer
174 minetest.get_node_timer(pos):start(math.random(5, 10))
175 end,
176 on_construct = function(pos)
177 minetest.get_node_timer(pos):start(math.random(5, 10))
182 -- Sound
185 local flame_sound = minetest.settings:get_bool("flame_sound")
186 if flame_sound == nil then
187 -- Enable if no setting present
188 flame_sound = true
191 if flame_sound then
193 local handles = {}
194 local timer = 0
196 -- Parameters
198 local radius = 8 -- Flame node search radius around player
199 local cycle = 3 -- Cycle time for sound updates
201 -- Update sound for player
203 function mcl_fire.update_player_sound(player)
204 local player_name = player:get_player_name()
205 -- Search for flame nodes in radius around player
206 local ppos = player:get_pos()
207 local areamin = vector.subtract(ppos, radius)
208 local areamax = vector.add(ppos, radius)
209 local fpos, num = minetest.find_nodes_in_area(
210 areamin,
211 areamax,
212 {"mcl_fire:fire", "mcl_fire:eternal_fire"}
214 -- Total number of flames in radius
215 local flames = (num["mcl_fire:fire"] or 0) +
216 (num["mcl_fire:eternal_fire"] or 0)
217 -- Stop previous sound
218 if handles[player_name] then
219 minetest.sound_stop(handles[player_name])
220 handles[player_name] = nil
222 -- If flames
223 if flames > 0 then
224 -- Find centre of flame positions
225 local fposmid = fpos[1]
226 -- If more than 1 flame
227 if #fpos > 1 then
228 local fposmin = areamax
229 local fposmax = areamin
230 for i = 1, #fpos do
231 local fposi = fpos[i]
232 if fposi.x > fposmax.x then
233 fposmax.x = fposi.x
235 if fposi.y > fposmax.y then
236 fposmax.y = fposi.y
238 if fposi.z > fposmax.z then
239 fposmax.z = fposi.z
241 if fposi.x < fposmin.x then
242 fposmin.x = fposi.x
244 if fposi.y < fposmin.y then
245 fposmin.y = fposi.y
247 if fposi.z < fposmin.z then
248 fposmin.z = fposi.z
251 fposmid = vector.divide(vector.add(fposmin, fposmax), 2)
253 -- Play sound
254 local handle = minetest.sound_play(
255 "fire_fire",
257 pos = fposmid,
258 to_player = player_name,
259 gain = math.min(0.06 * (1 + flames * 0.125), 0.18),
260 max_hear_distance = 32,
261 loop = true, -- In case of lag
264 -- Store sound handle for this player
265 if handle then
266 handles[player_name] = handle
271 -- Cycle for updating players sounds
273 minetest.register_globalstep(function(dtime)
274 timer = timer + dtime
275 if timer < cycle then
276 return
279 timer = 0
280 local players = minetest.get_connected_players()
281 for n = 1, #players do
282 mcl_fire.update_player_sound(players[n])
284 end)
286 -- Stop sound and clear handle on player leave
288 minetest.register_on_leaveplayer(function(player)
289 local player_name = player:get_player_name()
290 if handles[player_name] then
291 minetest.sound_stop(handles[player_name])
292 handles[player_name] = nil
294 end)
299 -- ABMs
302 -- Extinguish all flames quickly with water and such
304 minetest.register_abm({
305 label = "Extinguish flame",
306 nodenames = {"mcl_fire:fire", "mcl_fire:eternal_fire"},
307 neighbors = {"group:puts_out_fire"},
308 interval = 3,
309 chance = 1,
310 catch_up = false,
311 action = function(pos, node, active_object_count, active_object_count_wider)
312 minetest.remove_node(pos)
313 minetest.sound_play("fire_extinguish_flame",
314 {pos = pos, max_hear_distance = 16, gain = 0.15})
315 end,
319 -- Enable the following ABMs according to 'enable fire' setting
321 local fire_enabled = minetest.settings:get_bool("enable_fire")
322 if fire_enabled == nil then
323 -- New setting not specified, check for old setting.
324 -- If old setting is also not specified, 'not nil' is true.
325 fire_enabled = not minetest.settings:get_bool("disable_fire")
328 if not fire_enabled then
330 -- Remove fire only if fire disabled
331 minetest.register_abm({
332 label = "Remove disabled fire",
333 nodenames = {"mcl_fire:fire"},
334 interval = 7,
335 chance = 1,
336 catch_up = false,
337 action = minetest.remove_node,
340 -- Set fire to air nodes (inverse pyramid pattern) above lava source
341 minetest.register_abm({
342 label = "Ignite fire by lava",
343 nodenames = {"mcl_core:lava_source"},
344 interval = 7,
345 chance = 2,
346 catch_up = false,
347 action = function(pos)
348 local function try_ignite(airs)
349 while #airs > 0 do
350 local r = math.random(1, #airs)
351 if minetest.find_node_near(airs[r], 1, {"group:flammable", "group:flammable_lava"}) then
352 minetest.set_node(airs[r], {name="mcl_fire:fire"})
353 return true
354 else
355 table.remove(airs, r)
358 return false
360 local airs1 = minetest.find_nodes_in_area({x=pos.x-1, y=pos.y+1, z=pos.z-1}, {x=pos.x+1, y=pos.y+1, z=pos.z+1}, {"air"})
361 local ok = try_ignite(airs1)
362 if not ok then
363 local airs2 = minetest.find_nodes_in_area({x=pos.x-2, y=pos.y+2, z=pos.z-2}, {x=pos.x+2, y=pos.y+2, z=pos.z+2}, {"air"})
364 try_ignite(airs2)
366 end,
369 else -- Fire enabled
371 -- Turn flammable nodes around fire into fire
372 minetest.register_abm({
373 label = "Remove flammable nodes",
374 nodenames = {"group:fire"},
375 neighbors = {"group:flammable"},
376 interval = 5,
377 chance = 18,
378 catch_up = false,
379 action = function(pos, node, active_object_count, active_object_count_wider)
380 local p = minetest.find_node_near(pos, 1, {"group:flammable"})
381 if p then
382 local flammable_node = minetest.get_node(p)
383 local def = minetest.registered_nodes[flammable_node.name]
384 if def.on_burn then
385 def.on_burn(p)
386 else
387 minetest.set_node(p, {name="mcl_fire:fire"})
388 minetest.check_for_falling(p)
391 end,
396 -- Set pointed_thing on (normal) fire
397 mcl_fire.set_fire = function(pointed_thing)
398 local n = minetest.get_node(pointed_thing.above)
399 if n.name == "air" and not minetest.is_protected(pointed_thing.above, "fire") then
400 minetest.add_node(pointed_thing.above, {name="mcl_fire:fire"})
404 minetest.register_alias("mcl_fire:basic_flame", "mcl_fire:fire")
405 minetest.register_alias("fire:basic_flame", "mcl_fire:fire")
406 minetest.register_alias("fire:permanent_flame", "mcl_fire:eternal_flame")
408 dofile(minetest.get_modpath(minetest.get_current_modname()).."/flint_and_steel.lua")
409 dofile(minetest.get_modpath(minetest.get_current_modname()).."/fire_charge.lua")