]> git.lizzy.rs Git - dragonfireclient.git/blobdiff - src/content_nodemeta.cpp
Tune "Connecting to server" and "Waiting content" screens
[dragonfireclient.git] / src / content_nodemeta.cpp
index 681e2b92584365e827fd39c62a2948cc8e54a86d..b36d57c89fc73654021ababc29ccf9232bc68c4d 100644 (file)
@@ -18,33 +18,159 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 */
 
 #include "content_nodemeta.h"
+
+#include <map>
 #include "inventory.h"
-#include "content_mapnode.h"
+#include "log.h"
+#include "utility.h"
+#include "craftdef.h"
+#include "gamedef.h"
+
+class Inventory;
+
+#define NODEMETA_GENERIC 1
+#define NODEMETA_SIGN 14
+#define NODEMETA_CHEST 15
+#define NODEMETA_FURNACE 16
+#define NODEMETA_LOCKABLE_CHEST 17
+
+core::map<u16, NodeMetadata::Factory> NodeMetadata::m_types;
+core::map<std::string, NodeMetadata::Factory2> NodeMetadata::m_names;
+
+class SignNodeMetadata : public NodeMetadata
+{
+public:
+       SignNodeMetadata(IGameDef *gamedef, std::string text);
+       //~SignNodeMetadata();
+       
+       virtual u16 typeId() const;
+       virtual const char* typeName() const
+       { return "sign"; }
+       static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
+       static NodeMetadata* create(IGameDef *gamedef);
+       virtual NodeMetadata* clone(IGameDef *gamedef);
+       virtual void serializeBody(std::ostream &os);
+       virtual std::string infoText();
+
+       virtual bool allowsTextInput(){ return true; }
+       virtual std::string getText(){ return m_text; }
+       virtual void setText(const std::string &t){ m_text = t; }
+
+private:
+       std::string m_text;
+};
+
+class ChestNodeMetadata : public NodeMetadata
+{
+public:
+       ChestNodeMetadata(IGameDef *gamedef);
+       ~ChestNodeMetadata();
+       
+       virtual u16 typeId() const;
+       virtual const char* typeName() const
+       { return "chest"; }
+       static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
+       static NodeMetadata* create(IGameDef *gamedef);
+       virtual NodeMetadata* clone(IGameDef *gamedef);
+       virtual void serializeBody(std::ostream &os);
+       virtual std::string infoText();
+       virtual Inventory* getInventory() {return m_inventory;}
+       virtual bool nodeRemovalDisabled();
+       virtual std::string getInventoryDrawSpecString();
+       
+private:
+       Inventory *m_inventory;
+};
+
+class LockingChestNodeMetadata : public NodeMetadata
+{
+public:
+       LockingChestNodeMetadata(IGameDef *gamedef);
+       ~LockingChestNodeMetadata();
+
+       virtual u16 typeId() const;
+       virtual const char* typeName() const
+       { return "locked_chest"; }
+       static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
+       static NodeMetadata* create(IGameDef *gamedef);
+       virtual NodeMetadata* clone(IGameDef *gamedef);
+       virtual void serializeBody(std::ostream &os);
+       virtual std::string infoText();
+       virtual Inventory* getInventory() {return m_inventory;}
+       virtual bool nodeRemovalDisabled();
+       virtual std::string getInventoryDrawSpecString();
+
+       virtual std::string getOwner(){ return m_text; }
+       virtual void setOwner(std::string t){ m_text = t; }
+
+private:
+       Inventory *m_inventory;
+       std::string m_text;
+};
+
+class FurnaceNodeMetadata : public NodeMetadata
+{
+public:
+       FurnaceNodeMetadata(IGameDef *gamedef);
+       ~FurnaceNodeMetadata();
+       
+       virtual u16 typeId() const;
+       virtual const char* typeName() const
+       { return "furnace"; }
+       virtual NodeMetadata* clone(IGameDef *gamedef);
+       static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
+       static NodeMetadata* create(IGameDef *gamedef);
+       virtual void serializeBody(std::ostream &os);
+       virtual std::string infoText();
+       virtual Inventory* getInventory() {return m_inventory;}
+       virtual void inventoryModified();
+       virtual bool step(float dtime);
+       virtual bool nodeRemovalDisabled();
+       virtual std::string getInventoryDrawSpecString();
+       
+protected:
+       bool getCookResult(bool remove, std::string &cookresult, float &cooktime);
+       bool getBurnResult(bool remove, float &burntime);
+
+private:
+       Inventory *m_inventory;
+       std::string m_infotext;
+       float m_step_accumulator;
+       float m_fuel_totaltime;
+       float m_fuel_time;
+       float m_src_totaltime;
+       float m_src_time;
+};
 
 /*
        SignNodeMetadata
 */
 
 // Prototype
-SignNodeMetadata proto_SignNodeMetadata("");
+SignNodeMetadata proto_SignNodeMetadata(NULL, "");
 
-SignNodeMetadata::SignNodeMetadata(std::string text):
+SignNodeMetadata::SignNodeMetadata(IGameDef *gamedef, std::string text):
+       NodeMetadata(gamedef),
        m_text(text)
 {
-       NodeMetadata::registerType(typeId(), create);
+       NodeMetadata::registerType(typeId(), typeName(), create, create);
 }
 u16 SignNodeMetadata::typeId() const
 {
-       return CONTENT_SIGN_WALL;
+       return NODEMETA_SIGN;
 }
-NodeMetadata* SignNodeMetadata::create(std::istream &is)
+NodeMetadata* SignNodeMetadata::create(std::istream &is, IGameDef *gamedef)
 {
        std::string text = deSerializeString(is);
-       return new SignNodeMetadata(text);
+       return new SignNodeMetadata(gamedef, text);
 }
-NodeMetadata* SignNodeMetadata::clone()
+NodeMetadata* SignNodeMetadata::create(IGameDef *gamedef)
 {
-       return new SignNodeMetadata(m_text);
+       return new SignNodeMetadata(gamedef, "");
+}
+NodeMetadata* SignNodeMetadata::clone(IGameDef *gamedef)
+{
+       return new SignNodeMetadata(gamedef, m_text);
 }
 void SignNodeMetadata::serializeBody(std::ostream &os)
 {
@@ -60,14 +186,13 @@ std::string SignNodeMetadata::infoText()
 */
 
 // Prototype
-ChestNodeMetadata proto_ChestNodeMetadata;
+ChestNodeMetadata proto_ChestNodeMetadata(NULL);
 
-ChestNodeMetadata::ChestNodeMetadata()
+ChestNodeMetadata::ChestNodeMetadata(IGameDef *gamedef):
+       NodeMetadata(gamedef)
 {
-       NodeMetadata::registerType(typeId(), create);
-       
-       m_inventory = new Inventory();
-       m_inventory->addList("0", 8*4);
+       NodeMetadata::registerType(typeId(), typeName(), create, create);
+       m_inventory = NULL;
 }
 ChestNodeMetadata::~ChestNodeMetadata()
 {
@@ -75,18 +200,26 @@ ChestNodeMetadata::~ChestNodeMetadata()
 }
 u16 ChestNodeMetadata::typeId() const
 {
-       return CONTENT_CHEST;
+       return NODEMETA_CHEST;
 }
-NodeMetadata* ChestNodeMetadata::create(std::istream &is)
+NodeMetadata* ChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
 {
-       ChestNodeMetadata *d = new ChestNodeMetadata();
+       ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
        d->m_inventory->deSerialize(is);
        return d;
 }
-NodeMetadata* ChestNodeMetadata::clone()
+NodeMetadata* ChestNodeMetadata::create(IGameDef *gamedef)
 {
-       ChestNodeMetadata *d = new ChestNodeMetadata();
-       *d->m_inventory = *m_inventory;
+       ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->addList("0", 8*4);
+       return d;
+}
+NodeMetadata* ChestNodeMetadata::clone(IGameDef *gamedef)
+{
+       ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
+       d->m_inventory = new Inventory(*m_inventory);
        return d;
 }
 void ChestNodeMetadata::serializeBody(std::ostream &os)
@@ -117,21 +250,92 @@ std::string ChestNodeMetadata::getInventoryDrawSpecString()
                "list[current_player;main;0,5;8,4;]";
 }
 
+/*
+       LockingChestNodeMetadata
+*/
+
+// Prototype
+LockingChestNodeMetadata proto_LockingChestNodeMetadata(NULL);
+
+LockingChestNodeMetadata::LockingChestNodeMetadata(IGameDef *gamedef):
+       NodeMetadata(gamedef)
+{
+       NodeMetadata::registerType(typeId(), typeName(), create, create);
+       m_inventory = NULL;
+}
+LockingChestNodeMetadata::~LockingChestNodeMetadata()
+{
+       delete m_inventory;
+}
+u16 LockingChestNodeMetadata::typeId() const
+{
+       return NODEMETA_LOCKABLE_CHEST;
+}
+NodeMetadata* LockingChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
+{
+       LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
+       d->setOwner(deSerializeString(is));
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->deSerialize(is);
+       return d;
+}
+NodeMetadata* LockingChestNodeMetadata::create(IGameDef *gamedef)
+{
+       LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->addList("0", 8*4);
+       return d;
+}
+NodeMetadata* LockingChestNodeMetadata::clone(IGameDef *gamedef)
+{
+       LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
+       d->m_inventory = new Inventory(*m_inventory);
+       return d;
+}
+void LockingChestNodeMetadata::serializeBody(std::ostream &os)
+{
+       os<<serializeString(m_text);
+       m_inventory->serialize(os);
+}
+std::string LockingChestNodeMetadata::infoText()
+{
+       return "Locking Chest";
+}
+bool LockingChestNodeMetadata::nodeRemovalDisabled()
+{
+       /*
+               Disable removal if chest contains something
+       */
+       InventoryList *list = m_inventory->getList("0");
+       if(list == NULL)
+               return false;
+       if(list->getUsedSlots() == 0)
+               return false;
+       return true;
+}
+std::string LockingChestNodeMetadata::getInventoryDrawSpecString()
+{
+       return
+               "invsize[8,9;]"
+               "list[current_name;0;0,0;8,4;]"
+               "list[current_player;main;0,5;8,4;]";
+}
+
 /*
        FurnaceNodeMetadata
 */
 
 // Prototype
-FurnaceNodeMetadata proto_FurnaceNodeMetadata;
+FurnaceNodeMetadata proto_FurnaceNodeMetadata(NULL);
 
-FurnaceNodeMetadata::FurnaceNodeMetadata()
+FurnaceNodeMetadata::FurnaceNodeMetadata(IGameDef *gamedef):
+       NodeMetadata(gamedef)
 {
-       NodeMetadata::registerType(typeId(), create);
+       NodeMetadata::registerType(typeId(), typeName(), create, create);
        
-       m_inventory = new Inventory();
-       m_inventory->addList("fuel", 1);
-       m_inventory->addList("src", 1);
-       m_inventory->addList("dst", 4);
+       m_inventory = NULL;
+
+       m_infotext = "Furnace is inactive";
 
        m_step_accumulator = 0;
        m_fuel_totaltime = 0;
@@ -145,64 +349,102 @@ FurnaceNodeMetadata::~FurnaceNodeMetadata()
 }
 u16 FurnaceNodeMetadata::typeId() const
 {
-       return CONTENT_FURNACE;
+       return NODEMETA_FURNACE;
 }
-NodeMetadata* FurnaceNodeMetadata::clone()
+NodeMetadata* FurnaceNodeMetadata::clone(IGameDef *gamedef)
 {
-       FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
-       *d->m_inventory = *m_inventory;
+       FurnaceNodeMetadata *d = new FurnaceNodeMetadata(m_gamedef);
+       d->m_inventory = new Inventory(*m_inventory);
        return d;
 }
-NodeMetadata* FurnaceNodeMetadata::create(std::istream &is)
+NodeMetadata* FurnaceNodeMetadata::create(std::istream &is, IGameDef *gamedef)
 {
-       FurnaceNodeMetadata *d = new FurnaceNodeMetadata();
+       FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
 
+       d->m_inventory = new Inventory(gamedef->idef());
        d->m_inventory->deSerialize(is);
 
-       int temp;
+       int temp = 0;
        is>>temp;
        d->m_fuel_totaltime = (float)temp/10;
+       temp = 0;
        is>>temp;
        d->m_fuel_time = (float)temp/10;
+       temp = 0;
+       is>>temp;
+       d->m_src_totaltime = (float)temp/10;
+       temp = 0;
+       is>>temp;
+       d->m_src_time = (float)temp/10;
+
+       if(is.eof())
+       {
+               // Old furnaces didn't serialize src_totaltime and src_time
+               d->m_src_totaltime = 0;
+               d->m_src_time = 0;
+               d->m_infotext = "";
+       }
+       else
+       {
+               // New furnaces also serialize the infotext (so that the
+               // client doesn't need to have the list of cooking recipes).
+               d->m_infotext = deSerializeJsonString(is);
+       }
 
        return d;
 }
+NodeMetadata* FurnaceNodeMetadata::create(IGameDef *gamedef)
+{
+       FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
+       d->m_inventory = new Inventory(gamedef->idef());
+       d->m_inventory->addList("fuel", 1);
+       d->m_inventory->addList("src", 1);
+       d->m_inventory->addList("dst", 4);
+       return d;
+}
 void FurnaceNodeMetadata::serializeBody(std::ostream &os)
 {
        m_inventory->serialize(os);
        os<<itos(m_fuel_totaltime*10)<<" ";
        os<<itos(m_fuel_time*10)<<" ";
+       os<<itos(m_src_totaltime*10)<<" ";
+       os<<itos(m_src_time*10)<<" ";
+       os<<serializeJsonString(m_infotext);
 }
 std::string FurnaceNodeMetadata::infoText()
 {
-       //return "Furnace";
-       if(m_fuel_time >= m_fuel_totaltime)
-       {
-               InventoryList *src_list = m_inventory->getList("src");
-               assert(src_list);
-               InventoryItem *src_item = src_list->getItem(0);
-
-               if(src_item)
-                       return "Furnace is out of fuel";
-               else
-                       return "Furnace is inactive";
-       }
-       else
-       {
-               std::string s = "Furnace is active (";
-               s += itos(m_fuel_time/m_fuel_totaltime*100);
-               s += "%)";
-               return s;
+       return m_infotext;
+}
+bool FurnaceNodeMetadata::nodeRemovalDisabled()
+{
+       /*
+               Disable removal if furnace is not empty
+       */
+       InventoryList *list[3] = {m_inventory->getList("src"),
+       m_inventory->getList("dst"), m_inventory->getList("fuel")};
+       
+       for(int i = 0; i < 3; i++) {
+               if(list[i] == NULL)
+                       continue;
+               if(list[i]->getUsedSlots() == 0)
+                       continue;
+               return true;
        }
+       return false;
+       
 }
 void FurnaceNodeMetadata::inventoryModified()
 {
-       dstream<<"Furnace inventory modification callback"<<std::endl;
+       infostream<<"Furnace inventory modification callback"<<std::endl;
 }
 bool FurnaceNodeMetadata::step(float dtime)
 {
        if(dtime > 60.0)
-               dstream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl;
+               infostream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl;
+
+       InventoryList *dst_list = m_inventory->getList("dst");
+       assert(dst_list);
+
        // Update at a fixed frequency
        const float interval = 2.0;
        m_step_accumulator += dtime;
@@ -212,96 +454,111 @@ bool FurnaceNodeMetadata::step(float dtime)
                m_step_accumulator -= interval;
                dtime = interval;
 
-               //dstream<<"Furnace step dtime="<<dtime<<std::endl;
-               
-               InventoryList *dst_list = m_inventory->getList("dst");
-               assert(dst_list);
+               //infostream<<"Furnace step dtime="<<dtime<<std::endl;
 
-               InventoryList *src_list = m_inventory->getList("src");
-               assert(src_list);
-               InventoryItem *src_item = src_list->getItem(0);
-               
-               // Start only if there are free slots in dst, so that it can
-               // accomodate any result item
-               if(dst_list->getFreeSlots() > 0 && src_item && src_item->isCookable())
+               bool changed_this_loop = false;
+
+               // Check
+               // 1. if the source item is cookable
+               // 2. if there is room for the cooked item
+               std::string cookresult;
+               float cooktime;
+               bool cookable = getCookResult(false, cookresult, cooktime);
+               ItemStack cookresult_item;
+               bool room_available = false;
+               if(cookable)
                {
-                       m_src_totaltime = 3;
+                       cookresult_item.deSerialize(cookresult, m_gamedef->idef());
+                       room_available = dst_list->roomForItem(cookresult_item);
                }
-               else
+
+               // Step fuel time
+               bool burning = (m_fuel_time < m_fuel_totaltime);
+               if(burning)
                {
-                       m_src_time = 0;
-                       m_src_totaltime = 0;
+                       changed_this_loop = true;
+                       m_fuel_time += dtime;
                }
-               
-               /*
-                       If fuel is burning, increment the burn counters.
-                       If item finishes cooking, move it to result.
-               */
-               if(m_fuel_time < m_fuel_totaltime)
+
+               std::string infotext;
+               if(room_available)
                {
-                       //dstream<<"Furnace is active"<<std::endl;
-                       m_fuel_time += dtime;
-                       m_src_time += dtime;
-                       if(m_src_time >= m_src_totaltime && m_src_totaltime > 0.001
-                                       && src_item)
+                       float burntime;
+                       if(burning)
+                       {
+                               changed_this_loop = true;
+                               m_src_time += dtime;
+                               m_src_totaltime = cooktime;
+                               infotext = "Furnace is cooking";
+                       }
+                       else if(getBurnResult(true, burntime))
+                       {
+                               // Fuel inserted
+                               changed_this_loop = true;
+                               m_fuel_time = 0;
+                               m_fuel_totaltime = burntime;
+                               //m_src_time += dtime;
+                               //m_src_totaltime = cooktime;
+                               infotext = "Furnace is cooking";
+                       }
+                       else
                        {
-                               InventoryItem *cookresult = src_item->createCookResult();
-                               dst_list->addItem(cookresult);
-                               src_list->decrementMaterials(1);
                                m_src_time = 0;
                                m_src_totaltime = 0;
+                               infotext = "Furnace is out of fuel";
+                       }
+                       if(m_src_totaltime > 0.001 && m_src_time >= m_src_totaltime)
+                       {
+                               // One item fully cooked
+                               changed_this_loop = true;
+                               dst_list->addItem(cookresult_item);
+                               getCookResult(true, cookresult, cooktime); // decrement source
+                               m_src_totaltime = 0;
+                               m_src_time = 0;
                        }
-                       changed = true;
-                       continue;
                }
-               
-               /*
-                       If there is no source item or source item is not cookable, stop loop.
-               */
-               if(src_item == NULL || m_src_totaltime < 0.001)
+               else
                {
-                       m_step_accumulator = 0;
-                       break;
+                       // Not cookable or no room available
+                       m_src_totaltime = 0;
+                       m_src_time = 0;
+                       if(cookable)
+                               infotext = "Furnace is overloaded";
+                       else if(burning)
+                               infotext = "Furnace is active";
+                       else
+                       {
+                               infotext = "Furnace is inactive";
+                               m_fuel_totaltime = 0;
+                               m_fuel_time = 0;
+                       }
                }
-               
-               //dstream<<"Furnace is out of fuel"<<std::endl;
 
-               InventoryList *fuel_list = m_inventory->getList("fuel");
-               assert(fuel_list);
-               InventoryItem *fuel_item = fuel_list->getItem(0);
-
-               if(ItemSpec(ITEM_MATERIAL, CONTENT_TREE).checkItem(fuel_item))
-               {
-                       m_fuel_totaltime = 30;
-                       m_fuel_time = 0;
-                       fuel_list->decrementMaterials(1);
-                       changed = true;
+               // Do this so it doesn't always show (0%) for weak fuel
+               if(m_fuel_totaltime > 3) {
+                       infotext += " (";
+                       infotext += itos(m_fuel_time/m_fuel_totaltime*100);
+                       infotext += "%)";
                }
-               else if(ItemSpec(ITEM_MATERIAL, CONTENT_WOOD).checkItem(fuel_item))
+
+               if(infotext != m_infotext)
                {
-                       m_fuel_totaltime = 30/4;
-                       m_fuel_time = 0;
-                       fuel_list->decrementMaterials(1);
-                       changed = true;
+                       m_infotext = infotext;
+                       changed_this_loop = true;
                }
-               else if(ItemSpec(ITEM_CRAFT, "Stick").checkItem(fuel_item))
+
+               if(burning && m_fuel_time >= m_fuel_totaltime)
                {
-                       m_fuel_totaltime = 30/4/4;
                        m_fuel_time = 0;
-                       fuel_list->decrementMaterials(1);
-                       changed = true;
+                       m_fuel_totaltime = 0;
                }
-               else if(ItemSpec(ITEM_CRAFT, "lump_of_coal").checkItem(fuel_item))
+
+               if(changed_this_loop)
                {
-                       m_fuel_totaltime = 40;
-                       m_fuel_time = 0;
-                       fuel_list->decrementMaterials(1);
                        changed = true;
                }
                else
                {
-                       //dstream<<"No fuel found"<<std::endl;
-                       // No fuel, stop loop.
                        m_step_accumulator = 0;
                        break;
                }
@@ -317,5 +574,269 @@ std::string FurnaceNodeMetadata::getInventoryDrawSpecString()
                "list[current_name;dst;5,1;2,2;]"
                "list[current_player;main;0,5;8,4;]";
 }
+bool FurnaceNodeMetadata::getCookResult(bool remove,
+               std::string &cookresult, float &cooktime)
+{
+       std::vector<ItemStack> items;
+       InventoryList *src_list = m_inventory->getList("src");
+       assert(src_list);
+       items.push_back(src_list->getItem(0));
+
+       CraftInput ci(CRAFT_METHOD_COOKING, 1, items);
+       CraftOutput co;
+       bool found = m_gamedef->getCraftDefManager()->getCraftResult(
+                       ci, co, remove, m_gamedef);
+       if(remove)
+               src_list->changeItem(0, ci.items[0]);
+
+       cookresult = co.item;
+       cooktime = co.time;
+       return found;
+}
+bool FurnaceNodeMetadata::getBurnResult(bool remove, float &burntime)
+{
+       std::vector<ItemStack> items;
+       InventoryList *fuel_list = m_inventory->getList("fuel");
+       assert(fuel_list);
+       items.push_back(fuel_list->getItem(0));
+
+       CraftInput ci(CRAFT_METHOD_FUEL, 1, items);
+       CraftOutput co;
+       bool found = m_gamedef->getCraftDefManager()->getCraftResult(
+                       ci, co, remove, m_gamedef);
+       if(remove)
+               fuel_list->changeItem(0, ci.items[0]);
+
+       burntime = co.time;
+       return found;
+}
 
 
+/*
+       GenericNodeMetadata
+*/
+
+class GenericNodeMetadata : public NodeMetadata
+{
+private:
+       Inventory *m_inventory;
+       std::string m_text;
+       std::string m_owner;
+
+       std::string m_infotext;
+       std::string m_inventorydrawspec;
+       bool m_allow_text_input;
+       bool m_removal_disabled;
+       bool m_enforce_owner;
+
+       bool m_inventory_modified;
+       bool m_text_modified;
+
+       std::map<std::string, std::string> m_stringvars;
+
+public:
+       u16 typeId() const
+       {
+               return NODEMETA_GENERIC;
+       }
+       const char* typeName() const
+       {
+               return "generic";
+       }
+
+       GenericNodeMetadata(IGameDef *gamedef):
+               NodeMetadata(gamedef),
+
+               m_inventory(NULL),
+               m_text(""),
+               m_owner(""),
+
+               m_infotext("GenericNodeMetadata"),
+               m_inventorydrawspec(""),
+               m_allow_text_input(false),
+               m_removal_disabled(false),
+               m_enforce_owner(false),
+
+               m_inventory_modified(false),
+               m_text_modified(false)
+       {
+               NodeMetadata::registerType(typeId(), typeName(), create, create);
+       }
+       virtual ~GenericNodeMetadata()
+       {
+               delete m_inventory;
+       }
+       NodeMetadata* clone(IGameDef *gamedef)
+       {
+               GenericNodeMetadata *d = new GenericNodeMetadata(m_gamedef);
+
+               d->m_inventory = new Inventory(*m_inventory);
+               d->m_text = m_text;
+               d->m_owner = m_owner;
+
+               d->m_infotext = m_infotext;
+               d->m_inventorydrawspec = m_inventorydrawspec;
+               d->m_allow_text_input = m_allow_text_input;
+               d->m_removal_disabled = m_removal_disabled;
+               d->m_enforce_owner = m_enforce_owner;
+               d->m_inventory_modified = m_inventory_modified;
+               d->m_text_modified = m_text_modified;
+               return d;
+       }
+       static NodeMetadata* create(IGameDef *gamedef)
+       {
+               GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
+               d->m_inventory = new Inventory(gamedef->idef());
+               return d;
+       }
+       static NodeMetadata* create(std::istream &is, IGameDef *gamedef)
+       {
+               GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
+               
+               d->m_inventory = new Inventory(gamedef->idef());
+               d->m_inventory->deSerialize(is);
+               d->m_text = deSerializeLongString(is);
+               d->m_owner = deSerializeString(is);
+               
+               d->m_infotext = deSerializeString(is);
+               d->m_inventorydrawspec = deSerializeString(is);
+               d->m_allow_text_input = readU8(is);
+               d->m_removal_disabled = readU8(is);
+               d->m_enforce_owner = readU8(is);
+
+               int num_vars = readU32(is);
+               for(int i=0; i<num_vars; i++){
+                       std::string name = deSerializeString(is);
+                       std::string var = deSerializeLongString(is);
+                       d->m_stringvars[name] = var;
+               }
+
+               return d;
+       }
+       void serializeBody(std::ostream &os)
+       {
+               m_inventory->serialize(os);
+               os<<serializeLongString(m_text);
+               os<<serializeString(m_owner);
+
+               os<<serializeString(m_infotext);
+               os<<serializeString(m_inventorydrawspec);
+               writeU8(os, m_allow_text_input);
+               writeU8(os, m_removal_disabled);
+               writeU8(os, m_enforce_owner);
+
+               int num_vars = m_stringvars.size();
+               writeU32(os, num_vars);
+               for(std::map<std::string, std::string>::iterator
+                               i = m_stringvars.begin(); i != m_stringvars.end(); i++){
+                       os<<serializeString(i->first);
+                       os<<serializeLongString(i->second);
+               }
+       }
+
+       std::string infoText()
+       {
+               return m_infotext;
+       }
+       Inventory* getInventory()
+       {
+               return m_inventory;
+       }
+       void inventoryModified()
+       {
+               m_inventory_modified = true;
+       }
+       bool step(float dtime)
+       {
+               return false;
+       }
+       bool nodeRemovalDisabled()
+       {
+               return m_removal_disabled;
+       }
+       std::string getInventoryDrawSpecString()
+       {
+               return m_inventorydrawspec;
+       }
+       bool allowsTextInput()
+       {
+               return m_allow_text_input;
+       }
+       std::string getText()
+       {
+               return m_text;
+       }
+       void setText(const std::string &t)
+       {
+               m_text = t;
+               m_text_modified = true;
+       }
+       std::string getOwner()
+       {
+               if(m_enforce_owner)
+                       return m_owner;
+               else
+                       return "";
+       }
+       void setOwner(std::string t)
+       {
+               m_owner = t;
+       }
+       
+       /* Interface for GenericNodeMetadata */
+
+       void setInfoText(const std::string &text)
+       {
+               infostream<<"GenericNodeMetadata::setInfoText(\""
+                               <<text<<"\")"<<std::endl;
+               m_infotext = text;
+       }
+       void setInventoryDrawSpec(const std::string &text)
+       {
+               m_inventorydrawspec = text;
+       }
+       void setAllowTextInput(bool b)
+       {
+               m_allow_text_input = b;
+       }
+       void setRemovalDisabled(bool b)
+       {
+               m_removal_disabled = b;
+       }
+       void setEnforceOwner(bool b)
+       {
+               m_enforce_owner = b;
+       }
+       bool isInventoryModified()
+       {
+               return m_inventory_modified;
+       }
+       void resetInventoryModified()
+       {
+               m_inventory_modified = false;
+       }
+       bool isTextModified()
+       {
+               return m_text_modified;
+       }
+       void resetTextModified()
+       {
+               m_text_modified = false;
+       }
+       void setString(const std::string &name, const std::string &var)
+       {
+               m_stringvars[name] = var;
+       }
+       std::string getString(const std::string &name)
+       {
+               std::map<std::string, std::string>::iterator i;
+               i = m_stringvars.find(name);
+               if(i == m_stringvars.end())
+                       return "";
+               return i->second;
+       }
+};
+
+// Prototype
+GenericNodeMetadata proto_GenericNodeMetadata(NULL);
+