utility.h: Change Buffer's interface to be more compatible with SharedBuffer's interf...
[oweals/minetest.git] / src / content_nodemeta.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License along
16 with this program; if not, write to the Free Software Foundation, Inc.,
17 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "content_nodemeta.h"
21 #include "inventory.h"
22 #include "content_mapnode.h"
23 #include "log.h"
24
25 /*
26         SignNodeMetadata
27 */
28
29 // Prototype
30 SignNodeMetadata proto_SignNodeMetadata("");
31
32 SignNodeMetadata::SignNodeMetadata(std::string text):
33         m_text(text)
34 {
35         NodeMetadata::registerType(typeId(), create);
36 }
37 u16 SignNodeMetadata::typeId() const
38 {
39         return CONTENT_SIGN_WALL;
40 }
41 NodeMetadata* SignNodeMetadata::create(std::istream &is)
42 {
43         std::string text = deSerializeString(is);
44         return new SignNodeMetadata(text);
45 }
46 NodeMetadata* SignNodeMetadata::clone()
47 {
48         return new SignNodeMetadata(m_text);
49 }
50 void SignNodeMetadata::serializeBody(std::ostream &os)
51 {
52         os<<serializeString(m_text);
53 }
54 std::string SignNodeMetadata::infoText()
55 {
56         return std::string("\"")+m_text+"\"";
57 }
58
59 /*
60         ChestNodeMetadata
61 */
62
63 // Prototype
64 ChestNodeMetadata proto_ChestNodeMetadata;
65
66 ChestNodeMetadata::ChestNodeMetadata()
67 {
68         NodeMetadata::registerType(typeId(), create);
69         
70         m_inventory = new Inventory();
71         m_inventory->addList("0", 8*4);
72 }
73 ChestNodeMetadata::~ChestNodeMetadata()
74 {
75         delete m_inventory;
76 }
77 u16 ChestNodeMetadata::typeId() const
78 {
79         return CONTENT_CHEST;
80 }
81 NodeMetadata* ChestNodeMetadata::create(std::istream &is)
82 {
83         ChestNodeMetadata *d = new ChestNodeMetadata();
84         d->m_inventory->deSerialize(is);
85         return d;
86 }
87 NodeMetadata* ChestNodeMetadata::clone()
88 {
89         ChestNodeMetadata *d = new ChestNodeMetadata();
90         *d->m_inventory = *m_inventory;
91         return d;
92 }
93 void ChestNodeMetadata::serializeBody(std::ostream &os)
94 {
95         m_inventory->serialize(os);
96 }
97 std::string ChestNodeMetadata::infoText()
98 {
99         return "Chest";
100 }
101 bool ChestNodeMetadata::nodeRemovalDisabled()
102 {
103         /*
104                 Disable removal if chest contains something
105         */
106         InventoryList *list = m_inventory->getList("0");
107         if(list == NULL)
108                 return false;
109         if(list->getUsedSlots() == 0)
110                 return false;
111         return true;
112 }
113 std::string ChestNodeMetadata::getInventoryDrawSpecString()
114 {
115         return
116                 "invsize[8,9;]"
117                 "list[current_name;0;0,0;8,4;]"
118                 "list[current_player;main;0,5;8,4;]";
119 }
120
121 /*
122         LockingChestNodeMetadata
123 */
124
125 // Prototype
126 LockingChestNodeMetadata proto_LockingChestNodeMetadata;
127
128 LockingChestNodeMetadata::LockingChestNodeMetadata()
129 {
130         NodeMetadata::registerType(typeId(), create);
131
132         m_inventory = new Inventory();
133         m_inventory->addList("0", 8*4);
134 }
135 LockingChestNodeMetadata::~LockingChestNodeMetadata()
136 {
137         delete m_inventory;
138 }
139 u16 LockingChestNodeMetadata::typeId() const
140 {
141         return CONTENT_LOCKABLE_CHEST;
142 }
143 NodeMetadata* LockingChestNodeMetadata::create(std::istream &is)
144 {
145         LockingChestNodeMetadata *d = new LockingChestNodeMetadata();
146         d->setOwner(deSerializeString(is));
147         d->m_inventory->deSerialize(is);
148         return d;
149 }
150 NodeMetadata* LockingChestNodeMetadata::clone()
151 {
152         LockingChestNodeMetadata *d = new LockingChestNodeMetadata();
153         *d->m_inventory = *m_inventory;
154         return d;
155 }
156 void LockingChestNodeMetadata::serializeBody(std::ostream &os)
157 {
158         os<<serializeString(m_text);
159         m_inventory->serialize(os);
160 }
161 std::string LockingChestNodeMetadata::infoText()
162 {
163         return "Locking Chest";
164 }
165 bool LockingChestNodeMetadata::nodeRemovalDisabled()
166 {
167         /*
168                 Disable removal if chest contains something
169         */
170         InventoryList *list = m_inventory->getList("0");
171         if(list == NULL)
172                 return false;
173         if(list->getUsedSlots() == 0)
174                 return false;
175         return true;
176 }
177 std::string LockingChestNodeMetadata::getInventoryDrawSpecString()
178 {
179         return
180                 "invsize[8,9;]"
181                 "list[current_name;0;0,0;8,4;]"
182                 "list[current_player;main;0,5;8,4;]";
183 }
184
185 /*
186         FurnaceNodeMetadata
187 */
188
189 // Prototype
190 FurnaceNodeMetadata proto_FurnaceNodeMetadata;
191
192 FurnaceNodeMetadata::FurnaceNodeMetadata()
193 {
194         NodeMetadata::registerType(typeId(), create);
195         
196         m_inventory = new Inventory();
197         m_inventory->addList("fuel", 1);
198         m_inventory->addList("src", 1);
199         m_inventory->addList("dst", 4);
200
201         m_step_accumulator = 0;
202         m_fuel_totaltime = 0;
203         m_fuel_time = 0;
204         m_src_totaltime = 0;
205         m_src_time = 0;
206 }
207 FurnaceNodeMetadata::~FurnaceNodeMetadata()
208 {
209         delete m_inventory;
210 }
211 u16 FurnaceNodeMetadata::typeId() const
212 {
213         return CONTENT_FURNACE;
214 }
215 NodeMetadata* FurnaceNodeMetadata::clone()
216 {
217         FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
218         *d->m_inventory = *m_inventory;
219         return d;
220 }
221 NodeMetadata* FurnaceNodeMetadata::create(std::istream &is)
222 {
223         FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
224
225         d->m_inventory->deSerialize(is);
226
227         int temp;
228         is>>temp;
229         d->m_fuel_totaltime = (float)temp/10;
230         is>>temp;
231         d->m_fuel_time = (float)temp/10;
232
233         return d;
234 }
235 void FurnaceNodeMetadata::serializeBody(std::ostream &os)
236 {
237         m_inventory->serialize(os);
238         os<<itos(m_fuel_totaltime*10)<<" ";
239         os<<itos(m_fuel_time*10)<<" ";
240 }
241 std::string FurnaceNodeMetadata::infoText()
242 {
243         //return "Furnace";
244         if(m_fuel_time >= m_fuel_totaltime)
245         {
246                 const InventoryList *src_list = m_inventory->getList("src");
247                 assert(src_list);
248                 const InventoryItem *src_item = src_list->getItem(0);
249
250                 if(src_item && src_item->isCookable()) {
251                         InventoryList *dst_list = m_inventory->getList("dst");
252                         if(!dst_list->roomForCookedItem(src_item))
253                                 return "Furnace is overloaded";
254                         return "Furnace is out of fuel";
255                 }
256                 else
257                         return "Furnace is inactive";
258         }
259         else
260         {
261                 std::string s = "Furnace is active";
262                 // Do this so it doesn't always show (0%) for weak fuel
263                 if(m_fuel_totaltime > 3) {
264                         s += " (";
265                         s += itos(m_fuel_time/m_fuel_totaltime*100);
266                         s += "%)";
267                 }
268                 return s;
269         }
270 }
271 bool FurnaceNodeMetadata::nodeRemovalDisabled()
272 {
273         /*
274                 Disable removal if furnace is not empty
275         */
276         InventoryList *list[3] = {m_inventory->getList("src"),
277         m_inventory->getList("dst"), m_inventory->getList("fuel")};
278         
279         for(int i = 0; i < 3; i++) {
280                 if(list[i] == NULL)
281                         continue;
282                 if(list[i]->getUsedSlots() == 0)
283                         continue;
284                 return true;
285         }
286         return false;
287         
288 }
289 void FurnaceNodeMetadata::inventoryModified()
290 {
291         infostream<<"Furnace inventory modification callback"<<std::endl;
292 }
293 bool FurnaceNodeMetadata::step(float dtime)
294 {
295         if(dtime > 60.0)
296                 infostream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl;
297         // Update at a fixed frequency
298         const float interval = 2.0;
299         m_step_accumulator += dtime;
300         bool changed = false;
301         while(m_step_accumulator > interval)
302         {
303                 m_step_accumulator -= interval;
304                 dtime = interval;
305
306                 //infostream<<"Furnace step dtime="<<dtime<<std::endl;
307                 
308                 InventoryList *dst_list = m_inventory->getList("dst");
309                 assert(dst_list);
310
311                 InventoryList *src_list = m_inventory->getList("src");
312                 assert(src_list);
313                 InventoryItem *src_item = src_list->getItem(0);
314                 
315                 bool room_available = false;
316                 
317                 if(src_item && src_item->isCookable())
318                         room_available = dst_list->roomForCookedItem(src_item);
319                 
320                 // Start only if there are free slots in dst, so that it can
321                 // accomodate any result item
322                 if(room_available)
323                 {
324                         m_src_totaltime = 3;
325                 }
326                 else
327                 {
328                         m_src_time = 0;
329                         m_src_totaltime = 0;
330                 }
331                 
332                 /*
333                         If fuel is burning, increment the burn counters.
334                         If item finishes cooking, move it to result.
335                 */
336                 if(m_fuel_time < m_fuel_totaltime)
337                 {
338                         //infostream<<"Furnace is active"<<std::endl;
339                         m_fuel_time += dtime;
340                         m_src_time += dtime;
341                         if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001
342                                         && src_item)
343                         {
344                                 InventoryItem *cookresult = src_item->createCookResult();
345                                 dst_list->addItem(cookresult);
346                                 src_list->decrementMaterials(1);
347                                 m_src_time = 0;
348                                 m_src_totaltime = 0;
349                         }
350                         changed = true;
351                         
352                         // If the fuel was not used up this step, just keep burning it
353                         if(m_fuel_time < m_fuel_totaltime)
354                                 continue;
355                 }
356                 
357                 /*
358                         Get the source again in case it has all burned
359                 */
360                 src_item = src_list->getItem(0);
361                 
362                 /*
363                         If there is no source item, or the source item is not cookable,
364                         or the furnace is still cooking, or the furnace became overloaded, stop loop.
365                 */
366                 if(src_item == NULL || !room_available || m_fuel_time < m_fuel_totaltime ||
367                         dst_list->roomForCookedItem(src_item) == false)
368                 {
369                         m_step_accumulator = 0;
370                         break;
371                 }
372                 
373                 //infostream<<"Furnace is out of fuel"<<std::endl;
374
375                 InventoryList *fuel_list = m_inventory->getList("fuel");
376                 assert(fuel_list);
377                 const InventoryItem *fuel_item = fuel_list->getItem(0);
378
379                 if(ItemSpec(ITEM_MATERIAL, CONTENT_TREE).checkItem(fuel_item))
380                 {
381                         m_fuel_totaltime = 30;
382                         m_fuel_time = 0;
383                         fuel_list->decrementMaterials(1);
384                         changed = true;
385                 }
386                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_JUNGLETREE).checkItem(fuel_item))
387                 {
388                         m_fuel_totaltime = 30;
389                         m_fuel_time = 0;
390                         fuel_list->decrementMaterials(1);
391                         changed = true;
392                 }
393                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_FENCE).checkItem(fuel_item))
394                 {
395                         m_fuel_totaltime = 30/2;
396                         m_fuel_time = 0;
397                         fuel_list->decrementMaterials(1);
398                         changed = true;
399                 }
400                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_WOOD).checkItem(fuel_item))
401                 {
402                         m_fuel_totaltime = 30/4;
403                         m_fuel_time = 0;
404                         fuel_list->decrementMaterials(1);
405                         changed = true;
406                 }
407                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_BOOKSHELF).checkItem(fuel_item))
408                 {
409                         m_fuel_totaltime = 30/4;
410                         m_fuel_time = 0;
411                         fuel_list->decrementMaterials(1);
412                         changed = true;
413                 }
414                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_LEAVES).checkItem(fuel_item))
415                 {
416                         m_fuel_totaltime = 30/16;
417                         m_fuel_time = 0;
418                         fuel_list->decrementMaterials(1);
419                         changed = true;
420                 }
421                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_PAPYRUS).checkItem(fuel_item))
422                 {
423                         m_fuel_totaltime = 30/32;
424                         m_fuel_time = 0;
425                         fuel_list->decrementMaterials(1);
426                         changed = true;
427                 }
428                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_JUNGLEGRASS).checkItem(fuel_item))
429                 {
430                         m_fuel_totaltime = 30/32;
431                         m_fuel_time = 0;
432                         fuel_list->decrementMaterials(1);
433                         changed = true;
434                 }
435                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_CACTUS).checkItem(fuel_item))
436                 {
437                         m_fuel_totaltime = 30/4;
438                         m_fuel_time = 0;
439                         fuel_list->decrementMaterials(1);
440                         changed = true;
441                 }
442                 else if(ItemSpec(ITEM_CRAFT, "Stick").checkItem(fuel_item))
443                 {
444                         m_fuel_totaltime = 30/4/4;
445                         m_fuel_time = 0;
446                         fuel_list->decrementMaterials(1);
447                         changed = true;
448                 }
449                 else if(ItemSpec(ITEM_CRAFT, "lump_of_coal").checkItem(fuel_item))
450                 {
451                         m_fuel_totaltime = 40;
452                         m_fuel_time = 0;
453                         fuel_list->decrementMaterials(1);
454                         changed = true;
455                 }
456                 else
457                 {
458                         //infostream<<"No fuel found"<<std::endl;
459                         // No fuel, stop loop.
460                         m_step_accumulator = 0;
461                         break;
462                 }
463         }
464         return changed;
465 }
466 std::string FurnaceNodeMetadata::getInventoryDrawSpecString()
467 {
468         return
469                 "invsize[8,9;]"
470                 "list[current_name;fuel;2,3;1,1;]"
471                 "list[current_name;src;2,1;1,1;]"
472                 "list[current_name;dst;5,1;2,2;]"
473                 "list[current_player;main;0,5;8,4;]";
474 }
475
476