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