3 Copyright (C) 2010-2011 celeron55, Perttu Ahola <celeron55@gmail.com>
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.
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.
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.
20 #include "content_nodemeta.h"
23 #include "inventory.h"
31 #define NODEMETA_GENERIC 1
32 #define NODEMETA_SIGN 14
33 #define NODEMETA_CHEST 15
34 #define NODEMETA_FURNACE 16
35 #define NODEMETA_LOCKABLE_CHEST 17
37 core::map<u16, NodeMetadata::Factory> NodeMetadata::m_types;
38 core::map<std::string, NodeMetadata::Factory2> NodeMetadata::m_names;
40 class SignNodeMetadata : public NodeMetadata
43 SignNodeMetadata(IGameDef *gamedef, std::string text);
44 //~SignNodeMetadata();
46 virtual u16 typeId() const;
47 virtual const char* typeName() const
49 static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
50 static NodeMetadata* create(IGameDef *gamedef);
51 virtual NodeMetadata* clone(IGameDef *gamedef);
52 virtual void serializeBody(std::ostream &os);
53 virtual std::string infoText();
55 virtual bool allowsTextInput(){ return true; }
56 virtual std::string getText(){ return m_text; }
57 virtual void setText(const std::string &t){ m_text = t; }
63 class ChestNodeMetadata : public NodeMetadata
66 ChestNodeMetadata(IGameDef *gamedef);
69 virtual u16 typeId() const;
70 virtual const char* typeName() const
72 static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
73 static NodeMetadata* create(IGameDef *gamedef);
74 virtual NodeMetadata* clone(IGameDef *gamedef);
75 virtual void serializeBody(std::ostream &os);
76 virtual std::string infoText();
77 virtual Inventory* getInventory() {return m_inventory;}
78 virtual bool nodeRemovalDisabled();
79 virtual std::string getInventoryDrawSpecString();
82 Inventory *m_inventory;
85 class LockingChestNodeMetadata : public NodeMetadata
88 LockingChestNodeMetadata(IGameDef *gamedef);
89 ~LockingChestNodeMetadata();
91 virtual u16 typeId() const;
92 virtual const char* typeName() const
93 { return "locked_chest"; }
94 static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
95 static NodeMetadata* create(IGameDef *gamedef);
96 virtual NodeMetadata* clone(IGameDef *gamedef);
97 virtual void serializeBody(std::ostream &os);
98 virtual std::string infoText();
99 virtual Inventory* getInventory() {return m_inventory;}
100 virtual bool nodeRemovalDisabled();
101 virtual std::string getInventoryDrawSpecString();
103 virtual std::string getOwner(){ return m_text; }
104 virtual void setOwner(std::string t){ m_text = t; }
107 Inventory *m_inventory;
111 class FurnaceNodeMetadata : public NodeMetadata
114 FurnaceNodeMetadata(IGameDef *gamedef);
115 ~FurnaceNodeMetadata();
117 virtual u16 typeId() const;
118 virtual const char* typeName() const
119 { return "furnace"; }
120 virtual NodeMetadata* clone(IGameDef *gamedef);
121 static NodeMetadata* create(std::istream &is, IGameDef *gamedef);
122 static NodeMetadata* create(IGameDef *gamedef);
123 virtual void serializeBody(std::ostream &os);
124 virtual std::string infoText();
125 virtual Inventory* getInventory() {return m_inventory;}
126 virtual void inventoryModified();
127 virtual bool step(float dtime);
128 virtual bool nodeRemovalDisabled();
129 virtual std::string getInventoryDrawSpecString();
132 bool getCookResult(bool remove, std::string &cookresult, float &cooktime);
133 bool getBurnResult(bool remove, float &burntime);
136 Inventory *m_inventory;
137 std::string m_infotext;
138 float m_step_accumulator;
139 float m_fuel_totaltime;
141 float m_src_totaltime;
150 SignNodeMetadata proto_SignNodeMetadata(NULL, "");
152 SignNodeMetadata::SignNodeMetadata(IGameDef *gamedef, std::string text):
153 NodeMetadata(gamedef),
156 NodeMetadata::registerType(typeId(), typeName(), create, create);
158 u16 SignNodeMetadata::typeId() const
160 return NODEMETA_SIGN;
162 NodeMetadata* SignNodeMetadata::create(std::istream &is, IGameDef *gamedef)
164 std::string text = deSerializeString(is);
165 return new SignNodeMetadata(gamedef, text);
167 NodeMetadata* SignNodeMetadata::create(IGameDef *gamedef)
169 return new SignNodeMetadata(gamedef, "");
171 NodeMetadata* SignNodeMetadata::clone(IGameDef *gamedef)
173 return new SignNodeMetadata(gamedef, m_text);
175 void SignNodeMetadata::serializeBody(std::ostream &os)
177 os<<serializeString(m_text);
179 std::string SignNodeMetadata::infoText()
181 return std::string("\"")+m_text+"\"";
189 ChestNodeMetadata proto_ChestNodeMetadata(NULL);
191 ChestNodeMetadata::ChestNodeMetadata(IGameDef *gamedef):
192 NodeMetadata(gamedef)
194 NodeMetadata::registerType(typeId(), typeName(), create, create);
197 ChestNodeMetadata::~ChestNodeMetadata()
201 u16 ChestNodeMetadata::typeId() const
203 return NODEMETA_CHEST;
205 NodeMetadata* ChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
207 ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
208 d->m_inventory = new Inventory(gamedef->idef());
209 d->m_inventory->deSerialize(is);
212 NodeMetadata* ChestNodeMetadata::create(IGameDef *gamedef)
214 ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
215 d->m_inventory = new Inventory(gamedef->idef());
216 d->m_inventory->addList("0", 8*4);
219 NodeMetadata* ChestNodeMetadata::clone(IGameDef *gamedef)
221 ChestNodeMetadata *d = new ChestNodeMetadata(gamedef);
222 d->m_inventory = new Inventory(*m_inventory);
225 void ChestNodeMetadata::serializeBody(std::ostream &os)
227 m_inventory->serialize(os);
229 std::string ChestNodeMetadata::infoText()
233 bool ChestNodeMetadata::nodeRemovalDisabled()
236 Disable removal if chest contains something
238 InventoryList *list = m_inventory->getList("0");
241 if(list->getUsedSlots() == 0)
245 std::string ChestNodeMetadata::getInventoryDrawSpecString()
249 "list[current_name;0;0,0;8,4;]"
250 "list[current_player;main;0,5;8,4;]";
254 LockingChestNodeMetadata
258 LockingChestNodeMetadata proto_LockingChestNodeMetadata(NULL);
260 LockingChestNodeMetadata::LockingChestNodeMetadata(IGameDef *gamedef):
261 NodeMetadata(gamedef)
263 NodeMetadata::registerType(typeId(), typeName(), create, create);
266 LockingChestNodeMetadata::~LockingChestNodeMetadata()
270 u16 LockingChestNodeMetadata::typeId() const
272 return NODEMETA_LOCKABLE_CHEST;
274 NodeMetadata* LockingChestNodeMetadata::create(std::istream &is, IGameDef *gamedef)
276 LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
277 d->setOwner(deSerializeString(is));
278 d->m_inventory = new Inventory(gamedef->idef());
279 d->m_inventory->deSerialize(is);
282 NodeMetadata* LockingChestNodeMetadata::create(IGameDef *gamedef)
284 LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
285 d->m_inventory = new Inventory(gamedef->idef());
286 d->m_inventory->addList("0", 8*4);
289 NodeMetadata* LockingChestNodeMetadata::clone(IGameDef *gamedef)
291 LockingChestNodeMetadata *d = new LockingChestNodeMetadata(gamedef);
292 d->m_inventory = new Inventory(*m_inventory);
295 void LockingChestNodeMetadata::serializeBody(std::ostream &os)
297 os<<serializeString(m_text);
298 m_inventory->serialize(os);
300 std::string LockingChestNodeMetadata::infoText()
302 return "Locking Chest";
304 bool LockingChestNodeMetadata::nodeRemovalDisabled()
307 Disable removal if chest contains something
309 InventoryList *list = m_inventory->getList("0");
312 if(list->getUsedSlots() == 0)
316 std::string LockingChestNodeMetadata::getInventoryDrawSpecString()
320 "list[current_name;0;0,0;8,4;]"
321 "list[current_player;main;0,5;8,4;]";
329 FurnaceNodeMetadata proto_FurnaceNodeMetadata(NULL);
331 FurnaceNodeMetadata::FurnaceNodeMetadata(IGameDef *gamedef):
332 NodeMetadata(gamedef)
334 NodeMetadata::registerType(typeId(), typeName(), create, create);
338 m_infotext = "Furnace is inactive";
340 m_step_accumulator = 0;
341 m_fuel_totaltime = 0;
346 FurnaceNodeMetadata::~FurnaceNodeMetadata()
350 u16 FurnaceNodeMetadata::typeId() const
352 return NODEMETA_FURNACE;
354 NodeMetadata* FurnaceNodeMetadata::clone(IGameDef *gamedef)
356 FurnaceNodeMetadata *d = new FurnaceNodeMetadata(m_gamedef);
357 d->m_inventory = new Inventory(*m_inventory);
360 NodeMetadata* FurnaceNodeMetadata::create(std::istream &is, IGameDef *gamedef)
362 FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
364 d->m_inventory = new Inventory(gamedef->idef());
365 d->m_inventory->deSerialize(is);
369 d->m_fuel_totaltime = (float)temp/10;
372 d->m_fuel_time = (float)temp/10;
375 d->m_src_totaltime = (float)temp/10;
378 d->m_src_time = (float)temp/10;
382 // Old furnaces didn't serialize src_totaltime and src_time
383 d->m_src_totaltime = 0;
389 // New furnaces also serialize the infotext (so that the
390 // client doesn't need to have the list of cooking recipes).
391 d->m_infotext = deSerializeJsonString(is);
396 NodeMetadata* FurnaceNodeMetadata::create(IGameDef *gamedef)
398 FurnaceNodeMetadata *d = new FurnaceNodeMetadata(gamedef);
399 d->m_inventory = new Inventory(gamedef->idef());
400 d->m_inventory->addList("fuel", 1);
401 d->m_inventory->addList("src", 1);
402 d->m_inventory->addList("dst", 4);
405 void FurnaceNodeMetadata::serializeBody(std::ostream &os)
407 m_inventory->serialize(os);
408 os<<itos(m_fuel_totaltime*10)<<" ";
409 os<<itos(m_fuel_time*10)<<" ";
410 os<<itos(m_src_totaltime*10)<<" ";
411 os<<itos(m_src_time*10)<<" ";
412 os<<serializeJsonString(m_infotext);
414 std::string FurnaceNodeMetadata::infoText()
418 bool FurnaceNodeMetadata::nodeRemovalDisabled()
421 Disable removal if furnace is not empty
423 InventoryList *list[3] = {m_inventory->getList("src"),
424 m_inventory->getList("dst"), m_inventory->getList("fuel")};
426 for(int i = 0; i < 3; i++) {
429 if(list[i]->getUsedSlots() == 0)
436 void FurnaceNodeMetadata::inventoryModified()
438 infostream<<"Furnace inventory modification callback"<<std::endl;
440 bool FurnaceNodeMetadata::step(float dtime)
443 infostream<<"Furnace stepping a long time ("<<dtime<<")"<<std::endl;
445 InventoryList *dst_list = m_inventory->getList("dst");
448 // Update at a fixed frequency
449 const float interval = 2.0;
450 m_step_accumulator += dtime;
451 bool changed = false;
452 while(m_step_accumulator > interval)
454 m_step_accumulator -= interval;
457 //infostream<<"Furnace step dtime="<<dtime<<std::endl;
459 bool changed_this_loop = false;
462 // 1. if the source item is cookable
463 // 2. if there is room for the cooked item
464 std::string cookresult;
466 bool cookable = getCookResult(false, cookresult, cooktime);
467 ItemStack cookresult_item;
468 bool room_available = false;
471 cookresult_item.deSerialize(cookresult, m_gamedef->idef());
472 room_available = dst_list->roomForItem(cookresult_item);
476 bool burning = (m_fuel_time < m_fuel_totaltime);
479 changed_this_loop = true;
480 m_fuel_time += dtime;
483 std::string infotext;
489 changed_this_loop = true;
491 m_src_totaltime = cooktime;
492 infotext = "Furnace is cooking";
494 else if(getBurnResult(true, burntime))
497 changed_this_loop = true;
499 m_fuel_totaltime = burntime;
500 //m_src_time += dtime;
501 //m_src_totaltime = cooktime;
502 infotext = "Furnace is cooking";
508 infotext = "Furnace is out of fuel";
510 if(m_src_totaltime > 0.001 && m_src_time >= m_src_totaltime)
512 // One item fully cooked
513 changed_this_loop = true;
514 dst_list->addItem(cookresult_item);
515 getCookResult(true, cookresult, cooktime); // decrement source
522 // Not cookable or no room available
526 infotext = "Furnace is overloaded";
528 infotext = "Furnace is active";
531 infotext = "Furnace is inactive";
532 m_fuel_totaltime = 0;
537 // Do this so it doesn't always show (0%) for weak fuel
538 if(m_fuel_totaltime > 3) {
540 infotext += itos(m_fuel_time/m_fuel_totaltime*100);
544 if(infotext != m_infotext)
546 m_infotext = infotext;
547 changed_this_loop = true;
550 if(burning && m_fuel_time >= m_fuel_totaltime)
553 m_fuel_totaltime = 0;
556 if(changed_this_loop)
562 m_step_accumulator = 0;
568 std::string FurnaceNodeMetadata::getInventoryDrawSpecString()
572 "list[current_name;fuel;2,3;1,1;]"
573 "list[current_name;src;2,1;1,1;]"
574 "list[current_name;dst;5,1;2,2;]"
575 "list[current_player;main;0,5;8,4;]";
577 bool FurnaceNodeMetadata::getCookResult(bool remove,
578 std::string &cookresult, float &cooktime)
580 std::vector<ItemStack> items;
581 InventoryList *src_list = m_inventory->getList("src");
583 items.push_back(src_list->getItem(0));
585 CraftInput ci(CRAFT_METHOD_COOKING, 1, items);
587 bool found = m_gamedef->getCraftDefManager()->getCraftResult(
588 ci, co, remove, m_gamedef);
590 src_list->changeItem(0, ci.items[0]);
592 cookresult = co.item;
596 bool FurnaceNodeMetadata::getBurnResult(bool remove, float &burntime)
598 std::vector<ItemStack> items;
599 InventoryList *fuel_list = m_inventory->getList("fuel");
601 items.push_back(fuel_list->getItem(0));
603 CraftInput ci(CRAFT_METHOD_FUEL, 1, items);
605 bool found = m_gamedef->getCraftDefManager()->getCraftResult(
606 ci, co, remove, m_gamedef);
608 fuel_list->changeItem(0, ci.items[0]);
619 class GenericNodeMetadata : public NodeMetadata
622 Inventory *m_inventory;
626 std::string m_infotext;
627 std::string m_inventorydrawspec;
628 bool m_allow_text_input;
629 bool m_removal_disabled;
630 bool m_enforce_owner;
632 bool m_inventory_modified;
633 bool m_text_modified;
635 std::map<std::string, std::string> m_stringvars;
640 return NODEMETA_GENERIC;
642 const char* typeName() const
647 GenericNodeMetadata(IGameDef *gamedef):
648 NodeMetadata(gamedef),
654 m_infotext("GenericNodeMetadata"),
655 m_inventorydrawspec(""),
656 m_allow_text_input(false),
657 m_removal_disabled(false),
658 m_enforce_owner(false),
660 m_inventory_modified(false),
661 m_text_modified(false)
663 NodeMetadata::registerType(typeId(), typeName(), create, create);
665 virtual ~GenericNodeMetadata()
669 NodeMetadata* clone(IGameDef *gamedef)
671 GenericNodeMetadata *d = new GenericNodeMetadata(m_gamedef);
673 d->m_inventory = new Inventory(*m_inventory);
675 d->m_owner = m_owner;
677 d->m_infotext = m_infotext;
678 d->m_inventorydrawspec = m_inventorydrawspec;
679 d->m_allow_text_input = m_allow_text_input;
680 d->m_removal_disabled = m_removal_disabled;
681 d->m_enforce_owner = m_enforce_owner;
682 d->m_inventory_modified = m_inventory_modified;
683 d->m_text_modified = m_text_modified;
686 static NodeMetadata* create(IGameDef *gamedef)
688 GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
689 d->m_inventory = new Inventory(gamedef->idef());
692 static NodeMetadata* create(std::istream &is, IGameDef *gamedef)
694 GenericNodeMetadata *d = new GenericNodeMetadata(gamedef);
696 d->m_inventory = new Inventory(gamedef->idef());
697 d->m_inventory->deSerialize(is);
698 d->m_text = deSerializeLongString(is);
699 d->m_owner = deSerializeString(is);
701 d->m_infotext = deSerializeString(is);
702 d->m_inventorydrawspec = deSerializeString(is);
703 d->m_allow_text_input = readU8(is);
704 d->m_removal_disabled = readU8(is);
705 d->m_enforce_owner = readU8(is);
707 int num_vars = readU32(is);
708 for(int i=0; i<num_vars; i++){
709 std::string name = deSerializeString(is);
710 std::string var = deSerializeLongString(is);
711 d->m_stringvars[name] = var;
716 void serializeBody(std::ostream &os)
718 m_inventory->serialize(os);
719 os<<serializeLongString(m_text);
720 os<<serializeString(m_owner);
722 os<<serializeString(m_infotext);
723 os<<serializeString(m_inventorydrawspec);
724 writeU8(os, m_allow_text_input);
725 writeU8(os, m_removal_disabled);
726 writeU8(os, m_enforce_owner);
728 int num_vars = m_stringvars.size();
729 writeU32(os, num_vars);
730 for(std::map<std::string, std::string>::iterator
731 i = m_stringvars.begin(); i != m_stringvars.end(); i++){
732 os<<serializeString(i->first);
733 os<<serializeLongString(i->second);
737 std::string infoText()
741 Inventory* getInventory()
745 void inventoryModified()
747 m_inventory_modified = true;
749 bool step(float dtime)
753 bool nodeRemovalDisabled()
755 return m_removal_disabled;
757 std::string getInventoryDrawSpecString()
759 return m_inventorydrawspec;
761 bool allowsTextInput()
763 return m_allow_text_input;
765 std::string getText()
769 void setText(const std::string &t)
772 m_text_modified = true;
774 std::string getOwner()
781 void setOwner(std::string t)
786 /* Interface for GenericNodeMetadata */
788 void setInfoText(const std::string &text)
790 infostream<<"GenericNodeMetadata::setInfoText(\""
791 <<text<<"\")"<<std::endl;
794 void setInventoryDrawSpec(const std::string &text)
796 m_inventorydrawspec = text;
798 void setAllowTextInput(bool b)
800 m_allow_text_input = b;
802 void setRemovalDisabled(bool b)
804 m_removal_disabled = b;
806 void setEnforceOwner(bool b)
810 bool isInventoryModified()
812 return m_inventory_modified;
814 void resetInventoryModified()
816 m_inventory_modified = false;
818 bool isTextModified()
820 return m_text_modified;
822 void resetTextModified()
824 m_text_modified = false;
826 void setString(const std::string &name, const std::string &var)
828 m_stringvars[name] = var;
830 std::string getString(const std::string &name)
832 std::map<std::string, std::string>::iterator i;
833 i = m_stringvars.find(name);
834 if(i == m_stringvars.end())
841 GenericNodeMetadata proto_GenericNodeMetadata(NULL);