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