]> git.lizzy.rs Git - dragonfireclient.git/blob - src/nodemetadata.cpp
f9468e4fa14f2a3dcc21f08788fdb734b2d24712
[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                 if(m_fuel_time < m_fuel_totaltime)
304                 {
305                         //dstream<<"Furnace is active"<<std::endl;
306                         m_fuel_time += dtime;
307                         m_src_time += dtime;
308                         if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001
309                                         && src_item)
310                         {
311                                 InventoryItem *cookresult = src_item->createCookResult();
312                                 dst_list->addItem(cookresult);
313                                 src_list->decrementMaterials(1);
314                                 m_src_time = 0;
315                                 m_src_totaltime = 0;
316                         }
317                         changed = true;
318                         continue;
319                 }
320                 
321                 if(src_item == NULL || m_src_totaltime < 0.001)
322                 {
323                         continue;
324                 }
325                 
326                 //dstream<<"Furnace is out of fuel"<<std::endl;
327
328                 InventoryList *fuel_list = m_inventory->getList("fuel");
329                 assert(fuel_list);
330                 InventoryItem *fuel_item = fuel_list->getItem(0);
331
332                 if(ItemSpec(ITEM_MATERIAL, CONTENT_TREE).checkItem(fuel_item))
333                 {
334                         m_fuel_totaltime = 30;
335                         m_fuel_time = 0;
336                         fuel_list->decrementMaterials(1);
337                         changed = true;
338                 }
339                 else if(ItemSpec(ITEM_MATERIAL, CONTENT_WOOD).checkItem(fuel_item))
340                 {
341                         m_fuel_totaltime = 30/4;
342                         m_fuel_time = 0;
343                         fuel_list->decrementMaterials(1);
344                         changed = true;
345                 }
346                 else if(ItemSpec(ITEM_CRAFT, "Stick").checkItem(fuel_item))
347                 {
348                         m_fuel_totaltime = 30/4/4;
349                         m_fuel_time = 0;
350                         fuel_list->decrementMaterials(1);
351                         changed = true;
352                 }
353                 else if(ItemSpec(ITEM_CRAFT, "lump_of_coal").checkItem(fuel_item))
354                 {
355                         m_fuel_totaltime = 40;
356                         m_fuel_time = 0;
357                         fuel_list->decrementMaterials(1);
358                         changed = true;
359                 }
360                 else
361                 {
362                         //dstream<<"No fuel found"<<std::endl;
363                 }
364         }
365         return changed;
366 }
367
368 /*
369         NodeMetadatalist
370 */
371
372 void NodeMetadataList::serialize(std::ostream &os)
373 {
374         u8 buf[6];
375         
376         u16 version = 1;
377         writeU16(buf, version);
378         os.write((char*)buf, 2);
379
380         u16 count = m_data.size();
381         writeU16(buf, count);
382         os.write((char*)buf, 2);
383
384         for(core::map<v3s16, NodeMetadata*>::Iterator
385                         i = m_data.getIterator();
386                         i.atEnd()==false; i++)
387         {
388                 v3s16 p = i.getNode()->getKey();
389                 NodeMetadata *data = i.getNode()->getValue();
390                 
391                 u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
392                 writeU16(buf, p16);
393                 os.write((char*)buf, 2);
394
395                 data->serialize(os);
396         }
397         
398 }
399 void NodeMetadataList::deSerialize(std::istream &is)
400 {
401         m_data.clear();
402
403         u8 buf[6];
404         
405         is.read((char*)buf, 2);
406         u16 version = readU16(buf);
407
408         if(version > 1)
409         {
410                 dstream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
411                                 <<std::endl;
412                 throw SerializationError("NodeMetadataList::deSerialize");
413         }
414         
415         is.read((char*)buf, 2);
416         u16 count = readU16(buf);
417         
418         for(u16 i=0; i<count; i++)
419         {
420                 is.read((char*)buf, 2);
421                 u16 p16 = readU16(buf);
422
423                 v3s16 p(0,0,0);
424                 p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
425                 p16 -= p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
426                 p.Y += p16 / MAP_BLOCKSIZE;
427                 p16 -= p.Y * MAP_BLOCKSIZE;
428                 p.X += p16;
429                 
430                 NodeMetadata *data = NodeMetadata::deSerialize(is);
431
432                 if(data == NULL)
433                         continue;
434                 
435                 if(m_data.find(p))
436                 {
437                         dstream<<"WARNING: NodeMetadataList::deSerialize(): "
438                                         <<"already set data at position"
439                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
440                                         <<std::endl;
441                         delete data;
442                         continue;
443                 }
444
445                 m_data.insert(p, data);
446         }
447 }
448         
449 NodeMetadataList::~NodeMetadataList()
450 {
451         for(core::map<v3s16, NodeMetadata*>::Iterator
452                         i = m_data.getIterator();
453                         i.atEnd()==false; i++)
454         {
455                 delete i.getNode()->getValue();
456         }
457 }
458
459 NodeMetadata* NodeMetadataList::get(v3s16 p)
460 {
461         core::map<v3s16, NodeMetadata*>::Node *n;
462         n = m_data.find(p);
463         if(n == NULL)
464                 return NULL;
465         return n->getValue();
466 }
467
468 void NodeMetadataList::remove(v3s16 p)
469 {
470         NodeMetadata *olddata = get(p);
471         if(olddata)
472         {
473                 delete olddata;
474                 m_data.remove(p);
475         }
476 }
477
478 void NodeMetadataList::set(v3s16 p, NodeMetadata *d)
479 {
480         remove(p);
481         m_data.insert(p, d);
482 }
483
484 bool NodeMetadataList::step(float dtime)
485 {
486         bool something_changed = false;
487         for(core::map<v3s16, NodeMetadata*>::Iterator
488                         i = m_data.getIterator();
489                         i.atEnd()==false; i++)
490         {
491                 v3s16 p = i.getNode()->getKey();
492                 NodeMetadata *meta = i.getNode()->getValue();
493                 bool changed = meta->step(dtime);
494                 if(changed)
495                         something_changed = true;
496                 /*if(res.inventory_changed)
497                 {
498                         std::string inv_id;
499                         inv_id += "nodemeta:";
500                         inv_id += itos(p.X);
501                         inv_id += ",";
502                         inv_id += itos(p.Y);
503                         inv_id += ",";
504                         inv_id += itos(p.Z);
505                         InventoryContext c;
506                         c.current_player = NULL;
507                         inv_mgr->inventoryModified(&c, inv_id);
508                 }*/
509         }
510         return something_changed;
511 }
512