21b4ed01d527042217e93f674ca2ba5ba7ba8f3b
[oweals/minetest.git] / src / nodemetadata.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 "nodemetadata.h"
21 #include "utility.h"
22 #include "mapnode.h"
23 #include "exceptions.h"
24 #include "inventory.h"
25 #include <sstream>
26
27 /*
28         NodeMetadata
29 */
30
31 core::map<u16, NodeMetadata::Factory> NodeMetadata::m_types;
32
33 NodeMetadata::NodeMetadata()
34 {
35 }
36
37 NodeMetadata::~NodeMetadata()
38 {
39 }
40
41 NodeMetadata* NodeMetadata::deSerialize(std::istream &is)
42 {
43         // Read id
44         u8 buf[2];
45         is.read((char*)buf, 2);
46         s16 id = readS16(buf);
47         
48         // Read data
49         std::string data = deSerializeString(is);
50         
51         // Find factory function
52         core::map<u16, Factory>::Node *n;
53         n = m_types.find(id);
54         if(n == NULL)
55         {
56                 // If factory is not found, just return.
57                 dstream<<"WARNING: NodeMetadata: No factory for typeId="
58                                 <<id<<std::endl;
59                 return NULL;
60         }
61         
62         // Try to load the metadata. If it fails, just return.
63         try
64         {
65                 std::istringstream iss(data, std::ios_base::binary);
66                 
67                 Factory f = n->getValue();
68                 NodeMetadata *meta = (*f)(iss);
69                 return meta;
70         }
71         catch(SerializationError &e)
72         {
73                 dstream<<"WARNING: NodeMetadata: ignoring SerializationError"<<std::endl;
74                 return NULL;
75         }
76 }
77
78 void NodeMetadata::serialize(std::ostream &os)
79 {
80         u8 buf[2];
81         writeU16(buf, typeId());
82         os.write((char*)buf, 2);
83         
84         std::ostringstream oss(std::ios_base::binary);
85         serializeBody(oss);
86         os<<serializeString(oss.str());
87 }
88
89 void NodeMetadata::registerType(u16 id, Factory f)
90 {
91         core::map<u16, Factory>::Node *n;
92         n = m_types.find(id);
93         if(n)
94                 return;
95         m_types.insert(id, f);
96 }
97
98 /*
99         SignNodeMetadata
100 */
101
102 SignNodeMetadata::SignNodeMetadata(std::string text):
103         m_text(text)
104 {
105         NodeMetadata::registerType(typeId(), create);
106 }
107 u16 SignNodeMetadata::typeId() const
108 {
109         return CONTENT_SIGN_WALL;
110 }
111 NodeMetadata* SignNodeMetadata::create(std::istream &is)
112 {
113         std::string text = deSerializeString(is);
114         return new SignNodeMetadata(text);
115 }
116 NodeMetadata* SignNodeMetadata::clone()
117 {
118         return new SignNodeMetadata(m_text);
119 }
120 void SignNodeMetadata::serializeBody(std::ostream &os)
121 {
122         os<<serializeString(m_text);
123 }
124 std::string SignNodeMetadata::infoText()
125 {
126         return std::string("\"")+m_text+"\"";
127 }
128
129 /*
130         ChestNodeMetadata
131 */
132
133 ChestNodeMetadata::ChestNodeMetadata()
134 {
135         NodeMetadata::registerType(typeId(), create);
136         
137         m_inventory = new Inventory();
138         m_inventory->addList("0", 8*4);
139 }
140 ChestNodeMetadata::~ChestNodeMetadata()
141 {
142         delete m_inventory;
143 }
144 u16 ChestNodeMetadata::typeId() const
145 {
146         return CONTENT_CHEST;
147 }
148 NodeMetadata* ChestNodeMetadata::create(std::istream &is)
149 {
150         ChestNodeMetadata *d = new ChestNodeMetadata();
151         d->m_inventory->deSerialize(is);
152         return d;
153 }
154 NodeMetadata* ChestNodeMetadata::clone()
155 {
156         ChestNodeMetadata *d = new ChestNodeMetadata();
157         *d->m_inventory = *m_inventory;
158         return d;
159 }
160 void ChestNodeMetadata::serializeBody(std::ostream &os)
161 {
162         m_inventory->serialize(os);
163 }
164 std::string ChestNodeMetadata::infoText()
165 {
166         return "Chest";
167 }
168
169 /*
170         FurnaceNodeMetadata
171 */
172
173 FurnaceNodeMetadata::FurnaceNodeMetadata()
174 {
175         NodeMetadata::registerType(typeId(), create);
176         
177         m_inventory = new Inventory();
178         m_inventory->addList("fuel", 1);
179         m_inventory->addList("src", 1);
180         m_inventory->addList("dst", 4);
181
182         m_step_accumulator = 0;
183         m_fuel_totaltime = 0;
184         m_fuel_time = 0;
185         m_src_totaltime = 0;
186         m_src_time = 0;
187 }
188 FurnaceNodeMetadata::~FurnaceNodeMetadata()
189 {
190         delete m_inventory;
191 }
192 u16 FurnaceNodeMetadata::typeId() const
193 {
194         return CONTENT_FURNACE;
195 }
196 NodeMetadata* FurnaceNodeMetadata::clone()
197 {
198         FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
199         *d->m_inventory = *m_inventory;
200         return d;
201 }
202 NodeMetadata* FurnaceNodeMetadata::create(std::istream &is)
203 {
204         FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
205
206         d->m_inventory->deSerialize(is);
207
208         int temp;
209         is>>temp;
210         d->m_fuel_totaltime = (float)temp/10;
211         is>>temp;
212         d->m_fuel_time = (float)temp/10;
213
214         return d;
215 }
216 void FurnaceNodeMetadata::serializeBody(std::ostream &os)
217 {
218         m_inventory->serialize(os);
219         os<<itos(m_fuel_totaltime*10)<<" ";
220         os<<itos(m_fuel_time*10)<<" ";
221 }
222 std::string FurnaceNodeMetadata::infoText()
223 {
224         //return "Furnace";
225         if(m_fuel_time >= m_fuel_totaltime)
226         {
227                 InventoryList *src_list = m_inventory->getList("src");
228                 assert(src_list);
229                 InventoryItem *src_item = src_list->getItem(0);
230
231                 if(src_item)
232                         return "Furnace is out of fuel";
233                 else
234                         return "Furnace is inactive";
235         }
236         else
237         {
238                 std::string s = "Furnace is active (";
239                 s += itos(m_fuel_time/m_fuel_totaltime*100);
240                 s += "%)";
241                 return s;
242         }
243 }
244 void FurnaceNodeMetadata::inventoryModified()
245 {
246         dstream<<"Furnace inventory modification callback"<<std::endl;
247 }
248 bool FurnaceNodeMetadata::step(float dtime)
249 {
250         // Update at a fixed frequency
251         const float interval = 0.5;
252         m_step_accumulator += dtime;
253         if(m_step_accumulator < interval)
254                 return false;
255         m_step_accumulator -= interval;
256         dtime = interval;
257
258         //dstream<<"Furnace step dtime="<<dtime<<std::endl;
259         
260         InventoryList *dst_list = m_inventory->getList("dst");
261         assert(dst_list);
262
263         InventoryList *src_list = m_inventory->getList("src");
264         assert(src_list);
265         InventoryItem *src_item = src_list->getItem(0);
266         
267         // Start only if there are free slots in dst, so that it can
268         // accomodate any result item
269         if(dst_list->getFreeSlots() > 0)
270         {
271                 m_src_totaltime = 3;
272         }
273         else
274         {
275                 m_src_time = 0;
276                 m_src_totaltime = 0;
277         }
278
279         if(m_fuel_time < m_fuel_totaltime)
280         {
281                 //dstream<<"Furnace is active"<<std::endl;
282                 m_fuel_time += dtime;
283                 m_src_time += dtime;
284                 if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001)
285                 {
286                         src_list->decrementMaterials(1);
287                         InventoryItem *cookresult = src_item->createCookResult();
288                         dst_list->addItem(cookresult);
289                         m_src_time = 0;
290                         m_src_totaltime = 0;
291                 }
292                 return true;
293         }
294         
295         if(src_item == NULL || m_src_totaltime < 0.001)
296         {
297                 return false;
298         }
299         
300         bool changed = false;
301
302         //dstream<<"Furnace is out of fuel"<<std::endl;
303
304         InventoryList *fuel_list = m_inventory->getList("fuel");
305         assert(fuel_list);
306         InventoryItem *fuel_item = fuel_list->getItem(0);
307
308         if(ItemSpec(ITEM_MATERIAL, CONTENT_TREE).checkItem(fuel_item))
309         {
310                 m_fuel_totaltime = 10;
311                 m_fuel_time = 0;
312                 fuel_list->decrementMaterials(1);
313                 changed = true;
314         }
315         else if(ItemSpec(ITEM_MATERIAL, CONTENT_WOOD).checkItem(fuel_item))
316         {
317                 m_fuel_totaltime = 5;
318                 m_fuel_time = 0;
319                 fuel_list->decrementMaterials(1);
320                 changed = true;
321         }
322         else if(ItemSpec(ITEM_CRAFT, "lump_of_coal").checkItem(fuel_item))
323         {
324                 m_fuel_totaltime = 10;
325                 m_fuel_time = 0;
326                 fuel_list->decrementMaterials(1);
327                 changed = true;
328         }
329         else
330         {
331                 //dstream<<"No fuel found"<<std::endl;
332         }
333
334         return changed;
335 }
336
337 /*
338         NodeMetadatalist
339 */
340
341 void NodeMetadataList::serialize(std::ostream &os)
342 {
343         u8 buf[6];
344         
345         u16 version = 1;
346         writeU16(buf, version);
347         os.write((char*)buf, 2);
348
349         u16 count = m_data.size();
350         writeU16(buf, count);
351         os.write((char*)buf, 2);
352
353         for(core::map<v3s16, NodeMetadata*>::Iterator
354                         i = m_data.getIterator();
355                         i.atEnd()==false; i++)
356         {
357                 v3s16 p = i.getNode()->getKey();
358                 NodeMetadata *data = i.getNode()->getValue();
359                 
360                 u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
361                 writeU16(buf, p16);
362                 os.write((char*)buf, 2);
363
364                 data->serialize(os);
365         }
366         
367 }
368 void NodeMetadataList::deSerialize(std::istream &is)
369 {
370         m_data.clear();
371
372         u8 buf[6];
373         
374         is.read((char*)buf, 2);
375         u16 version = readU16(buf);
376
377         if(version > 1)
378         {
379                 dstream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
380                                 <<std::endl;
381                 throw SerializationError("NodeMetadataList::deSerialize");
382         }
383         
384         is.read((char*)buf, 2);
385         u16 count = readU16(buf);
386         
387         for(u16 i=0; i<count; i++)
388         {
389                 is.read((char*)buf, 2);
390                 u16 p16 = readU16(buf);
391
392                 v3s16 p(0,0,0);
393                 p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
394                 p16 -= p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
395                 p.Y += p16 / MAP_BLOCKSIZE;
396                 p16 -= p.Y * MAP_BLOCKSIZE;
397                 p.X += p16;
398                 
399                 NodeMetadata *data = NodeMetadata::deSerialize(is);
400
401                 if(data == NULL)
402                         continue;
403                 
404                 if(m_data.find(p))
405                 {
406                         dstream<<"WARNING: NodeMetadataList::deSerialize(): "
407                                         <<"already set data at position"
408                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
409                                         <<std::endl;
410                         delete data;
411                         continue;
412                 }
413
414                 m_data.insert(p, data);
415         }
416 }
417         
418 NodeMetadataList::~NodeMetadataList()
419 {
420         for(core::map<v3s16, NodeMetadata*>::Iterator
421                         i = m_data.getIterator();
422                         i.atEnd()==false; i++)
423         {
424                 delete i.getNode()->getValue();
425         }
426 }
427
428 NodeMetadata* NodeMetadataList::get(v3s16 p)
429 {
430         core::map<v3s16, NodeMetadata*>::Node *n;
431         n = m_data.find(p);
432         if(n == NULL)
433                 return NULL;
434         return n->getValue();
435 }
436
437 void NodeMetadataList::remove(v3s16 p)
438 {
439         NodeMetadata *olddata = get(p);
440         if(olddata)
441         {
442                 delete olddata;
443                 m_data.remove(p);
444         }
445 }
446
447 void NodeMetadataList::set(v3s16 p, NodeMetadata *d)
448 {
449         remove(p);
450         m_data.insert(p, d);
451 }
452
453 bool NodeMetadataList::step(float dtime)
454 {
455         bool something_changed = false;
456         for(core::map<v3s16, NodeMetadata*>::Iterator
457                         i = m_data.getIterator();
458                         i.atEnd()==false; i++)
459         {
460                 v3s16 p = i.getNode()->getKey();
461                 NodeMetadata *meta = i.getNode()->getValue();
462                 bool changed = meta->step(dtime);
463                 if(changed)
464                         something_changed = true;
465                 /*if(res.inventory_changed)
466                 {
467                         std::string inv_id;
468                         inv_id += "nodemeta:";
469                         inv_id += itos(p.X);
470                         inv_id += ",";
471                         inv_id += itos(p.Y);
472                         inv_id += ",";
473                         inv_id += itos(p.Z);
474                         InventoryContext c;
475                         c.current_player = NULL;
476                         inv_mgr->inventoryModified(&c, inv_id);
477                 }*/
478         }
479         return something_changed;
480 }
481