]> git.lizzy.rs Git - dragonfireclient.git/blob - src/nodemetadata.cpp
6822173cb39771b65ee5685c4c1f6353c03e8d02
[dragonfireclient.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 // Prototype
103 SignNodeMetadata proto_SignNodeMetadata("");
104
105 SignNodeMetadata::SignNodeMetadata(std::string text):
106         m_text(text)
107 {
108         NodeMetadata::registerType(typeId(), create);
109 }
110 u16 SignNodeMetadata::typeId() const
111 {
112         return CONTENT_SIGN_WALL;
113 }
114 NodeMetadata* SignNodeMetadata::create(std::istream &is)
115 {
116         std::string text = deSerializeString(is);
117         return new SignNodeMetadata(text);
118 }
119 NodeMetadata* SignNodeMetadata::clone()
120 {
121         return new SignNodeMetadata(m_text);
122 }
123 void SignNodeMetadata::serializeBody(std::ostream &os)
124 {
125         os<<serializeString(m_text);
126 }
127 std::string SignNodeMetadata::infoText()
128 {
129         return std::string("\"")+m_text+"\"";
130 }
131
132 /*
133         ChestNodeMetadata
134 */
135
136 // Prototype
137 ChestNodeMetadata proto_ChestNodeMetadata;
138
139 ChestNodeMetadata::ChestNodeMetadata()
140 {
141         NodeMetadata::registerType(typeId(), create);
142         
143         m_inventory = new Inventory();
144         m_inventory->addList("0", 8*4);
145 }
146 ChestNodeMetadata::~ChestNodeMetadata()
147 {
148         delete m_inventory;
149 }
150 u16 ChestNodeMetadata::typeId() const
151 {
152         return CONTENT_CHEST;
153 }
154 NodeMetadata* ChestNodeMetadata::create(std::istream &is)
155 {
156         ChestNodeMetadata *d = new ChestNodeMetadata();
157         d->m_inventory->deSerialize(is);
158         return d;
159 }
160 NodeMetadata* ChestNodeMetadata::clone()
161 {
162         ChestNodeMetadata *d = new ChestNodeMetadata();
163         *d->m_inventory = *m_inventory;
164         return d;
165 }
166 void ChestNodeMetadata::serializeBody(std::ostream &os)
167 {
168         m_inventory->serialize(os);
169 }
170 std::string ChestNodeMetadata::infoText()
171 {
172         return "Chest";
173 }
174 bool ChestNodeMetadata::nodeRemovalDisabled()
175 {
176         /*
177                 Disable removal if chest contains something
178         */
179         InventoryList *list = m_inventory->getList("0");
180         if(list == NULL)
181                 return false;
182         if(list->getUsedSlots() == 0)
183                 return false;
184         return true;
185 }
186
187 /*
188         FurnaceNodeMetadata
189 */
190
191 // Prototype
192 FurnaceNodeMetadata proto_FurnaceNodeMetadata;
193
194 FurnaceNodeMetadata::FurnaceNodeMetadata()
195 {
196         NodeMetadata::registerType(typeId(), create);
197         
198         m_inventory = new Inventory();
199         m_inventory->addList("fuel", 1);
200         m_inventory->addList("src", 1);
201         m_inventory->addList("dst", 4);
202
203         m_step_accumulator = 0;
204         m_fuel_totaltime = 0;
205         m_fuel_time = 0;
206         m_src_totaltime = 0;
207         m_src_time = 0;
208 }
209 FurnaceNodeMetadata::~FurnaceNodeMetadata()
210 {
211         delete m_inventory;
212 }
213 u16 FurnaceNodeMetadata::typeId() const
214 {
215         return CONTENT_FURNACE;
216 }
217 NodeMetadata* FurnaceNodeMetadata::clone()
218 {
219         FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
220         *d->m_inventory = *m_inventory;
221         return d;
222 }
223 NodeMetadata* FurnaceNodeMetadata::create(std::istream &is)
224 {
225         FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
226
227         d->m_inventory->deSerialize(is);
228
229         int temp;
230         is>>temp;
231         d->m_fuel_totaltime = (float)temp/10;
232         is>>temp;
233         d->m_fuel_time = (float)temp/10;
234
235         return d;
236 }
237 void FurnaceNodeMetadata::serializeBody(std::ostream &os)
238 {
239         m_inventory->serialize(os);
240         os<<itos(m_fuel_totaltime*10)<<" ";
241         os<<itos(m_fuel_time*10)<<" ";
242 }
243 std::string FurnaceNodeMetadata::infoText()
244 {
245         //return "Furnace";
246         if(m_fuel_time >= m_fuel_totaltime)
247         {
248                 InventoryList *src_list = m_inventory->getList("src");
249                 assert(src_list);
250                 InventoryItem *src_item = src_list->getItem(0);
251
252                 if(src_item)
253                         return "Furnace is out of fuel";
254                 else
255                         return "Furnace is inactive";
256         }
257         else
258         {
259                 std::string s = "Furnace is active (";
260                 s += itos(m_fuel_time/m_fuel_totaltime*100);
261                 s += "%)";
262                 return s;
263         }
264 }
265 void FurnaceNodeMetadata::inventoryModified()
266 {
267         dstream<<"Furnace inventory modification callback"<<std::endl;
268 }
269 bool FurnaceNodeMetadata::step(float dtime)
270 {
271         if(dtime > 60.0)
272                 dstream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl;
273         // Update at a fixed frequency
274         const float interval = 2.0;
275         m_step_accumulator += dtime;
276         bool changed = false;
277         while(m_step_accumulator > interval)
278         {
279                 m_step_accumulator -= interval;
280                 dtime = interval;
281
282                 //dstream<<"Furnace step dtime="<<dtime<<std::endl;
283                 
284                 InventoryList *dst_list = m_inventory->getList("dst");
285                 assert(dst_list);
286
287                 InventoryList *src_list = m_inventory->getList("src");
288                 assert(src_list);
289                 InventoryItem *src_item = src_list->getItem(0);
290                 
291                 // Start only if there are free slots in dst, so that it can
292                 // accomodate any result item
293                 if(dst_list->getFreeSlots() > 0 && src_item && src_item->isCookable())
294                 {
295                         m_src_totaltime = 3;
296                 }
297                 else
298                 {
299                         m_src_time = 0;
300                         m_src_totaltime = 0;
301                 }
302                 
303                 /*
304                         If fuel is burning, increment the burn counters.
305                         If item finishes cooking, move it to result.
306                 */
307                 if(m_fuel_time < m_fuel_totaltime)
308                 {
309                         //dstream<<"Furnace is active"<<std::endl;
310                         m_fuel_time += dtime;
311                         m_src_time += dtime;
312                         if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001
313                                         && src_item)
314                         {
315                                 InventoryItem *cookresult = src_item->createCookResult();
316                                 dst_list->addItem(cookresult);
317                                 src_list->decrementMaterials(1);
318                                 m_src_time = 0;
319                                 m_src_totaltime = 0;
320                         }
321                         changed = true;
322                         continue;
323                 }
324                 
325                 /*
326                         If there is no source item or source item is not cookable, stop loop.
327                 */
328                 if(src_item == NULL || m_src_totaltime < 0.001)
329                 {
330                         m_step_accumulator = 0;
331                         break;
332                 }
333                 
334                 //dstream<<"Furnace is out of fuel"<<std::endl;
335
336                 InventoryList *fuel_list = m_inventory->getList("fuel");
337                 assert(fuel_list);
338                 InventoryItem *fuel_item = fuel_list->getItem(0);
339
340                 if(ItemSpec(ITEM_MATERIAL, CONTENT_TREE).checkItem(fuel_item))
341                 {
342                         m_fuel_totaltime = 30;
343                         m_fuel_time = 0;
344                         fuel_list->decrementMaterials(1);
345                         changed = true;
346                 }
347                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_WOOD).checkItem(fuel_item))
348                 {
349                         m_fuel_totaltime = 30/4;
350                         m_fuel_time = 0;
351                         fuel_list->decrementMaterials(1);
352                         changed = true;
353                 }
354                 else if(ItemSpec(ITEM_CRAFT, "Stick").checkItem(fuel_item))
355                 {
356                         m_fuel_totaltime = 30/4/4;
357                         m_fuel_time = 0;
358                         fuel_list->decrementMaterials(1);
359                         changed = true;
360                 }
361                 else if(ItemSpec(ITEM_CRAFT, "lump_of_coal").checkItem(fuel_item))
362                 {
363                         m_fuel_totaltime = 40;
364                         m_fuel_time = 0;
365                         fuel_list->decrementMaterials(1);
366                         changed = true;
367                 }
368                 else
369                 {
370                         //dstream<<"No fuel found"<<std::endl;
371                         // No fuel, stop loop.
372                         m_step_accumulator = 0;
373                         break;
374                 }
375         }
376         return changed;
377 }
378
379 /*
380         NodeMetadatalist
381 */
382
383 void NodeMetadataList::serialize(std::ostream &os)
384 {
385         u8 buf[6];
386         
387         u16 version = 1;
388         writeU16(buf, version);
389         os.write((char*)buf, 2);
390
391         u16 count = m_data.size();
392         writeU16(buf, count);
393         os.write((char*)buf, 2);
394
395         for(core::map<v3s16, NodeMetadata*>::Iterator
396                         i = m_data.getIterator();
397                         i.atEnd()==false; i++)
398         {
399                 v3s16 p = i.getNode()->getKey();
400                 NodeMetadata *data = i.getNode()->getValue();
401                 
402                 u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
403                 writeU16(buf, p16);
404                 os.write((char*)buf, 2);
405
406                 data->serialize(os);
407         }
408         
409 }
410 void NodeMetadataList::deSerialize(std::istream &is)
411 {
412         m_data.clear();
413
414         u8 buf[6];
415         
416         is.read((char*)buf, 2);
417         u16 version = readU16(buf);
418
419         if(version > 1)
420         {
421                 dstream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
422                                 <<std::endl;
423                 throw SerializationError("NodeMetadataList::deSerialize");
424         }
425         
426         is.read((char*)buf, 2);
427         u16 count = readU16(buf);
428         
429         for(u16 i=0; i<count; i++)
430         {
431                 is.read((char*)buf, 2);
432                 u16 p16 = readU16(buf);
433
434                 v3s16 p(0,0,0);
435                 p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
436                 p16 -= p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
437                 p.Y += p16 / MAP_BLOCKSIZE;
438                 p16 -= p.Y * MAP_BLOCKSIZE;
439                 p.X += p16;
440                 
441                 NodeMetadata *data = NodeMetadata::deSerialize(is);
442
443                 if(data == NULL)
444                         continue;
445                 
446                 if(m_data.find(p))
447                 {
448                         dstream<<"WARNING: NodeMetadataList::deSerialize(): "
449                                         <<"already set data at position"
450                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
451                                         <<std::endl;
452                         delete data;
453                         continue;
454                 }
455
456                 m_data.insert(p, data);
457         }
458 }
459         
460 NodeMetadataList::~NodeMetadataList()
461 {
462         for(core::map<v3s16, NodeMetadata*>::Iterator
463                         i = m_data.getIterator();
464                         i.atEnd()==false; i++)
465         {
466                 delete i.getNode()->getValue();
467         }
468 }
469
470 NodeMetadata* NodeMetadataList::get(v3s16 p)
471 {
472         core::map<v3s16, NodeMetadata*>::Node *n;
473         n = m_data.find(p);
474         if(n == NULL)
475                 return NULL;
476         return n->getValue();
477 }
478
479 void NodeMetadataList::remove(v3s16 p)
480 {
481         NodeMetadata *olddata = get(p);
482         if(olddata)
483         {
484                 delete olddata;
485                 m_data.remove(p);
486         }
487 }
488
489 void NodeMetadataList::set(v3s16 p, NodeMetadata *d)
490 {
491         remove(p);
492         m_data.insert(p, d);
493 }
494
495 bool NodeMetadataList::step(float dtime)
496 {
497         bool something_changed = false;
498         for(core::map<v3s16, NodeMetadata*>::Iterator
499                         i = m_data.getIterator();
500                         i.atEnd()==false; i++)
501         {
502                 v3s16 p = i.getNode()->getKey();
503                 NodeMetadata *meta = i.getNode()->getValue();
504                 bool changed = meta->step(dtime);
505                 if(changed)
506                         something_changed = true;
507                 /*if(res.inventory_changed)
508                 {
509                         std::string inv_id;
510                         inv_id += "nodemeta:";
511                         inv_id += itos(p.X);
512                         inv_id += ",";
513                         inv_id += itos(p.Y);
514                         inv_id += ",";
515                         inv_id += itos(p.Z);
516                         InventoryContext c;
517                         c.current_player = NULL;
518                         inv_mgr->inventoryModified(&c, inv_id);
519                 }*/
520         }
521         return something_changed;
522 }
523