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