]> git.lizzy.rs Git - minetest.git/blob - src/inventory.cpp
6d0a2a21440b4171f26e302558579b3fbe3de37c
[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 "player.h"
31 #include "log.h"
32 #include "nodedef.h"
33 #include "tooldef.h"
34 #include "gamedef.h"
35
36 /*
37         InventoryItem
38 */
39
40 InventoryItem::InventoryItem(IGameDef *gamedef, u16 count):
41         m_gamedef(gamedef),
42         m_count(count)
43 {
44         assert(m_gamedef);
45 }
46
47 InventoryItem::~InventoryItem()
48 {
49 }
50
51 content_t content_translate_from_19_to_internal(content_t c_from)
52 {
53         for(u32 i=0; i<sizeof(trans_table_19)/sizeof(trans_table_19[0]); i++)
54         {
55                 if(trans_table_19[i][1] == c_from)
56                 {
57                         return trans_table_19[i][0];
58                 }
59         }
60         return c_from;
61 }
62
63 InventoryItem* InventoryItem::deSerialize(std::istream &is, IGameDef *gamedef)
64 {
65         DSTACK(__FUNCTION_NAME);
66
67         //is.imbue(std::locale("C"));
68         // Read name
69         std::string name;
70         std::getline(is, name, ' ');
71         
72         if(name == "MaterialItem")
73         {
74                 // u16 reads directly as a number (u8 doesn't)
75                 u16 material;
76                 is>>material;
77                 u16 count;
78                 is>>count;
79                 // Convert old materials
80                 if(material <= 0xff)
81                 {
82                         material = content_translate_from_19_to_internal(material);
83                 }
84                 if(material > MAX_CONTENT)
85                         throw SerializationError("Too large material number");
86                 return new MaterialItem(gamedef, material, count);
87         }
88         else if(name == "MaterialItem2")
89         {
90                 u16 material;
91                 is>>material;
92                 u16 count;
93                 is>>count;
94                 if(material > MAX_CONTENT)
95                         throw SerializationError("Too large material number");
96                 return new MaterialItem(gamedef, material, count);
97         }
98         else if(name == "MBOItem")
99         {
100                 std::string inventorystring;
101                 std::getline(is, inventorystring, '|');
102                 throw SerializationError("MBOItem not supported anymore");
103         }
104         else if(name == "CraftItem")
105         {
106                 std::string subname;
107                 std::getline(is, subname, ' ');
108                 u16 count;
109                 is>>count;
110                 return new CraftItem(gamedef, subname, count);
111         }
112         else if(name == "ToolItem")
113         {
114                 std::string toolname;
115                 std::getline(is, toolname, ' ');
116                 u16 wear;
117                 is>>wear;
118                 return new ToolItem(gamedef, toolname, wear);
119         }
120         else
121         {
122                 infostream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
123                 throw SerializationError("Unknown InventoryItem name");
124         }
125 }
126
127 std::string InventoryItem::getItemString() {
128         // Get item string
129         std::ostringstream os(std::ios_base::binary);
130         serialize(os);
131         return os.str();
132 }
133
134 ServerActiveObject* InventoryItem::createSAO(ServerEnvironment *env, u16 id, v3f pos)
135 {
136         /*
137                 Create an ItemSAO
138         */
139         pos.Y -= BS*0.25; // let it drop a bit
140         // Randomize a bit
141         pos.X += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
142         pos.Z += BS*0.2*(float)myrand_range(-1000,1000)/1000.0;
143         // Create object
144         ServerActiveObject *obj = new ItemSAO(env, pos, getItemString());
145         return obj;
146 }
147
148 /*
149         MaterialItem
150 */
151
152 #ifndef SERVER
153 video::ITexture * MaterialItem::getImage(ITextureSource *tsrc) const
154 {
155         return m_gamedef->getNodeDefManager()->get(m_content).inventory_texture;
156 }
157 #endif
158
159 bool MaterialItem::isCookable() const
160 {
161         INodeDefManager *ndef = m_gamedef->ndef();
162         const ContentFeatures &f = ndef->get(m_content);
163         return (f.cookresult_item != "");
164 }
165
166 InventoryItem *MaterialItem::createCookResult() const
167 {
168         INodeDefManager *ndef = m_gamedef->ndef();
169         const ContentFeatures &f = ndef->get(m_content);
170         std::istringstream is(f.cookresult_item, std::ios::binary);
171         return InventoryItem::deSerialize(is, m_gamedef);
172 }
173
174 /*
175         ToolItem
176 */
177
178 std::string ToolItem::getImageBasename() const
179 {
180         return m_gamedef->getToolDefManager()->getImagename(m_toolname);
181 }
182
183 #ifndef SERVER
184 video::ITexture * ToolItem::getImage(ITextureSource *tsrc) const
185 {
186         if(tsrc == NULL)
187                 return NULL;
188         
189         std::string basename = getImageBasename();
190         
191         /*
192                 Calculate a progress value with sane amount of
193                 maximum states
194         */
195         u32 maxprogress = 30;
196         u32 toolprogress = (65535-m_wear)/(65535/maxprogress);
197         
198         float value_f = (float)toolprogress / (float)maxprogress;
199         std::ostringstream os;
200         os<<basename<<"^[progressbar"<<value_f;
201
202         return tsrc->getTextureRaw(os.str());
203 }
204
205 video::ITexture * ToolItem::getImageRaw(ITextureSource *tsrc) const
206 {
207         if(tsrc == NULL)
208                 return NULL;
209         
210         return tsrc->getTextureRaw(getImageBasename());
211 }
212 #endif
213
214 /*
215         CraftItem
216 */
217
218 #ifndef SERVER
219 video::ITexture * CraftItem::getImage(ITextureSource *tsrc) const
220 {
221         if(tsrc == NULL)
222                 return NULL;
223         
224         std::string name = item_craft_get_image_name(m_subname, m_gamedef);
225
226         // Get such a texture
227         return tsrc->getTextureRaw(name);
228 }
229 #endif
230
231 ServerActiveObject* CraftItem::createSAO(ServerEnvironment *env, u16 id, v3f pos)
232 {
233         // Special cases
234         ServerActiveObject *obj = item_craft_create_object(m_subname, env, pos);
235         if(obj)
236                 return obj;
237         // Default
238         return InventoryItem::createSAO(env, id, pos);
239 }
240
241 u16 CraftItem::getDropCount() const
242 {
243         // Special cases
244         s16 dc = item_craft_get_drop_count(m_subname, m_gamedef);
245         if(dc != -1)
246                 return dc;
247         // Default
248         return InventoryItem::getDropCount();
249 }
250
251 bool CraftItem::isCookable() const
252 {
253         return item_craft_is_cookable(m_subname, m_gamedef);
254 }
255
256 InventoryItem *CraftItem::createCookResult() const
257 {
258         return item_craft_create_cook_result(m_subname, m_gamedef);
259 }
260
261 bool CraftItem::use(ServerEnvironment *env, ServerActiveObject *user)
262 {
263         if(!item_craft_is_eatable(m_subname, m_gamedef))
264                 return false;
265         
266         u16 result_count = getCount() - 1; // Eat one at a time
267         s16 hp_change = item_craft_eat_hp_change(m_subname, m_gamedef);
268         s16 hp = user->getHP();
269         hp += hp_change;
270         if(hp < 0)
271                 hp = 0;
272         user->setHP(hp);
273         
274         if(result_count < 1)
275                 return true;
276                 
277         setCount(result_count);
278         return false;
279 }
280
281 /*
282         Inventory
283 */
284
285 InventoryList::InventoryList(std::string name, u32 size)
286 {
287         m_name = name;
288         m_size = size;
289         clearItems();
290         //m_dirty = false;
291 }
292
293 InventoryList::~InventoryList()
294 {
295         for(u32 i=0; i<m_items.size(); i++)
296         {
297                 if(m_items[i])
298                         delete m_items[i];
299         }
300 }
301
302 void InventoryList::clearItems()
303 {
304         for(u32 i=0; i<m_items.size(); i++)
305         {
306                 if(m_items[i])
307                         delete m_items[i];
308         }
309
310         m_items.clear();
311
312         for(u32 i=0; i<m_size; i++)
313         {
314                 m_items.push_back(NULL);
315         }
316
317         //setDirty(true);
318 }
319
320 void InventoryList::serialize(std::ostream &os) const
321 {
322         //os.imbue(std::locale("C"));
323         
324         for(u32 i=0; i<m_items.size(); i++)
325         {
326                 InventoryItem *item = m_items[i];
327                 if(item != NULL)
328                 {
329                         os<<"Item ";
330                         item->serialize(os);
331                 }
332                 else
333                 {
334                         os<<"Empty";
335                 }
336                 os<<"\n";
337         }
338
339         os<<"EndInventoryList\n";
340 }
341
342 void InventoryList::deSerialize(std::istream &is, IGameDef *gamedef)
343 {
344         //is.imbue(std::locale("C"));
345
346         clearItems();
347         u32 item_i = 0;
348
349         for(;;)
350         {
351                 std::string line;
352                 std::getline(is, line, '\n');
353
354                 std::istringstream iss(line);
355                 //iss.imbue(std::locale("C"));
356
357                 std::string name;
358                 std::getline(iss, name, ' ');
359
360                 if(name == "EndInventoryList")
361                 {
362                         break;
363                 }
364                 // This is a temporary backwards compatibility fix
365                 else if(name == "end")
366                 {
367                         break;
368                 }
369                 else if(name == "Item")
370                 {
371                         if(item_i > getSize() - 1)
372                                 throw SerializationError("too many items");
373                         InventoryItem *item = InventoryItem::deSerialize(iss, gamedef);
374                         m_items[item_i++] = item;
375                 }
376                 else if(name == "Empty")
377                 {
378                         if(item_i > getSize() - 1)
379                                 throw SerializationError("too many items");
380                         m_items[item_i++] = NULL;
381                 }
382                 else
383                 {
384                         throw SerializationError("Unknown inventory identifier");
385                 }
386         }
387 }
388
389 InventoryList::InventoryList(const InventoryList &other)
390 {
391         /*
392                 Do this so that the items get cloned. Otherwise the pointers
393                 in the array will just get copied.
394         */
395         *this = other;
396 }
397
398 InventoryList & InventoryList::operator = (const InventoryList &other)
399 {
400         m_name = other.m_name;
401         m_size = other.m_size;
402         clearItems();
403         for(u32 i=0; i<other.m_items.size(); i++)
404         {
405                 InventoryItem *item = other.m_items[i];
406                 if(item != NULL)
407                 {
408                         m_items[i] = item->clone();
409                 }
410         }
411         //setDirty(true);
412
413         return *this;
414 }
415
416 const std::string &InventoryList::getName() const
417 {
418         return m_name;
419 }
420
421 u32 InventoryList::getSize()
422 {
423         return m_items.size();
424 }
425
426 u32 InventoryList::getUsedSlots()
427 {
428         u32 num = 0;
429         for(u32 i=0; i<m_items.size(); i++)
430         {
431                 InventoryItem *item = m_items[i];
432                 if(item != NULL)
433                         num++;
434         }
435         return num;
436 }
437
438 u32 InventoryList::getFreeSlots()
439 {
440         return getSize() - getUsedSlots();
441 }
442
443 const InventoryItem * InventoryList::getItem(u32 i) const
444 {
445         if(i > m_items.size() - 1)
446                 return NULL;
447         return m_items[i];
448 }
449
450 InventoryItem * InventoryList::getItem(u32 i)
451 {
452         if(i > m_items.size() - 1)
453                 return NULL;
454         return m_items[i];
455 }
456
457 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
458 {
459         assert(i < m_items.size());
460
461         InventoryItem *olditem = m_items[i];
462         m_items[i] = newitem;
463         //setDirty(true);
464         return olditem;
465 }
466
467 void InventoryList::deleteItem(u32 i)
468 {
469         assert(i < m_items.size());
470         InventoryItem *item = changeItem(i, NULL);
471         if(item)
472                 delete item;
473 }
474
475 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
476 {
477         if(newitem == NULL)
478                 return NULL;
479         
480         /*
481                 First try to find if it could be added to some existing items
482         */
483         for(u32 i=0; i<m_items.size(); i++)
484         {
485                 // Ignore empty slots
486                 if(m_items[i] == NULL)
487                         continue;
488                 // Try adding
489                 newitem = addItem(i, newitem);
490                 if(newitem == NULL)
491                         return NULL; // All was eaten
492         }
493
494         /*
495                 Then try to add it to empty slots
496         */
497         for(u32 i=0; i<m_items.size(); i++)
498         {
499                 // Ignore unempty slots
500                 if(m_items[i] != NULL)
501                         continue;
502                 // Try adding
503                 newitem = addItem(i, newitem);
504                 if(newitem == NULL)
505                         return NULL; // All was eaten
506         }
507
508         // Return leftover
509         return newitem;
510 }
511
512 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
513 {
514         if(newitem == NULL)
515                 return NULL;
516         
517         //setDirty(true);
518         
519         // If it is an empty position, it's an easy job.
520         InventoryItem *to_item = getItem(i);
521         if(to_item == NULL)
522         {
523                 m_items[i] = newitem;
524                 return NULL;
525         }
526         
527         // If not addable, return the item
528         if(newitem->addableTo(to_item) == false)
529                 return newitem;
530         
531         // If the item fits fully in the slot, add counter and delete it
532         if(newitem->getCount() <= to_item->freeSpace())
533         {
534                 to_item->add(newitem->getCount());
535                 delete newitem;
536                 return NULL;
537         }
538         // Else the item does not fit fully. Add all that fits and return
539         // the rest.
540         else
541         {
542                 u16 freespace = to_item->freeSpace();
543                 to_item->add(freespace);
544                 newitem->remove(freespace);
545                 return newitem;
546         }
547 }
548
549 bool InventoryList::itemFits(const u32 i, const InventoryItem *newitem)
550 {
551         // If it is an empty position, it's an easy job.
552         const InventoryItem *to_item = getItem(i);
553         if(to_item == NULL)
554         {
555                 return true;
556         }
557         
558         // If not addable, fail
559         if(newitem->addableTo(to_item) == false)
560                 return false;
561         
562         // If the item fits fully in the slot, pass
563         if(newitem->getCount() <= to_item->freeSpace())
564         {
565                 return true;
566         }
567
568         return false;
569 }
570
571 bool InventoryList::roomForItem(const InventoryItem *item)
572 {
573         for(u32 i=0; i<m_items.size(); i++)
574                 if(itemFits(i, item))
575                         return true;
576         return false;
577 }
578
579 bool InventoryList::roomForCookedItem(const InventoryItem *item)
580 {
581         if(!item)
582                 return false;
583         const InventoryItem *cook = item->createCookResult();
584         if(!cook)
585                 return false;
586         bool room = roomForItem(cook);
587         delete cook;
588         return room;
589 }
590
591 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
592 {
593         if(count == 0)
594                 return NULL;
595         
596         //setDirty(true);
597
598         InventoryItem *item = getItem(i);
599         // If it is an empty position, return NULL
600         if(item == NULL)
601                 return NULL;
602         
603         if(count >= item->getCount())
604         {
605                 // Get the item by swapping NULL to its place
606                 return changeItem(i, NULL);
607         }
608         else
609         {
610                 InventoryItem *item2 = item->clone();
611                 item->remove(count);
612                 item2->setCount(count);
613                 return item2;
614         }
615         
616         return false;
617 }
618
619 void InventoryList::decrementMaterials(u16 count)
620 {
621         for(u32 i=0; i<m_items.size(); i++)
622         {
623                 InventoryItem *item = takeItem(i, count);
624                 if(item)
625                         delete item;
626         }
627 }
628
629 void InventoryList::print(std::ostream &o)
630 {
631         o<<"InventoryList:"<<std::endl;
632         for(u32 i=0; i<m_items.size(); i++)
633         {
634                 InventoryItem *item = m_items[i];
635                 if(item != NULL)
636                 {
637                         o<<i<<": ";
638                         item->serialize(o);
639                         o<<"\n";
640                 }
641         }
642 }
643
644 /*
645         Inventory
646 */
647
648 Inventory::~Inventory()
649 {
650         clear();
651 }
652
653 void Inventory::clear()
654 {
655         for(u32 i=0; i<m_lists.size(); i++)
656         {
657                 delete m_lists[i];
658         }
659         m_lists.clear();
660 }
661
662 Inventory::Inventory()
663 {
664 }
665
666 Inventory::Inventory(const Inventory &other)
667 {
668         *this = other;
669 }
670
671 Inventory & Inventory::operator = (const Inventory &other)
672 {
673         clear();
674         for(u32 i=0; i<other.m_lists.size(); i++)
675         {
676                 m_lists.push_back(new InventoryList(*other.m_lists[i]));
677         }
678         return *this;
679 }
680
681 void Inventory::serialize(std::ostream &os) const
682 {
683         for(u32 i=0; i<m_lists.size(); i++)
684         {
685                 InventoryList *list = m_lists[i];
686                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
687                 list->serialize(os);
688         }
689
690         os<<"EndInventory\n";
691 }
692
693 void Inventory::deSerialize(std::istream &is, IGameDef *gamedef)
694 {
695         clear();
696
697         for(;;)
698         {
699                 std::string line;
700                 std::getline(is, line, '\n');
701
702                 std::istringstream iss(line);
703
704                 std::string name;
705                 std::getline(iss, name, ' ');
706
707                 if(name == "EndInventory")
708                 {
709                         break;
710                 }
711                 // This is a temporary backwards compatibility fix
712                 else if(name == "end")
713                 {
714                         break;
715                 }
716                 else if(name == "List")
717                 {
718                         std::string listname;
719                         u32 listsize;
720
721                         std::getline(iss, listname, ' ');
722                         iss>>listsize;
723
724                         InventoryList *list = new InventoryList(listname, listsize);
725                         list->deSerialize(is, gamedef);
726
727                         m_lists.push_back(list);
728                 }
729                 else
730                 {
731                         throw SerializationError("Unknown inventory identifier");
732                 }
733         }
734 }
735
736 InventoryList * Inventory::addList(const std::string &name, u32 size)
737 {
738         s32 i = getListIndex(name);
739         if(i != -1)
740         {
741                 if(m_lists[i]->getSize() != size)
742                 {
743                         delete m_lists[i];
744                         m_lists[i] = new InventoryList(name, size);
745                 }
746                 return m_lists[i];
747         }
748         else
749         {
750                 m_lists.push_back(new InventoryList(name, size));
751                 return m_lists.getLast();
752         }
753 }
754
755 InventoryList * Inventory::getList(const std::string &name)
756 {
757         s32 i = getListIndex(name);
758         if(i == -1)
759                 return NULL;
760         return m_lists[i];
761 }
762
763 const InventoryList * Inventory::getList(const std::string &name) const
764 {
765         s32 i = getListIndex(name);
766         if(i == -1)
767                 return NULL;
768         return m_lists[i];
769 }
770
771 const s32 Inventory::getListIndex(const std::string &name) const
772 {
773         for(u32 i=0; i<m_lists.size(); i++)
774         {
775                 if(m_lists[i]->getName() == name)
776                         return i;
777         }
778         return -1;
779 }
780
781 /*
782         InventoryAction
783 */
784
785 InventoryAction * InventoryAction::deSerialize(std::istream &is)
786 {
787         std::string type;
788         std::getline(is, type, ' ');
789
790         InventoryAction *a = NULL;
791
792         if(type == "Move")
793         {
794                 a = new IMoveAction(is);
795         }
796
797         return a;
798 }
799
800 static std::string describeC(const struct InventoryContext *c)
801 {
802         if(c->current_player == NULL)
803                 return "current_player=NULL";
804         else
805                 return std::string("current_player=") + c->current_player->getName();
806 }
807
808 IMoveAction::IMoveAction(std::istream &is)
809 {
810         std::string ts;
811
812         std::getline(is, ts, ' ');
813         count = stoi(ts);
814
815         std::getline(is, from_inv, ' ');
816
817         std::getline(is, from_list, ' ');
818
819         std::getline(is, ts, ' ');
820         from_i = stoi(ts);
821
822         std::getline(is, to_inv, ' ');
823
824         std::getline(is, to_list, ' ');
825
826         std::getline(is, ts, ' ');
827         to_i = stoi(ts);
828 }
829
830 void IMoveAction::apply(InventoryContext *c, InventoryManager *mgr)
831 {
832         Inventory *inv_from = mgr->getInventory(c, from_inv);
833         Inventory *inv_to = mgr->getInventory(c, to_inv);
834         
835         if(!inv_from){
836                 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
837                                 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
838                                 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
839                 return;
840         }
841         if(!inv_to){
842                 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
843                                 "context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
844                                 <<", to_inv=\""<<to_inv<<"\""<<std::endl;
845                 return;
846         }
847
848         InventoryList *list_from = inv_from->getList(from_list);
849         InventoryList *list_to = inv_to->getList(to_list);
850
851         /*
852                 If a list doesn't exist or the source item doesn't exist
853         */
854         if(!list_from){
855                 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
856                                 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
857                                 <<", from_list=\""<<from_list<<"\""<<std::endl;
858                 return;
859         }
860         if(!list_to){
861                 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
862                                 <<"context=["<<describeC(c)<<"], to_inv=\""<<to_inv<<"\""
863                                 <<", to_list=\""<<to_list<<"\""<<std::endl;
864                 return;
865         }
866         if(list_from->getItem(from_i) == NULL)
867         {
868                 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
869                                 <<"context=["<<describeC(c)<<"], from_inv=\""<<from_inv<<"\""
870                                 <<", from_list=\""<<from_list<<"\""
871                                 <<" from_i="<<from_i<<std::endl;
872                 return;
873         }
874         /*
875                 If the source and the destination slots are the same
876         */
877         if(inv_from == inv_to && list_from == list_to && from_i == to_i)
878         {
879                 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
880                                 <<"are the same: inv=\""<<from_inv<<"\" list=\""<<from_list
881                                 <<"\" i="<<from_i<<std::endl;
882                 return;
883         }
884         
885         // Take item from source list
886         InventoryItem *item1 = NULL;
887         if(count == 0)
888                 item1 = list_from->changeItem(from_i, NULL);
889         else
890                 item1 = list_from->takeItem(from_i, count);
891
892         // Try to add the item to destination list
893         InventoryItem *olditem = item1;
894         item1 = list_to->addItem(to_i, item1);
895
896         // If something is returned, the item was not fully added
897         if(item1 != NULL)
898         {
899                 // If olditem is returned, nothing was added.
900                 bool nothing_added = (item1 == olditem);
901                 
902                 // If something else is returned, part of the item was left unadded.
903                 // Add the other part back to the source item
904                 list_from->addItem(from_i, item1);
905
906                 // If olditem is returned, nothing was added.
907                 // Swap the items
908                 if(nothing_added)
909                 {
910                         // Take item from source list
911                         item1 = list_from->changeItem(from_i, NULL);
912                         // Adding was not possible, swap the items.
913                         InventoryItem *item2 = list_to->changeItem(to_i, item1);
914                         // Put item from destination list to the source list
915                         list_from->changeItem(from_i, item2);
916                 }
917         }
918
919         mgr->inventoryModified(c, from_inv);
920         if(from_inv != to_inv)
921                 mgr->inventoryModified(c, to_inv);
922         
923         infostream<<"IMoveAction::apply(): moved at "
924                         <<"["<<describeC(c)<<"]"
925                         <<" from inv=\""<<from_inv<<"\""
926                         <<" list=\""<<from_list<<"\""
927                         <<" i="<<from_i
928                         <<" to inv=\""<<to_inv<<"\""
929                         <<" list=\""<<to_list<<"\""
930                         <<" i="<<to_i
931                         <<std::endl;
932 }
933
934 /*
935         Craft checking system
936 */
937
938 bool ItemSpec::checkItem(const InventoryItem *item) const
939 {
940         if(type == ITEM_NONE)
941         {
942                 // Has to be no item
943                 if(item != NULL)
944                         return false;
945                 return true;
946         }
947         
948         // There should be an item
949         if(item == NULL)
950                 return false;
951
952         std::string itemname = item->getName();
953
954         if(type == ITEM_MATERIAL)
955         {
956                 if(itemname != "MaterialItem")
957                         return false;
958                 MaterialItem *mitem = (MaterialItem*)item;
959                 if(mitem->getMaterial() != num)
960                         return false;
961         }
962         else if(type == ITEM_CRAFT)
963         {
964                 if(itemname != "CraftItem")
965                         return false;
966                 CraftItem *mitem = (CraftItem*)item;
967                 if(mitem->getSubName() != name)
968                         return false;
969         }
970         else if(type == ITEM_TOOL)
971         {
972                 // Not supported yet
973                 assert(0);
974         }
975         else if(type == ITEM_MBO)
976         {
977                 // Not supported yet
978                 assert(0);
979         }
980         else
981         {
982                 // Not supported yet
983                 assert(0);
984         }
985         return true;
986 }
987
988 bool checkItemCombination(InventoryItem const * const *items, const ItemSpec *specs)
989 {
990         u16 items_min_x = 100;
991         u16 items_max_x = 100;
992         u16 items_min_y = 100;
993         u16 items_max_y = 100;
994         for(u16 y=0; y<3; y++)
995         for(u16 x=0; x<3; x++)
996         {
997                 if(items[y*3 + x] == NULL)
998                         continue;
999                 if(items_min_x == 100 || x < items_min_x)
1000                         items_min_x = x;
1001                 if(items_min_y == 100 || y < items_min_y)
1002                         items_min_y = y;
1003                 if(items_max_x == 100 || x > items_max_x)
1004                         items_max_x = x;
1005                 if(items_max_y == 100 || y > items_max_y)
1006                         items_max_y = y;
1007         }
1008         // No items at all, just return false
1009         if(items_min_x == 100)
1010                 return false;
1011         
1012         u16 items_w = items_max_x - items_min_x + 1;
1013         u16 items_h = items_max_y - items_min_y + 1;
1014
1015         u16 specs_min_x = 100;
1016         u16 specs_max_x = 100;
1017         u16 specs_min_y = 100;
1018         u16 specs_max_y = 100;
1019         for(u16 y=0; y<3; y++)
1020         for(u16 x=0; x<3; x++)
1021         {
1022                 if(specs[y*3 + x].type == ITEM_NONE)
1023                         continue;
1024                 if(specs_min_x == 100 || x < specs_min_x)
1025                         specs_min_x = x;
1026                 if(specs_min_y == 100 || y < specs_min_y)
1027                         specs_min_y = y;
1028                 if(specs_max_x == 100 || x > specs_max_x)
1029                         specs_max_x = x;
1030                 if(specs_max_y == 100 || y > specs_max_y)
1031                         specs_max_y = y;
1032         }
1033         // No specs at all, just return false
1034         if(specs_min_x == 100)
1035                 return false;
1036
1037         u16 specs_w = specs_max_x - specs_min_x + 1;
1038         u16 specs_h = specs_max_y - specs_min_y + 1;
1039
1040         // Different sizes
1041         if(items_w != specs_w || items_h != specs_h)
1042                 return false;
1043
1044         for(u16 y=0; y<specs_h; y++)
1045         for(u16 x=0; x<specs_w; x++)
1046         {
1047                 u16 items_x = items_min_x + x;
1048                 u16 items_y = items_min_y + y;
1049                 u16 specs_x = specs_min_x + x;
1050                 u16 specs_y = specs_min_y + y;
1051                 const InventoryItem *item = items[items_y * 3 + items_x];
1052                 const ItemSpec &spec = specs[specs_y * 3 + specs_x];
1053
1054                 if(spec.checkItem(item) == false)
1055                         return false;
1056         }
1057
1058         return true;
1059 }
1060         
1061 //END