Prevent potential crash caused by moss growth ABM
[oweals/minetest_game.git] / mods / default / furnace.lua
1
2 --
3 -- Formspecs
4 --
5
6 function default.get_furnace_active_formspec(fuel_percent, item_percent)
7         return "size[8,8.5]"..
8                 "list[context;src;2.75,0.5;1,1;]"..
9                 "list[context;fuel;2.75,2.5;1,1;]"..
10                 "image[2.75,1.5;1,1;default_furnace_fire_bg.png^[lowpart:"..
11                 (100-fuel_percent)..":default_furnace_fire_fg.png]"..
12                 "image[3.75,1.5;1,1;gui_furnace_arrow_bg.png^[lowpart:"..
13                 (item_percent)..":gui_furnace_arrow_fg.png^[transformR270]"..
14                 "list[context;dst;4.75,0.96;2,2;]"..
15                 "list[current_player;main;0,4.25;8,1;]"..
16                 "list[current_player;main;0,5.5;8,3;8]"..
17                 "listring[context;dst]"..
18                 "listring[current_player;main]"..
19                 "listring[context;src]"..
20                 "listring[current_player;main]"..
21                 "listring[context;fuel]"..
22                 "listring[current_player;main]"..
23                 default.get_hotbar_bg(0, 4.25)
24 end
25
26 function default.get_furnace_inactive_formspec()
27         return "size[8,8.5]"..
28                 "list[context;src;2.75,0.5;1,1;]"..
29                 "list[context;fuel;2.75,2.5;1,1;]"..
30                 "image[2.75,1.5;1,1;default_furnace_fire_bg.png]"..
31                 "image[3.75,1.5;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
32                 "list[context;dst;4.75,0.96;2,2;]"..
33                 "list[current_player;main;0,4.25;8,1;]"..
34                 "list[current_player;main;0,5.5;8,3;8]"..
35                 "listring[context;dst]"..
36                 "listring[current_player;main]"..
37                 "listring[context;src]"..
38                 "listring[current_player;main]"..
39                 "listring[context;fuel]"..
40                 "listring[current_player;main]"..
41                 default.get_hotbar_bg(0, 4.25)
42 end
43
44 --
45 -- Node callback functions that are the same for active and inactive furnace
46 --
47
48 local function can_dig(pos, player)
49         local meta = minetest.get_meta(pos);
50         local inv = meta:get_inventory()
51         return inv:is_empty("fuel") and inv:is_empty("dst") and inv:is_empty("src")
52 end
53
54 local function allow_metadata_inventory_put(pos, listname, index, stack, player)
55         if minetest.is_protected(pos, player:get_player_name()) then
56                 return 0
57         end
58         local meta = minetest.get_meta(pos)
59         local inv = meta:get_inventory()
60         if listname == "fuel" then
61                 if minetest.get_craft_result({method="fuel", width=1, items={stack}}).time ~= 0 then
62                         if inv:is_empty("src") then
63                                 meta:set_string("infotext", "Furnace is empty")
64                         end
65                         return stack:get_count()
66                 else
67                         return 0
68                 end
69         elseif listname == "src" then
70                 return stack:get_count()
71         elseif listname == "dst" then
72                 return 0
73         end
74 end
75
76 local function allow_metadata_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
77         local meta = minetest.get_meta(pos)
78         local inv = meta:get_inventory()
79         local stack = inv:get_stack(from_list, from_index)
80         return allow_metadata_inventory_put(pos, to_list, to_index, stack, player)
81 end
82
83 local function allow_metadata_inventory_take(pos, listname, index, stack, player)
84         if minetest.is_protected(pos, player:get_player_name()) then
85                 return 0
86         end
87         return stack:get_count()
88 end
89
90 local function swap_node(pos, name)
91         local node = minetest.get_node(pos)
92         if node.name == name then
93                 return
94         end
95         node.name = name
96         minetest.swap_node(pos, node)
97 end
98
99 local function furnace_node_timer(pos, elapsed)
100         --
101         -- Inizialize metadata
102         --
103         local meta = minetest.get_meta(pos)
104         local fuel_time = meta:get_float("fuel_time") or 0
105         local src_time = meta:get_float("src_time") or 0
106         local fuel_totaltime = meta:get_float("fuel_totaltime") or 0
107
108         local inv = meta:get_inventory()
109         local srclist, fuellist
110
111         local cookable, cooked
112         local fuel
113
114         local update = true
115         while elapsed > 0 and update do
116                 update = false
117
118                 srclist = inv:get_list("src")
119                 fuellist = inv:get_list("fuel")
120
121                 --
122                 -- Cooking
123                 --
124
125                 -- Check if we have cookable content
126                 local aftercooked
127                 cooked, aftercooked = minetest.get_craft_result({method = "cooking", width = 1, items = srclist})
128                 cookable = cooked.time ~= 0
129
130                 local el = math.min(elapsed, fuel_totaltime - fuel_time)
131                 if cookable then -- fuel lasts long enough, adjust el to cooking duration
132                         el = math.min(el, cooked.time - src_time)
133                 end
134
135                 -- Check if we have enough fuel to burn
136                 if fuel_time < fuel_totaltime then
137                         -- The furnace is currently active and has enough fuel
138                         fuel_time = fuel_time + el
139                         -- If there is a cookable item then check if it is ready yet
140                         if cookable then
141                                 src_time = src_time + el
142                                 if src_time >= cooked.time then
143                                         -- Place result in dst list if possible
144                                         if inv:room_for_item("dst", cooked.item) then
145                                                 inv:add_item("dst", cooked.item)
146                                                 inv:set_stack("src", 1, aftercooked.items[1])
147                                                 src_time = src_time - cooked.time
148                                                 update = true
149                                         end
150                                 else
151                                         -- Item could not be cooked: probably missing fuel
152                                         update = true
153                                 end
154                         end
155                 else
156                         -- Furnace ran out of fuel
157                         if cookable then
158                                 -- We need to get new fuel
159                                 local afterfuel
160                                 fuel, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
161
162                                 if fuel.time == 0 then
163                                         -- No valid fuel in fuel list
164                                         fuel_totaltime = 0
165                                         src_time = 0
166                                 else
167                                         -- Take fuel from fuel list
168                                         inv:set_stack("fuel", 1, afterfuel.items[1])
169                                         update = true
170                                         fuel_totaltime = fuel.time + (fuel_totaltime - fuel_time)
171                                 end
172                         else
173                                 -- We don't need to get new fuel since there is no cookable item
174                                 fuel_totaltime = 0
175                                 src_time = 0
176                         end
177                         fuel_time = 0
178                 end
179
180                 elapsed = elapsed - el
181         end
182
183         if fuel and fuel_totaltime > fuel.time then
184                 fuel_totaltime = fuel.time
185         end
186         if srclist[1]:is_empty() then
187                 src_time = 0
188         end
189
190         --
191         -- Update formspec, infotext and node
192         --
193         local formspec
194         local item_state
195         local item_percent = 0
196         if cookable then
197                 item_percent = math.floor(src_time / cooked.time * 100)
198                 if item_percent > 100 then
199                         item_state = "100% (output full)"
200                 else
201                         item_state = item_percent .. "%"
202                 end
203         else
204                 if srclist[1]:is_empty() then
205                         item_state = "Empty"
206                 else
207                         item_state = "Not cookable"
208                 end
209         end
210
211         local fuel_state = "Empty"
212         local active = "inactive"
213         local result = false
214
215         if fuel_totaltime ~= 0 then
216                 active = "active"
217                 local fuel_percent = math.floor(fuel_time / fuel_totaltime * 100)
218                 fuel_state = fuel_percent .. "%"
219                 formspec = default.get_furnace_active_formspec(fuel_percent, item_percent)
220                 swap_node(pos, "default:furnace_active")
221                 -- make sure timer restarts automatically
222                 result = true
223         else
224                 if not fuellist[1]:is_empty() then
225                         fuel_state = "0%"
226                 end
227                 formspec = default.get_furnace_inactive_formspec()
228                 swap_node(pos, "default:furnace")
229                 -- stop timer on the inactive furnace
230                 minetest.get_node_timer(pos):stop()
231         end
232
233         local infotext = "Furnace " .. active .. "\n(Item: " .. item_state ..
234                 "; Fuel: " .. fuel_state .. ")"
235
236         --
237         -- Set meta values
238         --
239         meta:set_float("fuel_totaltime", fuel_totaltime)
240         meta:set_float("fuel_time", fuel_time)
241         meta:set_float("src_time", src_time)
242         meta:set_string("formspec", formspec)
243         meta:set_string("infotext", infotext)
244
245         return result
246 end
247
248 --
249 -- Node definitions
250 --
251
252 minetest.register_node("default:furnace", {
253         description = "Furnace",
254         tiles = {
255                 "default_furnace_top.png", "default_furnace_bottom.png",
256                 "default_furnace_side.png", "default_furnace_side.png",
257                 "default_furnace_side.png", "default_furnace_front.png"
258         },
259         paramtype2 = "facedir",
260         groups = {cracky=2},
261         legacy_facedir_simple = true,
262         is_ground_content = false,
263         sounds = default.node_sound_stone_defaults(),
264
265         can_dig = can_dig,
266
267         on_timer = furnace_node_timer,
268
269         on_construct = function(pos)
270                 local meta = minetest.get_meta(pos)
271                 meta:set_string("formspec", default.get_furnace_inactive_formspec())
272                 local inv = meta:get_inventory()
273                 inv:set_size('src', 1)
274                 inv:set_size('fuel', 1)
275                 inv:set_size('dst', 4)
276         end,
277
278         on_metadata_inventory_move = function(pos)
279                 minetest.get_node_timer(pos):start(1.0)
280         end,
281         on_metadata_inventory_put = function(pos)
282                 -- start timer function, it will sort out whether furnace can burn or not.
283                 minetest.get_node_timer(pos):start(1.0)
284         end,
285         on_blast = function(pos)
286                 local drops = {}
287                 default.get_inventory_drops(pos, "src", drops)
288                 default.get_inventory_drops(pos, "fuel", drops)
289                 default.get_inventory_drops(pos, "dst", drops)
290                 drops[#drops+1] = "default:furnace"
291                 minetest.remove_node(pos)
292                 return drops
293         end,
294
295         allow_metadata_inventory_put = allow_metadata_inventory_put,
296         allow_metadata_inventory_move = allow_metadata_inventory_move,
297         allow_metadata_inventory_take = allow_metadata_inventory_take,
298 })
299
300 minetest.register_node("default:furnace_active", {
301         description = "Furnace",
302         tiles = {
303                 "default_furnace_top.png", "default_furnace_bottom.png",
304                 "default_furnace_side.png", "default_furnace_side.png",
305                 "default_furnace_side.png",
306                 {
307                         image = "default_furnace_front_active.png",
308                         backface_culling = false,
309                         animation = {
310                                 type = "vertical_frames",
311                                 aspect_w = 16,
312                                 aspect_h = 16,
313                                 length = 1.5
314                         },
315                 }
316         },
317         paramtype2 = "facedir",
318         light_source = 8,
319         drop = "default:furnace",
320         groups = {cracky=2, not_in_creative_inventory=1},
321         legacy_facedir_simple = true,
322         is_ground_content = false,
323         sounds = default.node_sound_stone_defaults(),
324         on_timer = furnace_node_timer,
325
326         can_dig = can_dig,
327
328         allow_metadata_inventory_put = allow_metadata_inventory_put,
329         allow_metadata_inventory_move = allow_metadata_inventory_move,
330         allow_metadata_inventory_take = allow_metadata_inventory_take,
331 })