0149fd9abe758c154bf799659b384bd0d71104be
[oweals/minetest.git] / src / inventorymanager.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 "inventorymanager.h"
21 #include "log.h"
22 #include "environment.h"
23 #include "scriptapi.h"
24 #include "serverobject.h"
25 #include "main.h"  // for g_settings
26 #include "settings.h"
27 #include "utility.h"
28
29 /*
30         InventoryLocation
31 */
32
33 std::string InventoryLocation::dump() const
34 {
35         std::ostringstream os(std::ios::binary);
36         serialize(os);
37         return os.str();
38 }
39
40 void InventoryLocation::serialize(std::ostream &os) const
41 {
42         switch(type){
43         case InventoryLocation::UNDEFINED:
44         {
45                 os<<"undefined";
46         }
47         break;
48         case InventoryLocation::CURRENT_PLAYER:
49         {
50                 os<<"current_player";
51         }
52         break;
53         case InventoryLocation::PLAYER:
54         {
55                 os<<"player:"<<name;
56         }
57         break;
58         case InventoryLocation::NODEMETA:
59         {
60                 os<<"nodemeta:"<<p.X<<","<<p.Y<<","<<p.Z;
61         }
62         break;
63         default:
64                 assert(0);
65         }
66 }
67
68 void InventoryLocation::deSerialize(std::istream &is)
69 {
70         std::string tname;
71         std::getline(is, tname, ':');
72         if(tname == "undefined")
73         {
74                 type = InventoryLocation::UNDEFINED;
75         }
76         else if(tname == "current_player")
77         {
78                 type = InventoryLocation::CURRENT_PLAYER;
79         }
80         else if(tname == "player")
81         {
82                 type = InventoryLocation::PLAYER;
83                 std::getline(is, name, '\n');
84         }
85         else if(tname == "nodemeta")
86         {
87                 type = InventoryLocation::NODEMETA;
88                 std::string pos;
89                 std::getline(is, pos, '\n');
90                 Strfnd fn(pos);
91                 p.X = stoi(fn.next(","));
92                 p.Y = stoi(fn.next(","));
93                 p.Z = stoi(fn.next(","));
94         }
95         else
96         {
97                 infostream<<"Unknown InventoryLocation type=\""<<tname<<"\""<<std::endl;
98                 throw SerializationError("Unknown InventoryLocation type");
99         }
100 }
101
102 void InventoryLocation::deSerialize(std::string s)
103 {
104         std::istringstream is(s, std::ios::binary);
105         deSerialize(is);
106 }
107
108 /*
109         InventoryAction
110 */
111
112 InventoryAction * InventoryAction::deSerialize(std::istream &is)
113 {
114         std::string type;
115         std::getline(is, type, ' ');
116
117         InventoryAction *a = NULL;
118
119         if(type == "Move")
120         {
121                 a = new IMoveAction(is);
122         }
123         else if(type == "Drop")
124         {
125                 a = new IDropAction(is);
126         }
127
128         return a;
129 }
130
131 IMoveAction::IMoveAction(std::istream &is)
132 {
133         std::string ts;
134
135         std::getline(is, ts, ' ');
136         count = stoi(ts);
137
138         std::getline(is, ts, ' ');
139         from_inv.deSerialize(ts);
140
141         std::getline(is, from_list, ' ');
142
143         std::getline(is, ts, ' ');
144         from_i = stoi(ts);
145
146         std::getline(is, ts, ' ');
147         to_inv.deSerialize(ts);
148
149         std::getline(is, to_list, ' ');
150
151         std::getline(is, ts, ' ');
152         to_i = stoi(ts);
153 }
154
155 void IMoveAction::apply(InventoryManager *mgr, ServerActiveObject *player)
156 {
157         Inventory *inv_from = mgr->getInventory(from_inv);
158         Inventory *inv_to = mgr->getInventory(to_inv);
159         
160         if(!inv_from){
161                 infostream<<"IMoveAction::apply(): FAIL: source inventory not found: "
162                                 <<"from_inv=\""<<from_inv.dump()<<"\""
163                                 <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
164                 return;
165         }
166         if(!inv_to){
167                 infostream<<"IMoveAction::apply(): FAIL: destination inventory not found: "
168                                 <<"from_inv=\""<<from_inv.dump()<<"\""
169                                 <<", to_inv=\""<<to_inv.dump()<<"\""<<std::endl;
170                 return;
171         }
172
173         InventoryList *list_from = inv_from->getList(from_list);
174         InventoryList *list_to = inv_to->getList(to_list);
175
176         /*
177                 If a list doesn't exist or the source item doesn't exist
178         */
179         if(!list_from){
180                 infostream<<"IMoveAction::apply(): FAIL: source list not found: "
181                                 <<"from_inv=\""<<from_inv.dump()<<"\""
182                                 <<", from_list=\""<<from_list<<"\""<<std::endl;
183                 return;
184         }
185         if(!list_to){
186                 infostream<<"IMoveAction::apply(): FAIL: destination list not found: "
187                                 <<"to_inv=\""<<to_inv.dump()<<"\""
188                                 <<", to_list=\""<<to_list<<"\""<<std::endl;
189                 return;
190         }
191         if(list_from->getItem(from_i).empty())
192         {
193                 infostream<<"IMoveAction::apply(): FAIL: source item not found: "
194                                 <<"from_inv=\""<<from_inv.dump()<<"\""
195                                 <<", from_list=\""<<from_list<<"\""
196                                 <<" from_i="<<from_i<<std::endl;
197                 return;
198         }
199         /*
200                 If the source and the destination slots are the same
201         */
202         if(inv_from == inv_to && list_from == list_to && from_i == to_i)
203         {
204                 infostream<<"IMoveAction::apply(): FAIL: source and destination slots "
205                                 <<"are the same: inv=\""<<from_inv.dump()
206                                 <<"\" list=\""<<from_list
207                                 <<"\" i="<<from_i<<std::endl;
208                 return;
209         }
210         
211         // Take item from source list
212         ItemStack item1;
213         if(count == 0)
214                 item1 = list_from->changeItem(from_i, ItemStack());
215         else
216                 item1 = list_from->takeItem(from_i, count);
217
218         // Try to add the item to destination list
219         int oldcount = item1.count;
220         item1 = list_to->addItem(to_i, item1);
221
222         // If something is returned, the item was not fully added
223         if(!item1.empty())
224         {
225                 // If olditem is returned, nothing was added.
226                 bool nothing_added = (item1.count == oldcount);
227                 
228                 // If something else is returned, part of the item was left unadded.
229                 // Add the other part back to the source item
230                 list_from->addItem(from_i, item1);
231
232                 // If olditem is returned, nothing was added.
233                 // Swap the items
234                 if(nothing_added)
235                 {
236                         // Take item from source list
237                         item1 = list_from->changeItem(from_i, ItemStack());
238                         // Adding was not possible, swap the items.
239                         ItemStack item2 = list_to->changeItem(to_i, item1);
240                         // Put item from destination list to the source list
241                         list_from->changeItem(from_i, item2);
242                 }
243         }
244
245         mgr->setInventoryModified(from_inv);
246         if(inv_from != inv_to)
247                 mgr->setInventoryModified(to_inv);
248         
249         infostream<<"IMoveAction::apply(): moved at "
250                         <<" count="<<count<<"\""
251                         <<" from inv=\""<<from_inv.dump()<<"\""
252                         <<" list=\""<<from_list<<"\""
253                         <<" i="<<from_i
254                         <<" to inv=\""<<to_inv.dump()<<"\""
255                         <<" list=\""<<to_list<<"\""
256                         <<" i="<<to_i
257                         <<std::endl;
258 }
259
260 IDropAction::IDropAction(std::istream &is)
261 {
262         std::string ts;
263
264         std::getline(is, ts, ' ');
265         count = stoi(ts);
266
267         std::getline(is, ts, ' ');
268         from_inv.deSerialize(ts);
269
270         std::getline(is, from_list, ' ');
271
272         std::getline(is, ts, ' ');
273         from_i = stoi(ts);
274 }
275
276 void IDropAction::apply(InventoryManager *mgr, ServerActiveObject *player)
277 {
278         Inventory *inv_from = mgr->getInventory(from_inv);
279         
280         if(!inv_from){
281                 infostream<<"IDropAction::apply(): FAIL: source inventory not found: "
282                                 <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
283                 return;
284         }
285
286         InventoryList *list_from = inv_from->getList(from_list);
287
288         /*
289                 If a list doesn't exist or the source item doesn't exist
290         */
291         if(!list_from){
292                 infostream<<"IDropAction::apply(): FAIL: source list not found: "
293                                 <<"from_inv=\""<<from_inv.dump()<<"\""<<std::endl;
294                 return;
295         }
296         if(list_from->getItem(from_i).empty())
297         {
298                 infostream<<"IDropAction::apply(): FAIL: source item not found: "
299                                 <<"from_inv=\""<<from_inv.dump()<<"\""
300                                 <<", from_list=\""<<from_list<<"\""
301                                 <<" from_i="<<from_i<<std::endl;
302                 return;
303         }
304
305         /*
306                 Drop the item
307         */
308         ItemStack item = list_from->getItem(from_i);
309         if(scriptapi_item_on_drop(player->getEnv()->getLua(), item, player,
310                                 player->getBasePosition() + v3f(0,1,0)))
311         {
312                 // Apply returned ItemStack
313                 if(g_settings->getBool("creative_mode") == false
314                                 || from_inv.type != InventoryLocation::PLAYER)
315                         list_from->changeItem(from_i, item);
316                 mgr->setInventoryModified(from_inv);
317         }
318
319         infostream<<"IDropAction::apply(): dropped "
320                         <<" from inv=\""<<from_inv.dump()<<"\""
321                         <<" list=\""<<from_list<<"\""
322                         <<" i="<<from_i
323                         <<std::endl;
324 }