]> git.lizzy.rs Git - minetest.git/blob - src/nodemetadata.cpp
be21622d36257e1a6131992be5c74e8653b768e6
[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 bool ChestNodeMetadata::nodeRemovalDisabled()
169 {
170         /*
171                 Disable removal if chest contains something
172         */
173         InventoryList *list = m_inventory->getList("0");
174         if(list == NULL)
175                 return false;
176         if(list->getUsedSlots() == 0)
177                 return false;
178         return true;
179 }
180
181 /*
182         FurnaceNodeMetadata
183 */
184
185 FurnaceNodeMetadata::FurnaceNodeMetadata()
186 {
187         NodeMetadata::registerType(typeId(), create);
188         
189         m_inventory = new Inventory();
190         m_inventory->addList("fuel", 1);
191         m_inventory->addList("src", 1);
192         m_inventory->addList("dst", 4);
193
194         m_step_accumulator = 0;
195         m_fuel_totaltime = 0;
196         m_fuel_time = 0;
197         m_src_totaltime = 0;
198         m_src_time = 0;
199 }
200 FurnaceNodeMetadata::~FurnaceNodeMetadata()
201 {
202         delete m_inventory;
203 }
204 u16 FurnaceNodeMetadata::typeId() const
205 {
206         return CONTENT_FURNACE;
207 }
208 NodeMetadata* FurnaceNodeMetadata::clone()
209 {
210         FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
211         *d->m_inventory = *m_inventory;
212         return d;
213 }
214 NodeMetadata* FurnaceNodeMetadata::create(std::istream &is)
215 {
216         FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
217
218         d->m_inventory->deSerialize(is);
219
220         int temp;
221         is>>temp;
222         d->m_fuel_totaltime = (float)temp/10;
223         is>>temp;
224         d->m_fuel_time = (float)temp/10;
225
226         return d;
227 }
228 void FurnaceNodeMetadata::serializeBody(std::ostream &os)
229 {
230         m_inventory->serialize(os);
231         os<<itos(m_fuel_totaltime*10)<<" ";
232         os<<itos(m_fuel_time*10)<<" ";
233 }
234 std::string FurnaceNodeMetadata::infoText()
235 {
236         //return "Furnace";
237         if(m_fuel_time >= m_fuel_totaltime)
238         {
239                 InventoryList *src_list = m_inventory->getList("src");
240                 assert(src_list);
241                 InventoryItem *src_item = src_list->getItem(0);
242
243                 if(src_item)
244                         return "Furnace is out of fuel";
245                 else
246                         return "Furnace is inactive";
247         }
248         else
249         {
250                 std::string s = "Furnace is active (";
251                 s += itos(m_fuel_time/m_fuel_totaltime*100);
252                 s += "%)";
253                 return s;
254         }
255 }
256 void FurnaceNodeMetadata::inventoryModified()
257 {
258         dstream<<"Furnace inventory modification callback"<<std::endl;
259 }
260 bool FurnaceNodeMetadata::step(float dtime)
261 {
262         // Update at a fixed frequency
263         const float interval = 0.5;
264         m_step_accumulator += dtime;
265         if(m_step_accumulator < interval)
266                 return false;
267         m_step_accumulator -= interval;
268         dtime = interval;
269
270         //dstream<<"Furnace step dtime="<<dtime<<std::endl;
271         
272         InventoryList *dst_list = m_inventory->getList("dst");
273         assert(dst_list);
274
275         InventoryList *src_list = m_inventory->getList("src");
276         assert(src_list);
277         InventoryItem *src_item = src_list->getItem(0);
278         
279         // Start only if there are free slots in dst, so that it can
280         // accomodate any result item
281         if(dst_list->getFreeSlots() > 0 && src_item && src_item->isCookable())
282         {
283                 m_src_totaltime = 3;
284         }
285         else
286         {
287                 m_src_time = 0;
288                 m_src_totaltime = 0;
289         }
290
291         if(m_fuel_time < m_fuel_totaltime)
292         {
293                 //dstream<<"Furnace is active"<<std::endl;
294                 m_fuel_time += dtime;
295                 m_src_time += dtime;
296                 if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001
297                                 && src_item)
298                 {
299                         InventoryItem *cookresult = src_item->createCookResult();
300                         dst_list->addItem(cookresult);
301                         src_list->decrementMaterials(1);
302                         m_src_time = 0;
303                         m_src_totaltime = 0;
304                 }
305                 return true;
306         }
307         
308         if(src_item == NULL || m_src_totaltime < 0.001)
309         {
310                 return false;
311         }
312         
313         bool changed = false;
314
315         //dstream<<"Furnace is out of fuel"<<std::endl;
316
317         InventoryList *fuel_list = m_inventory->getList("fuel");
318         assert(fuel_list);
319         InventoryItem *fuel_item = fuel_list->getItem(0);
320
321         if(ItemSpec(ITEM_MATERIAL, CONTENT_TREE).checkItem(fuel_item))
322         {
323                 m_fuel_totaltime = 10;
324                 m_fuel_time = 0;
325                 fuel_list->decrementMaterials(1);
326                 changed = true;
327         }
328         else if(ItemSpec(ITEM_MATERIAL, CONTENT_WOOD).checkItem(fuel_item))
329         {
330                 m_fuel_totaltime = 5;
331                 m_fuel_time = 0;
332                 fuel_list->decrementMaterials(1);
333                 changed = true;
334         }
335         else if(ItemSpec(ITEM_CRAFT, "lump_of_coal").checkItem(fuel_item))
336         {
337                 m_fuel_totaltime = 10;
338                 m_fuel_time = 0;
339                 fuel_list->decrementMaterials(1);
340                 changed = true;
341         }
342         else
343         {
344                 //dstream<<"No fuel found"<<std::endl;
345         }
346
347         return changed;
348 }
349
350 /*
351         NodeMetadatalist
352 */
353
354 void NodeMetadataList::serialize(std::ostream &os)
355 {
356         u8 buf[6];
357         
358         u16 version = 1;
359         writeU16(buf, version);
360         os.write((char*)buf, 2);
361
362         u16 count = m_data.size();
363         writeU16(buf, count);
364         os.write((char*)buf, 2);
365
366         for(core::map<v3s16, NodeMetadata*>::Iterator
367                         i = m_data.getIterator();
368                         i.atEnd()==false; i++)
369         {
370                 v3s16 p = i.getNode()->getKey();
371                 NodeMetadata *data = i.getNode()->getValue();
372                 
373                 u16 p16 = p.Z*MAP_BLOCKSIZE*MAP_BLOCKSIZE + p.Y*MAP_BLOCKSIZE + p.X;
374                 writeU16(buf, p16);
375                 os.write((char*)buf, 2);
376
377                 data->serialize(os);
378         }
379         
380 }
381 void NodeMetadataList::deSerialize(std::istream &is)
382 {
383         m_data.clear();
384
385         u8 buf[6];
386         
387         is.read((char*)buf, 2);
388         u16 version = readU16(buf);
389
390         if(version > 1)
391         {
392                 dstream<<__FUNCTION_NAME<<": version "<<version<<" not supported"
393                                 <<std::endl;
394                 throw SerializationError("NodeMetadataList::deSerialize");
395         }
396         
397         is.read((char*)buf, 2);
398         u16 count = readU16(buf);
399         
400         for(u16 i=0; i<count; i++)
401         {
402                 is.read((char*)buf, 2);
403                 u16 p16 = readU16(buf);
404
405                 v3s16 p(0,0,0);
406                 p.Z += p16 / MAP_BLOCKSIZE / MAP_BLOCKSIZE;
407                 p16 -= p.Z * MAP_BLOCKSIZE * MAP_BLOCKSIZE;
408                 p.Y += p16 / MAP_BLOCKSIZE;
409                 p16 -= p.Y * MAP_BLOCKSIZE;
410                 p.X += p16;
411                 
412                 NodeMetadata *data = NodeMetadata::deSerialize(is);
413
414                 if(data == NULL)
415                         continue;
416                 
417                 if(m_data.find(p))
418                 {
419                         dstream<<"WARNING: NodeMetadataList::deSerialize(): "
420                                         <<"already set data at position"
421                                         <<"("<<p.X<<","<<p.Y<<","<<p.Z<<"): Ignoring."
422                                         <<std::endl;
423                         delete data;
424                         continue;
425                 }
426
427                 m_data.insert(p, data);
428         }
429 }
430         
431 NodeMetadataList::~NodeMetadataList()
432 {
433         for(core::map<v3s16, NodeMetadata*>::Iterator
434                         i = m_data.getIterator();
435                         i.atEnd()==false; i++)
436         {
437                 delete i.getNode()->getValue();
438         }
439 }
440
441 NodeMetadata* NodeMetadataList::get(v3s16 p)
442 {
443         core::map<v3s16, NodeMetadata*>::Node *n;
444         n = m_data.find(p);
445         if(n == NULL)
446                 return NULL;
447         return n->getValue();
448 }
449
450 void NodeMetadataList::remove(v3s16 p)
451 {
452         NodeMetadata *olddata = get(p);
453         if(olddata)
454         {
455                 delete olddata;
456                 m_data.remove(p);
457         }
458 }
459
460 void NodeMetadataList::set(v3s16 p, NodeMetadata *d)
461 {
462         remove(p);
463         m_data.insert(p, d);
464 }
465
466 bool NodeMetadataList::step(float dtime)
467 {
468         bool something_changed = false;
469         for(core::map<v3s16, NodeMetadata*>::Iterator
470                         i = m_data.getIterator();
471                         i.atEnd()==false; i++)
472         {
473                 v3s16 p = i.getNode()->getKey();
474                 NodeMetadata *meta = i.getNode()->getValue();
475                 bool changed = meta->step(dtime);
476                 if(changed)
477                         something_changed = true;
478                 /*if(res.inventory_changed)
479                 {
480                         std::string inv_id;
481                         inv_id += "nodemeta:";
482                         inv_id += itos(p.X);
483                         inv_id += ",";
484                         inv_id += itos(p.Y);
485                         inv_id += ",";
486                         inv_id += itos(p.Z);
487                         InventoryContext c;
488                         c.current_player = NULL;
489                         inv_mgr->inventoryModified(&c, inv_id);
490                 }*/
491         }
492         return something_changed;
493 }
494