]> git.lizzy.rs Git - minetest.git/blob - src/content_nodemeta.cpp
Implement minetest.register_on_dieplayer()
[minetest.git] / src / content_nodemeta.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 "content_nodemeta.h"
21
22 #include <map>
23 #include "inventory.h"
24 #include "log.h"
25 #include "utility.h"
26
27 class Inventory;
28
29 #define NODEMETA_GENERIC 1
30 #define NODEMETA_SIGN 14
31 #define NODEMETA_CHEST 15
32 #define NODEMETA_FURNACE 16
33 #define NODEMETA_LOCKABLE_CHEST 17
34
35 core::map<u16, NodeMetadata::Factory> NodeMetadata::m_types;
36 core::map<std::string, NodeMetadata::Factory2> NodeMetadata::m_names;
37
38 class SignNodeMetadata : public NodeMetadata
39 {
40 public:
41         SignNodeMetadata(IGameDef *gamedef, std::string text);
42         //~SignNodeMetadata();
43         
44         virtual u16 typeId() const;
45         virtual const char* typeName() const
46         { return "sign"; }
47         static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
48         static NodeMetadata* create(IGameDef *gamedef);
49         virtual NodeMetadata* clone(IGameDef *gamedef);
50         virtual void serializeBody(std::ostream &os);
51         virtual std::string infoText();
52
53         virtual bool allowsTextInput(){ return true; }
54         virtual std::string getText(){ return m_text; }
55         virtual void setText(const std::string &t){ m_text = t; }
56
57 private:
58         std::string m_text;
59 };
60
61 class ChestNodeMetadata : public NodeMetadata
62 {
63 public:
64         ChestNodeMetadata(IGameDef *gamedef);
65         ~ChestNodeMetadata();
66         
67         virtual u16 typeId() const;
68         virtual const char* typeName() const
69         { return "chest"; }
70         static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
71         static NodeMetadata* create(IGameDef *gamedef);
72         virtual NodeMetadata* clone(IGameDef *gamedef);
73         virtual void serializeBody(std::ostream &os);
74         virtual std::string infoText();
75         virtual Inventory* getInventory() {return m_inventory;}
76         virtual bool nodeRemovalDisabled();
77         virtual std::string getInventoryDrawSpecString();
78         
79 private:
80         Inventory *m_inventory;
81 };
82
83 class LockingChestNodeMetadata : public NodeMetadata
84 {
85 public:
86         LockingChestNodeMetadata(IGameDef *gamedef);
87         ~LockingChestNodeMetadata();
88
89         virtual u16 typeId() const;
90         virtual const char* typeName() const
91         { return "locked_chest"; }
92         static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
93         static NodeMetadata* create(IGameDef *gamedef);
94         virtual NodeMetadata* clone(IGameDef *gamedef);
95         virtual void serializeBody(std::ostream &os);
96         virtual std::string infoText();
97         virtual Inventory* getInventory() {return m_inventory;}
98         virtual bool nodeRemovalDisabled();
99         virtual std::string getInventoryDrawSpecString();
100
101         virtual std::string getOwner(){ return m_text; }
102         virtual void setOwner(std::string t){ m_text = t; }
103
104 private:
105         Inventory *m_inventory;
106         std::string m_text;
107 };
108
109 class FurnaceNodeMetadata : public NodeMetadata
110 {
111 public:
112         FurnaceNodeMetadata(IGameDef *gamedef);
113         ~FurnaceNodeMetadata();
114         
115         virtual u16 typeId() const;
116         virtual const char* typeName() const
117         { return "furnace"; }
118         virtual NodeMetadata* clone(IGameDef *gamedef);
119         static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
120         static NodeMetadata* create(IGameDef *gamedef);
121         virtual void serializeBody(std::ostream &os);
122         virtual std::string infoText();
123         virtual Inventory* getInventory() {return m_inventory;}
124         virtual void inventoryModified();
125         virtual bool step(float dtime);
126         virtual bool nodeRemovalDisabled();
127         virtual std::string getInventoryDrawSpecString();
128
129 private:
130         Inventory *m_inventory;
131         float m_step_accumulator;
132         float m_fuel_totaltime;
133         float m_fuel_time;
134         float m_src_totaltime;
135         float m_src_time;
136 };
137
138 /*
139         SignNodeMetadata
140 */
141
142 // Prototype
143 SignNodeMetadata proto_SignNodeMetadata(NULL, "");
144
145 SignNodeMetadata::SignNodeMetadata(IGameDef *gamedef, std::string text):
146         NodeMetadata(gamedef),
147         m_text(text)
148 {
149         NodeMetadata::registerType(typeId(), typeName(), create, create);
150 }
151 u16 SignNodeMetadata::typeId() const
152 {
153         return NODEMETA_SIGN;
154 }
155 NodeMetadata* SignNodeMetadata::create(std::istream &is, IGameDef *gamedef)
156 {
157         std::string text = deSerializeString(is);
158         return new SignNodeMetadata(gamedef, text);
159 }
160 NodeMetadata* SignNodeMetadata::create(IGameDef *gamedef)
161 {
162         return new SignNodeMetadata(gamedef, "");
163 }
164 NodeMetadata* SignNodeMetadata::clone(IGameDef *gamedef)
165 {
166         return new SignNodeMetadata(gamedef, m_text);
167 }
168 void SignNodeMetadata::serializeBody(std::ostream &os)
169 {
170         os<<serializeString(m_text);
171 }
172 std::string SignNodeMetadata::infoText()
173 {
174         return std::string("\"")+m_text+"\"";
175 }
176
177 /*
178         ChestNodeMetadata
179 */
180
181 // Prototype
182 ChestNodeMetadata proto_ChestNodeMetadata(NULL);
183
184 ChestNodeMetadata::ChestNodeMetadata(IGameDef *gamedef):
185         NodeMetadata(gamedef)
186 {
187         NodeMetadata::registerType(typeId(), typeName(), create, create);
188         
189         m_inventory = new Inventory();
190         m_inventory->addList("0", 8*4);
191 }
192 ChestNodeMetadata::~ChestNodeMetadata()
193 {
194         delete m_inventory;
195 }
196 u16 ChestNodeMetadata::typeId() const
197 {
198         return NODEMETA_CHEST;
199 }
200 NodeMetadata* ChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
201 {
202         ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
203         d->m_inventory->deSerialize(is, gamedef);
204         return d;
205 }
206 NodeMetadata* ChestNodeMetadata::create(IGameDef *gamedef)
207 {
208         ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
209         return d;
210 }
211 NodeMetadata* ChestNodeMetadata::clone(IGameDef *gamedef)
212 {
213         ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
214         *d->m_inventory = *m_inventory;
215         return d;
216 }
217 void ChestNodeMetadata::serializeBody(std::ostream &os)
218 {
219         m_inventory->serialize(os);
220 }
221 std::string ChestNodeMetadata::infoText()
222 {
223         return "Chest";
224 }
225 bool ChestNodeMetadata::nodeRemovalDisabled()
226 {
227         /*
228                 Disable removal if chest contains something
229         */
230         InventoryList *list = m_inventory->getList("0");
231         if(list == NULL)
232                 return false;
233         if(list->getUsedSlots() == 0)
234                 return false;
235         return true;
236 }
237 std::string ChestNodeMetadata::getInventoryDrawSpecString()
238 {
239         return
240                 "invsize[8,9;]"
241                 "list[current_name;0;0,0;8,4;]"
242                 "list[current_player;main;0,5;8,4;]";
243 }
244
245 /*
246         LockingChestNodeMetadata
247 */
248
249 // Prototype
250 LockingChestNodeMetadata proto_LockingChestNodeMetadata(NULL);
251
252 LockingChestNodeMetadata::LockingChestNodeMetadata(IGameDef *gamedef):
253         NodeMetadata(gamedef)
254 {
255         NodeMetadata::registerType(typeId(), typeName(), create, create);
256
257         m_inventory = new Inventory();
258         m_inventory->addList("0", 8*4);
259 }
260 LockingChestNodeMetadata::~LockingChestNodeMetadata()
261 {
262         delete m_inventory;
263 }
264 u16 LockingChestNodeMetadata::typeId() const
265 {
266         return NODEMETA_LOCKABLE_CHEST;
267 }
268 NodeMetadata* LockingChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
269 {
270         LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
271         d->setOwner(deSerializeString(is));
272         d->m_inventory->deSerialize(is, gamedef);
273         return d;
274 }
275 NodeMetadata* LockingChestNodeMetadata::create(IGameDef *gamedef)
276 {
277         LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
278         return d;
279 }
280 NodeMetadata* LockingChestNodeMetadata::clone(IGameDef *gamedef)
281 {
282         LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
283         *d->m_inventory = *m_inventory;
284         return d;
285 }
286 void LockingChestNodeMetadata::serializeBody(std::ostream &os)
287 {
288         os<<serializeString(m_text);
289         m_inventory->serialize(os);
290 }
291 std::string LockingChestNodeMetadata::infoText()
292 {
293         return "Locking Chest";
294 }
295 bool LockingChestNodeMetadata::nodeRemovalDisabled()
296 {
297         /*
298                 Disable removal if chest contains something
299         */
300         InventoryList *list = m_inventory->getList("0");
301         if(list == NULL)
302                 return false;
303         if(list->getUsedSlots() == 0)
304                 return false;
305         return true;
306 }
307 std::string LockingChestNodeMetadata::getInventoryDrawSpecString()
308 {
309         return
310                 "invsize[8,9;]"
311                 "list[current_name;0;0,0;8,4;]"
312                 "list[current_player;main;0,5;8,4;]";
313 }
314
315 /*
316         FurnaceNodeMetadata
317 */
318
319 // Prototype
320 FurnaceNodeMetadata proto_FurnaceNodeMetadata(NULL);
321
322 FurnaceNodeMetadata::FurnaceNodeMetadata(IGameDef *gamedef):
323         NodeMetadata(gamedef)
324 {
325         NodeMetadata::registerType(typeId(), typeName(), create, create);
326         
327         m_inventory = new Inventory();
328         m_inventory->addList("fuel", 1);
329         m_inventory->addList("src", 1);
330         m_inventory->addList("dst", 4);
331
332         m_step_accumulator = 0;
333         m_fuel_totaltime = 0;
334         m_fuel_time = 0;
335         m_src_totaltime = 0;
336         m_src_time = 0;
337 }
338 FurnaceNodeMetadata::~FurnaceNodeMetadata()
339 {
340         delete m_inventory;
341 }
342 u16 FurnaceNodeMetadata::typeId() const
343 {
344         return NODEMETA_FURNACE;
345 }
346 NodeMetadata* FurnaceNodeMetadata::clone(IGameDef *gamedef)
347 {
348         FurnaceNodeMetadata *d = new FurnaceNodeMetadata(m_gamedef);
349         *d->m_inventory = *m_inventory;
350         return d;
351 }
352 NodeMetadata* FurnaceNodeMetadata::create(std::istream &is, IGameDef *gamedef)
353 {
354         FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
355
356         d->m_inventory->deSerialize(is, gamedef);
357
358         int temp;
359         is>>temp;
360         d->m_fuel_totaltime = (float)temp/10;
361         is>>temp;
362         d->m_fuel_time = (float)temp/10;
363
364         return d;
365 }
366 NodeMetadata* FurnaceNodeMetadata::create(IGameDef *gamedef)
367 {
368         FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
369         return d;
370 }
371 void FurnaceNodeMetadata::serializeBody(std::ostream &os)
372 {
373         m_inventory->serialize(os);
374         os<<itos(m_fuel_totaltime*10)<<" ";
375         os<<itos(m_fuel_time*10)<<" ";
376 }
377 std::string FurnaceNodeMetadata::infoText()
378 {
379         //return "Furnace";
380         if(m_fuel_time >= m_fuel_totaltime)
381         {
382                 const InventoryList *src_list = m_inventory->getList("src");
383                 assert(src_list);
384                 const InventoryItem *src_item = src_list->getItem(0);
385
386                 if(src_item && src_item->isCookable()) {
387                         InventoryList *dst_list = m_inventory->getList("dst");
388                         if(!dst_list->roomForCookedItem(src_item))
389                                 return "Furnace is overloaded";
390                         return "Furnace is out of fuel";
391                 }
392                 else
393                         return "Furnace is inactive";
394         }
395         else
396         {
397                 std::string s = "Furnace is active";
398                 // Do this so it doesn't always show (0%) for weak fuel
399                 if(m_fuel_totaltime > 3) {
400                         s += " (";
401                         s += itos(m_fuel_time/m_fuel_totaltime*100);
402                         s += "%)";
403                 }
404                 return s;
405         }
406 }
407 bool FurnaceNodeMetadata::nodeRemovalDisabled()
408 {
409         /*
410                 Disable removal if furnace is not empty
411         */
412         InventoryList *list[3] = {m_inventory->getList("src"),
413         m_inventory->getList("dst"), m_inventory->getList("fuel")};
414         
415         for(int i = 0; i < 3; i++) {
416                 if(list[i] == NULL)
417                         continue;
418                 if(list[i]->getUsedSlots() == 0)
419                         continue;
420                 return true;
421         }
422         return false;
423         
424 }
425 void FurnaceNodeMetadata::inventoryModified()
426 {
427         infostream<<"Furnace inventory modification callback"<<std::endl;
428 }
429 bool FurnaceNodeMetadata::step(float dtime)
430 {
431         if(dtime > 60.0)
432                 infostream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl;
433         // Update at a fixed frequency
434         const float interval = 2.0;
435         m_step_accumulator += dtime;
436         bool changed = false;
437         while(m_step_accumulator > interval)
438         {
439                 m_step_accumulator -= interval;
440                 dtime = interval;
441
442                 //infostream<<"Furnace step dtime="<<dtime<<std::endl;
443                 
444                 InventoryList *dst_list = m_inventory->getList("dst");
445                 assert(dst_list);
446
447                 InventoryList *src_list = m_inventory->getList("src");
448                 assert(src_list);
449                 InventoryItem *src_item = src_list->getItem(0);
450                 
451                 bool room_available = false;
452                 
453                 if(src_item && src_item->isCookable())
454                         room_available = dst_list->roomForCookedItem(src_item);
455                 
456                 // Start only if there are free slots in dst, so that it can
457                 // accomodate any result item
458                 if(room_available)
459                 {
460                         m_src_totaltime = src_item->getCookTime();
461                 }
462                 else
463                 {
464                         m_src_time = 0;
465                         m_src_totaltime = 0;
466                 }
467                 
468                 /*
469                         If fuel is burning, increment the burn counters.
470                         If item finishes cooking, move it to result.
471                 */
472                 if(m_fuel_time < m_fuel_totaltime)
473                 {
474                         //infostream<<"Furnace is active"<<std::endl;
475                         m_fuel_time += dtime;
476                         m_src_time += dtime;
477                         if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001
478                                         && src_item)
479                         {
480                                 InventoryItem *cookresult = src_item->createCookResult();
481                                 dst_list->addItem(cookresult);
482                                 src_list->decrementMaterials(1);
483                                 m_src_time = 0;
484                                 m_src_totaltime = 0;
485                         }
486                         changed = true;
487                         
488                         // If the fuel was not used up this step, just keep burning it
489                         if(m_fuel_time < m_fuel_totaltime)
490                                 continue;
491                 }
492                 
493                 /*
494                         Get the source again in case it has all burned
495                 */
496                 src_item = src_list->getItem(0);
497                 
498                 /*
499                         If there is no source item, or the source item is not cookable,
500                         or the furnace is still cooking, or the furnace became overloaded, stop loop.
501                 */
502                 if(src_item == NULL || !room_available || m_fuel_time < m_fuel_totaltime ||
503                         dst_list->roomForCookedItem(src_item) == false)
504                 {
505                         m_step_accumulator = 0;
506                         break;
507                 }
508                 
509                 //infostream<<"Furnace is out of fuel"<<std::endl;
510
511                 InventoryList *fuel_list = m_inventory->getList("fuel");
512                 assert(fuel_list);
513                 const InventoryItem *fuel_item = fuel_list->getItem(0);
514
515                 if(fuel_item && fuel_item->getBurnTime() >= 0){
516                         m_fuel_totaltime = fuel_item->getBurnTime();
517                         m_fuel_time = 0;
518                         fuel_list->decrementMaterials(1);
519                         changed = true;
520                 } else {
521                         //infostream<<"No fuel found"<<std::endl;
522                         // No fuel, stop loop.
523                         m_step_accumulator = 0;
524                         break;
525                 }
526         }
527         return changed;
528 }
529 std::string FurnaceNodeMetadata::getInventoryDrawSpecString()
530 {
531         return
532                 "invsize[8,9;]"
533                 "list[current_name;fuel;2,3;1,1;]"
534                 "list[current_name;src;2,1;1,1;]"
535                 "list[current_name;dst;5,1;2,2;]"
536                 "list[current_player;main;0,5;8,4;]";
537 }
538
539 /*
540         GenericNodeMetadata
541 */
542
543 class GenericNodeMetadata : public NodeMetadata
544 {
545 private:
546         Inventory *m_inventory;
547         std::string m_text;
548         std::string m_owner;
549
550         std::string m_infotext;
551         std::string m_inventorydrawspec;
552         bool m_allow_text_input;
553         bool m_removal_disabled;
554         bool m_enforce_owner;
555
556         bool m_inventory_modified;
557         bool m_text_modified;
558
559         std::map<std::string, std::string> m_stringvars;
560
561 public:
562         u16 typeId() const
563         {
564                 return NODEMETA_GENERIC;
565         }
566         const char* typeName() const
567         {
568                 return "generic";
569         }
570
571         GenericNodeMetadata(IGameDef *gamedef):
572                 NodeMetadata(gamedef),
573
574                 m_inventory(new Inventory()),
575                 m_text(""),
576                 m_owner(""),
577
578                 m_infotext("GenericNodeMetadata"),
579                 m_inventorydrawspec(""),
580                 m_allow_text_input(false),
581                 m_removal_disabled(false),
582                 m_enforce_owner(false),
583
584                 m_inventory_modified(false),
585                 m_text_modified(false)
586         {
587                 NodeMetadata::registerType(typeId(), typeName(), create, create);
588         }
589         virtual ~GenericNodeMetadata()
590         {
591                 delete m_inventory;
592         }
593         NodeMetadata* clone(IGameDef *gamedef)
594         {
595                 GenericNodeMetadata *d = new GenericNodeMetadata(m_gamedef);
596
597                 *d->m_inventory = *m_inventory;
598                 d->m_text = m_text;
599                 d->m_owner = m_owner;
600
601                 d->m_infotext = m_infotext;
602                 d->m_inventorydrawspec = m_inventorydrawspec;
603                 d->m_allow_text_input = m_allow_text_input;
604                 d->m_removal_disabled = m_removal_disabled;
605                 d->m_enforce_owner = m_enforce_owner;
606                 d->m_inventory_modified = m_inventory_modified;
607                 d->m_text_modified = m_text_modified;
608                 return d;
609         }
610         static NodeMetadata* create(IGameDef *gamedef)
611         {
612                 GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
613                 return d;
614         }
615         static NodeMetadata* create(std::istream &is, IGameDef *gamedef)
616         {
617                 GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
618                 
619                 d->m_inventory->deSerialize(is, gamedef);
620                 d->m_text = deSerializeLongString(is);
621                 d->m_owner = deSerializeString(is);
622                 
623                 d->m_infotext = deSerializeString(is);
624                 d->m_inventorydrawspec = deSerializeString(is);
625                 d->m_allow_text_input = readU8(is);
626                 d->m_removal_disabled = readU8(is);
627                 d->m_enforce_owner = readU8(is);
628
629                 int num_vars = readU32(is);
630                 for(int i=0; i<num_vars; i++){
631                         std::string name = deSerializeString(is);
632                         std::string var = deSerializeLongString(is);
633                         d->m_stringvars[name] = var;
634                 }
635
636                 return d;
637         }
638         void serializeBody(std::ostream &os)
639         {
640                 m_inventory->serialize(os);
641                 os<<serializeLongString(m_text);
642                 os<<serializeString(m_owner);
643
644                 os<<serializeString(m_infotext);
645                 os<<serializeString(m_inventorydrawspec);
646                 writeU8(os, m_allow_text_input);
647                 writeU8(os, m_removal_disabled);
648                 writeU8(os, m_enforce_owner);
649
650                 int num_vars = m_stringvars.size();
651                 writeU32(os, num_vars);
652                 for(std::map<std::string, std::string>::iterator
653                                 i = m_stringvars.begin(); i != m_stringvars.end(); i++){
654                         os<<serializeString(i->first);
655                         os<<serializeLongString(i->second);
656                 }
657         }
658
659         std::string infoText()
660         {
661                 return m_infotext;
662         }
663         Inventory* getInventory()
664         {
665                 return m_inventory;
666         }
667         void inventoryModified()
668         {
669                 m_inventory_modified = true;
670         }
671         bool step(float dtime)
672         {
673                 return false;
674         }
675         bool nodeRemovalDisabled()
676         {
677                 return m_removal_disabled;
678         }
679         std::string getInventoryDrawSpecString()
680         {
681                 return m_inventorydrawspec;
682         }
683         bool allowsTextInput()
684         {
685                 return m_allow_text_input;
686         }
687         std::string getText()
688         {
689                 return m_text;
690         }
691         void setText(const std::string &t)
692         {
693                 m_text = t;
694                 m_text_modified = true;
695         }
696         std::string getOwner()
697         {
698                 if(m_enforce_owner)
699                         return m_owner;
700                 else
701                         return "";
702         }
703         void setOwner(std::string t)
704         {
705                 m_owner = t;
706         }
707         
708         /* Interface for GenericNodeMetadata */
709
710         void setInfoText(const std::string &text)
711         {
712                 infostream<<"GenericNodeMetadata::setInfoText(\""
713                                 <<text<<"\")"<<std::endl;
714                 m_infotext = text;
715         }
716         void setInventoryDrawSpec(const std::string &text)
717         {
718                 m_inventorydrawspec = text;
719         }
720         void setAllowTextInput(bool b)
721         {
722                 m_allow_text_input = b;
723         }
724         void setRemovalDisabled(bool b)
725         {
726                 m_removal_disabled = b;
727         }
728         void setEnforceOwner(bool b)
729         {
730                 m_enforce_owner = b;
731         }
732         bool isInventoryModified()
733         {
734                 return m_inventory_modified;
735         }
736         void resetInventoryModified()
737         {
738                 m_inventory_modified = false;
739         }
740         bool isTextModified()
741         {
742                 return m_text_modified;
743         }
744         void resetTextModified()
745         {
746                 m_text_modified = false;
747         }
748         void setString(const std::string &name, const std::string &var)
749         {
750                 m_stringvars[name] = var;
751         }
752         std::string getString(const std::string &name)
753         {
754                 std::map<std::string, std::string>::iterator i;
755                 i = m_stringvars.find(name);
756                 if(i == m_stringvars.end())
757                         return "";
758                 return i->second;
759         }
760 };
761
762 // Prototype
763 GenericNodeMetadata proto_GenericNodeMetadata(NULL);
764