Allow server-side teleporting
[oweals/minetest.git] / src / inventory.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 "inventory.h"
21 #include "serialization.h"
22 #include "utility.h"
23 #include "debug.h"
24 #include <sstream>
25 #include "main.h" // For tsrc, g_toolmanager
26 #include "serverobject.h"
27 #include "content_mapnode.h"
28 #include "content_inventory.h"
29 #include "content_sao.h"
30 #include "environment.h"
31 #include "mapblock.h"
32 #include "player.h"
33 #include "log.h"
34 #include "nodedef.h"
35 #include "tooldef.h"
36 #include "gamedef.h"
37 #include "strfnd.h"
38
39 /*
40         InventoryItem
41 */
42
43 InventoryItem::InventoryItem(IGameDef *gamedef, u16 count):
44         m_gamedef(gamedef),
45         m_count(count)
46 {
47         assert(m_gamedef);
48 }
49
50 InventoryItem::~InventoryItem()
51 {
52 }
53
54 content_t content_translate_from_19_to_internal(content_t c_from)
55 {
56         for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
57         {
58                 if(trans_table_19[i][1] == c_from)
59                 {
60                         return trans_table_19[i][0];
61                 }
62         }
63         return c_from;
64 }
65
66 InventoryItem* InventoryItem::deSerialize(std::istream &is, IGameDef *gamedef)
67 {
68         DSTACK(__FUNCTION_NAME);
69
70         //is.imbue(std::locale("C"));
71         // Read name
72         std::string name;
73         std::getline(is, name, ' ');
74         
75         if(name == "MaterialItem")
76         {
77                 // u16 reads directly as a number (u8 doesn't)
78                 u16 material;
79                 is>>material;
80                 u16 count;
81                 is>>count;
82                 // Convert old materials
83                 if(material <= 0xff)
84                         material = content_translate_from_19_to_internal(material);
85                 if(material > MAX_CONTENT)
86                         throw SerializationError("Too large material number");
87                 return new MaterialItem(gamedef, material, count);
88         }
89         else if(name == "MaterialItem2")
90         {
91                 u16 material;
92                 is>>material;
93                 u16 count;
94                 is>>count;
95                 if(material > MAX_CONTENT)
96                         throw SerializationError("Too large material number");
97                 return new MaterialItem(gamedef, material, count);
98         }
99         else if(name == "NodeItem" || name == "MaterialItem3")
100         {
101                 std::string all;
102                 std::getline(is, all, '\n');
103                 std::string nodename;
104                 // First attempt to read inside ""
105                 Strfnd fnd(all);
106                 fnd.next("\"");
107                 // If didn't skip to end, we have ""s
108                 if(!fnd.atend()){
109                         nodename = fnd.next("\"");
110                 } else { // No luck, just read a word then
111                         fnd.start(all);
112                         nodename = fnd.next(" ");
113                 }
114                 fnd.skip_over(" ");
115                 u16 count = stoi(trim(fnd.next("")));
116                 return new MaterialItem(gamedef, nodename, count);
117         }
118         else if(name == "MBOItem")
119         {
120                 std::string inventorystring;
121                 std::getline(is, inventorystring, '|');
122                 throw SerializationError("MBOItem not supported anymore");
123         }
124         else if(name == "CraftItem")
125         {
126                 std::string all;
127                 std::getline(is, all, '\n');
128                 std::string subname;
129                 // First attempt to read inside ""
130                 Strfnd fnd(all);
131                 fnd.next("\"");
132                 // If didn't skip to end, we have ""s
133                 if(!fnd.atend()){
134                         subname = fnd.next("\"");
135                 } else { // No luck, just read a word then
136                         fnd.start(all);
137                         subname = fnd.next(" ");
138                 }
139                 // Then read count
140                 fnd.skip_over(" ");
141                 u16 count = stoi(trim(fnd.next("")));
142                 return new CraftItem(gamedef, subname, count);
143         }
144         else if(name == "ToolItem")
145         {
146                 std::string all;
147                 std::getline(is, all, '\n');
148                 std::string toolname;
149                 // First attempt to read inside ""
150                 Strfnd fnd(all);
151                 fnd.next("\"");
152                 // If didn't skip to end, we have ""s
153                 if(!fnd.atend()){
154                         toolname = fnd.next("\"");
155                 } else { // No luck, just read a word then
156                         fnd.start(all);
157                         toolname = fnd.next(" ");
158                 }
159                 // Then read wear
160                 fnd.skip_over(" ");
161                 u16 wear = stoi(trim(fnd.next("")));
162                 return new ToolItem(gamedef, toolname, wear);
163         }
164         else
165         {
166                 infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
167                 throw SerializationError("Unknown InventoryItem name");
168         }
169 }
170
171 std::string InventoryItem::getItemString() {
172         // Get item string
173         std::ostringstream os(std::ios_base::binary);
174         serialize(os);
175         return os.str();
176 }
177
178 ServerActiveObject* InventoryItem::createSAO(ServerEnvironment *env, v3f pos)
179 {
180         /*
181                 Create an ItemSAO
182         */
183         pos.Y -= BS*0.25; // let it drop a bit
184         // Randomize a bit
185         pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
186         pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
187         // Create object
188         ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
189         return obj;
190 }
191
192 /*
193         MaterialItem
194 */
195
196 MaterialItem::MaterialItem(IGameDef *gamedef, std::string nodename, u16 count):
197         InventoryItem(gamedef, count)
198 {
199         if(nodename == "")
200                 nodename = "unknown_block";
201         m_nodename = nodename;
202 }
203 // Legacy constructor
204 MaterialItem::MaterialItem(IGameDef *gamedef, content_t content, u16 count):
205         InventoryItem(gamedef, count)
206 {
207         INodeDefManager *ndef = m_gamedef->ndef();
208         std::string nodename = ndef->get(content).name;
209         if(nodename == "")
210                 nodename = "unknown_block";
211         m_nodename = nodename;
212 }
213
214 #ifndef SERVER
215 video::ITexture * MaterialItem::getImage() const
216 {
217         return m_gamedef->getNodeDefManager()->get(m_nodename).inventory_texture;
218 }
219 #endif
220
221 bool MaterialItem::isCookable() const
222 {
223         INodeDefManager *ndef = m_gamedef->ndef();
224         const ContentFeatures &f = ndef->get(m_nodename);
225         return (f.cookresult_item != "");
226 }
227
228 InventoryItem *MaterialItem::createCookResult() const
229 {
230         INodeDefManager *ndef = m_gamedef->ndef();
231         const ContentFeatures &f = ndef->get(m_nodename);
232         std::istringstream is(f.cookresult_item, std::ios::binary);
233         return InventoryItem::deSerialize(is, m_gamedef);
234 }
235
236 float MaterialItem::getCookTime() const
237 {
238         INodeDefManager *ndef = m_gamedef->ndef();
239         const ContentFeatures &f = ndef->get(m_nodename);
240         return f.furnace_cooktime;
241 }
242
243 float MaterialItem::getBurnTime() const
244 {
245         INodeDefManager *ndef = m_gamedef->ndef();
246         const ContentFeatures &f = ndef->get(m_nodename);
247         return f.furnace_burntime;
248 }
249
250 content_t MaterialItem::getMaterial() const
251 {
252         INodeDefManager *ndef = m_gamedef->ndef();
253         content_t id = CONTENT_IGNORE;
254         ndef->getId(m_nodename, id);
255         return id;
256 }
257
258 /*
259         ToolItem
260 */
261
262 std::string ToolItem::getImageBasename() const
263 {
264         return m_gamedef->getToolDefManager()->getImagename(m_toolname);
265 }
266
267 #ifndef SERVER
268 video::ITexture * ToolItem::getImage() const
269 {
270         ITextureSource *tsrc = m_gamedef->tsrc();
271
272         std::string basename = getImageBasename();
273         
274         /*
275                 Calculate a progress value with sane amount of
276                 maximum states
277         */
278         u32 maxprogress = 30;
279         u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
280         
281         float value_f = (float)toolprogress / (float)maxprogress;
282         std::ostringstream os;
283         os<<basename<<"^[progressbar"<<value_f;
284
285         return tsrc->getTextureRaw(os.str());
286 }
287
288 video::ITexture * ToolItem::getImageRaw() const
289 {
290         ITextureSource *tsrc = m_gamedef->tsrc();
291         
292         return tsrc->getTextureRaw(getImageBasename());
293 }
294 #endif
295
296 /*
297         CraftItem
298 */
299
300 #ifndef SERVER
301 video::ITexture * CraftItem::getImage() const
302 {
303         ITextureSource *tsrc = m_gamedef->tsrc();
304
305         std::string name = item_craft_get_image_name(m_subname, m_gamedef);
306
307         // Get such a texture
308         return tsrc->getTextureRaw(name);
309 }
310 #endif
311
312 ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, v3f pos)
313 {
314         // Special cases
315         ServerActiveObject *obj = item_craft_create_object(m_subname, env, pos);
316         if(obj)
317                 return obj;
318         // Default
319         return InventoryItem::createSAO(env, pos);
320 }
321
322 u16 CraftItem::getDropCount() const
323 {
324         // Special cases
325         s16 dc = item_craft_get_drop_count(m_subname, m_gamedef);
326         if(dc != -1)
327                 return dc;
328         // Default
329         return InventoryItem::getDropCount();
330 }
331
332 bool CraftItem::isCookable() const
333 {
334         return item_craft_is_cookable(m_subname, m_gamedef);
335 }
336
337 InventoryItem *CraftItem::createCookResult() const
338 {
339         return item_craft_create_cook_result(m_subname, m_gamedef);
340 }
341
342 float CraftItem::getCookTime() const
343 {
344         return 3.0;
345 }
346
347 float CraftItem::getBurnTime() const
348 {
349         if(m_subname == "lump_of_coal")
350                 return 40;
351         return -1;
352 }
353
354 bool CraftItem::use(ServerEnvironment *env, ServerActiveObject *user)
355 {
356         if(!item_craft_is_eatable(m_subname, m_gamedef))
357                 return false;
358         
359         u16 result_count = getCount() - 1; // Eat one at a time
360         s16 hp_change = item_craft_eat_hp_change(m_subname, m_gamedef);
361         s16 hp = user->getHP();
362         hp += hp_change;
363         if(hp < 0)
364                 hp = 0;
365         user->setHP(hp);
366         
367         if(result_count < 1)
368                 return true;
369                 
370         setCount(result_count);
371         return false;
372 }
373
374 /*
375         Inventory
376 */
377
378 InventoryList::InventoryList(std::string name, u32 size)
379 {
380         m_name = name;
381         m_size = size;
382         clearItems();
383         //m_dirty = false;
384 }
385
386 InventoryList::~InventoryList()
387 {
388         for(u32 i=0; i<m_items.size(); i++)
389         {
390                 if(m_items[i])
391                         delete m_items[i];
392         }
393 }
394
395 void InventoryList::clearItems()
396 {
397         for(u32 i=0; i<m_items.size(); i++)
398         {
399                 if(m_items[i])
400                         delete m_items[i];
401         }
402
403         m_items.clear();
404
405         for(u32 i=0; i<m_size; i++)
406         {
407                 m_items.push_back(NULL);
408         }
409
410         //setDirty(true);
411 }
412
413 void InventoryList::serialize(std::ostream &os) const
414 {
415         //os.imbue(std::locale("C"));
416         
417         for(u32 i=0; i<m_items.size(); i++)
418         {
419                 InventoryItem *item = m_items[i];
420                 if(item != NULL)
421                 {
422                         os<<"Item ";
423                         item->serialize(os);
424                 }
425                 else
426                 {
427                         os<<"Empty";
428                 }
429                 os<<"\n";
430         }
431
432         os<<"EndInventoryList\n";
433 }
434
435 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
436 {
437         //is.imbue(std::locale("C"));
438
439         clearItems();
440         u32 item_i = 0;
441
442         for(;;)
443         {
444                 std::string line;
445                 std::getline(is, line, '\n');
446
447                 std::istringstream iss(line);
448                 //iss.imbue(std::locale("C"));
449
450                 std::string name;
451                 std::getline(iss, name, ' ');
452
453                 if(name == "EndInventoryList")
454                 {
455                         break;
456                 }
457                 // This is a temporary backwards compatibility fix
458                 else if(name == "end")
459                 {
460                         break;
461                 }
462                 else if(name == "Item")
463                 {
464                         if(item_i > getSize() - 1)
465                                 throw SerializationError("too many items");
466                         InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
467                         m_items[item_i++] = item;
468                 }
469                 else if(name == "Empty")
470                 {
471                         if(item_i > getSize() - 1)
472                                 throw SerializationError("too many items");
473                         m_items[item_i++] = NULL;
474                 }
475                 else
476                 {
477                         throw SerializationError("Unknown inventory identifier");
478                 }
479         }
480 }
481
482 InventoryList::InventoryList(const InventoryList &other)
483 {
484         /*
485                 Do this so that the items get cloned. Otherwise the pointers
486                 in the array will just get copied.
487         */
488         *this = other;
489 }
490
491 InventoryList & InventoryList::operator = (const InventoryList &other)
492 {
493         m_name = other.m_name;
494         m_size = other.m_size;
495         clearItems();
496         for(u32 i=0; i<other.m_items.size(); i++)
497         {
498                 InventoryItem *item = other.m_items[i];
499                 if(item != NULL)
500                 {
501                         m_items[i] = item->clone();
502                 }
503         }
504         //setDirty(true);
505
506         return *this;
507 }
508
509 const std::string &InventoryList::getName() const
510 {
511         return m_name;
512 }
513
514 u32 InventoryList::getSize()
515 {
516         return m_items.size();
517 }
518
519 u32 InventoryList::getUsedSlots()
520 {
521         u32 num = 0;
522         for(u32 i=0; i<m_items.size(); i++)
523         {
524                 InventoryItem *item = m_items[i];
525                 if(item != NULL)
526                         num++;
527         }
528         return num;
529 }
530
531 u32 InventoryList::getFreeSlots()
532 {
533         return getSize() - getUsedSlots();
534 }
535
536 const InventoryItem * InventoryList::getItem(u32 i) const
537 {
538         if(i > m_items.size() - 1)
539                 return NULL;
540         return m_items[i];
541 }
542
543 InventoryItem * InventoryList::getItem(u32 i)
544 {
545         if(i > m_items.size() - 1)
546                 return NULL;
547         return m_items[i];
548 }
549
550 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
551 {
552         assert(i < m_items.size());
553
554         InventoryItem *olditem = m_items[i];
555         m_items[i] = newitem;
556         //setDirty(true);
557         return olditem;
558 }
559
560 void InventoryList::deleteItem(u32 i)
561 {
562         assert(i < m_items.size());
563         InventoryItem *item = changeItem(i, NULL);
564         if(item)
565                 delete item;
566 }
567
568 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
569 {
570         if(newitem == NULL)
571                 return NULL;
572         
573         /*
574                 First try to find if it could be added to some existing items
575         */
576         for(u32 i=0; i<m_items.size(); i++)
577         {
578                 // Ignore empty slots
579                 if(m_items[i] == NULL)
580                         continue;
581                 // Try adding
582                 newitem = addItem(i, newitem);
583                 if(newitem == NULL)
584                         return NULL; // All was eaten
585         }
586
587         /*
588                 Then try to add it to empty slots
589         */
590         for(u32 i=0; i<m_items.size(); i++)
591         {
592                 // Ignore unempty slots
593                 if(m_items[i] != NULL)
594                         continue;
595                 // Try adding
596                 newitem = addItem(i, newitem);
597                 if(newitem == NULL)
598                         return NULL; // All was eaten
599         }
600
601         // Return leftover
602         return newitem;
603 }
604
605 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
606 {
607         if(newitem == NULL)
608                 return NULL;
609         
610         //setDirty(true);
611         
612         // If it is an empty position, it's an easy job.
613         InventoryItem *to_item = getItem(i);
614         if(to_item == NULL)
615         {
616                 m_items[i] = newitem;
617                 return NULL;
618         }
619         
620         // If not addable, return the item
621         if(newitem->addableTo(to_item) == false)
622                 return newitem;
623         
624         // If the item fits fully in the slot, add counter and delete it
625         if(newitem->getCount() <= to_item->freeSpace())
626         {
627                 to_item->add(newitem->getCount());
628                 delete newitem;
629                 return NULL;
630         }
631         // Else the item does not fit fully. Add all that fits and return
632         // the rest.
633         else
634         {
635                 u16 freespace = to_item->freeSpace();
636                 to_item->add(freespace);
637                 newitem->remove(freespace);
638                 return newitem;
639         }
640 }
641
642 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
643 {
644         // If it is an empty position, it's an easy job.
645         const InventoryItem *to_item = getItem(i);
646         if(to_item == NULL)
647         {
648                 return true;
649         }
650         
651         // If not addable, fail
652         if(newitem->addableTo(to_item) == false)
653                 return false;
654         
655         // If the item fits fully in the slot, pass
656         if(newitem->getCount() <= to_item->freeSpace())
657         {
658                 return true;
659         }
660
661         return false;
662 }
663
664 bool InventoryList::roomForItem(const InventoryItem *item)
665 {
666         for(u32 i=0; i<m_items.size(); i++)
667                 if(itemFits(i, item))
668                         return true;
669         return false;
670 }
671
672 bool InventoryList::roomForCookedItem(const InventoryItem *item)
673 {
674         if(!item)
675                 return false;
676         const InventoryItem *cook = item->createCookResult();
677         if(!cook)
678                 return false;
679         bool room = roomForItem(cook);
680         delete cook;
681         return room;
682 }
683
684 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
685 {
686         if(count == 0)
687                 return NULL;
688         
689         //setDirty(true);
690
691         InventoryItem *item = getItem(i);
692         // If it is an empty position, return NULL
693         if(item == NULL)
694                 return NULL;
695         
696         if(count >= item->getCount())
697         {
698                 // Get the item by swapping NULL to its place
699                 return changeItem(i, NULL);
700         }
701         else
702         {
703                 InventoryItem *item2 = item->clone();
704                 item->remove(count);
705                 item2->setCount(count);
706                 return item2;
707         }
708         
709         return false;
710 }
711
712 void InventoryList::decrementMaterials(u16 count)
713 {
714         for(u32 i=0; i<m_items.size(); i++)
715         {
716                 InventoryItem *item = takeItem(i, count);
717                 if(item)
718                         delete item;
719         }
720 }
721
722 void InventoryList::print(std::ostream &o)
723 {
724         o<<"InventoryList:"<<std::endl;
725         for(u32 i=0; i<m_items.size(); i++)
726         {
727                 InventoryItem *item = m_items[i];
728                 if(item != NULL)
729                 {
730                         o<<i<<": ";
731                         item->serialize(o);
732                         o<<"\n";
733                 }
734         }
735 }
736
737 /*
738         Inventory
739 */
740
741 Inventory::~Inventory()
742 {
743         clear();
744 }
745
746 void Inventory::clear()
747 {
748         for(u32 i=0; i<m_lists.size(); i++)
749         {
750                 delete m_lists[i];
751         }
752         m_lists.clear();
753 }
754
755 Inventory::Inventory()
756 {
757 }
758
759 Inventory::Inventory(const Inventory &other)
760 {
761         *this = other;
762 }
763
764 Inventory & Inventory::operator = (const Inventory &other)
765 {
766         clear();
767         for(u32 i=0; i<other.m_lists.size(); i++)
768         {
769                 m_lists.push_back(new InventoryList(*other.m_lists[i]));
770         }
771         return *this;
772 }
773
774 void Inventory::serialize(std::ostream &os) const
775 {
776         for(u32 i=0; i<m_lists.size(); i++)
777         {
778                 InventoryList *list = m_lists[i];
779                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
780                 list->serialize(os);
781         }
782
783         os<<"EndInventory\n";
784 }
785
786 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
787 {
788         clear();
789
790         for(;;)
791         {
792                 std::string line;
793                 std::getline(is, line, '\n');
794
795                 std::istringstream iss(line);
796
797                 std::string name;
798                 std::getline(iss, name, ' ');
799
800                 if(name == "EndInventory")
801                 {
802                         break;
803                 }
804                 // This is a temporary backwards compatibility fix
805                 else if(name == "end")
806                 {
807                         break;
808                 }
809                 else if(name == "List")
810                 {
811                         std::string listname;
812                         u32 listsize;
813
814                         std::getline(iss, listname, ' ');
815                         iss>>listsize;
816
817                         InventoryList *list = new InventoryList(listname, listsize);
818                         list->deSerialize(is, gamedef);
819
820                         m_lists.push_back(list);
821                 }
822                 else
823                 {
824                         throw SerializationError("Unknown inventory identifier");
825                 }
826         }
827 }
828
829 InventoryList * Inventory::addList(const std::string &name, u32 size)
830 {
831         s32 i = getListIndex(name);
832         if(i != -1)
833         {
834                 if(m_lists[i]->getSize() != size)
835                 {
836                         delete m_lists[i];
837                         m_lists[i] = new InventoryList(name, size);
838                 }
839                 return m_lists[i];
840         }
841         else
842         {
843                 m_lists.push_back(new InventoryList(name, size));
844                 return m_lists.getLast();
845         }
846 }
847
848 InventoryList * Inventory::getList(const std::string &name)
849 {
850         s32 i = getListIndex(name);
851         if(i == -1)
852                 return NULL;
853         return m_lists[i];
854 }
855
856 const InventoryList * Inventory::getList(const std::string &name) const
857 {
858         s32 i = getListIndex(name);
859         if(i == -1)
860                 return NULL;
861         return m_lists[i];
862 }
863
864 const s32 Inventory::getListIndex(const std::string &name) const
865 {
866         for(u32 i=0; i<m_lists.size(); i++)
867         {
868                 if(m_lists[i]->getName() == name)
869                         return i;
870         }
871         return -1;
872 }
873
874 /*
875         InventoryAction
876 */
877
878 InventoryAction * InventoryAction::deSerialize(std::istream &is)
879 {
880         std::string type;
881         std::getline(is, type, ' ');
882
883         InventoryAction *a = NULL;
884
885         if(type == "Move")
886         {
887                 a = new IMoveAction(is);
888         }
889         else if(type == "Drop")
890         {
891                 a = new IDropAction(is);
892         }
893
894         return a;
895 }
896
897 static std::string describeC(const struct InventoryContext *c)
898 {
899         if(c->current_player == NULL)
900                 return "current_player=NULL";
901         else
902                 return std::string("current_player=") + c->current_player->getName();
903 }
904
905 IMoveAction::IMoveAction(std::istream &is)
906 {
907         std::string ts;
908
909         std::getline(is, ts, ' ');
910         count = stoi(ts);
911
912         std::getline(is, from_inv, ' ');
913
914         std::getline(is, from_list, ' ');
915
916         std::getline(is, ts, ' ');
917         from_i = stoi(ts);
918
919         std::getline(is, to_inv, ' ');
920
921         std::getline(is, to_list, ' ');
922
923         std::getline(is, ts, ' ');
924         to_i = stoi(ts);
925 }
926
927 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr,
928                 ServerEnvironment *env)
929 {
930         Inventory *inv_from = mgr->getInventory(c, from_inv);
931         Inventory *inv_to = mgr->getInventory(c, to_inv);
932         
933         if(!inv_from){
934                 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
935                                 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
936                                 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
937                 return;
938         }
939         if(!inv_to){
940                 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
941                                 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
942                                 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
943                 return;
944         }
945
946         InventoryList *list_from = inv_from->getList(from_list);
947         InventoryList *list_to = inv_to->getList(to_list);
948
949         /*
950                 If a list doesn't exist or the source item doesn't exist
951         */
952         if(!list_from){
953                 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
954                                 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
955                                 <<", from_list=\""<<from_list<<"\""<<std::endl;
956                 return;
957         }
958         if(!list_to){
959                 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
960                                 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
961                                 <<", to_list=\""<<to_list<<"\""<<std::endl;
962                 return;
963         }
964         if(list_from->getItem(from_i) == NULL)
965         {
966                 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
967                                 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
968                                 <<", from_list=\""<<from_list<<"\""
969                                 <<" from_i="<<from_i<<std::endl;
970                 return;
971         }
972         /*
973                 If the source and the destination slots are the same
974         */
975         if(inv_from == inv_to && list_from == list_to && from_i == to_i)
976         {
977                 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
978                                 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
979                                 <<"\" i="<<from_i<<std::endl;
980                 return;
981         }
982         
983         // Take item from source list
984         InventoryItem *item1 = NULL;
985         if(count == 0)
986                 item1 = list_from->changeItem(from_i, NULL);
987         else
988                 item1 = list_from->takeItem(from_i, count);
989
990         // Try to add the item to destination list
991         InventoryItem *olditem = item1;
992         item1 = list_to->addItem(to_i, item1);
993
994         // If something is returned, the item was not fully added
995         if(item1 != NULL)
996         {
997                 // If olditem is returned, nothing was added.
998                 bool nothing_added = (item1 == olditem);
999                 
1000                 // If something else is returned, part of the item was left unadded.
1001                 // Add the other part back to the source item
1002                 list_from->addItem(from_i, item1);
1003
1004                 // If olditem is returned, nothing was added.
1005                 // Swap the items
1006                 if(nothing_added)
1007                 {
1008                         // Take item from source list
1009                         item1 = list_from->changeItem(from_i, NULL);
1010                         // Adding was not possible, swap the items.
1011                         InventoryItem *item2 = list_to->changeItem(to_i, item1);
1012                         // Put item from destination list to the source list
1013                         list_from->changeItem(from_i, item2);
1014                 }
1015         }
1016
1017         mgr->inventoryModified(c, from_inv);
1018         if(from_inv != to_inv)
1019                 mgr->inventoryModified(c, to_inv);
1020         
1021         infostream<<"IMoveAction::apply(): moved at "
1022                         <<"["<<describeC(c)<<"]"
1023                         <<" from inv=\""<<from_inv<<"\""
1024                         <<" list=\""<<from_list<<"\""
1025                         <<" i="<<from_i
1026                         <<" to inv=\""<<to_inv<<"\""
1027                         <<" list=\""<<to_list<<"\""
1028                         <<" i="<<to_i
1029                         <<std::endl;
1030 }
1031
1032 IDropAction::IDropAction(std::istream &is)
1033 {
1034         std::string ts;
1035
1036         std::getline(is, ts, ' ');
1037         count = stoi(ts);
1038
1039         std::getline(is, from_inv, ' ');
1040
1041         std::getline(is, from_list, ' ');
1042
1043         std::getline(is, ts, ' ');
1044         from_i = stoi(ts);
1045 }
1046
1047 void IDropAction::apply(InventoryContext *c, InventoryManager *mgr,
1048                 ServerEnvironment *env)
1049 {
1050         Inventory *inv_from = mgr->getInventory(c, from_inv);
1051         
1052         if(!inv_from){
1053                 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
1054                                 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""<<std::endl;
1055                 return;
1056         }
1057
1058         InventoryList *list_from = inv_from->getList(from_list);
1059
1060         /*
1061                 If a list doesn't exist or the source item doesn't exist
1062         */
1063         if(!list_from){
1064                 infostream<<"IDropAction::apply(): FAIL: source list not found: "
1065                                 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1066                                 <<", from_list=\""<<from_list<<"\""<<std::endl;
1067                 return;
1068         }
1069         if(list_from->getItem(from_i) == NULL)
1070         {
1071                 infostream<<"IDropAction::apply(): FAIL: source item not found: "
1072                                 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
1073                                 <<", from_list=\""<<from_list<<"\""
1074                                 <<" from_i="<<from_i<<std::endl;
1075                 return;
1076         }
1077
1078         v3f pos = c->current_player->getPosition();
1079         pos.Y += 0.5*BS;
1080         v3s16 blockpos = getNodeBlockPos(floatToInt(pos, BS));
1081
1082         /*
1083                 Ensure that the block is loaded so that the item
1084                 can properly be added to the static list too
1085         */
1086         MapBlock *block = env->getMap().emergeBlock(blockpos, false);
1087         if(block==NULL)
1088         {
1089                 infostream<<"IDropAction::apply(): FAIL: block not found: "
1090                                 <<blockpos.X<<","<<blockpos.Y<<","<<blockpos.Z
1091                                 <<std::endl;
1092                 return;
1093         }
1094
1095         // Take item from source list
1096         if(count == 0)
1097                 count = list_from->getItem(from_i)->getDropCount();
1098         InventoryItem *item1 = list_from->takeItem(from_i, count);
1099
1100         // Create an active object
1101         ServerActiveObject *obj = item1->createSAO(env, pos);
1102         if(obj == NULL)
1103         {
1104                 infostream<<"IDropAction::apply(): item resulted in NULL object, "
1105                         <<"not placing onto map"
1106                         <<std::endl;
1107         }
1108         else
1109         {
1110                 // Add the object to the environment
1111                 env->addActiveObject(obj);
1112
1113                 infostream<<"Dropped object"<<std::endl;
1114         }
1115
1116         mgr->inventoryModified(c, from_inv);
1117
1118         infostream<<"IDropAction::apply(): dropped "
1119                         <<"["<<describeC(c)<<"]"
1120                         <<" from inv=\""<<from_inv<<"\""
1121                         <<" list=\""<<from_list<<"\""
1122                         <<" i="<<from_i
1123                         <<std::endl;
1124 }
1125
1126 /*
1127         Craft checking system
1128 */
1129
1130 bool ItemSpec::checkItem(const InventoryItem *item) const
1131 {
1132         if(type == ITEM_NONE)
1133         {
1134                 // Has to be no item
1135                 if(item != NULL)
1136                         return false;
1137                 return true;
1138         }
1139         
1140         // There should be an item
1141         if(item == NULL)
1142                 return false;
1143
1144         std::string itemname = item->getName();
1145
1146         if(type == ITEM_MATERIAL)
1147         {
1148                 if(itemname != "MaterialItem")
1149                         return false;
1150                 MaterialItem *mitem = (MaterialItem*)item;
1151                 if(num != 65535){
1152                         if(mitem->getMaterial() != num)
1153                                 return false;
1154                 } else {
1155                         if(mitem->getNodeName() != name)
1156                                 return false;
1157                 }
1158         }
1159         else if(type == ITEM_CRAFT)
1160         {
1161                 if(itemname != "CraftItem")
1162                         return false;
1163                 CraftItem *mitem = (CraftItem*)item;
1164                 if(mitem->getSubName() != name)
1165                         return false;
1166         }
1167         else if(type == ITEM_TOOL)
1168         {
1169                 // Not supported yet
1170                 assert(0);
1171         }
1172         else if(type == ITEM_MBO)
1173         {
1174                 // Not supported yet
1175                 assert(0);
1176         }
1177         else
1178         {
1179                 // Not supported yet
1180                 assert(0);
1181         }
1182         return true;
1183 }
1184
1185 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
1186 {
1187         u16 items_min_x = 100;
1188         u16 items_max_x = 100;
1189         u16 items_min_y = 100;
1190         u16 items_max_y = 100;
1191         for(u16 y=0; y<3; y++)
1192         for(u16 x=0; x<3; x++)
1193         {
1194                 if(items[y*3 + x] == NULL)
1195                         continue;
1196                 if(items_min_x == 100 || x < items_min_x)
1197                         items_min_x = x;
1198                 if(items_min_y == 100 || y < items_min_y)
1199                         items_min_y = y;
1200                 if(items_max_x == 100 || x > items_max_x)
1201                         items_max_x = x;
1202                 if(items_max_y == 100 || y > items_max_y)
1203                         items_max_y = y;
1204         }
1205         // No items at all, just return false
1206         if(items_min_x == 100)
1207                 return false;
1208         
1209         u16 items_w = items_max_x - items_min_x + 1;
1210         u16 items_h = items_max_y - items_min_y + 1;
1211
1212         u16 specs_min_x = 100;
1213         u16 specs_max_x = 100;
1214         u16 specs_min_y = 100;
1215         u16 specs_max_y = 100;
1216         for(u16 y=0; y<3; y++)
1217         for(u16 x=0; x<3; x++)
1218         {
1219                 if(specs[y*3 + x].type == ITEM_NONE)
1220                         continue;
1221                 if(specs_min_x == 100 || x < specs_min_x)
1222                         specs_min_x = x;
1223                 if(specs_min_y == 100 || y < specs_min_y)
1224                         specs_min_y = y;
1225                 if(specs_max_x == 100 || x > specs_max_x)
1226                         specs_max_x = x;
1227                 if(specs_max_y == 100 || y > specs_max_y)
1228                         specs_max_y = y;
1229         }
1230         // No specs at all, just return false
1231         if(specs_min_x == 100)
1232                 return false;
1233
1234         u16 specs_w = specs_max_x - specs_min_x + 1;
1235         u16 specs_h = specs_max_y - specs_min_y + 1;
1236
1237         // Different sizes
1238         if(items_w != specs_w || items_h != specs_h)
1239                 return false;
1240
1241         for(u16 y=0; y<specs_h; y++)
1242         for(u16 x=0; x<specs_w; x++)
1243         {
1244                 u16 items_x = items_min_x + x;
1245                 u16 items_y = items_min_y + y;
1246                 u16 specs_x = specs_min_x + x;
1247                 u16 specs_y = specs_min_y + y;
1248                 const InventoryItem *item = items[items_y * 3 + items_x];
1249                 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1250
1251                 if(spec.checkItem(item) == false)
1252                         return false;
1253         }
1254
1255         return true;
1256 }
1257
1258 bool checkItemCombination(const InventoryItem * const * items,
1259                 const InventoryItem * const * specs)
1260 {
1261         u16 items_min_x = 100;
1262         u16 items_max_x = 100;
1263         u16 items_min_y = 100;
1264         u16 items_max_y = 100;
1265         for(u16 y=0; y<3; y++)
1266         for(u16 x=0; x<3; x++)
1267         {
1268                 if(items[y*3 + x] == NULL)
1269                         continue;
1270                 if(items_min_x == 100 || x < items_min_x)
1271                         items_min_x = x;
1272                 if(items_min_y == 100 || y < items_min_y)
1273                         items_min_y = y;
1274                 if(items_max_x == 100 || x > items_max_x)
1275                         items_max_x = x;
1276                 if(items_max_y == 100 || y > items_max_y)
1277                         items_max_y = y;
1278         }
1279         // No items at all, just return false
1280         if(items_min_x == 100)
1281                 return false;
1282         
1283         u16 items_w = items_max_x - items_min_x + 1;
1284         u16 items_h = items_max_y - items_min_y + 1;
1285
1286         u16 specs_min_x = 100;
1287         u16 specs_max_x = 100;
1288         u16 specs_min_y = 100;
1289         u16 specs_max_y = 100;
1290         for(u16 y=0; y<3; y++)
1291         for(u16 x=0; x<3; x++)
1292         {
1293                 if(specs[y*3 + x] == NULL)
1294                         continue;
1295                 if(specs_min_x == 100 || x < specs_min_x)
1296                         specs_min_x = x;
1297                 if(specs_min_y == 100 || y < specs_min_y)
1298                         specs_min_y = y;
1299                 if(specs_max_x == 100 || x > specs_max_x)
1300                         specs_max_x = x;
1301                 if(specs_max_y == 100 || y > specs_max_y)
1302                         specs_max_y = y;
1303         }
1304         // No specs at all, just return false
1305         if(specs_min_x == 100)
1306                 return false;
1307
1308         u16 specs_w = specs_max_x - specs_min_x + 1;
1309         u16 specs_h = specs_max_y - specs_min_y + 1;
1310
1311         // Different sizes
1312         if(items_w != specs_w || items_h != specs_h)
1313                 return false;
1314
1315         for(u16 y=0; y<specs_h; y++)
1316         for(u16 x=0; x<specs_w; x++)
1317         {
1318                 u16 items_x = items_min_x + x;
1319                 u16 items_y = items_min_y + y;
1320                 u16 specs_x = specs_min_x + x;
1321                 u16 specs_y = specs_min_y + y;
1322                 const InventoryItem *item = items[items_y * 3 + items_x];
1323                 const InventoryItem *spec = specs[specs_y * 3 + specs_x];
1324                 
1325                 if(item == NULL && spec == NULL)
1326                         continue;
1327                 if(item == NULL && spec != NULL)
1328                         return false;
1329                 if(item != NULL && spec == NULL)
1330                         return false;
1331                 if(!spec->isSubsetOf(item))
1332                         return false;
1333         }
1334
1335         return true;
1336 }
1337
1338 //END