]> git.lizzy.rs Git - minetest.git/blob - src/inventory.cpp
fixed bug in sneaking
[minetest.git] / src / inventory.cpp
1 /*
2 Minetest-c55
3 Copyright (C) 2010 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 /*
21 (c) 2010 Perttu Ahola <celeron55@gmail.com>
22 */
23
24 #include "inventory.h"
25 #include "serialization.h"
26 #include "utility.h"
27 #include "debug.h"
28 #include <sstream>
29 #include "main.h"
30
31 /*
32         InventoryItem
33 */
34
35 InventoryItem::InventoryItem(u16 count)
36 {
37         m_count = count;
38 }
39
40 InventoryItem::~InventoryItem()
41 {
42 }
43
44 InventoryItem* InventoryItem::deSerialize(std::istream &is)
45 {
46         DSTACK(__FUNCTION_NAME);
47
48         //is.imbue(std::locale("C"));
49         // Read name
50         std::string name;
51         std::getline(is, name, ' ');
52         
53         if(name == "MaterialItem")
54         {
55                 // u16 reads directly as a number (u8 doesn't)
56                 u16 material;
57                 is>>material;
58                 u16 count;
59                 is>>count;
60                 if(material > 255)
61                         throw SerializationError("Too large material number");
62                 return new MaterialItem(material, count);
63         }
64         else if(name == "MBOItem")
65         {
66                 std::string inventorystring;
67                 std::getline(is, inventorystring, '|');
68                 return new MapBlockObjectItem(inventorystring);
69         }
70         else if(name == "CraftItem")
71         {
72                 std::string subname;
73                 std::getline(is, subname, ' ');
74                 u16 count;
75                 is>>count;
76                 return new CraftItem(subname, count);
77         }
78         else if(name == "ToolItem")
79         {
80                 std::string toolname;
81                 std::getline(is, toolname, ' ');
82                 u16 wear;
83                 is>>wear;
84                 return new ToolItem(toolname, wear);
85         }
86         else
87         {
88                 dstream<<"Unknown InventoryItem name=\""<<name<<"\""<<std::endl;
89                 throw SerializationError("Unknown InventoryItem name");
90         }
91 }
92
93 /*
94         MapBlockObjectItem
95 */
96 #ifndef SERVER
97 video::ITexture * MapBlockObjectItem::getImage()
98 {
99         //TODO
100         
101         if(m_inventorystring.substr(0,3) == "Rat")
102                 //return g_device->getVideoDriver()->getTexture(porting::getDataPath("rat.png").c_str());
103                 //return g_irrlicht->getTexture("rat.png");
104                 return NULL;
105         
106         if(m_inventorystring.substr(0,4) == "Sign")
107                 //return g_device->getVideoDriver()->getTexture(porting::getDataPath("sign.png").c_str());
108                 //return g_irrlicht->getTexture("sign.png");
109                 return NULL;
110
111         return NULL;
112 }
113 #endif
114 std::string MapBlockObjectItem::getText()
115 {
116         if(m_inventorystring.substr(0,3) == "Rat")
117                 return "";
118         
119         if(m_inventorystring.substr(0,4) == "Sign")
120                 return "";
121
122         return "obj";
123 }
124
125 MapBlockObject * MapBlockObjectItem::createObject
126                 (v3f pos, f32 player_yaw, f32 player_pitch)
127 {
128         std::istringstream is(m_inventorystring);
129         std::string name;
130         std::getline(is, name, ' ');
131         
132         if(name == "None")
133         {
134                 return NULL;
135         }
136         else if(name == "Sign")
137         {
138                 std::string text;
139                 std::getline(is, text, '|');
140                 SignObject *obj = new SignObject(NULL, -1, pos);
141                 obj->setText(text);
142                 obj->setYaw(-player_yaw);
143                 return obj;
144         }
145         else if(name == "Rat")
146         {
147                 RatObject *obj = new RatObject(NULL, -1, pos);
148                 return obj;
149         }
150         else if(name == "ItemObj")
151         {
152                 /*
153                         Now we are an inventory item containing the serialization
154                         string of an object that contains the serialization
155                         string of an inventory item. Fuck this.
156                 */
157                 //assert(0);
158                 dstream<<__FUNCTION_NAME<<": WARNING: Ignoring ItemObj "
159                                 <<"because an item-object should never be inside "
160                                 <<"an object-item."<<std::endl;
161                 return NULL;
162         }
163         else
164         {
165                 return NULL;
166         }
167 }
168
169 /*
170         Inventory
171 */
172
173 InventoryList::InventoryList(std::string name, u32 size)
174 {
175         m_name = name;
176         m_size = size;
177         clearItems();
178 }
179
180 InventoryList::~InventoryList()
181 {
182         for(u32 i=0; i<m_items.size(); i++)
183         {
184                 if(m_items[i])
185                         delete m_items[i];
186         }
187 }
188
189 void InventoryList::clearItems()
190 {
191         for(u32 i=0; i<m_items.size(); i++)
192         {
193                 if(m_items[i])
194                         delete m_items[i];
195         }
196
197         m_items.clear();
198
199         for(u32 i=0; i<m_size; i++)
200         {
201                 m_items.push_back(NULL);
202         }
203 }
204
205 void InventoryList::serialize(std::ostream &os)
206 {
207         //os.imbue(std::locale("C"));
208         
209         for(u32 i=0; i<m_items.size(); i++)
210         {
211                 InventoryItem *item = m_items[i];
212                 if(item != NULL)
213                 {
214                         os<<"Item ";
215                         item->serialize(os);
216                 }
217                 else
218                 {
219                         os<<"Empty";
220                 }
221                 os<<"\n";
222         }
223
224         os<<"EndInventoryList\n";
225 }
226
227 void InventoryList::deSerialize(std::istream &is)
228 {
229         //is.imbue(std::locale("C"));
230
231         clearItems();
232         u32 item_i = 0;
233
234         for(;;)
235         {
236                 std::string line;
237                 std::getline(is, line, '\n');
238
239                 std::istringstream iss(line);
240                 //iss.imbue(std::locale("C"));
241
242                 std::string name;
243                 std::getline(iss, name, ' ');
244
245                 if(name == "EndInventoryList")
246                 {
247                         break;
248                 }
249                 // This is a temporary backwards compatibility fix
250                 else if(name == "end")
251                 {
252                         break;
253                 }
254                 else if(name == "Item")
255                 {
256                         if(item_i > getSize() - 1)
257                                 throw SerializationError("too many items");
258                         InventoryItem *item = InventoryItem::deSerialize(iss);
259                         m_items[item_i++] = item;
260                 }
261                 else if(name == "Empty")
262                 {
263                         if(item_i > getSize() - 1)
264                                 throw SerializationError("too many items");
265                         m_items[item_i++] = NULL;
266                 }
267                 else
268                 {
269                         throw SerializationError("Unknown inventory identifier");
270                 }
271         }
272 }
273
274 InventoryList::InventoryList(const InventoryList &other)
275 {
276         /*
277                 Do this so that the items get cloned. Otherwise the pointers
278                 in the array will just get copied.
279         */
280         *this = other;
281 }
282
283 InventoryList & InventoryList::operator = (const InventoryList &other)
284 {
285         m_name = other.m_name;
286         m_size = other.m_size;
287         clearItems();
288         for(u32 i=0; i<other.m_items.size(); i++)
289         {
290                 InventoryItem *item = other.m_items[i];
291                 if(item != NULL)
292                 {
293                         m_items[i] = item->clone();
294                 }
295         }
296
297         return *this;
298 }
299
300 std::string InventoryList::getName()
301 {
302         return m_name;
303 }
304
305 u32 InventoryList::getSize()
306 {
307         return m_items.size();
308 }
309
310 u32 InventoryList::getUsedSlots()
311 {
312         u32 num = 0;
313         for(u32 i=0; i<m_items.size(); i++)
314         {
315                 InventoryItem *item = m_items[i];
316                 if(item != NULL)
317                         num++;
318         }
319         return num;
320 }
321
322 InventoryItem * InventoryList::getItem(u32 i)
323 {
324         if(i > m_items.size() - 1)
325                 return NULL;
326         return m_items[i];
327 }
328
329 InventoryItem * InventoryList::changeItem(u32 i, InventoryItem *newitem)
330 {
331         assert(i < m_items.size());
332
333         InventoryItem *olditem = m_items[i];
334         m_items[i] = newitem;
335         return olditem;
336 }
337
338 void InventoryList::deleteItem(u32 i)
339 {
340         assert(i < m_items.size());
341         InventoryItem *item = changeItem(i, NULL);
342         if(item)
343                 delete item;
344 }
345
346 InventoryItem * InventoryList::addItem(InventoryItem *newitem)
347 {
348         /*
349                 First try to find if it could be added to some existing items
350         */
351         for(u32 i=0; i<m_items.size(); i++)
352         {
353                 // Ignore empty slots
354                 if(m_items[i] == NULL)
355                         continue;
356                 // Try adding
357                 newitem = addItem(i, newitem);
358                 if(newitem == NULL)
359                         return NULL; // All was eaten
360         }
361
362         /*
363                 Then try to add it to empty slots
364         */
365         for(u32 i=0; i<m_items.size(); i++)
366         {
367                 // Ignore unempty slots
368                 if(m_items[i] != NULL)
369                         continue;
370                 // Try adding
371                 newitem = addItem(i, newitem);
372                 if(newitem == NULL)
373                         return NULL; // All was eaten
374         }
375
376         // Return leftover
377         return newitem;
378 }
379
380 InventoryItem * InventoryList::addItem(u32 i, InventoryItem *newitem)
381 {
382         // If it is an empty position, it's an easy job.
383         InventoryItem *to_item = m_items[i];
384         if(to_item == NULL)
385         {
386                 m_items[i] = newitem;
387                 return NULL;
388         }
389         
390         // If not addable, return the item
391         if(newitem->addableTo(to_item) == false)
392                 return newitem;
393         
394         // If the item fits fully in the slot, add counter and delete it
395         if(newitem->getCount() <= to_item->freeSpace())
396         {
397                 to_item->add(newitem->getCount());
398                 delete newitem;
399                 return NULL;
400         }
401         // Else the item does not fit fully. Add all that fits and return
402         // the rest.
403         else
404         {
405                 u16 freespace = to_item->freeSpace();
406                 to_item->add(freespace);
407                 newitem->remove(freespace);
408                 return newitem;
409         }
410 }
411
412 InventoryItem * InventoryList::takeItem(u32 i, u32 count)
413 {
414         if(count == 0)
415                 return NULL;
416
417         InventoryItem *item = m_items[i];
418         // If it is an empty position, return NULL
419         if(item == NULL)
420                 return NULL;
421         
422         if(count >= item->getCount())
423         {
424                 // Get the item by swapping NULL to its place
425                 return changeItem(i, NULL);
426         }
427         else
428         {
429                 InventoryItem *item2 = item->clone();
430                 item->remove(count);
431                 item2->setCount(count);
432                 return item2;
433         }
434         
435         return false;
436 }
437
438 void InventoryList::decrementMaterials(u16 count)
439 {
440         for(u32 i=0; i<m_items.size(); i++)
441         {
442                 InventoryItem *item = takeItem(i, count);
443                 if(item)
444                         delete item;
445         }
446 }
447
448 void InventoryList::print(std::ostream &o)
449 {
450         o<<"InventoryList:"<<std::endl;
451         for(u32 i=0; i<m_items.size(); i++)
452         {
453                 InventoryItem *item = m_items[i];
454                 if(item != NULL)
455                 {
456                         o<<i<<": ";
457                         item->serialize(o);
458                         o<<"\n";
459                 }
460         }
461 }
462
463 /*
464         Inventory
465 */
466
467 Inventory::~Inventory()
468 {
469         clear();
470 }
471
472 void Inventory::clear()
473 {
474         for(u32 i=0; i<m_lists.size(); i++)
475         {
476                 delete m_lists[i];
477         }
478         m_lists.clear();
479 }
480
481 Inventory::Inventory()
482 {
483 }
484
485 Inventory::Inventory(const Inventory &other)
486 {
487         *this = other;
488 }
489
490 Inventory & Inventory::operator = (const Inventory &other)
491 {
492         clear();
493         for(u32 i=0; i<other.m_lists.size(); i++)
494         {
495                 m_lists.push_back(new InventoryList(*other.m_lists[i]));
496         }
497         return *this;
498 }
499
500 void Inventory::serialize(std::ostream &os)
501 {
502         for(u32 i=0; i<m_lists.size(); i++)
503         {
504                 InventoryList *list = m_lists[i];
505                 os<<"List "<<list->getName()<<" "<<list->getSize()<<"\n";
506                 list->serialize(os);
507         }
508
509         os<<"EndInventory\n";
510 }
511
512 void Inventory::deSerialize(std::istream &is)
513 {
514         clear();
515
516         for(;;)
517         {
518                 std::string line;
519                 std::getline(is, line, '\n');
520
521                 std::istringstream iss(line);
522
523                 std::string name;
524                 std::getline(iss, name, ' ');
525
526                 if(name == "EndInventory")
527                 {
528                         break;
529                 }
530                 // This is a temporary backwards compatibility fix
531                 else if(name == "end")
532                 {
533                         break;
534                 }
535                 else if(name == "List")
536                 {
537                         std::string listname;
538                         u32 listsize;
539
540                         std::getline(iss, listname, ' ');
541                         iss>>listsize;
542
543                         InventoryList *list = new InventoryList(listname, listsize);
544                         list->deSerialize(is);
545
546                         m_lists.push_back(list);
547                 }
548                 else
549                 {
550                         throw SerializationError("Unknown inventory identifier");
551                 }
552         }
553 }
554
555 InventoryList * Inventory::addList(const std::string &name, u32 size)
556 {
557         s32 i = getListIndex(name);
558         if(i != -1)
559         {
560                 if(m_lists[i]->getSize() != size)
561                 {
562                         delete m_lists[i];
563                         m_lists[i] = new InventoryList(name, size);
564                 }
565                 return m_lists[i];
566         }
567         else
568         {
569                 m_lists.push_back(new InventoryList(name, size));
570                 return m_lists.getLast();
571         }
572 }
573
574 InventoryList * Inventory::getList(const std::string &name)
575 {
576         s32 i = getListIndex(name);
577         if(i == -1)
578                 return NULL;
579         return m_lists[i];
580 }
581
582 s32 Inventory::getListIndex(const std::string &name)
583 {
584         for(u32 i=0; i<m_lists.size(); i++)
585         {
586                 if(m_lists[i]->getName() == name)
587                         return i;
588         }
589         return -1;
590 }
591
592 /*
593         InventoryAction
594 */
595
596 InventoryAction * InventoryAction::deSerialize(std::istream &is)
597 {
598         std::string type;
599         std::getline(is, type, ' ');
600
601         InventoryAction *a = NULL;
602
603         if(type == "Move")
604         {
605                 a = new IMoveAction(is);
606         }
607
608         return a;
609 }
610
611 void IMoveAction::apply(Inventory *inventory)
612 {
613         /*dstream<<"from_name="<<from_name<<" to_name="<<to_name<<std::endl;
614         dstream<<"from_i="<<from_i<<" to_i="<<to_i<<std::endl;*/
615         InventoryList *list_from = inventory->getList(from_name);
616         InventoryList *list_to = inventory->getList(to_name);
617         /*dstream<<"list_from="<<list_from<<" list_to="<<list_to
618                         <<std::endl;*/
619         /*if(list_from)
620                 dstream<<" list_from->getItem(from_i)="<<list_from->getItem(from_i)
621                                 <<std::endl;
622         if(list_to)
623                 dstream<<" list_to->getItem(to_i)="<<list_to->getItem(to_i)
624                                 <<std::endl;*/
625         
626         /*
627                 If a list doesn't exist or the source item doesn't exist
628                 or the source and the destination slots are the same
629         */
630         if(!list_from || !list_to || list_from->getItem(from_i) == NULL
631                         || (list_from == list_to && from_i == to_i))
632         {
633                 dstream<<__FUNCTION_NAME<<": Operation not allowed"<<std::endl;
634                 return;
635         }
636         
637         // Take item from source list
638         InventoryItem *item1 = NULL;
639         if(count == 0)
640                 item1 = list_from->changeItem(from_i, NULL);
641         else
642                 item1 = list_from->takeItem(from_i, count);
643
644         // Try to add the item to destination list
645         InventoryItem *olditem = item1;
646         item1 = list_to->addItem(to_i, item1);
647
648         // If nothing is returned, the item was fully added
649         if(item1 == NULL)
650                 return;
651         
652         // If olditem is returned, nothing was added.
653         bool nothing_added = (item1 == olditem);
654         
655         // If something else is returned, part of the item was left unadded.
656         // Add the other part back to the source item
657         list_from->addItem(from_i, item1);
658
659         // If olditem is returned, nothing was added.
660         // Swap the items
661         if(nothing_added)
662         {
663                 // Take item from source list
664                 item1 = list_from->changeItem(from_i, NULL);
665                 // Adding was not possible, swap the items.
666                 InventoryItem *item2 = list_to->changeItem(to_i, item1);
667                 // Put item from destination list to the source list
668                 list_from->changeItem(from_i, item2);
669                 return;
670         }
671 }
672         
673 //END