1 /*
2  * Copyright 2011-2013 Arx Libertatis Team (see the AUTHORS file)
3  *
4  * This file is part of Arx Libertatis.
5  *
6  * Arx Libertatis is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * Arx Libertatis is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with Arx Libertatis.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 /* Based on:
20 ===========================================================================
21 ARX FATALIS GPL Source Code
22 Copyright (C) 1999-2010 Arkane Studios SA, a ZeniMax Media company.
23 
24 This file is part of the Arx Fatalis GPL Source Code ('Arx Fatalis Source Code').
25 
26 Arx Fatalis Source Code is free software: you can redistribute it and/or modify it under the terms of the GNU General Public
27 License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.
28 
29 Arx Fatalis Source Code is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied
30 warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
31 
32 You should have received a copy of the GNU General Public License along with Arx Fatalis Source Code.  If not, see
33 <http://www.gnu.org/licenses/>.
34 
35 In addition, the Arx Fatalis Source Code is also subject to certain additional terms. You should have received a copy of these
36 additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Arx
37 Fatalis Source Code. If not, please request a copy in writing from Arkane Studios at the address below.
38 
39 If you have questions concerning this license or the applicable additional terms, you may contact in writing Arkane Studios, c/o
40 ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
41 ===========================================================================
42 */
43 // Code: Cyril Meynier
44 //
45 // Copyright (c) 1999-2001 ARKANE Studios SA. All rights reserved
46 
47 #include "game/Inventory.h"
48 
49 #include <algorithm>
50 #include <set>
51 #include <vector>
52 
53 #include <boost/foreach.hpp>
54 
55 #include "ai/Paths.h"
56 
57 #include "core/Application.h"
58 #include "core/Config.h"
59 
60 #include "game/EntityManager.h"
61 #include "game/Item.h"
62 #include "game/Player.h"
63 
64 #include "gui/Interface.h"
65 
66 #include "graphics/GraphicsTypes.h"
67 #include "graphics/Math.h"
68 #include "graphics/data/Mesh.h"
69 #include "graphics/data/TextureContainer.h"
70 
71 #include "input/Input.h"
72 
73 #include "io/log/Logger.h"
74 
75 #include "math/Angle.h"
76 #include "math/Vector2.h"
77 #include "math/Vector3.h"
78 
79 #include "physics/Box.h"
80 
81 #include "platform/Platform.h"
82 
83 #include "scene/Light.h"
84 #include "scene/Interactive.h"
85 #include "scene/GameSound.h"
86 
87 #include "script/Script.h"
88 
89 using std::vector;
90 using std::string;
91 
92 //------------------------------------------------------------------------------------
93 extern E_ARX_STATE_MOUSE eMouseState;
94 extern float InventoryX;
95 extern float InventoryDir;
96 extern long PLAYER_INTERFACE_HIDE_COUNT;
97 extern long	DRAGGING;
98 
99 extern bool TRUE_PLAYER_MOUSELOOK_ON;
100 
101 void ARX_INTERFACE_Combat_Mode(long i);
102 void ARX_INVENTORY_ReOrder();
103 void ARX_INVENTORY_IdentifyIO(Entity * _pIO);
104 
105 //------------------------------------------------------------------------------------
106 //CInventory Inventory;
107 INVENTORY_SLOT inventory[3][INVENTORY_X][INVENTORY_Y];
108 INVENTORY_DATA * SecondaryInventory = NULL;
109 Entity * DRAGINTER = NULL;
110 Entity * ioSteal = NULL;
111 long InventoryY = 100;
112 static long HERO_OR_SECONDARY = 0;
113 short sActiveInventory = 0;
114 
115 // 1 player 2 secondary
116 short sInventory = -1;
117 short sInventoryX = -1;
118 short sInventoryY = -1;
119 
120 /*!
121  * Declares an IO as entering into player Inventory
122  * Sends appropriate INVENTORYIN Event to player AND concerned io.
123  */
ARX_INVENTORY_Declare_InventoryIn(Entity * io)124 static void ARX_INVENTORY_Declare_InventoryIn(Entity * io) {
125 
126 	if(!io) {
127 		return;
128 	}
129 
130 	io->show = SHOW_FLAG_IN_INVENTORY;
131 
132 	if(io->ignition > 0) {
133 
134 		if(ValidDynLight(io->ignit_light)) {
135 			DynLight[io->ignit_light].exist = 0;
136 		}
137 		io->ignit_light = -1;
138 
139 		if (io->ignit_sound != audio::INVALID_ID) {
140 			ARX_SOUND_Stop(io->ignit_sound);
141 			io->ignit_sound = audio::INVALID_ID;
142 		}
143 
144 		io->ignition = 0;
145 	}
146 
147 	EVENT_SENDER = io;
148 	SendIOScriptEvent(entities.player(), SM_INVENTORYIN);
149 	EVENT_SENDER = entities.player();
150 	SendIOScriptEvent(io, SM_INVENTORYIN);
151 	EVENT_SENDER = NULL;
152 }
153 
154 //*************************************************************************************
155 // void CleanInventory()
156 //-------------------------------------------------------------------------------------
157 // FUNCTION/RESULT:
158 //   Cleans Player inventory
159 //*************************************************************************************
CleanInventory()160 void CleanInventory() {
161 
162 	for(long iNbBag = 0; iNbBag < 3; iNbBag++) {
163 		for(size_t j = 0; j < INVENTORY_Y; j++) {
164 			for(size_t i = 0; i < INVENTORY_X; i++) {
165 				inventory[iNbBag][i][j].io	 = NULL;
166 				inventory[iNbBag][i][j].show = 1;
167 			}
168 		}
169 	}
170 
171 	sActiveInventory = 0;
172 }
173 
174 extern Vec2s DANAEMouse;
175 extern long DANAESIZX;
176 extern long DANAESIZY;
177 extern long DANAECENTERX;
178 extern long DANAECENTERY;
179 
GetInventoryObj(Vec2s * pos)180 static Entity * GetInventoryObj(Vec2s * pos) {
181 
182 	long tx, ty;
183 
184 
185 	float fCenterX	= DANAECENTERX - INTERFACE_RATIO(320) + INTERFACE_RATIO(35);
186 	float fSizY		= DANAESIZY - INTERFACE_RATIO(101) + INTERFACE_RATIO_LONG(InventoryY);
187 
188 	int iPosX = checked_range_cast<int>(fCenterX);
189 	int iPosY = checked_range_cast<int>(fSizY);
190 
191 
192 	if (player.Interface & INTER_INVENTORY)
193 	{
194 		tx = pos->x - iPosX; //-4
195 		ty = pos->y - iPosY; //-2
196 
197 		if ((tx >= 0) && (ty >= 0))
198 		{
199 
200 			tx = checked_range_cast<long>(tx / INTERFACE_RATIO(32));
201 			ty = checked_range_cast<long>(ty / INTERFACE_RATIO(32));
202 
203 
204 			if ((tx >= 0) && ((size_t)tx < INVENTORY_X) && (ty >= 0) && ((size_t)ty < INVENTORY_Y))
205 			{
206 				if ((inventory[sActiveInventory][tx][ty].io)
207 				        &&	(inventory[sActiveInventory][tx][ty].io->gameFlags & GFLAG_INTERACTIVITY))
208 				{
209 					HERO_OR_SECONDARY = 1;
210 					return (inventory[sActiveInventory][tx][ty].io);
211 				}
212 			}
213 
214 			return NULL;
215 		}
216 	}
217 	else if (player.Interface & INTER_INVENTORYALL)
218 	{
219 
220 		float fBag	= (player.bag - 1) * INTERFACE_RATIO(-121);
221 
222 		int iY = checked_range_cast<int>(fBag);
223 
224 
225 		for (int i = 0; i < player.bag; i++)
226 		{
227 			tx = pos->x - iPosX;
228 			ty = pos->y - iPosY - iY;
229 
230 			tx = checked_range_cast<long>(tx / INTERFACE_RATIO(32));
231 			ty = checked_range_cast<long>(ty / INTERFACE_RATIO(32));
232 
233 			if ((tx >= 0) && ((size_t)tx < INVENTORY_X) && (ty >= 0) && ((size_t)ty < INVENTORY_Y))
234 			{
235 				if ((inventory[i][tx][ty].io)
236 					&&	(inventory[i][tx][ty].io->gameFlags & GFLAG_INTERACTIVITY))
237 				{
238 					HERO_OR_SECONDARY = 1;
239 					return (inventory[i][tx][ty].io);
240 				}
241 
242 				return NULL;
243 			}
244 
245 			iY += checked_range_cast<int>(INTERFACE_RATIO(121));
246 		}
247 	}
248 
249 	return NULL;
250 }
251 
252 
253 //*************************************************************************************
254 // Entity * GetInventoryObj_INVENTORYUSE(EERIE_S2D * pos)
255 //-------------------------------------------------------------------------------------
256 // FUNCTION/RESULT:
257 //
258 //*************************************************************************************
GetInventoryObj_INVENTORYUSE(Vec2s * pos)259 Entity * GetInventoryObj_INVENTORYUSE(Vec2s * pos)
260 {
261 	Entity * io = GetFromInventory(pos);
262 
263 	if (io != NULL)
264 	{
265 		if (HERO_OR_SECONDARY == 2)
266 		{
267 			if (SecondaryInventory != NULL)
268 			{
269 				Entity * temp = (Entity *)SecondaryInventory->io;
270 
271 				if (temp->ioflags & IO_SHOP) return NULL;
272 			}
273 		}
274 
275 		return io;
276 	}
277 
278 	if (InInventoryPos(pos)) return NULL;
279 
280 	if ((io = InterClick(pos)) != NULL)
281 	{
282 		return io;
283 	}
284 
285 	return NULL;
286 
287 }
288 
289 //*************************************************************************************
290 // void PutInFrontOfPlayer(Entity * io,long flag)
291 //-------------------------------------------------------------------------------------
292 // FUNCTION/RESULT:
293 //   Puts an IO in front of the player
294 //*************************************************************************************
PutInFrontOfPlayer(Entity * io)295 void PutInFrontOfPlayer(Entity * io)
296 {
297 	if (io == NULL) return;
298 
299 	float t = radians(player.angle.b);
300 	io->pos.x = player.pos.x - (float)EEsin(t) * 80.f;
301 	io->pos.y = player.pos.y + 20.f;
302 	io->pos.z = player.pos.z + (float)EEcos(t) * 80.f;
303 	io->velocity.y = 0.3f;
304 	io->velocity.x = 0;
305 	io->velocity.z = 0;
306 	io->angle.a = 0.f;
307 	io->angle.b = 0;
308 	io->angle.g = 0.f;
309 	io->stopped = 0;
310 	io->show = SHOW_FLAG_IN_SCENE;
311 
312 	if (io->obj && io->obj->pbox)
313 	{
314 		Vec3f vector = Vec3f(0.f, 100.f, 0.f);
315 		Vec3f pos = io->pos;
316 		io->soundtime = 0;
317 		io->soundcount = 0;
318 		EERIE_PHYSICS_BOX_Launch(io->obj, &pos, &vector);
319 	}
320 }
321 
322 //*************************************************************************************
323 // void IO_Drop_Item(Entity * io_src,Entity * io)
324 //-------------------------------------------------------------------------------------
325 // FUNCTION/RESULT:
326 //   forces "io_scr" IO to drop "io" item with physics
327 //*************************************************************************************
IO_Drop_Item(Entity * io_src,Entity * io)328 void IO_Drop_Item(Entity * io_src, Entity * io)
329 {
330 	// Validity Check
331 	if ((!io) || (!io_src)) return;
332 
333 	float t = radians(io_src->angle.b);
334 	io->velocity.x = -(float)EEsin(t) * 50.f;
335 	io->velocity.y = 0.3f;
336 	io->velocity.z = (float)EEcos(t) * 50.f;
337 	io->angle.a = 0.f;
338 	io->angle.b = 0;
339 	io->angle.g = 0.f;
340 	io->stopped = 0;
341 	io->show = SHOW_FLAG_IN_SCENE;
342 
343 	if(io->obj && io->obj->pbox) {
344 		Vec3f vector(0.f, 100.f, 0.f);
345 		Vec3f pos = io->pos;
346 		io->soundtime = 0;
347 		io->soundcount = 0;
348 		EERIE_PHYSICS_BOX_Launch_NOCOL(io, io->obj, &pos, &vector);
349 	}
350 }
351 
operator <<(std::ostream & strm,const InventoryPos & p)352 std::ostream & operator<<(std::ostream & strm, const InventoryPos & p) {
353 	return strm << '(' << p.io << ", " << p.bag << ", " << p.x << ", " << p.y << ')';
354 }
355 
356 namespace {
357 
358 // Glue code to access both player and IO inventories in a uniform way.
359 template <size_t MaxBags, size_t MaxWidth, size_t MaxHeight>
360 struct InventoryArray {
361 	typedef INVENTORY_SLOT type[MaxBags][MaxWidth][MaxHeight];
362 	typedef InventoryPos::index_type index_type;
index__anon52c49df60111::InventoryArray363 	static INVENTORY_SLOT & index(type & data, index_type bag,
364 	                       index_type x, index_type y) {
365 		return data[bag][x][y];
366 	}
index__anon52c49df60111::InventoryArray367 	static const INVENTORY_SLOT & index(const type & data, index_type bag,
368 	                             index_type x, index_type y) {
369 		return data[bag][x][y];
370 	}
371 };
372 template <size_t MaxWidth, size_t MaxHeight>
373 struct InventoryArray<1, MaxWidth, MaxHeight> {
374 	typedef INVENTORY_SLOT type[MaxWidth][MaxHeight];
375 	typedef InventoryPos::index_type index_type;
index__anon52c49df60111::InventoryArray376 	static INVENTORY_SLOT & index(type & data, index_type bag,
377 	                       index_type x, index_type y) {
378 		ARX_UNUSED(bag);
379 		return data[x][y];
380 	}
index__anon52c49df60111::InventoryArray381 	static const INVENTORY_SLOT & index(const type & data, index_type bag,
382 	                             index_type x, index_type y) {
383 		ARX_UNUSED(bag);
384 		return data[x][y];
385 	}
386 };
387 
388 //! Compare items by their size and name
389 struct ItemSizeComaparator {
operator ()__anon52c49df60111::ItemSizeComaparator390 	bool operator()(const Entity * a, const Entity * b) const {
391 		int sizea = a->sizex * a->sizey * a->sizey;
392 		int sizeb = b->sizex * b->sizey * b->sizey;
393 		if(sizea != sizeb) {
394 			return (sizea > sizeb);
395 		}
396 		int locname = a->locname.compare(b->locname);
397 		if(locname != 0) {
398 			return (locname < 0);
399 		}
400 		int name = a->long_name().compare(b->long_name());
401 		if(name != 0) {
402 			return (name < 0);
403 		}
404 		return (a < b);
405 	}
406 };
407 
408 // TODO this class should probably be used directly in player and IO objects
409 template <size_t MaxBags, size_t MaxWidth, size_t MaxHeight>
410 class Inventory {
411 
412 	typedef InventoryPos Pos;
413 
414 public:
415 
416 	typedef InventoryArray<MaxBags, MaxWidth, MaxHeight> Array;
417 	typedef typename Array::type array_type;
418 	typedef unsigned short size_type;
419 	typedef InventoryPos::index_type index_type;
420 
421 private:
422 
423 	long io;
424 	array_type & data;
425 	size_type bags;
426 	size_type width;
427 	size_type height;
428 
index(index_type bag,index_type x,index_type y)429 	INVENTORY_SLOT & index(index_type bag, index_type x, index_type y) {
430 		return Array::index(data, bag, x, y);
431 	}
index(index_type bag,index_type x,index_type y) const432 	const INVENTORY_SLOT & index(index_type bag, index_type x, index_type y) const {
433 		return Array::index(data, bag, x, y);
434 	}
435 
index(const InventoryPos & pos)436 	INVENTORY_SLOT & index(const InventoryPos & pos) {
437 		return index(pos.bag, pos.x, pos.y);
438 	}
index(const InventoryPos & pos) const439 	const INVENTORY_SLOT & index(const InventoryPos & pos) const {
440 		return index(pos.bag, pos.x, pos.y);
441 	}
442 
insertImpl(Entity * item)443 	Pos insertImpl(Entity * item) {
444 		arx_assert(item != NULL && (item->ioflags & IO_ITEM));
445 		if(Pos pos = insertIntoStack(item)) {
446 			return pos;
447 		}
448 		return insertIntoNewSlot(item);
449 	}
450 
insertImpl(Entity * item,const Pos & pos)451 	Pos insertImpl(Entity * item, const Pos & pos) {
452 		arx_assert(item != NULL && (item->ioflags & IO_ITEM));
453 		if(insertIntoStackAt(item, pos)) {
454 			return pos;
455 		}
456 		if(Pos newPos = insertIntoStack(item)) {
457 			return newPos;
458 		}
459 		if(insertIntoNewSlotAt(item, pos)) {
460 			return pos;
461 		}
462 		return insertIntoNewSlot(item);
463 	}
464 
removeAt(const Entity * item,const Pos & pos)465 	void removeAt(const Entity * item, const Pos & pos) {
466 
467 		arx_assert(item != NULL && (item->ioflags & IO_ITEM));
468 		arx_assert(pos.x + item->sizex <= width);
469 		arx_assert(pos.y + item->sizey <= height);
470 		arx_assert(index(pos).io == item);
471 
472 		LogDebug(" - " << pos << " remove " << item->long_name()
473 		         << " [" << item->_itemdata->count << '/'
474 		         << item->_itemdata->playerstacksize << "]: "
475 		         << int(item->sizex) << 'x' << int(item->sizey));
476 
477 		for(index_type j = pos.y; j < (pos.y + size_type(item->sizey)); j++) {
478 			for(index_type i = pos.x; i < (pos.x + size_type(item->sizex)); i++) {
479 				index(pos.bag, i, j).io = NULL;
480 				index(pos.bag, i, j).show = 0;
481 			}
482 		}
483 	}
484 
insertIntoNewSlotAt(Entity * item,const Pos & pos)485 	bool insertIntoNewSlotAt(Entity * item, const Pos & pos) {
486 
487 		if(!pos || pos.bag >= bags) {
488 			return false;
489 		}
490 
491 		if(pos.x + item->sizex > width || pos.y + item->sizey > height) {
492 			return false;
493 		}
494 
495 		arx_assert(item != NULL && (item->ioflags & IO_ITEM));
496 
497 		// Check if the whole area required by this item is empty
498 		for(index_type j = pos.y; j < pos.y + item->sizey; j++) {
499 			for(index_type i = pos.x; i < pos.x + item->sizex; i++) {
500 				if(index(pos.bag, i, j).io != NULL) {
501 					return false;
502 				}
503 			}
504 		}
505 
506 		LogDebug(" - " << pos << " := " << item->long_name()
507 		         << " [" << item->_itemdata->count << '/'
508 		         << item->_itemdata->playerstacksize << "]: "
509 		         << int(item->sizex) << 'x' << int(item->sizey));
510 
511 		// Insert the item at the found position
512 		for(index_type j = pos.y; j < (pos.y + item->sizey); j++) {
513 			for (index_type i = pos.x; i < (pos.x + item->sizex); i++) {
514 				index(pos.bag, i, j).io = item;
515 				index(pos.bag, i, j).show = 0;
516 			}
517 		}
518 		index(pos).show = 1;
519 
520 		return true;
521 	}
522 
insertIntoNewSlot(Entity * item)523 	Pos insertIntoNewSlot(Entity * item) {
524 
525 		arx_assert(item != NULL && (item->ioflags & IO_ITEM));
526 
527 		for(index_type bag = 0; bag < bags; bag++) {
528 			for(index_type i = 0; i < width + 1 - item->sizex; i++) {
529 				for(index_type j = 0; j < height + 1 - item->sizey; j++) {
530 
531 					// Ignore already used inventory slots
532 					if(index(bag, i, j).io != NULL) {
533 						continue;
534 					}
535 
536 					Pos pos(io, bag, i, j);
537 					if(insertIntoNewSlotAt(item, pos)) {
538 						return pos;
539 					}
540 				}
541 			}
542 		}
543 
544 		return Pos();
545 	}
546 
insertIntoStackAt(Entity * item,const Pos & pos)547 	bool insertIntoStackAt(Entity * item, const Pos & pos) {
548 
549 		if(!pos || pos.bag >= bags) {
550 			return false;
551 		}
552 
553 		if(pos.x + item->sizex > width || pos.y + item->sizey > height) {
554 			return false;
555 		}
556 
557 		arx_assert(item != NULL && (item->ioflags & IO_ITEM));
558 
559 		Entity * io = index(pos).io;
560 
561 		// Ignore empty slots and different or non-stackeable items
562 		if(!io || io->_itemdata->playerstacksize <= 1 || !IsSameObject(item, io)) {
563 			return false;
564 		}
565 
566 		// Ignore full stacks
567 		if(io->_itemdata->count >= io->_itemdata->playerstacksize) {
568 			return false;
569 		}
570 
571 		// Get the number of items to add to the stack
572 		short int remainingSpace = io->_itemdata->playerstacksize - io->_itemdata->count;
573 		short int count = std::min(item->_itemdata->count, remainingSpace);
574 
575 		LogDebug(" - " << pos << " " << io->long_name()
576 		         << " [" << io->_itemdata->count << '/'
577 		         << io->_itemdata->playerstacksize << "] += "
578 		         << item->long_name() << " x" << count << '/' << item->_itemdata->count);
579 
580 		io->_itemdata->count += count, item->_itemdata->count -= count;
581 
582 		if(item->_itemdata->count != 0) {
583 			// We inserted some of the items into the stack, but there was not enough
584 			// space for all of them.
585 			return false;
586 		}
587 
588 		// Delete or hide the old item
589 		if(item->scriptload) {
590 			delete item;
591 		} else {
592 			item->show = SHOW_FLAG_KILLED;
593 		}
594 		return true;
595 	}
596 
insertIntoStack(Entity * item)597 	Pos insertIntoStack(Entity * item) {
598 
599 		arx_assert(item != NULL && (item->ioflags & IO_ITEM));
600 
601 		// Try to add the items to an existing stack
602 		for(index_type bag = 0; bag < bags; bag++) {
603 			for(index_type i = 0; i < width + 1 - size_type(item->sizex); i++) {
604 				for(index_type j = 0; j < height + 1 - size_type(item->sizey); j++) {
605 					Pos pos(io, bag, i, j);
606 					if(insertIntoStackAt(item, pos)) {
607 						return pos;
608 					}
609 				}
610 			}
611 		}
612 
613 		return Pos();
614 	}
615 
616 public:
617 
Inventory(long io,array_type & data,size_type bags,size_type width,size_type height)618 	Inventory(long io, array_type & data, size_type bags, size_type width, size_type height)
619 		: io(io), data(data), bags(bags), width(width), height(height) { }
620 
Inventory(const Inventory & o)621 	Inventory(const Inventory & o)
622 		: io(o.io), data(o.data), bags(o.bags), width(o.width), height(o.height) { }
623 
624 	/*!
625 	 * Insert an item into the inventory
626 	 * The item will be added to existing stacks if possible.
627 	 * Otherwise the first empty slot will be used.
628 	 *
629 	 * Does not check if the item is already in the inventory!
630 	 *
631 	 * @param item the item to insert
632 	 *
633 	 * @return true if the item was inserted, false otherwise
634 	 */
insert(Entity * item)635 	bool insert(Entity * item) {
636 		if(item && (item->ioflags & IO_ITEM)) {
637 			if(Pos pos = insertImpl(item)) {
638 				ARX_INVENTORY_Declare_InventoryIn(get(pos));
639 				return true;
640 			}
641 		}
642 		return false;
643 	}
644 
645 	/*!
646 	 * Insert an item into the inventory
647 	 * The item will be added to existing stacks if possible.
648 	 * Otherwise, the item will be inserted at the specified position.
649 	 * If that fails, the first empty slot will be used.
650 	 *
651 	 * Does not check if the item is already in the inventory!
652 	 *
653 	 * @param item the item to insert
654 	 *
655 	 * @return true if the item was inserted, false otherwise
656 	 */
insert(Entity * item,const Pos & pos)657 	bool insert(Entity * item, const Pos & pos) {
658 		if(item && (item->ioflags & IO_ITEM)) {
659 			if(Pos newPos = insertImpl(item, pos)) {
660 				ARX_INVENTORY_Declare_InventoryIn(get(newPos));
661 				return true;
662 			}
663 		}
664 		return false;
665 	}
666 
667 	//! Sort the inventory and stack duplicate items
optimize()668 	void optimize() {
669 
670 		LogDebug("collecting items");
671 
672 		// Collect all inventory items
673 		vector<Entity *> items;
674 		for(size_t bag = 0; bag < bags; bag++) {
675 			for(size_t j = 0 ; j < height; j++) {
676 				for(size_t i = 0 ; i < width; i++) {
677 					INVENTORY_SLOT & slot = index(bag, i, j);
678 					if(slot.io && slot.show) {
679 						items.push_back(slot.io);
680 						removeAt(slot.io, Pos(io, bag, i, j));
681 					}
682 					slot.io = NULL;
683 					slot.show = 0;
684 				}
685 			}
686 		}
687 
688 		// Sort the items by their size and name
689 		std::sort(items.begin(), items.end(), ItemSizeComaparator());
690 
691 		LogDebug("sorting");
692 	#ifdef ARX_DEBUG
693 		BOOST_FOREACH(const Entity * item, items) {
694 			LogDebug(" - " << item->long_name() << ": "
695 							<< int(item->sizex) << 'x' << int(item->sizey));
696 		}
697 	#endif
698 
699 		LogDebug("putting back items");
700 
701 		// Now put the items back into the inventory
702 		BOOST_FOREACH(Entity * item, items) {
703 			if(!insertImpl(item)) {
704 				// TODO: oops - there was no space for the item
705 				// ideally this should not happen, but the current sorting algorithm does not
706 				// guarantee that the resulting order is at least as good as the existing one
707 				LogDebug("could not insert " << item->long_name() << " after sorting inventory");
708 				PutInFrontOfPlayer(item);
709 			}
710 		}
711 	}
712 
713 	/*!
714 	 * Get the position of an item in the inventory.
715 	 *
716 	 * @return the position of the item
717 	 */
locate(const Entity * item) const718 	Pos locate(const Entity * item) const {
719 		for(size_t bag = 0; bag < bags; bag++) {
720 			for(size_t i = 0; i < width; i++) {
721 				for(size_t j = 0; j < height; j++) {
722 					if(index(bag, i, j).io == item) {
723 						return Pos(io, bag, i, j);
724 					}
725 				}
726 			}
727 		}
728 		return Pos();
729 	}
730 
731 	/*!
732 	 * Remove an item from the inventory.
733 	 * The item is not deleted.
734 	 *
735 	 * @return the old position of the item
736 	 */
remove(const Entity * item)737 	Pos remove(const Entity * item) {
738 		Pos pos = locate(item);
739 		if(pos) {
740 			/* TODO There was a bug cauing corrupted inventories where an item
741 			 * takes up more slots than specified by its size. Ideally, this should
742 			 * be fixed while loading old save games, but until then we just fix it
743 			 * here when removing items.
744 			removeAt(item, pos);
745 			 */
746 			for(size_t j = 0; j < height; j++) {
747 				for(size_t i = 0; i < width; i++) {
748 					if(index(pos.bag, i, j).io == item) {
749 						index(pos.bag, i, j).io = NULL;
750 						index(pos.bag, i, j).show = 0;
751 					}
752 				}
753 			}
754 		}
755 		return pos;
756 	}
757 
get(const Pos & pos) const758 	Entity * get(const Pos & pos) const {
759 		return pos ? index(pos).io : NULL;
760 	}
761 
762 };
763 
getPlayerInventory()764 Inventory<3, INVENTORY_X, INVENTORY_Y> getPlayerInventory() {
765 	return Inventory<3, INVENTORY_X, INVENTORY_Y>(0, inventory, player.bag,
766 	                                              INVENTORY_X, INVENTORY_Y);
767 }
768 
getIoInventory(Entity * io)769 Inventory<1, 20, 20> getIoInventory(Entity * io) {
770 	arx_assert(io != NULL && io->inventory != NULL);
771 	INVENTORY_DATA * inv = io->inventory;
772 	return Inventory<1, 20, 20>(io->index(), inv->slot, 1, inv->sizex, inv->sizey);
773 }
774 
775 } // anonymous namespace
776 
777 PlayerInventory playerInventory;
778 
locate(const Entity * item)779 PlayerInventory::Pos PlayerInventory::locate(const Entity * item) {
780 	return getPlayerInventory().locate(item);
781 }
782 
remove(const Entity * item)783 PlayerInventory::Pos PlayerInventory::remove(const Entity * item) {
784 	return getPlayerInventory().remove(item);
785 }
786 
insert(Entity * item)787 bool PlayerInventory::insert(Entity * item) {
788 	if(item && (item->ioflags & IO_GOLD)) {
789 		ARX_PLAYER_AddGold(item);
790 		return true;
791 	}
792 	return getPlayerInventory().insert(item);
793 }
794 
insert(Entity * item,const Pos & pos)795 bool PlayerInventory::insert(Entity * item, const Pos & pos) {
796 	if(item && (item->ioflags & IO_GOLD)) {
797 		ARX_PLAYER_AddGold(item);
798 		return true;
799 	}
800 	return getPlayerInventory().insert(item, pos);
801 }
802 
optimize()803 void PlayerInventory::optimize() {
804 	getPlayerInventory().optimize();
805 }
806 
giveToPlayer(Entity * item)807 bool giveToPlayer(Entity * item) {
808 	if(playerInventory.insert(item)) {
809 		return true;
810 	} else {
811 		PutInFrontOfPlayer(item);
812 		return false;
813 	}
814 }
815 
giveToPlayer(Entity * item,const InventoryPos & pos)816 bool giveToPlayer(Entity * item, const InventoryPos & pos) {
817 	if(playerInventory.insert(item, pos)) {
818 		return true;
819 	} else {
820 		PutInFrontOfPlayer(item);
821 		return false;
822 	}
823 }
824 
removeFromInventories(Entity * item)825 InventoryPos removeFromInventories(Entity * item) {
826 
827 	// TODO the item should know the inventory it is in and position
828 
829 	InventoryPos oldPos = playerInventory.remove(item);
830 	if(oldPos) {
831 		return oldPos;
832 	}
833 
834 	for(size_t i = 1; i < entities.size(); i++) {
835 		if(entities[i] && entities[i]->inventory) {
836 			oldPos = getIoInventory(entities[i]).remove(item);
837 			if(oldPos) {
838 				return oldPos;
839 			}
840 		}
841 	}
842 
843 	return InventoryPos();
844 }
845 
locateInInventories(const Entity * item)846 InventoryPos locateInInventories(const Entity * item) {
847 
848 	InventoryPos pos = playerInventory.locate(item);
849 	if(pos) {
850 		return pos;
851 	}
852 
853 	for(size_t i = 1; i < entities.size(); i++) {
854 		if(entities[i] && entities[i]->inventory) {
855 			pos = getIoInventory(entities[i]).locate(item);
856 			if(pos) {
857 				return pos;
858 			}
859 		}
860 	}
861 
862 	return InventoryPos();
863 }
864 
insertIntoInventory(Entity * item,const InventoryPos & pos)865 bool insertIntoInventory(Entity * item, const InventoryPos & pos) {
866 
867 	if(pos.io == 0) {
868 		return giveToPlayer(item, pos);
869 	}
870 
871 	if(ValidIONum(pos.io) && entities[pos.io]->inventory) {
872 		if(getIoInventory(entities[pos.io]).insert(item, pos)) {
873 			return true;
874 		}
875 	}
876 
877 	PutInFrontOfPlayer(item);
878 	return false;
879 }
880 
881 //*************************************************************************************
882 // bool CanBePutInInventory(Entity * io)
883 //-------------------------------------------------------------------------------------
884 // FUNCTION/RESULT:
885 //   tries to put an object in player inventory
886 //*************************************************************************************
887 // TODO replace remaining uses of this with playerInventory.insert()
CanBePutInInventory(Entity * io)888 bool CanBePutInInventory(Entity * io)
889 {
890 	if (io == NULL) return false;
891 
892 	if (io->ioflags & IO_MOVABLE) return false;
893 
894 	if(io->ioflags & IO_GOLD) {
895 		ARX_PLAYER_AddGold(io);
896 		return true;
897 	}
898 
899 	long sx, sy;
900 	long i, j, k, l;
901 
902 	sx = io->sizex;
903 	sy = io->sizey;
904 
905 	// on essaie de le remettre à son ancienne place --------------------------
906 	if (sInventory == 1 &&
907 	        (sInventoryX >= 0) &&
908 	        ((size_t)sInventoryX <= INVENTORY_X - sx) &&
909 	        (sInventoryY >= 0) &&
910 	        ((size_t)sInventoryY <= INVENTORY_Y - sy))
911 	{
912 		j = sInventoryY;
913 		i = sInventoryX;
914 
915 		// first try to stack -------------------------------------------------
916 		if (player.bag)
917 		{
918 			for (int iNbBag = 0; iNbBag < player.bag; iNbBag++)
919 			{
920 
921 				Entity * ioo = inventory[iNbBag][i][j].io;
922 
923 				if ((ioo)
924 				        &&	(ioo->_itemdata->playerstacksize > 1)
925 				        &&	(IsSameObject(io, ioo)))
926 				{
927 					if (ioo->_itemdata->count < ioo->_itemdata->playerstacksize)
928 					{
929 						ioo->_itemdata->count += io->_itemdata->count;
930 
931 						if (ioo->_itemdata->count > ioo->_itemdata->playerstacksize)
932 						{
933 							io->_itemdata->count = ioo->_itemdata->count - ioo->_itemdata->playerstacksize;
934 							ioo->_itemdata->count = ioo->_itemdata->playerstacksize;
935 						}
936 						else io->_itemdata->count = 0;
937 
938 						if(!io->_itemdata->count) {
939 							io->destroy();
940 						}
941 
942 						ARX_INVENTORY_Declare_InventoryIn(ioo);
943 						sInventory = -1;
944 						return true;
945 					}
946 				}
947 			}
948 		}
949 
950 
951 		if (player.bag)
952 		{
953 			for (int iNbBag = 0; iNbBag < player.bag; iNbBag++)
954 			{
955 				if (inventory[iNbBag][i][j].io == NULL)
956 				{
957 					bool valid = true;
958 
959 					if ((sx == 0) || (sy == 0)) valid = false;
960 
961 					for (k = j; k < j + sy; k++)
962 						for (l = i; l < i + sx; l++)
963 						{
964 							if (inventory[iNbBag][l][k].io != NULL) valid = false;
965 						}
966 
967 					if (valid)
968 					{
969 						for (k = j; k < j + sy; k++)
970 							for (l = i; l < i + sx; l++)
971 							{
972 								inventory[iNbBag][l][k].io = io;
973 								inventory[iNbBag][l][k].show = 0;
974 							}
975 
976 						inventory[iNbBag][i][j].show = 1;
977 						ARX_INVENTORY_Declare_InventoryIn(io);
978 						sInventory = -1;
979 						return true;
980 					}
981 				}
982 			}
983 		}
984 	}
985 
986 
987 	if(player.bag) {
988 		for (int iNbBag = 0; iNbBag < player.bag; iNbBag++) {
989 			for(size_t i = 0; i <= INVENTORY_X - sx; i++) {
990 				for(size_t j = 0; j <= INVENTORY_Y - sy; j++) {
991 
992 					Entity * ioo = inventory[iNbBag][i][j].io;
993 
994 					if ((ioo)
995 					        &&	(ioo->_itemdata->playerstacksize > 1)
996 					        &&	(IsSameObject(io, ioo)))
997 					{
998 						if (ioo->_itemdata->count < ioo->_itemdata->playerstacksize)
999 						{
1000 
1001 							ioo->_itemdata->count += io->_itemdata->count;
1002 
1003 
1004 							if (ioo->_itemdata->count > ioo->_itemdata->playerstacksize)
1005 							{
1006 								io->_itemdata->count = ioo->_itemdata->count - ioo->_itemdata->playerstacksize;
1007 								ioo->_itemdata->count = ioo->_itemdata->playerstacksize;
1008 							}
1009 							else io->_itemdata->count = 0;
1010 
1011 							if(!io->_itemdata->count) {
1012 								io->destroy();
1013 								ARX_INVENTORY_Declare_InventoryIn(ioo);
1014 								return true;
1015 							}
1016 						}
1017 					}
1018 				}
1019 			}
1020 		}
1021 	}
1022 
1023 	if(player.bag) {
1024 		for(int iNbBag = 0; iNbBag < player.bag; iNbBag++) {
1025 			for(size_t i = 0; i <= INVENTORY_X - sx; i++) {
1026 				for(size_t j = 0; j <= INVENTORY_Y - sy; j++) {
1027 
1028 					if (inventory[iNbBag][i][j].io == NULL)
1029 					{
1030 						bool valid = true;
1031 
1032 						if ((sx == 0) || (sy == 0)) valid = false;
1033 
1034 						for (size_t k = j; k < j + sy; k++)
1035 							for (size_t l = i; l < i + sx; l++)
1036 							{
1037 								if (inventory[iNbBag][l][k].io != NULL) valid = false;
1038 							}
1039 
1040 						if (valid)
1041 						{
1042 							for (size_t k = j; k < j + sy; k++)
1043 								for (size_t l = i; l < i + sx; l++)
1044 								{
1045 									inventory[iNbBag][l][k].io = io;
1046 									inventory[iNbBag][l][k].show = 0;
1047 								}
1048 
1049 							inventory[iNbBag][i][j].show = 1;
1050 							ARX_INVENTORY_Declare_InventoryIn(io);
1051 							return true;
1052 						}
1053 					}
1054 				}
1055 			}
1056 		}
1057 	}
1058 
1059 	return false;
1060 }
1061 
1062 //*************************************************************************************
1063 // bool CanBePutInSecondaryInventory(INVENTORY_DATA * id,Entity * io,long * xx,long * yy)
1064 //------------------------------------------------------------------------------------------------
1065 // FUNCTION/RESULT:
1066 //   Tries to put an object in secondary inventory
1067 //*************************************************************************************
CanBePutInSecondaryInventory(INVENTORY_DATA * id,Entity * io,long * xx,long * yy)1068 bool CanBePutInSecondaryInventory(INVENTORY_DATA * id, Entity * io, long * xx, long * yy)
1069 {
1070 	if (!id) return false;
1071 
1072 	if (!io) return false;
1073 
1074 	if (io->ioflags & IO_MOVABLE) return false;
1075 
1076 	long sx, sy;
1077 	long i, j, k, l;
1078 
1079 	*xx = -1;
1080 	*yy = -1;
1081 
1082 	sx = io->sizex;
1083 	sy = io->sizey;
1084 
1085 	// on essaie de le remettre à son ancienne place
1086 	if (sInventory == 2 &&
1087 	        (sInventoryX >= 0) &&
1088 	        (sInventoryX <= id->sizex - sx) &&
1089 	        (sInventoryY >= 0) &&
1090 	        (sInventoryY <= id->sizey - sy))
1091 	{
1092 		j = sInventoryY;
1093 		i = sInventoryX;
1094 		// first try to stack
1095 
1096 
1097 		Entity * ioo = id->slot[i][j].io;
1098 
1099 		if (ioo)
1100 			if ((ioo->_itemdata->playerstacksize > 1) &&
1101 			        (IsSameObject(io, ioo)))
1102 			{
1103 				if ((ioo->_itemdata->count < ioo->_itemdata->playerstacksize)
1104 				        && (ioo->durability == io->durability))
1105 				{
1106 					if (io->ioflags & IO_GOLD)
1107 					{
1108 						ioo->_itemdata->price += io->_itemdata->price;
1109 					}
1110 					else
1111 					{
1112 						ioo->_itemdata->count += io->_itemdata->count;
1113 						ioo->scale = 1.f;
1114 					}
1115 
1116 					io->destroy();
1117 
1118 					sInventory = -1;
1119 					return true;
1120 				}
1121 			}
1122 
1123 
1124 		ioo = id->slot[i][j].io;
1125 
1126 		if (!ioo)
1127 		{
1128 			long valid = 1;
1129 
1130 			if ((sx == 0) || (sy == 0)) valid = 0;
1131 
1132 			for (k = j; k < j + sy; k++)
1133 				for (l = i; l < i + sx; l++)
1134 				{
1135 					if (id->slot[l][k].io != NULL)
1136 					{
1137 						valid = 0;
1138 						break;
1139 					}
1140 				}
1141 
1142 			if (valid)
1143 			{
1144 				for (k = j; k < j + sy; k++)
1145 					for (l = i; l < i + sx; l++)
1146 					{
1147 						id->slot[l][k].io = io;
1148 						id->slot[l][k].show = 0;
1149 					}
1150 
1151 				id->slot[i][j].show = 1;
1152 				*xx = i;
1153 				*yy = j;
1154 				sInventory = -1;
1155 				return true;
1156 			}
1157 		}
1158 	}
1159 
1160 	for (j = 0; j <= id->sizey - sy; j++)
1161 		for (i = 0; i <= id->sizex - sx; i++)
1162 		{
1163 			Entity * ioo = id->slot[i][j].io;
1164 
1165 			if (ioo)
1166 				if ((ioo->_itemdata->playerstacksize > 1) &&
1167 				        (IsSameObject(io, ioo)))
1168 				{
1169 					if ((ioo->_itemdata->count < ioo->_itemdata->playerstacksize)
1170 					        && (ioo->durability == io->durability))
1171 					{
1172 						if (io->ioflags & IO_GOLD)
1173 						{
1174 							ioo->_itemdata->price += io->_itemdata->price;
1175 						}
1176 						else
1177 						{
1178 							ioo->_itemdata->count += io->_itemdata->count;
1179 							ioo->scale = 1.f;
1180 						}
1181 
1182 						io->destroy();
1183 
1184 						return true;
1185 					}
1186 				}
1187 		}
1188 
1189 	for (j = 0; j <= id->sizey - sy; j++)
1190 		for (i = 0; i <= id->sizex - sx; i++)
1191 		{
1192 			Entity * ioo = id->slot[i][j].io;
1193 
1194 			if (!ioo)
1195 			{
1196 				long valid = 1;
1197 
1198 				if ((sx == 0) || (sy == 0)) valid = 0;
1199 
1200 				for (k = j; k < j + sy; k++)
1201 					for (l = i; l < i + sx; l++)
1202 					{
1203 						if (id->slot[l][k].io != NULL)
1204 						{
1205 							valid = 0;
1206 							break;
1207 						}
1208 					}
1209 
1210 				if (valid)
1211 				{
1212 					for (k = j; k < j + sy; k++)
1213 						for (l = i; l < i + sx; l++)
1214 						{
1215 							id->slot[l][k].io = io;
1216 							id->slot[l][k].show = 0;
1217 						}
1218 
1219 					id->slot[i][j].show = 1;
1220 					*xx = i;
1221 					*yy = j;
1222 					return true;
1223 				}
1224 			}
1225 		}
1226 
1227 	*xx = -1;
1228 	*yy = -1;
1229 
1230 	return false;
1231 }
1232 
1233 //*************************************************************************************
1234 // bool PutInInventory()
1235 //-------------------------------------------------------------------------------------
1236 // FUNCTION/RESULT:
1237 //   Try to put DRAGINTER object in an inventory
1238 //*************************************************************************************
PutInInventory()1239 bool PutInInventory()
1240 {
1241 	// Check Validity
1242 	if ((!DRAGINTER)
1243 	        ||	(DRAGINTER->ioflags & IO_MOVABLE))
1244 		return false;
1245 
1246 	short tx, ty;
1247 	long sx, sy;
1248 	long i, j;
1249 
1250 
1251 	tx = ty = 0;
1252 
1253 	sx = DRAGINTER->sizex;
1254 	sy = DRAGINTER->sizey;
1255 
1256 	// Check for backpack Icon
1257 	if (MouseInRect((float)DANAESIZX - 35, (float)DANAESIZY - 113, (float)DANAESIZX - 35 + 32, (float)DANAESIZY - 113 + 32))
1258 	{
1259 		if (CanBePutInInventory(DRAGINTER))
1260 		{
1261 			if (DRAGINTER)
1262 				DRAGINTER->show = SHOW_FLAG_IN_INVENTORY;
1263 
1264 			ARX_SOUND_PlayInterface(SND_INVSTD);
1265 			Set_DragInter(NULL);
1266 		}
1267 
1268 		return false;
1269 	}
1270 
1271 	// First Look for Identical Item...
1272 	if ((SecondaryInventory != NULL) && (InSecondaryInventoryPos(&DANAEMouse)))
1273 	{
1274 		Entity * io =	(Entity *)SecondaryInventory->io;
1275 
1276 		float fcos = ARX_INTERACTIVE_GetPrice(DRAGINTER, io) / 3.0f; //>>1;
1277 		long cos = checked_range_cast<long>(fcos);
1278 		cos *= DRAGINTER->_itemdata->count;
1279 		fcos = cos + cos * ((float)player.Full_Skill_Intuition) * 0.005f;
1280 		cos = checked_range_cast<long>(fcos);
1281 
1282 
1283 		if (io->ioflags & IO_SHOP)
1284 		{
1285 			if(!io->shop_category.empty() && DRAGINTER->groups.find(io->shop_category) == DRAGINTER->groups.end())
1286 				return false;
1287 
1288 			if (cos <= 0) return false;
1289 		}
1290 
1291 		Entity * ioo;
1292 
1293 		if (io->ioflags & IO_SHOP) // SHOP
1294 		{
1295 
1296 			// Check shop group
1297 			for (j = 0; j < SecondaryInventory->sizey; j++)
1298 				for (i = 0; i < SecondaryInventory->sizex; i++)
1299 				{
1300 					ioo = (Entity *)SecondaryInventory->slot[i][j].io;
1301 
1302 					if (ioo)
1303 					{
1304 						if (IsSameObject(DRAGINTER, ioo))
1305 						{
1306 							ioo->_itemdata->count += DRAGINTER->_itemdata->count;
1307 							ioo->scale = 1.f;
1308 
1309 							DRAGINTER->destroy();
1310 
1311 							ARX_PLAYER_AddGold(cos);
1312 							ARX_SOUND_PlayInterface(SND_GOLD);
1313 							ARX_SOUND_PlayInterface(SND_INVSTD);
1314 							return true;
1315 						}
1316 					}
1317 				}
1318 		}
1319 
1320 
1321 
1322 		tx = DANAEMouse.x + static_cast<short>(InventoryX) - SHORT_INTERFACE_RATIO(2);
1323 		ty = DANAEMouse.y - SHORT_INTERFACE_RATIO(13);
1324 		tx = tx / SHORT_INTERFACE_RATIO(32);
1325 		ty = ty / SHORT_INTERFACE_RATIO(32);
1326 
1327 
1328 		if ((tx <= SecondaryInventory->sizex - sx) && (ty <= SecondaryInventory->sizey - sy))
1329 		{
1330 
1331 			float fcos = ARX_INTERACTIVE_GetPrice(DRAGINTER, io) / 3.0f;
1332 			long cos = checked_range_cast<long>(fcos);
1333 			cos *= DRAGINTER->_itemdata->count;
1334 			fcos = cos + cos * ((float)player.Full_Skill_Intuition) * 0.005f;
1335 			cos = checked_range_cast<long>(fcos);
1336 
1337 			for (j = 0; j < sy; j++)
1338 				for (i = 0; i < sx; i++)
1339 				{
1340 					if (SecondaryInventory->slot[tx+i][ty+j].io != NULL)
1341 					{
1342 						long xx, yy;
1343 						DRAGINTER->show = SHOW_FLAG_IN_INVENTORY;
1344 
1345 						//Superposition d'objets
1346 						Entity * ioo = SecondaryInventory->slot[tx+i][ty+j].io;
1347 
1348 						if ((ioo->_itemdata->playerstacksize > 1) &&
1349 						        (IsSameObject(DRAGINTER, ioo)) &&
1350 						        (ioo->_itemdata->count < ioo->_itemdata->playerstacksize))
1351 						{
1352 							ioo->_itemdata->count += DRAGINTER->_itemdata->count;
1353 
1354 							if (ioo->_itemdata->count > ioo->_itemdata->playerstacksize)
1355 							{
1356 								DRAGINTER->_itemdata->count = ioo->_itemdata->count - ioo->_itemdata->playerstacksize;
1357 								ioo->_itemdata->count = ioo->_itemdata->playerstacksize;
1358 							}
1359 							else DRAGINTER->_itemdata->count = 0;
1360 						}
1361 
1362 						if (DRAGINTER->_itemdata->count)
1363 						{
1364 							if (CanBePutInSecondaryInventory(SecondaryInventory, DRAGINTER, &xx, &yy))
1365 							{
1366 								if (io->ioflags & IO_SHOP) // SHOP
1367 								{
1368 									ARX_PLAYER_AddGold(cos);
1369 									ARX_SOUND_PlayInterface(SND_GOLD);
1370 								}
1371 							}
1372 							else return false;
1373 						}
1374 
1375 						ARX_SOUND_PlayInterface(SND_INVSTD);
1376 						Set_DragInter(NULL);
1377 						return true;
1378 					}
1379 				}
1380 
1381 			if(DRAGINTER->ioflags & IO_GOLD) {
1382 				ARX_PLAYER_AddGold(DRAGINTER);
1383 				Set_DragInter(NULL);
1384 				return true;
1385 			}
1386 
1387 			for (j = 0; j < sy; j++)
1388 				for (i = 0; i < sx; i++)
1389 				{
1390 					SecondaryInventory->slot[tx+i][ty+j].io = DRAGINTER;
1391 					SecondaryInventory->slot[tx+i][ty+j].show = 0;
1392 				}
1393 
1394 			if (io->ioflags & IO_SHOP) // SHOP
1395 			{
1396 				player.gold += cos;
1397 				ARX_SOUND_PlayInterface(SND_GOLD);
1398 			}
1399 
1400 			SecondaryInventory->slot[tx][ty].show = 1;
1401 			DRAGINTER->show = SHOW_FLAG_IN_INVENTORY;
1402 			ARX_SOUND_PlayInterface(SND_INVSTD);
1403 			Set_DragInter(NULL);
1404 			return true;
1405 		}
1406 	}
1407 
1408 	if (!(player.Interface & INTER_INVENTORY) && !(player.Interface & INTER_INVENTORYALL))
1409 		return false;
1410 
1411 	if (InventoryY != 0) return false;
1412 
1413 	if (!InPlayerInventoryPos(&DANAEMouse)) return false;
1414 
1415 	int iBag = 0;
1416 
1417 
1418 	float fCenterX	= DANAECENTERX - INTERFACE_RATIO(320) + INTERFACE_RATIO(35);
1419 	float fSizY		= DANAESIZY - INTERFACE_RATIO(101) + INTERFACE_RATIO_LONG(InventoryY);
1420 
1421 	short iPosX = checked_range_cast<short>(fCenterX);
1422 	short iPosY = checked_range_cast<short>(fSizY);
1423 
1424 
1425 	if (player.Interface & INTER_INVENTORY)
1426 	{
1427 
1428 		tx = DANAEMouse.x - iPosX;
1429 		ty = DANAEMouse.y - iPosY;
1430 		tx = tx / SHORT_INTERFACE_RATIO(32);
1431 		ty = ty / SHORT_INTERFACE_RATIO(32);
1432 
1433 
1434 		if ((tx >= 0) && (tx <= 16 - sx) && (ty >= 0) && (ty <= 3 - sy))
1435 			iBag = sActiveInventory;
1436 		else return false;
1437 	}
1438 	else
1439 	{
1440 		bool bOk = false;
1441 
1442 
1443 		float fBag	= (player.bag - 1) * INTERFACE_RATIO(-121);
1444 
1445 		short iY = checked_range_cast<short>(fBag);
1446 
1447 
1448 
1449 		//We must enter the for-loop to initialyze tx/ty
1450 		arx_assert(0 < player.bag);
1451 
1452 
1453 		for (int i = 0; i < player.bag; i++)
1454 		{
1455 			tx = DANAEMouse.x - iPosX;
1456 			ty = DANAEMouse.y - iPosY - iY;
1457 
1458 			if ((tx >= 0) && (ty >= 0))
1459 			{
1460 
1461 				tx = tx / SHORT_INTERFACE_RATIO(32);
1462 				ty = ty / SHORT_INTERFACE_RATIO(32);
1463 
1464 				if ((tx >= 0) && (tx <= 16 - sx) && (ty >= 0) && (ty <= 3 - sy))
1465 				{
1466 					bOk = true;
1467 					iBag = i;
1468 					break;
1469 				}
1470 			}
1471 
1472 
1473 			float fRatio	= INTERFACE_RATIO(121);
1474 
1475 			iY += checked_range_cast<short>(fRatio);
1476 
1477 		}
1478 
1479 		if (!bOk)
1480 			return false;
1481 	}
1482 
1483 	if(DRAGINTER->ioflags & IO_GOLD) {
1484 		ARX_PLAYER_AddGold(DRAGINTER);
1485 		Set_DragInter(NULL);
1486 		return true;
1487 	}
1488 
1489 	for (j = 0; j < sy; j++)
1490 		for (i = 0; i < sx; i++)
1491 		{
1492 			Entity * ioo = inventory[iBag][tx+i][ty+j].io;
1493 
1494 			if (ioo != NULL)
1495 			{
1496 				ARX_INVENTORY_IdentifyIO(ioo);
1497 
1498 				if ((ioo->_itemdata->playerstacksize > 1) &&
1499 				        (IsSameObject(DRAGINTER, ioo)) &&
1500 				        (ioo->_itemdata->count < ioo->_itemdata->playerstacksize))
1501 				{
1502 					ioo->_itemdata->count += DRAGINTER->_itemdata->count;
1503 
1504 					if (ioo->_itemdata->count > ioo->_itemdata->playerstacksize)
1505 					{
1506 						DRAGINTER->_itemdata->count = ioo->_itemdata->count - ioo->_itemdata->playerstacksize;
1507 						ioo->_itemdata->count = ioo->_itemdata->playerstacksize;
1508 					}
1509 					else DRAGINTER->_itemdata->count = 0;
1510 
1511 					ioo->scale = 1.f;
1512 					ARX_INVENTORY_Declare_InventoryIn(DRAGINTER);
1513 
1514 					if(!DRAGINTER->_itemdata->count) {
1515 						DRAGINTER->destroy();
1516 					}
1517 
1518 					ARX_SOUND_PlayInterface(SND_INVSTD);
1519 					return true;
1520 				}
1521 
1522 				return false;
1523 			}
1524 		}
1525 
1526 	for (j = 0; j < sy; j++)
1527 		for (i = 0; i < sx; i++)
1528 		{
1529 			inventory[iBag][tx+i][ty+j].io = DRAGINTER;
1530 			inventory[iBag][tx+i][ty+j].show = 0;
1531 		}
1532 
1533 	inventory[iBag][tx][ty].show = 1;
1534 
1535 	ARX_INVENTORY_Declare_InventoryIn(DRAGINTER);
1536 	ARX_SOUND_PlayInterface(SND_INVSTD);
1537 	DRAGINTER->show = SHOW_FLAG_IN_INVENTORY;
1538 	Set_DragInter(NULL);
1539 	return true;
1540 }
1541 //*************************************************************************************
1542 // bool InSecondaryInventoryPos(EERIE_S2D * pos)
1543 //-------------------------------------------------------------------------------------
1544 // FUNCTION/RESULT:
1545 //   Returns true if xx,yy is a position in secondary inventory
1546 //*************************************************************************************
InSecondaryInventoryPos(Vec2s * pos)1547 bool InSecondaryInventoryPos(Vec2s * pos)
1548 {
1549 	if (SecondaryInventory != NULL)
1550 	{
1551 		short tx, ty;
1552 
1553 		tx = pos->x + checked_range_cast<short>(InventoryX) - SHORT_INTERFACE_RATIO(2);
1554 		ty = pos->y - SHORT_INTERFACE_RATIO(13);
1555 		tx = tx / SHORT_INTERFACE_RATIO(32);
1556 		ty = ty / SHORT_INTERFACE_RATIO(32);
1557 
1558 
1559 		if ((tx < 0) || (tx >= SecondaryInventory->sizex)) return false;
1560 
1561 		if ((ty < 0) || (ty >= SecondaryInventory->sizey)) return false;
1562 
1563 		return true;
1564 	}
1565 
1566 	return false;
1567 }
1568 
1569 //*************************************************************************************
1570 // bool InPlayerInventoryPos(EERIE_S2D * pos)
1571 //-------------------------------------------------------------------------------------
1572 // FUNCTION/RESULT:
1573 //   Returns true if xx,yy is a position in player inventory
1574 //*************************************************************************************
InPlayerInventoryPos(Vec2s * pos)1575 bool InPlayerInventoryPos(Vec2s * pos)
1576 {
1577 	if (PLAYER_INTERFACE_HIDE_COUNT) return false;
1578 
1579 
1580 	float fCenterX	= DANAECENTERX - INTERFACE_RATIO(320) + INTERFACE_RATIO(35);
1581 	float fSizY		= DANAESIZY - INTERFACE_RATIO(101) + INTERFACE_RATIO_LONG(InventoryY);
1582 
1583 	short iPosX = checked_range_cast<short>(fCenterX);
1584 	short iPosY = checked_range_cast<short>(fSizY);
1585 
1586 	short tx, ty;
1587 
1588 	if (player.Interface & INTER_INVENTORY)
1589 	{
1590 		tx = pos->x - iPosX;
1591 		ty = pos->y - iPosY;//-2;
1592 
1593 		if ((tx >= 0) && (ty >= 0))
1594 		{
1595 			tx = tx / SHORT_INTERFACE_RATIO(32);
1596 			ty = ty / SHORT_INTERFACE_RATIO(32);
1597 
1598 			if ((tx >= 0) && ((size_t)tx <= INVENTORY_X) && (ty >= 0) && ((size_t)ty < INVENTORY_Y))
1599 				return true;
1600 			else
1601 				return false;
1602 		}
1603 	}
1604 
1605 	else if (player.Interface & INTER_INVENTORYALL)
1606 	{
1607 		float fBag	= (player.bag - 1) * INTERFACE_RATIO(-121);
1608 
1609 		short iY = checked_range_cast<short>(fBag);
1610 
1611 		if ((
1612 		            (pos->x >= iPosX) &&
1613 		            (pos->x <= iPosX + INVENTORY_X * INTERFACE_RATIO(32)) &&
1614 		            (pos->y >= iPosY + iY) &&
1615 		            (pos->y <= DANAESIZY)))
1616 			return true;
1617 
1618 		for (int i = 0; i < player.bag; i++)
1619 		{
1620 			tx = pos->x - iPosX;
1621 			ty = pos->y - iPosY - iY;
1622 
1623 			if ((tx >= 0) && (ty >= 0))
1624 			{
1625 				tx = tx / SHORT_INTERFACE_RATIO(32);
1626 				ty = ty / SHORT_INTERFACE_RATIO(32);
1627 
1628 				if ((tx >= 0) && ((size_t)tx <= INVENTORY_X) && (ty >= 0) && ((size_t)ty < INVENTORY_Y))
1629 					return true;
1630 			}
1631 
1632 			float fRatio	= INTERFACE_RATIO(121);
1633 
1634 			iY = checked_range_cast<short>(iY + fRatio);
1635 		}
1636 	}
1637 
1638 	return false;
1639 }
1640 //*************************************************************************************
1641 // bool InInventoryPos(EERIE_S2D * pos)
1642 //-------------------------------------------------------------------------------------
1643 // FUNCTION/RESULT:
1644 //   Returns true if "pos" is a position in player inventory or in SECONDARY inventory
1645 //*************************************************************************************
InInventoryPos(Vec2s * pos)1646 bool InInventoryPos(Vec2s * pos)
1647 {
1648 	if (InSecondaryInventoryPos(pos))
1649 		return true;
1650 
1651 	return (InPlayerInventoryPos(pos));
1652 }
1653 
1654 //*************************************************************************************
1655 // bool IsFlyingOverInventory(EERIE_S2D * pos)
1656 //-------------------------------------------------------------------------------------
1657 // FUNCTION/RESULT:
1658 //   returns true if cursor is flying over any inventory
1659 //*************************************************************************************
IsFlyingOverInventory(Vec2s * pos)1660 bool IsFlyingOverInventory(Vec2s * pos)
1661 {
1662 	//	if(eMouseState==MOUSE_IN_WORLD) return false;
1663 
1664 	if (SecondaryInventory != NULL)
1665 	{
1666 
1667 		short tx = pos->x + checked_range_cast<short>(InventoryX) - SHORT_INTERFACE_RATIO(2);
1668 		short ty = pos->y - SHORT_INTERFACE_RATIO(13);
1669 		tx /= SHORT_INTERFACE_RATIO(32);
1670 		ty /= SHORT_INTERFACE_RATIO(32);
1671 
1672 
1673 		if ((tx >= 0) && (tx <= SecondaryInventory->sizex) && (ty >= 0) && (ty <= SecondaryInventory->sizey))
1674 			return true;
1675 	}
1676 
1677 	return InPlayerInventoryPos(pos);
1678 }
1679 
1680 //*************************************************************************************
1681 // Entity * GetFromInventory(EERIE_S2D * pos)
1682 //-------------------------------------------------------------------------------------
1683 // FUNCTION/RESULT:
1684 //   Returns IO under position xx,yy in any INVENTORY or NULL if no IO
1685 //   was found
1686 //*************************************************************************************
GetFromInventory(Vec2s * pos)1687 Entity * GetFromInventory(Vec2s * pos)
1688 {
1689 	HERO_OR_SECONDARY = 0;
1690 
1691 	if (!IsFlyingOverInventory(pos))
1692 		return NULL;
1693 
1694 	if (SecondaryInventory != NULL)
1695 	{
1696 		short tx = pos->x + checked_range_cast<short>(InventoryX) - SHORT_INTERFACE_RATIO(2);
1697 		short ty = pos->y - SHORT_INTERFACE_RATIO(13);
1698 
1699 		if ((tx >= 0) && (ty >= 0))
1700 		{
1701 			tx = tx / SHORT_INTERFACE_RATIO(32);
1702 			ty = ty / SHORT_INTERFACE_RATIO(32);
1703 
1704 			if ((tx >= 0) && (tx <= SecondaryInventory->sizex)
1705 			        && (ty >= 0) && (ty <= SecondaryInventory->sizey))
1706 			{
1707 				if (SecondaryInventory->slot[tx][ty].io == NULL)
1708 					return NULL;
1709 
1710 				if (((player.Interface & INTER_STEAL) && (!ARX_PLAYER_CanStealItem(SecondaryInventory->slot[tx][ty].io))))
1711 					return NULL;
1712 
1713 				Entity * io = SecondaryInventory->slot[tx][ty].io;
1714 
1715 				if (!(io->gameFlags & GFLAG_INTERACTIVITY))
1716 					return NULL;
1717 
1718 				HERO_OR_SECONDARY = 2;
1719 				return io;
1720 			}
1721 		}
1722 	}
1723 
1724 	return GetInventoryObj(pos);
1725 }
1726 
1727 //*************************************************************************************
1728 // bool GetItemWorldPosition( Entity * io,EERIE_3D * pos)
1729 //-------------------------------------------------------------------------------------
1730 // FUNCTION:
1731 //   Gets real world position for an IO (can be used for non items)
1732 //   (even in an inventory or being dragged)
1733 // RESULT:
1734 //   Put the position in "pos". returns true if position was found
1735 //   or false if object is invalid, or position not defined.
1736 //*************************************************************************************
GetItemWorldPosition(Entity * io,Vec3f * pos)1737 bool GetItemWorldPosition(Entity * io, Vec3f * pos)
1738 {
1739 	// Valid IO ?
1740 	if (!io) return false;
1741 
1742 	// Is this object being Dragged by player ?
1743 	if (DRAGINTER == io)
1744 	{
1745 		// Set position to approximate center of player.
1746 		pos->x = player.pos.x;
1747 		pos->y = player.pos.y + 80.f;
1748 		pos->z = player.pos.z;
1749 		return true;
1750 	}
1751 
1752 	// Not in scene ?
1753 	if (io->show != SHOW_FLAG_IN_SCENE)
1754 	{
1755 		// Is it equiped ?
1756 		if (IsEquipedByPlayer(io))
1757 		{
1758 			// in player inventory
1759 			pos->x = player.pos.x;
1760 			pos->y = player.pos.y + 80.f;
1761 			pos->z = player.pos.z;
1762 			return true;
1763 		}
1764 
1765 		// Is it in any player inventory ?
1766 		for(long iNbBag = 0; iNbBag < player.bag; iNbBag++) {
1767 			for(size_t j = 0; j < INVENTORY_Y; j++) {
1768 				for(size_t i = 0; i < INVENTORY_X; i++) {
1769 					if(inventory[iNbBag][i][j].io == io) {
1770 						pos->x = player.pos.x;
1771 						pos->y = player.pos.y + 80.f;
1772 						pos->z = player.pos.z;
1773 						return true;
1774 					}
1775 				}
1776 			}
1777 		}
1778 
1779 		// Is it in any other IO inventory ?
1780 		for(size_t i = 0; i < entities.size(); i++) {
1781 			Entity * ioo = entities[i];
1782 			if(ioo && ioo->inventory) {
1783 				INVENTORY_DATA * id = ioo->inventory;
1784 				for(long j = 0; j < id->sizey; j++) {
1785 					for(long k = 0; k < id->sizex; k++) {
1786 						if(id->slot[k][j].io == io) {
1787 							*pos = ioo->pos;
1788 							return true;
1789 						}
1790 					}
1791 				}
1792 			}
1793 		}
1794 	}
1795 
1796 	// Default position.
1797 	*pos = io->pos;
1798 	return true;
1799 }
1800 
1801 //*************************************************************************************
1802 // bool GetItemWorldPositionSound( Entity * io,EERIE_3D * pos)
1803 //-------------------------------------------------------------------------------------
1804 // FUNCTION:
1805 //   Gets real world position for an IO to spawn a sound
1806 //*************************************************************************************
GetItemWorldPositionSound(const Entity * io,Vec3f * pos)1807 bool GetItemWorldPositionSound(const Entity * io, Vec3f * pos) {
1808 
1809 	if(!io) {
1810 		return false;
1811 	}
1812 
1813 	if (DRAGINTER == io) {
1814 		ARX_PLAYER_FrontPos(pos);
1815 		return true;
1816 	}
1817 
1818 	if(io->show != SHOW_FLAG_IN_SCENE) {
1819 
1820 		if(IsEquipedByPlayer(io)) {
1821 			// in player inventory
1822 			ARX_PLAYER_FrontPos(pos);
1823 			return true;
1824 		}
1825 
1826 		if(player.bag) {
1827 			for(int iNbBag = 0; iNbBag < player.bag; iNbBag++) {
1828 				for(size_t j = 0; j < INVENTORY_Y; j++) {
1829 					for(size_t i = 0; i < INVENTORY_X; i++) {
1830 						if(inventory[iNbBag][i][j].io == io) {
1831 							// in player inventory
1832 							ARX_PLAYER_FrontPos(pos);
1833 							return true;
1834 						}
1835 					}
1836 				}
1837 			}
1838 		}
1839 
1840 		for(size_t i = 0; i < entities.size(); i++) {
1841 			Entity * ioo = entities[i];
1842 			if(ioo && ioo->inventory) {
1843 				INVENTORY_DATA * id = ioo->inventory;
1844 				for(long j = 0; j < id->sizey; j++) {
1845 					for(long k = 0; k < id->sizex; k++) {
1846 						if(id->slot[k][j].io == io) {
1847 							*pos = ioo->pos;
1848 							return true;
1849 						}
1850 					}
1851 				}
1852 			}
1853 		}
1854 	}
1855 
1856 	*pos = io->pos;
1857 	return true;
1858 }
1859 
1860 //*************************************************************************************
1861 // void RemoveFromAllInventories(Entity * io)
1862 //-------------------------------------------------------------------------------------
1863 // FUNCTION:
1864 //   Seeks an IO in all Inventories to remove it
1865 //*************************************************************************************
RemoveFromAllInventories(const Entity * io)1866 void RemoveFromAllInventories(const Entity * io) {
1867 
1868 	if(!io) {
1869 		return;
1870 	}
1871 
1872 	// Seek IO in Player Inventory/ies
1873 	playerInventory.remove(io);
1874 
1875 	// Seek IO in Other IO's Inventories
1876 	for(size_t i = 0; i < entities.size(); i++) {
1877 		if(entities[i] != NULL) {
1878 			if(entities[i]->inventory != NULL) {
1879 				INVENTORY_DATA * id = entities[i]->inventory;
1880 				for(long j = 0; j < id->sizey; j++) {
1881 					for(long k = 0; k < id->sizex; k++) {
1882 						if(id->slot[k][j].io == io) {
1883 							id->slot[k][j].io = NULL;
1884 							id->slot[k][j].show = 1;
1885 						}
1886 					}
1887 				}
1888 			}
1889 		}
1890 	}
1891 }
1892 
1893 //*************************************************************************************
1894 // Seeks an IO in all Inventories to replace it by another IO
1895 //*************************************************************************************
CheckForInventoryReplaceMe(Entity * io,Entity * old)1896 void CheckForInventoryReplaceMe(Entity * io, Entity * old) {
1897 
1898 	for(size_t i = 0; i < entities.size(); i++) {
1899 		if(entities[i] != NULL) {
1900 			if(entities[i]->inventory != NULL) {
1901 				INVENTORY_DATA * id = entities[i]->inventory;
1902 
1903 				for(long j = 0; j < id->sizey; j++) {
1904 					for(long k = 0; k < id->sizex; k++) {
1905 						if (id->slot[k][j].io == old) {
1906 							long xx, yy;
1907 							if(CanBePutInSecondaryInventory(id, io, &xx, &yy)) {
1908 								return;
1909 							}
1910 							PutInFrontOfPlayer(io);
1911 							return;
1912 						}
1913 					}
1914 				}
1915 			}
1916 		}
1917 	}
1918 }
1919 
1920 //*************************************************************************************
1921 // Takes an object from an inventory (be it player's or secondary inventory)
1922 // at screen position "xx,yy"
1923 // Puts that object in player's "hand" (cursor)
1924 // returns true if an object was taken false elseway
1925 //*************************************************************************************
TakeFromInventory(Vec2s * pos)1926 bool TakeFromInventory(Vec2s * pos)
1927 {
1928 	long i, j;
1929 	Entity * io = GetFromInventory(pos);
1930 	Entity * ioo;
1931 
1932 	if (io == NULL) return false;
1933 
1934 	if (SecondaryInventory != NULL)
1935 	{
1936 		if (InSecondaryInventoryPos(pos))
1937 		{
1938 			ioo = (Entity *)SecondaryInventory->io;
1939 
1940 			if (ioo->ioflags & IO_SHOP)   // SHOP !
1941 			{
1942 				{
1943 					if (io->ioflags & IO_ITEM) // Just in case...
1944 					{
1945 						long cos = ARX_INTERACTIVE_GetPrice(io, ioo);
1946 
1947 						float fcos	= cos - cos * ((float)player.Full_Skill_Intuition) * 0.005f;
1948 						cos = checked_range_cast<long>(fcos);
1949 
1950 						if (player.gold < cos)
1951 						{
1952 							return false;
1953 						}
1954 
1955 						ARX_SOUND_PlayInterface(SND_GOLD);
1956 						player.gold -= cos;
1957 
1958 						if(io->_itemdata->count > 1) {
1959 							ioo = CloneIOItem(io);
1960 							ioo->show = SHOW_FLAG_NOT_DRAWN;
1961 							ioo->scriptload = 1;
1962 							ioo->_itemdata->count = 1;
1963 							io->_itemdata->count--;
1964 							ARX_SOUND_PlayInterface(SND_INVSTD);
1965 							Set_DragInter(ioo);
1966 							return true;
1967 						}
1968 					}
1969 				}
1970 			}
1971 			else if ((io->ioflags & IO_ITEM) && io->_itemdata->count > 1) {
1972 
1973 				if(!GInput->actionPressed(CONTROLS_CUST_STEALTHMODE)) {
1974 					ioo = CloneIOItem(io);
1975 					ioo->show = SHOW_FLAG_NOT_DRAWN;
1976 					ioo->scriptload = 1;
1977 					ioo->_itemdata->count = 1;
1978 					io->_itemdata->count--;
1979 					ARX_SOUND_PlayInterface(SND_INVSTD);
1980 					Set_DragInter(ioo);
1981 					sInventory = 2;
1982 
1983 
1984 					float fCalcX = (pos->x + InventoryX - INTERFACE_RATIO(2)) / INTERFACE_RATIO(32);
1985 					float fCalcY = (pos->y - INTERFACE_RATIO(13)) / INTERFACE_RATIO(32);
1986 
1987 					sInventoryX = checked_range_cast<short>(fCalcX);
1988 					sInventoryY = checked_range_cast<short>(fCalcY);
1989 
1990 					//ARX_INVENTORY_Object_Out(SecondaryInventory->io, ioo);
1991 
1992 					ARX_INVENTORY_IdentifyIO(ioo);
1993 					return true;
1994 				}
1995 			}
1996 
1997 		}
1998 
1999 		for (j = 0; j < SecondaryInventory->sizey; j++)
2000 			for (i = 0; i < SecondaryInventory->sizex; i++)
2001 			{
2002 				if (SecondaryInventory->slot[i][j].io == io)
2003 				{
2004 					SecondaryInventory->slot[i][j].io = NULL;
2005 					SecondaryInventory->slot[i][j].show = 1;
2006 					sInventory = 2;
2007 
2008 					float fCalcX = (pos->x + InventoryX - INTERFACE_RATIO(2)) / INTERFACE_RATIO(32);
2009 					float fCalcY = (pos->y - INTERFACE_RATIO(13)) / INTERFACE_RATIO(32);
2010 
2011 					sInventoryX = checked_range_cast<short>(fCalcX);
2012 					sInventoryY = checked_range_cast<short>(fCalcY);
2013 
2014 				}
2015 			}
2016 	}
2017 
2018 
2019 	float fCenterX	= DANAECENTERX - INTERFACE_RATIO(320) + INTERFACE_RATIO(35);
2020 	float fSizY		= DANAESIZY - INTERFACE_RATIO(101) + INTERFACE_RATIO_LONG(InventoryY);
2021 
2022 	int iPosX = checked_range_cast<int>(fCenterX);
2023 	int iPosY = checked_range_cast<int>(fSizY);
2024 
2025 	if(InPlayerInventoryPos(pos)) {
2026 		if(!GInput->actionPressed(CONTROLS_CUST_STEALTHMODE)) {
2027 			if((io->ioflags & IO_ITEM) && io->_itemdata->count > 1) {
2028 				if(io->_itemdata->count - 1 > 0) {
2029 
2030 					ioo = AddItem(io->classPath());
2031 					ioo->show = SHOW_FLAG_NOT_DRAWN;
2032 					ioo->_itemdata->count = 1;
2033 					io->_itemdata->count--;
2034 					ioo->scriptload = 1;
2035 					ARX_SOUND_PlayInterface(SND_INVSTD);
2036 					Set_DragInter(ioo);
2037 					RemoveFromAllInventories(ioo);
2038 					sInventory = 1;
2039 
2040 					float fX = (pos->x - iPosX) / INTERFACE_RATIO(32);
2041 					float fY = (pos->y - iPosY) / INTERFACE_RATIO(32);
2042 
2043 					sInventoryX = checked_range_cast<short>(fX);
2044 					sInventoryY = checked_range_cast<short>(fY);
2045 
2046 					SendInitScriptEvent(ioo);
2047 					ARX_INVENTORY_IdentifyIO(ioo);
2048 					return true;
2049 				}
2050 			}
2051 		}
2052 	}
2053 
2054 	if(player.bag) {
2055 		for(int iNbBag = 0; iNbBag < player.bag; iNbBag++) {
2056 			for(size_t j = 0; j < INVENTORY_Y; j++) {
2057 				for(size_t i = 0; i < INVENTORY_X; i++) {
2058 					if (inventory[iNbBag][i][j].io == io) {
2059 
2060 						inventory[iNbBag][i][j].io = NULL;
2061 						inventory[iNbBag][i][j].show = 1;
2062 						sInventory = 1;
2063 
2064 						float fX = (pos->x - iPosX) / INTERFACE_RATIO(32);
2065 						float fY = (pos->y - iPosY) / INTERFACE_RATIO(32);
2066 
2067 						sInventoryX = checked_range_cast<short>(fX);
2068 						sInventoryY = checked_range_cast<short>(fY);
2069 					}
2070 				}
2071 			}
2072 		}
2073 	}
2074 
2075 	Set_DragInter(io);
2076 
2077 	RemoveFromAllInventories(io);
2078 	ARX_INVENTORY_IdentifyIO(io);
2079 	return true;
2080 }
2081 
IsInPlayerInventory(Entity * io)2082 bool IsInPlayerInventory(Entity * io) {
2083 
2084 	for(long iNbBag = 0; iNbBag < player.bag; iNbBag ++) {
2085 		for(size_t j = 0; j < INVENTORY_Y; j++) {
2086 			for(size_t i = 0; i < INVENTORY_X; i++) {
2087 				if(inventory[iNbBag][i][j].io == io) {
2088 					return true;
2089 				}
2090 			}
2091 		}
2092 	}
2093 
2094 	return false;
2095 }
2096 
IsInSecondaryInventory(Entity * io)2097 bool IsInSecondaryInventory(Entity * io) {
2098 
2099 	if(SecondaryInventory) {
2100 		for(long j = 0; j < SecondaryInventory->sizey; j++) {
2101 			for(long i = 0; i < SecondaryInventory->sizex; i++) {
2102 				if(SecondaryInventory->slot[i][j].io == io) {
2103 					return true;
2104 				}
2105 			}
2106 		}
2107 	}
2108 
2109 	return false;
2110 }
2111 
SendInventoryObjectCommand(const string & _lpszText,ScriptMessage _lCommand)2112 void SendInventoryObjectCommand(const string & _lpszText, ScriptMessage _lCommand) {
2113 
2114 	if(player.bag) {
2115 		for(int iNbBag = 0; iNbBag < player.bag; iNbBag++) {
2116 			for(size_t j = 0; j < INVENTORY_Y; j++) {
2117 				for(size_t i = 0; i < INVENTORY_X; i++) {
2118 
2119 					if(inventory[iNbBag][i][j].io && inventory[iNbBag][i][j].io->obj) {
2120 						Entity * item = inventory[iNbBag][i][j].io;
2121 						for(size_t lTex = 0; lTex < item->obj->texturecontainer.size(); lTex++) {
2122 							if(!item->obj->texturecontainer.empty()) {
2123 								if(item->obj->texturecontainer[lTex]) {
2124 									if(item->obj->texturecontainer[lTex]->m_texName == _lpszText) {
2125 										if(item->gameFlags & GFLAG_INTERACTIVITY) {
2126 											SendIOScriptEvent(item, _lCommand);
2127 										}
2128 										return;
2129 									}
2130 								}
2131 							}
2132 						}
2133 					}
2134 				}
2135 			}
2136 		}
2137 	}
2138 }
2139 
ARX_INVENTORY_GetTorchLowestDurability()2140 Entity * ARX_INVENTORY_GetTorchLowestDurability() {
2141 
2142 	Entity * io = NULL;
2143 
2144 	if(player.bag) {
2145 		for(int iNbBag = 0; iNbBag < player.bag; iNbBag++) {
2146 			for(size_t j = 0; j < INVENTORY_Y; j++) {
2147 				for(size_t i = 0; i < INVENTORY_X; i++) {
2148 					if(inventory[iNbBag][i][j].io) {
2149 						if(inventory[iNbBag][i][j].io->locname == "description_torch") {
2150 							if(!io) {
2151 								io = inventory[iNbBag][i][j].io;
2152 							} else {
2153 								if(inventory[iNbBag][i][j].io->durability < io->durability) {
2154 									io = inventory[iNbBag][i][j].io;
2155 								}
2156 							}
2157 						}
2158 					}
2159 				}
2160 			}
2161 		}
2162 	}
2163 
2164 	return io;
2165 }
2166 
ARX_INVENTORY_IdentifyIO(Entity * _pIO)2167 void ARX_INVENTORY_IdentifyIO(Entity * _pIO) {
2168 	if(_pIO && (_pIO->ioflags & IO_ITEM) && _pIO->_itemdata->equipitem) {
2169 		if(player.Full_Skill_Object_Knowledge + player.Full_Attribute_Mind
2170 		   >= _pIO->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Identify_Value].value) {
2171 			SendIOScriptEvent(_pIO, SM_IDENTIFY);
2172 		}
2173 	}
2174 }
2175 
ARX_INVENTORY_IdentifyAll()2176 void ARX_INVENTORY_IdentifyAll() {
2177 
2178 	if(player.bag) {
2179 		for(int iNbBag = 0; iNbBag < player.bag; iNbBag++) {
2180 			for(size_t j = 0; j < INVENTORY_Y; j++) {
2181 				for(size_t i = 0; i < INVENTORY_X; i++) {
2182 					Entity * io = inventory[iNbBag][i][j].io;
2183 					if(io && (io->ioflags & IO_ITEM) && io->_itemdata->equipitem) {
2184 						if(player.Full_Skill_Object_Knowledge + player.Full_Attribute_Mind
2185 						   >= io->_itemdata->equipitem->elements[IO_EQUIPITEM_ELEMENT_Identify_Value].value) {
2186 							SendIOScriptEvent(io, SM_IDENTIFY);
2187 						}
2188 					}
2189 				}
2190 			}
2191 		}
2192 	}
2193 }
2194 
2195 extern bool bInventoryClosing;
2196 
2197 //-----------------------------------------------------------------------------
ARX_INVENTORY_OpenClose(Entity * _io)2198 void ARX_INVENTORY_OpenClose(Entity * _io)
2199 {
2200 	if ((_io && (SecondaryInventory == _io->inventory)) || (_io == NULL)) // CLOSING
2201 	{
2202 		if (SecondaryInventory && (SecondaryInventory->io != NULL))
2203 			SendIOScriptEvent(SecondaryInventory->io, SM_INVENTORY2_CLOSE);
2204 
2205 		InventoryDir = -1;
2206 		TSecondaryInventory = SecondaryInventory;
2207 		SecondaryInventory = NULL;
2208 		EERIEMouseButton &= ~4;
2209 
2210 		if (DRAGGING) DRAGGING = 0;
2211 	}
2212 	else
2213 	{
2214 		if (TSecondaryInventory
2215 		        && TSecondaryInventory->io) SendIOScriptEvent(TSecondaryInventory->io, SM_INVENTORY2_CLOSE);
2216 
2217 		InventoryDir = 1;
2218 		TSecondaryInventory = SecondaryInventory = _io->inventory;
2219 
2220 		if (SecondaryInventory && SecondaryInventory->io != NULL)
2221 		{
2222 			if (SendIOScriptEvent(SecondaryInventory->io, SM_INVENTORY2_OPEN) == REFUSE)
2223 			{
2224 				InventoryDir = -1;
2225 				TSecondaryInventory = SecondaryInventory = NULL;
2226 				return;
2227 			}
2228 		}
2229 
2230 		if (player.Interface & INTER_COMBATMODE)
2231 		{
2232 			ARX_INTERFACE_Combat_Mode(0);
2233 		}
2234 
2235 		if(!config.input.autoReadyWeapon) {
2236 			TRUE_PLAYER_MOUSELOOK_ON = false;
2237 		}
2238 
2239 		if (SecondaryInventory && SecondaryInventory->io
2240 		        && (SecondaryInventory->io->ioflags & IO_SHOP))
2241 			ARX_INVENTORY_ReOrder();
2242 
2243 		EERIEMouseButton &= ~4;
2244 
2245 		if (DRAGGING) DRAGGING = 0;
2246 	}
2247 
2248 	if (player.Interface & INTER_INVENTORYALL)
2249 	{
2250 		ARX_SOUND_PlayInterface(SND_BACKPACK, 0.9F + 0.2F * rnd());
2251 		bInventoryClosing = true;
2252 	}
2253 }
2254 
2255 //-----------------------------------------------------------------------------
ARX_INVENTORY_TakeAllFromSecondaryInventory()2256 void ARX_INVENTORY_TakeAllFromSecondaryInventory()
2257 {
2258 	bool bSound = false;
2259 
2260 	if (TSecondaryInventory)
2261 	{
2262 
2263 		(void)checked_range_cast<short>(TSecondaryInventory->sizey);
2264 		(void)checked_range_cast<short>(TSecondaryInventory->sizex);
2265 
2266 
2267 		for (long j = 0; j < TSecondaryInventory->sizey; j++)
2268 			for (long i = 0; i < TSecondaryInventory->sizex; i++)
2269 			{
2270 				if (TSecondaryInventory->slot[i][j].io && TSecondaryInventory->slot[i][j].show)
2271 				{
2272 					long sx = TSecondaryInventory->slot[i][j].io->sizex;
2273 					long sy = TSecondaryInventory->slot[i][j].io->sizey;
2274 					Entity * io = TSecondaryInventory->slot[i][j].io;
2275 
2276 					if (!(io->ioflags & IO_GOLD))
2277 						RemoveFromAllInventories(io);
2278 
2279 					if(playerInventory.insert(io)) {
2280 						bSound = true;
2281 					} else {
2282 						sInventory = 2;
2283 
2284 						sInventoryX = static_cast<short>(i);
2285 						sInventoryY = static_cast<short>(j);
2286 
2287 						sx = i;
2288 						sy = j;
2289 						CanBePutInSecondaryInventory(TSecondaryInventory, io, &sx, &sy);
2290 					}
2291 				}
2292 			}
2293 	}
2294 
2295 	if (bSound)
2296 		ARX_SOUND_PlayInterface(SND_INVSTD);
2297 	else
2298 		ARX_SOUND_PlayInterface(SND_INVSTD, 0.1f);
2299 }
2300 
2301 //-----------------------------------------------------------------------------
ARX_INVENTORY_ReOrder()2302 void ARX_INVENTORY_ReOrder()
2303 {
2304 	if (TSecondaryInventory)
2305 	{
2306 
2307 		(void)checked_range_cast<short>(TSecondaryInventory->sizey);
2308 		(void)checked_range_cast<short>(TSecondaryInventory->sizex);
2309 
2310 
2311 		for (long j = 0; j < TSecondaryInventory->sizey; j++)
2312 			for (long i = 0; i < TSecondaryInventory->sizex; i++)
2313 			{
2314 				if (TSecondaryInventory->slot[i][j].io && TSecondaryInventory->slot[i][j].show)
2315 				{
2316 					long sx = TSecondaryInventory->slot[i][j].io->sizex;
2317 					long sy = TSecondaryInventory->slot[i][j].io->sizey;
2318 					Entity * io = TSecondaryInventory->slot[i][j].io;
2319 
2320 					RemoveFromAllInventories(io);
2321 					long x, y;
2322 					sInventory = 2;
2323 					sInventoryX = 0;
2324 					sInventoryY = 0;
2325 
2326 					if (CanBePutInSecondaryInventory(TSecondaryInventory, io, &x, &y))
2327 					{
2328 					}
2329 					else
2330 					{
2331 						sInventory = 2;
2332 
2333 						sInventoryX = static_cast<short>(i);
2334 						sInventoryY = static_cast<short>(j);
2335 
2336 						sx = i;
2337 						sy = j;
2338 						CanBePutInSecondaryInventory(TSecondaryInventory, io, &sx, &sy);
2339 					}
2340 				}
2341 			}
2342 	}
2343 }
2344