1 /* GemRB - Infinity Engine Emulator
2  * Copyright (C) 2003 The GemRB Project
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13 
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  *
19  */
20 
21 //This class represents the inventory of stores (.sto), area containers (.are)
22 //or actors (.cre).
23 
24 #include "Inventory.h"
25 
26 #include "strrefs.h"
27 
28 #include "CharAnimations.h"
29 #include "DisplayMessage.h"
30 #include "Game.h"
31 #include "GameData.h"
32 #include "GameScript/GSUtils.h"
33 #include "Interface.h"
34 #include "Item.h"
35 #include "Map.h"
36 #include "ScriptEngine.h"
37 #include "Scriptable/Actor.h"
38 #include "System/StringBuffer.h"
39 
40 #include <cstdio>
41 
42 namespace GemRB {
43 
44 static int SLOT_HEAD = -1;
45 static int SLOT_MAGIC = -1;
46 static int SLOT_FIST = -1;
47 static int SLOT_MELEE = -1;
48 static int LAST_MELEE = -1;
49 static int SLOT_RANGED = -1;
50 static int LAST_RANGED = -1;
51 static int SLOT_QUICK = -1;
52 static int LAST_QUICK = -1;
53 static int SLOT_INV = -1;
54 static int LAST_INV = -1;
55 static int SLOT_LEFT = -1;
56 static int SLOT_ARMOR = -1;
57 
58 //IWD2 style slots
59 static bool IWD2 = false;
60 
61 [[noreturn]]
InvalidSlot(int slot)62 static void InvalidSlot(int slot)
63 {
64 	error("Inventory", "Invalid slot: %d!\n", slot);
65 }
66 
67 //This inline function returns both an item pointer and the slot data.
68 //slot is a dynamic slot number (SLOT_*)
GetItemPointer(ieDword slot,CREItem * & item) const69 inline Item *Inventory::GetItemPointer(ieDword slot, CREItem *&item) const
70 {
71 	item = GetSlotItem(slot);
72 	if (!item) return NULL;
73 	if (!item->ItemResRef[0]) return NULL;
74 	return gamedata->GetItem(item->ItemResRef);
75 }
76 
Init()77 void Inventory::Init()
78 {
79 	SLOT_MAGIC=-1;
80 	SLOT_FIST=-1;
81 	SLOT_MELEE=-1;
82 	LAST_MELEE=-1;
83 	SLOT_RANGED=-1;
84 	LAST_RANGED=-1;
85 	SLOT_QUICK=-1;
86 	LAST_QUICK=-1;
87 	SLOT_LEFT=-1;
88 	SLOT_ARMOR=-1;
89 	//TODO: set this correctly
90 	IWD2 = false;
91 }
92 
Inventory()93 Inventory::Inventory()
94 {
95 	Owner = NULL;
96 	InventoryType = INVENTORY_HEAP;
97 	Weight = 0;
98 	Equipped = IW_NO_EQUIPPED;
99 	EquippedHeader = 0;
100 	ItemExcl = 0;
101 	memset(ItemTypes, 0, sizeof(ItemTypes));
102 }
103 
~Inventory()104 Inventory::~Inventory()
105 {
106 	for (size_t i = 0; i < Slots.size(); i++) {
107 		delete Slots[i];
108 		Slots[i] = NULL;
109 	}
110 }
111 
112 // duplicates the source inventory into the current one
113 // also changes the items to not drop, so simulacrum and similar don't become factories
CopyFrom(const Actor * source)114 void Inventory::CopyFrom(const Actor *source)
115 {
116 	if (!source) {
117 		return;
118 	}
119 
120 	SetSlotCount(source->inventory.GetSlotCount());
121 
122 	// allocate the items and mark them undroppable
123 	CREItem *tmp, *item;
124 	for (size_t i = 0; i < source->inventory.Slots.size(); i++) {
125 		item = source->inventory.Slots[i];
126 		if (item) {
127 			tmp = new CREItem();
128 			memcpy(tmp, item, sizeof(CREItem));
129 			tmp->Flags |= IE_INV_ITEM_UNDROPPABLE;
130 			int ret = AddSlotItem(tmp, i);
131 			if (ret != ASI_SUCCESS) {
132 				delete tmp;
133 			}
134 		}
135 	}
136 
137 	// preserve the equipped status
138 	Equipped = source->inventory.GetEquipped();
139 	EquippedHeader = source->inventory.GetEquippedHeader();
140 
141 	CalculateWeight();
142 }
143 
GetItem(unsigned int slot)144 CREItem *Inventory::GetItem(unsigned int slot)
145 {
146 	if (slot >= Slots.size() ) {
147 		InvalidSlot(slot);
148 	}
149 	CREItem *item = Slots[slot];
150 	Slots.erase(Slots.begin()+slot);
151 	CalculateWeight();
152 	return item;
153 }
154 
AddItem(CREItem * item)155 void Inventory::AddItem(CREItem *item)
156 {
157 	if (!item) return; //invalid items get no slot
158 	Slots.push_back(item);
159 	CalculateWeight();
160 }
161 
CalculateWeight()162 void Inventory::CalculateWeight()
163 {
164 	Weight = 0;
165 	for (size_t i = 0; i < Slots.size(); i++) {
166 		CREItem *slot = Slots[i];
167 		if (!slot) {
168 			continue;
169 		}
170 		if (slot->Weight == -1) {
171 			Item *itm = gamedata->GetItem(slot->ItemResRef, true);
172 			if (itm) {
173 				slot->Weight = itm->Weight;
174 				gamedata->FreeItem( itm, slot->ItemResRef, false );
175 
176 				// some items can't be dropped once they've been picked up,
177 				// e.g. the portal key in BG2
178 				if (!(slot->Flags & IE_INV_ITEM_MOVABLE)) {
179 					slot->Flags |= IE_INV_ITEM_UNDROPPABLE;
180 				}
181 			} else {
182 				Log(ERROR, "Inventory", "Invalid item: %s!", slot->ItemResRef);
183 				slot->Weight = 0;
184 			}
185 		} else {
186 			slot->Flags &= ~IE_INV_ITEM_ACQUIRED;
187 		}
188 		if (slot->Weight > 0) {
189 			Weight += slot->Weight * ((slot->Usages[0] && slot->MaxStackAmount) ? slot->Usages[0] : 1);
190 		}
191 	}
192 
193 	if (Owner) {
194 		Owner->SetBase(IE_ENCUMBRANCE, Weight);
195 	}
196 }
197 
AddSlotEffects(ieDword index)198 void Inventory::AddSlotEffects(ieDword index)
199 {
200 	CREItem* slot;
201 
202 	const Item *itm = GetItemPointer(index, slot);
203 	if (!itm) {
204 		Log(ERROR, "Inventory", "Invalid item equipped...");
205 		return;
206 	}
207 	ItemExcl|=itm->ItemExcl;
208 	ieDword pos = itm->ItemType/32;
209 	ieDword bit = itm->ItemType%32;
210 	if (pos<8) {
211 		ItemTypes[pos]|=1<<bit;
212 	}
213 
214 	ieWord gradient = itm->GetWieldedGradient();
215 	if (gradient!=0xffff) {
216 		Owner->SetBase(IE_COLORS, gradient);
217 	}
218 
219 	//get the equipping effects
220 	EffectQueue *eqfx = itm->GetEffectBlock(Owner, Owner->Pos, -1, index, 0);
221 	gamedata->FreeItem( itm, slot->ItemResRef, false );
222 
223 	// always refresh, as even if eqfx is null, other effects may have been selfapplied from the block
224 	Owner->RefreshEffects(eqfx);
225 	//call gui for possible paperdoll animation changes
226 	if (Owner->InParty) {
227 		core->SetEventFlag(EF_UPDATEANIM);
228 	}
229 }
230 
231 //no need to know the item effects 'personally', the equipping slot
232 //is stored in them
RemoveSlotEffects(ieDword index)233 void Inventory::RemoveSlotEffects(ieDword index)
234 {
235 	if (Owner->fxqueue.RemoveEquippingEffects(index)) {
236 		Owner->RefreshEffects(NULL);
237 		//call gui for possible paperdoll animation changes
238 		if (Owner->InParty) {
239 			core->SetEventFlag(EF_UPDATEANIM);
240 		}
241 	}
242 }
243 
SetInventoryType(int arg)244 void Inventory::SetInventoryType(int arg)
245 {
246 	InventoryType = arg;
247 }
248 
SetSlotCount(unsigned int size)249 void Inventory::SetSlotCount(unsigned int size)
250 {
251 	if (Slots.size()) {
252 		error("Core", "Inventory size changed???\n");
253 		//we don't allow reassignment,
254 		//if you want this, delete the previous Slots here
255 	}
256 	Slots.assign((size_t) size, NULL);
257 }
258 
259 /** if you supply a "" string, then it checks if the slot is empty */
HasItemInSlot(const char * resref,unsigned int slot) const260 bool Inventory::HasItemInSlot(const char *resref, unsigned int slot) const
261 {
262 	if (slot >= Slots.size()) {
263 		return false;
264 	}
265 	const CREItem *item = Slots[slot];
266 	if (!item) {
267 		return false;
268 	}
269 	if (!resref[0]) {
270 		return true;
271 	}
272 	if (strnicmp( item->ItemResRef, resref, 8 )==0) {
273 		return true;
274 	}
275 	return false;
276 }
277 
HasItemType(ieDword type) const278 bool Inventory::HasItemType(ieDword type) const
279 {
280 	if (type>255) return false;
281 	int idx = type/32;
282 	int bit = type%32;
283 	return (ItemTypes[idx] & (1<<bit) )!=0;
284 }
285 
286 /** counts the items in the inventory, if stacks == 1 then stacks are
287 		accounted for their heap size */
CountItems(const char * resref,bool stacks) const288 int Inventory::CountItems(const char *resref, bool stacks) const
289 {
290 	int count = 0;
291 	size_t slot = Slots.size();
292 	while(slot--) {
293 		const CREItem *item = Slots[slot];
294 		if (!item) {
295 			continue;
296 		}
297 		if (resref && resref[0]) {
298 			if (strnicmp(resref, item->ItemResRef, 8) )
299 				continue;
300 		}
301 		if (stacks && (item->Flags&IE_INV_ITEM_STACKED) ) {
302 			count+=item->Usages[0];
303 			assert(count!=0);
304 		}
305 		else {
306 			count++;
307 		}
308 	}
309 	return count;
310 }
311 
312 /** this function can look for stolen, equipped, identified, destructible
313 		etc, items. You just have to specify the flags in the bitmask
314 		specifying 1 in a bit signifies a requirement */
HasItem(const char * resref,ieDword flags) const315 bool Inventory::HasItem(const char *resref, ieDword flags) const
316 {
317 	size_t slot = Slots.size();
318 	while(slot--) {
319 		const CREItem *item = Slots[slot];
320 		if (!item) {
321 			continue;
322 		}
323 		if ( (flags&item->Flags)!=flags) {
324 				continue;
325 		}
326 		if (resref[0] && strnicmp(item->ItemResRef, resref,8) ) {
327 			continue;
328 		}
329 		return true;
330 	}
331 	return false;
332 }
333 
KillSlot(unsigned int index)334 void Inventory::KillSlot(unsigned int index)
335 {
336 	if (InventoryType==INVENTORY_HEAP) {
337 		Slots.erase(Slots.begin()+index);
338 		return;
339 	}
340 	const CREItem *item = Slots[index];
341 	if (!item) {
342 		return;
343 	}
344 
345 	//the used up item vanishes from the quickslot bar
346 	if (Owner->IsSelected()) {
347 		core->SetEventFlag( EF_ACTION );
348 	}
349 
350 	Slots[index] = NULL;
351 	CalculateWeight();
352 
353 	int effect = core->QuerySlotEffects( index );
354 	if (!effect) {
355 		return;
356 	}
357 	RemoveSlotEffects( index );
358 	const Item *itm = gamedata->GetItem(item->ItemResRef, true);
359 	//this cannot happen, but stuff happens!
360 	if (!itm) {
361 		error("Inventory", "Invalid item: %s!", item->ItemResRef);
362 	}
363 	ItemExcl &= ~itm->ItemExcl;
364 	int eqslot = GetEquippedSlot();
365 	ieDword equip;
366 
367 	switch (effect) {
368 		case SLOT_EFFECT_LEFT:
369 			UpdateShieldAnimation(nullptr);
370 			break;
371 		case SLOT_EFFECT_MISSILE:
372 			//getting a new projectile of the same type
373 			if (eqslot == (int) index) {
374 				if (Equipped < 0) {
375 					//always get the projectile weapon header (this quiver was equipped)
376 					const ITMExtHeader *header = itm->GetWeaponHeader(true);
377 					//remove potential launcher effects too
378 					RemoveSlotEffects(FindTypedRangedWeapon(header->ProjectileQualifier));
379 					equip = FindRangedProjectile(header->ProjectileQualifier);
380 					if (equip != IW_NO_EQUIPPED) {
381 						EquipItem(GetWeaponSlot(equip));
382 					} else {
383 						EquipBestWeapon(EQUIP_MELEE);
384 					}
385 				}
386 			}
387 			UpdateWeaponAnimation();
388 			break;
389 		case SLOT_EFFECT_MAGIC:
390 		case SLOT_EFFECT_MELEE:
391 			// reset Equipped if it was the removed item
392 			if (eqslot == (int)index) {
393 				SetEquippedSlot(IW_NO_EQUIPPED, 0);
394 			} else if (Equipped < 0) {
395 				//always get the projectile weapon header (this is a bow, because Equipped is negative)
396 				const ITMExtHeader *header = itm->GetWeaponHeader(true);
397 				if (header) {
398 					//find the equipped type
399 					int type = header->ProjectileQualifier;
400 					int weaponslot = FindTypedRangedWeapon(type);
401 					CREItem *item2 = Slots[weaponslot];
402 					if (weaponslot == SLOT_FIST) { // a ranged weapon was not found - freshly unequipped
403 						EquipBestWeapon(EQUIP_MELEE);
404 					} else if (item2) {
405 						const Item *itm2 = gamedata->GetItem(item2->ItemResRef, true);
406 						if (itm2) {
407 							if (type == header->ProjectileQualifier) {
408 								equip = FindRangedProjectile(header->ProjectileQualifier);
409 								if (equip != IW_NO_EQUIPPED) {
410 									EquipItem(GetWeaponSlot(equip));
411 								} else {
412 									EquipBestWeapon(EQUIP_MELEE);
413 								}
414 							}
415 							gamedata->FreeItem(itm2, item2->ItemResRef, false);
416 						}
417 					}
418 				}
419 			}
420 			// reset Equipped if it is a ranged weapon slot
421 			// but not magic weapon slot!
422 
423 			UpdateWeaponAnimation();
424 			break;
425 		case SLOT_EFFECT_HEAD:
426 			Owner->SetUsedHelmet("\0");
427 			break;
428 		case SLOT_EFFECT_ITEM:
429 			//remove the armor type only if this item is responsible for it
430 			if ((ieDword) (itm->AnimationType[0]-'1') == Owner->GetBase(IE_ARMOR_TYPE)) {
431 				Owner->SetBase(IE_ARMOR_TYPE, 0);
432 			}
433 			break;
434 	}
435 	gamedata->FreeItem(itm, item->ItemResRef, false);
436 }
437 /** if resref is "", then destroy ALL items
438 this function can look for stolen, equipped, identified, destructible
439 etc, items. You just have to specify the flags in the bitmask
440 specifying 1 in a bit signifies a requirement */
DestroyItem(const char * resref,ieDword flags,ieDword count)441 unsigned int Inventory::DestroyItem(const char *resref, ieDword flags, ieDword count)
442 {
443 	unsigned int destructed = 0;
444 	size_t slot = Slots.size();
445 
446 	while(slot--) {
447 		//ignore the fist slot
448 		if (slot == (unsigned int)SLOT_FIST) {
449 			continue;
450 		}
451 
452 		CREItem *item = Slots[slot];
453 		if (!item) {
454 			continue;
455 		}
456 		// here you can simply destroy all items of a specific type
457 		if ( (flags&item->Flags)!=flags) {
458 			continue;
459 		}
460 		if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
461 			continue;
462 		}
463 		//we need to acknowledge that the item was destroyed
464 		//use unequip stuff etc,
465 		//until that, we simply erase it
466 		ieDword removed;
467 
468 		if (item->Flags&IE_INV_ITEM_STACKED) {
469 			removed=item->Usages[0];
470 			if (count && (removed + destructed > count) ) {
471 				removed = count - destructed;
472 				item = RemoveItem( (unsigned int) slot, removed );
473 			}
474 			else {
475 				KillSlot( (unsigned int) slot);
476 			}
477 		} else {
478 			removed=1;
479 			KillSlot( (unsigned int) slot);
480 		}
481 		delete item;
482 		destructed+=removed;
483 		if (count && (destructed>=count) )
484 			break;
485 	}
486 	if (destructed && Owner && Owner->InParty) displaymsg->DisplayConstantString(STR_LOSTITEM, DMC_BG2XPGREEN);
487 
488 	return destructed;
489 }
490 
RemoveItem(unsigned int slot,unsigned int count)491 CREItem *Inventory::RemoveItem(unsigned int slot, unsigned int count)
492 {
493 	CREItem *item;
494 
495 	if (slot >= Slots.size() ) {
496 		InvalidSlot(slot);
497 	}
498 	item = Slots[slot];
499 
500 	if (!item) {
501 		return NULL;
502 	}
503 
504 	if (!count || !(item->Flags & IE_INV_ITEM_STACKED) || (count >= item->Usages[0])) {
505 		KillSlot(slot);
506 		return item;
507 	}
508 
509 	CREItem *returned = new CREItem(*item);
510 	item->Usages[0]-=count;
511 	returned->Usages[0]=(ieWord) count;
512 	CalculateWeight();
513 	return returned;
514 }
515 
516 //flags set disable item transfer
517 //except for undroppable which is opposite (and shouldn't be set)
RemoveItem(const char * resref,unsigned int flags,CREItem ** res_item,int count)518 int Inventory::RemoveItem(const char *resref, unsigned int flags, CREItem **res_item, int count)
519 {
520 	size_t slot = Slots.size();
521 	unsigned int mask = (flags^IE_INV_ITEM_UNDROPPABLE);
522 	if (core->HasFeature(GF_NO_DROP_CAN_MOVE) ) {
523 		mask &= ~IE_INV_ITEM_UNDROPPABLE;
524 	}
525 	while(slot--) {
526 		CREItem *item = Slots[slot];
527 		if (!item) {
528 			continue;
529 		}
530 
531 		if (flags && (mask&item->Flags)==flags) {
532 			continue;
533 		}
534 		if (!flags && (mask&item->Flags)!=0) {
535 			continue;
536 		}
537 		if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
538 			continue;
539 		}
540 		*res_item=RemoveItem( (unsigned int) slot, count);
541 		return (int) slot;
542 	}
543 	*res_item = NULL;
544 	return -1;
545 }
546 
SetSlotItem(CREItem * item,unsigned int slot)547 void Inventory::SetSlotItem(CREItem* item, unsigned int slot)
548 {
549 	if (slot >= Slots.size() ) {
550 		InvalidSlot(slot);
551 	}
552 
553 	delete Slots[slot];
554 	Slots[slot] = item;
555 
556 	CalculateWeight();
557 
558 	//update the action bar next time
559 	if (Owner->IsSelected()) {
560 		core->SetEventFlag( EF_ACTION );
561 	}
562 }
563 
AddSlotItem(CREItem * item,int slot,int slottype,bool ranged)564 int Inventory::AddSlotItem(CREItem* item, int slot, int slottype, bool ranged)
565 {
566 	int twohanded = item->Flags&IE_INV_ITEM_TWOHANDED;
567 	if (slot >= 0) {
568 		if ((unsigned)slot >= Slots.size()) {
569 			InvalidSlot(slot);
570 		}
571 
572 		//check for equipping weapons
573 		if (WhyCantEquip(slot, twohanded, ranged)) {
574 			return ASI_FAILED;
575 		}
576 
577 		if (!Slots[slot]) {
578 			item->Flags |= IE_INV_ITEM_ACQUIRED;
579 			SetSlotItem(item, slot);
580 			EquipItem(slot);
581 			return ASI_SUCCESS;
582 		}
583 
584 		return MergeItems(slot, item);
585 	}
586 
587 	bool which = (slot == SLOT_AUTOEQUIP);
588 	int res = ASI_FAILED;
589 	int max = (int) Slots.size();
590 	for (int i = 0;i<max;i++) {
591 		//never autoequip in the magic slot!
592 		if (i==SLOT_MAGIC)
593 			continue;
594 		if ((i<SLOT_INV || i>LAST_INV)!=which)
595 			continue;
596 		if (!(core->QuerySlotType(i)&slottype))
597 			continue;
598 		//the slot has been disabled for this actor
599 		if (i>=SLOT_MELEE && i<=LAST_MELEE) {
600 			if (Owner->GetQuickSlot(i-SLOT_MELEE)==0xffff) {
601 				continue;
602 			}
603 		}
604 		int part_res = AddSlotItem (item, i);
605 		if (part_res == ASI_SUCCESS) return ASI_SUCCESS;
606 		else if (part_res == ASI_PARTIAL) res = ASI_PARTIAL;
607 	}
608 
609 	return res;
610 }
611 
612 //Used by FillSlot
TryEquipAll(int slot)613 void Inventory::TryEquipAll(int slot)
614 {
615 	for(int i=SLOT_INV;i<=LAST_INV;i++) {
616 		CREItem *item = Slots[i];
617 		if (!item) {
618 			continue;
619 		}
620 
621 		Slots[i]=NULL;
622 		if (AddSlotItem(item, slot) == ASI_SUCCESS) {
623 			return;
624 		}
625 		//try to stuff it back, it should work
626 		if (AddSlotItem(item, i) != ASI_SUCCESS) {
627 			delete item;
628 		}
629 	}
630 }
631 
AddStoreItem(STOItem * item,int action)632 int Inventory::AddStoreItem(STOItem* item, int action)
633 {
634 	CREItem *temp;
635 	int ret = -1;
636 
637 	// item->PurchasedAmount is the number of items bought
638 	// (you can still add grouped objects in a single step,
639 	// just set up STOItem)
640 	while (item->PurchasedAmount) {
641 		//the first part of a STOItem is essentially a CREItem
642 		temp = new CREItem(item);
643 
644 		//except the Expired flag
645 		temp->Expired=0;
646 		if (action==STA_STEAL && !core->HasFeature(GF_PST_STATE_FLAGS)) {
647 			temp->Flags |= IE_INV_ITEM_STOLEN; // "steel" in pst
648 		}
649 		temp->Flags &= ~IE_INV_ITEM_SELECTED;
650 
651 		ret = AddSlotItem( temp, SLOT_ONLYINVENTORY );
652 		if (ret != ASI_SUCCESS) {
653 			delete temp;
654 			break;
655 		}
656 		if (item->InfiniteSupply!=-1) {
657 			if (!item->AmountInStock) {
658 				break;
659 			}
660 			item->AmountInStock--;
661 		}
662 		item->PurchasedAmount--;
663 	}
664 	return ret;
665 }
666 
667 /* could the source item be dropped on the target item to merge them */
ItemsAreCompatible(const CREItem * target,const CREItem * source) const668 bool Inventory::ItemsAreCompatible(const CREItem* target, const CREItem* source) const
669 {
670 	if (!target) {
671 		//this isn't always ok, please check!
672 		Log(WARNING, "Inventory", "Null item encountered by ItemsAreCompatible()");
673 		return true;
674 	}
675 
676 	if (!(source->Flags&IE_INV_ITEM_STACKED) ) {
677 		return false;
678 	}
679 
680 	if (!strnicmp( target->ItemResRef, source->ItemResRef,8 )) {
681 		return true;
682 	}
683 	return false;
684 }
685 
686 //depletes a magical item
687 //if flags==0 then magical weapons are not harmed
DepleteItem(ieDword flags)688 int Inventory::DepleteItem(ieDword flags)
689 {
690 	for (size_t i = 0; i < Slots.size(); i++) {
691 		CREItem *item = Slots[i];
692 		if (!item) {
693 			continue;
694 		}
695 
696 		//don't harm critical items
697 		//don't harm nonmagical items
698 		//don't harm indestructible items
699 		if ( (item->Flags&(IE_INV_ITEM_CRITICAL|IE_INV_DEPLETABLE)) != IE_INV_DEPLETABLE) {
700 			continue;
701 		}
702 
703 		//if flags = 0 then weapons are not depleted
704 		if (!flags) {
705 			Item *itm = gamedata->GetItem(item->ItemResRef, true);
706 			if (!itm) {
707 				Log(WARNING, "Inventory", "Invalid item to deplete: %s!", item->ItemResRef);
708 				continue;
709 			}
710 			//if the item is usable in weapon slot, then it is weapon
711 			int weapon = core->CanUseItemType( SLOT_WEAPON, itm );
712 			gamedata->FreeItem( itm, item->ItemResRef, false );
713 			if (weapon)
714 				continue;
715 		}
716 		//deplete item
717 		item->Usages[0]=0;
718 		item->Usages[1]=0;
719 		item->Usages[2]=0;
720 	}
721 	return -1;
722 }
723 
724 // if flags is 0, skips undroppable items
725 // if flags is IE_INV_ITEM_UNDROPPABLE, doesn't skip undroppable items
726 // TODO: once all callers have been checked, this can be reversed to make more sense
FindItem(const char * resref,unsigned int flags,unsigned int skip) const727 int Inventory::FindItem(const char *resref, unsigned int flags, unsigned int skip) const
728 {
729 	unsigned int mask = (flags^IE_INV_ITEM_UNDROPPABLE);
730 	if (core->HasFeature(GF_NO_DROP_CAN_MOVE) ) {
731 		mask &= ~IE_INV_ITEM_UNDROPPABLE;
732 	}
733 	for (size_t i = 0; i < Slots.size(); i++) {
734 		const CREItem *item = Slots[i];
735 		if (!item) {
736 			continue;
737 		}
738 		if ( mask & item->Flags ) {
739 			continue;
740 		}
741 		if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
742 			continue;
743 		}
744 		if (skip) {
745 			skip--;
746 		} else {
747 			return (int) i;
748 		}
749 	}
750 	return -1;
751 }
752 
DropItemAtLocation(unsigned int slot,unsigned int flags,Map * map,const Point & loc)753 bool Inventory::DropItemAtLocation(unsigned int slot, unsigned int flags, Map *map, const Point &loc)
754 {
755 	if (slot >= Slots.size()) {
756 		return false;
757 	}
758 	//these slots will never 'drop' the item
759 	if ((slot==(unsigned int) SLOT_FIST) || (slot==(unsigned int) SLOT_MAGIC)) {
760 		return false;
761 	}
762 
763 	CREItem *item = Slots[slot];
764 	if (!item) {
765 		return false;
766 	}
767 	//if you want to drop undoppable items, simply set IE_INV_UNDROPPABLE
768 	//by default, it won't drop them
769 	if ( ((flags^IE_INV_ITEM_UNDROPPABLE)&item->Flags)!=flags) {
770 		return false;
771 	}
772 	if (!map) {
773 		return false;
774 	}
775 	map->AddItemToLocation(loc, item);
776 	KillSlot(slot);
777 	return true;
778 }
779 
DropItemAtLocation(const char * resref,unsigned int flags,Map * map,const Point & loc)780 bool Inventory::DropItemAtLocation(const char *resref, unsigned int flags, Map *map, const Point &loc)
781 {
782 	bool dropped = false;
783 
784 	if (!map) {
785 		return false;
786 	}
787 
788 	//this loop is going from start
789 	for (size_t i = 0; i < Slots.size(); i++) {
790 		//these slots will never 'drop' the item
791 		if ((i==(unsigned int) SLOT_FIST) || (i==(unsigned int) SLOT_MAGIC)) {
792 			continue;
793 		}
794 		CREItem *item = Slots[i];
795 		if (!item) {
796 			continue;
797 		}
798 		//if you want to drop undroppable items, simply set IE_INV_UNDROPPABLE
799 		//by default, it won't drop them
800 		if ( ((flags^IE_INV_ITEM_UNDROPPABLE)&item->Flags)!=flags) {
801 				continue;
802 		}
803 		if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
804 			continue;
805 		}
806 		// mark it as unequipped, so it doesn't cause problems in stores
807 		item->Flags &= ~ IE_INV_ITEM_EQUIPPED;
808 		map->AddItemToLocation(loc, item);
809 		dropped = true;
810 		KillSlot((unsigned int) i);
811 		//if it isn't all items then we stop here
812 		if (resref[0])
813 			break;
814 	}
815 
816 	//dropping gold too
817 	if (!resref[0]) {
818 		if (!Owner->GetBase(IE_GOLD)) {
819 			return dropped;
820 		}
821 		Owner->BaseStats[IE_GOLD] = 0;
822 		CREItem *gold = new CREItem();
823 		if (CreateItemCore(gold, core->GoldResRef, static_cast<int>(Owner->BaseStats[IE_GOLD]), 0, 0)) {
824 			map->AddItemToLocation(loc, gold);
825 		} else {
826 			delete gold;
827 		}
828 	}
829 	return dropped;
830 }
831 
GetSlotItem(ieDword slot) const832 CREItem *Inventory::GetSlotItem(ieDword slot) const
833 {
834 	if (slot >= Slots.size() ) {
835 		InvalidSlot(slot);
836 	}
837 	return Slots[slot];
838 }
839 
GetItemFlag(unsigned int slot) const840 ieDword Inventory::GetItemFlag(unsigned int slot) const
841 {
842 	const CREItem *item = GetSlotItem(slot);
843 	if (!item) {
844 		return 0;
845 	}
846 	return item->Flags;
847 }
848 
ChangeItemFlag(ieDword slot,ieDword arg,int op)849 bool Inventory::ChangeItemFlag(ieDword slot, ieDword arg, int op)
850 {
851 	CREItem *item = GetSlotItem(slot);
852 	if (!item) {
853 		return false;
854 	}
855 	SetBits(item->Flags, arg, op);
856 	return true;
857 }
858 
859 //this is the low level equipping
860 //all checks have been made previously
EquipItem(ieDword slot)861 bool Inventory::EquipItem(ieDword slot)
862 {
863 	const ITMExtHeader *header;
864 
865 	if (!Owner) {
866 		//maybe assertion too?
867 		return false;
868 	}
869 	const CREItem *item = GetSlotItem(slot);
870 	if (!item) {
871 		return false;
872 	}
873 
874 	int weaponslot;
875 
876 	// add effects of an item just being equipped to actor's effect queue
877 	int effect = core->QuerySlotEffects( slot );
878 	const Item *itm = gamedata->GetItem(item->ItemResRef, true);
879 	if (!itm) {
880 		print("Invalid item Equipped: %s Slot: %d", item->ItemResRef, slot);
881 		return false;
882 	}
883 
884 	Owner->ClearCurrentStanceAnims();
885 
886 	switch (effect) {
887 	case SLOT_EFFECT_FIST:
888 		SetEquippedSlot(IW_NO_EQUIPPED, 0);
889 		break;
890 	case SLOT_EFFECT_LEFT:
891 		//no idea if the offhand weapon has style, or simply the right
892 		//hand style is dominant
893 		UpdateShieldAnimation(itm);
894 		break;
895 	case SLOT_EFFECT_MELEE:
896 		//if weapon is bow, then find quarrel for it and equip that
897 		weaponslot = GetWeaponQuickSlot(slot);
898 		EquippedHeader = 0;
899 		if (Owner->PCStats) {
900 			int eheader = Owner->PCStats->GetHeaderForSlot(slot);
901 			if (eheader >= 0) {
902 				EquippedHeader = eheader;
903 			}
904 		}
905 		header = itm->GetExtHeader(EquippedHeader);
906 		if (header) {
907 			ieDword equip;
908 			if (header->AttackType == ITEM_AT_BOW) {
909 				//find the ranged projectile associated with it, this returns equipped code
910 				equip = FindRangedProjectile(header->ProjectileQualifier);
911 				//this is the real item slot of the quarrel
912 				slot = equip + SLOT_MELEE;
913 			} else {
914 				//this is always 0-3
915 				equip = weaponslot;
916 				slot = GetWeaponSlot(weaponslot);
917 			}
918 			if (equip != IW_NO_EQUIPPED) {
919 				Owner->SetupQuickSlot(ACT_WEAPON1+weaponslot, slot, EquippedHeader);
920 			}
921 			SetEquippedSlot(equip, EquippedHeader);
922 			effect = 0; // SetEquippedSlot will already call AddSlotEffects
923 		}
924 		break;
925 	case SLOT_EFFECT_MISSILE:
926 		//Get the ranged header of the projectile (so we theoretically allow shooting of daggers)
927 		EquippedHeader = itm->GetWeaponHeaderNumber(true);
928 		header = itm->GetExtHeader(EquippedHeader);
929 		if (header) {
930 			weaponslot = FindTypedRangedWeapon(header->ProjectileQualifier);
931 			if (weaponslot != SLOT_FIST) {
932 				weaponslot -= SLOT_MELEE;
933 				SetEquippedSlot((ieWordSigned) (slot-SLOT_MELEE), EquippedHeader);
934 				//It is unsure if we can have multiple equipping headers for bows/arrow
935 				//It is unclear which item's header index should go there
936 				Owner->SetupQuickSlot(ACT_WEAPON1+weaponslot, slot, 0);
937 			}
938 			UpdateWeaponAnimation();
939 		}
940 		break;
941 	case SLOT_EFFECT_HEAD:
942 		Owner->SetUsedHelmet(itm->AnimationType);
943 		break;
944 	case SLOT_EFFECT_ITEM:
945 		//adjusting armour level if needed
946 		{
947 			int l = itm->AnimationType[0]-'1';
948 			if (l >= IE_ANI_NO_ARMOR && l <= IE_ANI_HEAVY_ARMOR) {
949 				Owner->SetBase(IE_ARMOR_TYPE, l);
950 			} else {
951 				UpdateShieldAnimation(itm);
952 			}
953 		}
954 		break;
955 	}
956 	gamedata->FreeItem(itm, item->ItemResRef, false);
957 	if (effect) {
958 		AddSlotEffects( slot );
959 	}
960 	return true;
961 }
962 
963 //the removecurse flag will check if it is possible to move the item to the inventory
964 //after a remove curse spell
UnEquipItem(ieDword slot,bool removecurse) const965 bool Inventory::UnEquipItem(ieDword slot, bool removecurse) const
966 {
967 	CREItem *item = GetSlotItem(slot);
968 	if (!item) {
969 		return false;
970 	}
971 	if (item->Flags & IE_INV_ITEM_UNDROPPABLE && !core->HasFeature(GF_NO_DROP_CAN_MOVE)) {
972 		return false;
973 	}
974 
975 	if (!removecurse && item->Flags & IE_INV_ITEM_CURSED && core->QuerySlotEffects(slot)) {
976 		return false;
977 	}
978 
979 	Owner->ClearCurrentStanceAnims();
980 	item->Flags &= ~IE_INV_ITEM_EQUIPPED; //no idea if this is needed, won't hurt
981 	return true;
982 }
983 
984 // find the projectile
985 // type = 1 - bow
986 //        2 - xbow
987 //        4 - sling
988 //returns equipped code
FindRangedProjectile(unsigned int type) const989 int Inventory::FindRangedProjectile(unsigned int type) const
990 {
991 	for(int i=SLOT_RANGED;i<=LAST_RANGED;i++) {
992 		CREItem *Slot;
993 
994 		const Item *itm = GetItemPointer(i, Slot);
995 		if (!itm) continue;
996 		ITMExtHeader *ext_header = itm->GetExtHeader(0);
997 		unsigned int weapontype = 0;
998 		if (ext_header) {
999 			weapontype = ext_header->ProjectileQualifier;
1000 		}
1001 		gamedata->FreeItem(itm, Slot->ItemResRef, false);
1002 		if (weapontype & type) {
1003 			return i-SLOT_MELEE;
1004 		}
1005 	}
1006 	return IW_NO_EQUIPPED;
1007 }
1008 
1009 // find which bow is attached to the projectile marked by 'Equipped'
1010 // returns slotcode
FindRangedWeapon() const1011 int Inventory::FindRangedWeapon() const
1012 {
1013 	if (Equipped>=0) return SLOT_FIST;
1014 	return FindSlotRangedWeapon(GetEquippedSlot());
1015 }
1016 
FindSlotRangedWeapon(ieDword slot) const1017 int Inventory::FindSlotRangedWeapon(ieDword slot) const
1018 {
1019 	if ((int)slot >= SLOT_MELEE) return SLOT_FIST;
1020 	CREItem *Slot;
1021 	Item *itm = GetItemPointer(slot, Slot);
1022 	if (!itm) return SLOT_FIST;
1023 
1024 	//always look for a ranged header when looking for a projectile/projector
1025 	ITMExtHeader *ext_header = itm->GetWeaponHeader(true);
1026 	unsigned int type = 0;
1027 	if (ext_header) {
1028 		type = ext_header->ProjectileQualifier;
1029 	}
1030 	gamedata->FreeItem(itm, Slot->ItemResRef, false);
1031 	return FindTypedRangedWeapon(type);
1032 }
1033 
1034 
1035 // find bow for a specific projectile type
FindTypedRangedWeapon(unsigned int type) const1036 int Inventory::FindTypedRangedWeapon(unsigned int type) const
1037 {
1038 	if (!type) {
1039 		return SLOT_FIST;
1040 	}
1041 	for(int i=SLOT_MELEE;i<=LAST_MELEE;i++) {
1042 		CREItem *Slot;
1043 
1044 		const Item *itm = GetItemPointer(i, Slot);
1045 		if (!itm) continue;
1046 		//always look for a ranged header when looking for a projectile/projector
1047 		ITMExtHeader *ext_header = itm->GetWeaponHeader(true);
1048 		int weapontype = 0;
1049 		if (ext_header && (ext_header->AttackType == ITEM_AT_BOW)) {
1050 			weapontype = ext_header->ProjectileQualifier;
1051 		}
1052 		gamedata->FreeItem(itm, Slot->ItemResRef, false);
1053 		if (weapontype & type) {
1054 			return i;
1055 		}
1056 	}
1057 	return SLOT_FIST;
1058 }
1059 
SetHeadSlot(int arg)1060 void Inventory::SetHeadSlot(int arg) { SLOT_HEAD=arg; }
SetFistSlot(int arg)1061 void Inventory::SetFistSlot(int arg) { SLOT_FIST=arg; }
SetMagicSlot(int arg)1062 void Inventory::SetMagicSlot(int arg) { SLOT_MAGIC=arg; }
SetWeaponSlot(int arg)1063 void Inventory::SetWeaponSlot(int arg)
1064 {
1065 	if (SLOT_MELEE==-1) {
1066 		SLOT_MELEE=arg;
1067 	}
1068 	LAST_MELEE=arg;
1069 }
1070 
1071 //ranged slots should be before MELEE slots
SetRangedSlot(int arg)1072 void Inventory::SetRangedSlot(int arg)
1073 {
1074 	assert(SLOT_MELEE!=-1);
1075 	if (SLOT_RANGED==-1) {
1076 		SLOT_RANGED=arg;
1077 	}
1078 	LAST_RANGED=arg;
1079 }
1080 
SetQuickSlot(int arg)1081 void Inventory::SetQuickSlot(int arg)
1082 {
1083 	if (SLOT_QUICK==-1) {
1084 		SLOT_QUICK=arg;
1085 	}
1086 	LAST_QUICK=arg;
1087 }
1088 
SetInventorySlot(int arg)1089 void Inventory::SetInventorySlot(int arg)
1090 {
1091 	if (SLOT_INV==-1) {
1092 		SLOT_INV=arg;
1093 	}
1094 	LAST_INV=arg;
1095 }
1096 
SetArmorSlot(int arg)1097 void Inventory::SetArmorSlot(int arg)
1098 {
1099 	if (SLOT_ARMOR==-1) {
1100 		SLOT_ARMOR=arg;
1101 	}
1102 }
1103 
1104 //multiple shield slots are allowed
1105 //but in this case they should be interspersed with melee slots
SetShieldSlot(int arg)1106 void Inventory::SetShieldSlot(int arg)
1107 {
1108 	if (SLOT_LEFT!=-1) {
1109 		assert(SLOT_MELEE+1==SLOT_LEFT);
1110 		IWD2=true;
1111 		return;
1112 	}
1113 	SLOT_LEFT=arg;
1114 }
1115 
GetHeadSlot()1116 int Inventory::GetHeadSlot()
1117 {
1118 	return SLOT_HEAD;
1119 }
1120 
GetFistSlot()1121 int Inventory::GetFistSlot()
1122 {
1123 	return SLOT_FIST;
1124 }
1125 
GetMagicSlot()1126 int Inventory::GetMagicSlot()
1127 {
1128 	return SLOT_MAGIC;
1129 }
1130 
GetWeaponSlot()1131 int Inventory::GetWeaponSlot()
1132 {
1133 	return SLOT_MELEE;
1134 }
1135 
GetWeaponQuickSlot(int weaponslot)1136 int Inventory::GetWeaponQuickSlot(int weaponslot)
1137 {
1138 	int slot = weaponslot-SLOT_MELEE;
1139 	if (IWD2 && (slot>=0 && slot<=7) ) slot/=2;
1140 	return slot;
1141 }
1142 
GetWeaponSlot(int quickslot)1143 int Inventory::GetWeaponSlot(int quickslot)
1144 {
1145 	if (IWD2 && (quickslot>=0 && quickslot<=3) ) quickslot*=2;
1146 	return quickslot+SLOT_MELEE;
1147 }
1148 
GetQuickSlot()1149 int Inventory::GetQuickSlot()
1150 {
1151 	return SLOT_QUICK;
1152 }
1153 
GetInventorySlot()1154 int Inventory::GetInventorySlot()
1155 {
1156 	return SLOT_INV;
1157 }
1158 
GetArmorSlot()1159 int Inventory::GetArmorSlot()
1160 {
1161 	return SLOT_ARMOR;
1162 }
1163 
1164 //if shield slot is empty, call again for fist slot!
GetShieldSlot() const1165 int Inventory::GetShieldSlot() const
1166 {
1167 	if (IWD2) {
1168 		//actually, in IWD2, the equipped slot never becomes IW_NO_EQUIPPED, it is always 0-3
1169 		//this is just a hack to prevent invalid shots from happening
1170 		if (Equipped == IW_NO_EQUIPPED) return SLOT_MELEE+1;
1171 
1172 		if (Equipped>=0 && Equipped<=3) {
1173 			return Equipped*2+SLOT_MELEE+1;
1174 		}
1175 		//still, what about magic weapons...
1176 		return -1;
1177 	}
1178 	return SLOT_LEFT;
1179 }
1180 
GetEquippedSlot() const1181 int Inventory::GetEquippedSlot() const
1182 {
1183 	if (Equipped == IW_NO_EQUIPPED) {
1184 		return SLOT_FIST;
1185 	}
1186 	if (IWD2 && Equipped>=0) {
1187 		//Equipped should never become IW_NO_EQUIPPED, this is just a hack to cover the bug
1188 		//about it still becoming invalid
1189 		if (Equipped >= 4) {
1190 			return SLOT_MELEE;
1191 		}
1192 		return Equipped*2+SLOT_MELEE;
1193 	}
1194 	return Equipped+SLOT_MELEE;
1195 }
1196 
SetEquippedSlot(ieWordSigned slotcode,ieWord header,bool noFX)1197 bool Inventory::SetEquippedSlot(ieWordSigned slotcode, ieWord header, bool noFX)
1198 {
1199 	EquippedHeader = header;
1200 
1201 	//doesn't work if magic slot is used, refresh the magic slot just in case
1202 	if (MagicSlotEquipped() && (slotcode!=SLOT_MAGIC-SLOT_MELEE)) {
1203 		Equipped = SLOT_MAGIC-SLOT_MELEE;
1204 		UpdateWeaponAnimation();
1205 		return false;
1206 	}
1207 
1208 	//if it is an illegal code, make it fist
1209 	if ((size_t) (GetWeaponSlot(slotcode))>Slots.size()) {
1210 		slotcode=IW_NO_EQUIPPED;
1211 	}
1212 
1213 	int oldslot = GetEquippedSlot();
1214 	int newslot = GetWeaponSlot(slotcode);
1215 
1216 	//remove previous slot effects
1217 	if (Equipped != IW_NO_EQUIPPED) {
1218 		RemoveSlotEffects(oldslot);
1219 		//for projectiles we may need to remove the launcher effects too
1220 		int oldeffects = core->QuerySlotEffects(oldslot);
1221 		if (oldeffects == SLOT_EFFECT_MISSILE) {
1222 			int launcher = FindSlotRangedWeapon(oldslot);
1223 			if (launcher != SLOT_FIST) {
1224 				RemoveSlotEffects(launcher);
1225 			}
1226 		}
1227 	}
1228 
1229 	//unequipping (fist slot will be used now)
1230 	if (slotcode == IW_NO_EQUIPPED || !HasItemInSlot("", newslot)) {
1231 		Equipped = IW_NO_EQUIPPED;
1232 		//fist slot equipping effects
1233 		AddSlotEffects(SLOT_FIST);
1234 		UpdateWeaponAnimation();
1235 		return true;
1236 	}
1237 
1238 	//equipping a weapon
1239 	Equipped = slotcode;
1240 	int effects = core->QuerySlotEffects( newslot);
1241 	if (effects) {
1242 		CREItem* item = GetSlotItem(newslot);
1243 		item->Flags|=IE_INV_ITEM_EQUIPPED;
1244 		if (!noFX) {
1245 			AddSlotEffects(newslot);
1246 
1247 			//in case of missiles also look for an appropriate launcher
1248 			if (effects == SLOT_EFFECT_MISSILE) {
1249 				newslot = FindRangedWeapon();
1250 				AddSlotEffects(newslot);
1251 			}
1252 		}
1253 	}
1254 	UpdateWeaponAnimation();
1255 	return true;
1256 }
1257 
GetEquipped() const1258 int Inventory::GetEquipped() const
1259 {
1260 	return Equipped;
1261 }
1262 
GetEquippedHeader() const1263 int Inventory::GetEquippedHeader() const
1264 {
1265 	return EquippedHeader;
1266 }
1267 
1268 // store this internally just like Equipped/EquippedHeader if it turns into a hot path
GetEquippedExtHeader(int header) const1269 ITMExtHeader *Inventory::GetEquippedExtHeader(int header) const
1270 {
1271 	int slot; // Equipped holds the projectile, not the weapon
1272 	CREItem *itm = GetUsedWeapon(false, slot); // check the main hand only
1273 	if (!itm) return NULL;
1274 	Item *item = gamedata->GetItem(itm->ItemResRef, true);
1275 	if (!item) return NULL;
1276 	return item->GetExtHeader(header);
1277 }
1278 
SetEquipped(ieWordSigned slot,ieWord header)1279 void Inventory::SetEquipped(ieWordSigned slot, ieWord header)
1280 {
1281 	Equipped = slot;
1282 	EquippedHeader = header;
1283 }
1284 
FistsEquipped() const1285 bool Inventory::FistsEquipped() const
1286 {
1287 	return Equipped == IW_NO_EQUIPPED;
1288 }
1289 
MagicSlotEquipped() const1290 bool Inventory::MagicSlotEquipped() const
1291 {
1292 	if (SLOT_MAGIC != -1) {
1293 		return Slots[SLOT_MAGIC] != NULL;
1294 	}
1295 	return false;
1296 }
1297 
1298 //returns the fist weapon if there is nothing else
1299 //This will return the actual weapon, I mean the bow in the case of bow+arrow combination
GetUsedWeapon(bool leftorright,int & slot) const1300 CREItem *Inventory::GetUsedWeapon(bool leftorright, int &slot) const
1301 {
1302 	CREItem *ret;
1303 
1304 	if (SLOT_MAGIC!=-1) {
1305 		slot = SLOT_MAGIC;
1306 		ret = GetSlotItem(slot);
1307 		if (ret && ret->ItemResRef[0]) {
1308 			return ret;
1309 		}
1310 	}
1311 	if (leftorright) {
1312 		//no shield slot
1313 		slot = GetShieldSlot();
1314 		if (slot>=0) {
1315 			ret = GetSlotItem(slot);
1316 			if (ret) {
1317 				return ret;
1318 			} else {
1319 				//we don't want to return fist for shield slot
1320 				return NULL;
1321 			}
1322 		} else {
1323 			// nothing in the shield slot, so nothing in the right hand, so just quit
1324 			return NULL;
1325 		}
1326 	}
1327 	slot = GetEquippedSlot();
1328 	if((core->QuerySlotEffects(slot) & SLOT_EFFECT_MISSILE) == SLOT_EFFECT_MISSILE) {
1329 		slot = FindRangedWeapon();
1330 	}
1331 	ret = GetSlotItem(slot);
1332 	if (!ret) {
1333 		//return fist weapon
1334 		slot = SLOT_FIST;
1335 		ret = GetSlotItem(slot);
1336 	}
1337 	return ret;
1338 }
1339 
1340 // Returns index of first empty slot or slot with the same
1341 // item and not full stack. On fail returns -1
1342 // Can be used to check for full inventory
FindCandidateSlot(int slottype,size_t first_slot,const char * resref) const1343 int Inventory::FindCandidateSlot(int slottype, size_t first_slot, const char *resref) const
1344 {
1345 	if (first_slot >= Slots.size())
1346 		return -1;
1347 
1348 	for (size_t i = first_slot; i < Slots.size(); i++) {
1349 		if (!(core->QuerySlotType( (unsigned int) i) & slottype) ) {
1350 			continue;
1351 		}
1352 
1353 		CREItem *item = Slots[i];
1354 
1355 		if (!item) {
1356 			return (int) i; //this is a good empty slot
1357 		}
1358 		if (!resref) {
1359 			continue;
1360 		}
1361 		if (!(item->Flags&IE_INV_ITEM_STACKED) ) {
1362 			continue;
1363 		}
1364 		if (strnicmp( item->ItemResRef, resref, 8 )!=0) {
1365 			continue;
1366 		}
1367 		// check if the item fits in this slot, we use the cached
1368 		// stackamount value
1369 		if (item->Usages[0]<item->MaxStackAmount) {
1370 			return (int) i;
1371 		}
1372 	}
1373 
1374 	return -1;
1375 }
1376 
AddSlotItemRes(const ieResRef ItemResRef,int SlotID,int Charge0,int Charge1,int Charge2)1377 void Inventory::AddSlotItemRes(const ieResRef ItemResRef, int SlotID, int Charge0, int Charge1, int Charge2)
1378 {
1379 	CREItem *TmpItem = new CREItem();
1380 	if (CreateItemCore(TmpItem, ItemResRef, Charge0, Charge1, Charge2)) {
1381 		int ret = AddSlotItem( TmpItem, SlotID );
1382 		if (ret != ASI_SUCCESS) {
1383 			// put the remainder on the ground
1384 			Map *area = core->GetGame()->GetCurrentArea();
1385 			if (area) {
1386 				// create or reuse the existing pile
1387 				area->AddItemToLocation(Owner->Pos, TmpItem);
1388 			} else {
1389 				Log(ERROR, "Inventory", "AddSlotItemRes: argh, no area and the inventory is full, bailing out!");
1390 				delete TmpItem;
1391 			}
1392 		}
1393 	} else {
1394 		delete TmpItem;
1395 	}
1396 }
1397 
SetSlotItemRes(const ieResRef ItemResRef,int SlotID,int Charge0,int Charge1,int Charge2)1398 void Inventory::SetSlotItemRes(const ieResRef ItemResRef, int SlotID, int Charge0, int Charge1, int Charge2)
1399 {
1400 	if(ItemResRef[0]) {
1401 		CREItem *TmpItem = new CREItem();
1402 		if (CreateItemCore(TmpItem, ItemResRef, Charge0, Charge1, Charge2)) {
1403 			SetSlotItem( TmpItem, SlotID );
1404 		} else {
1405 			delete TmpItem;
1406 		}
1407 	} else {
1408 		//if the item isn't creatable, we still destroy the old item
1409 		KillSlot( SlotID );
1410 	}
1411 }
1412 
GetShieldItemType() const1413 ieWord Inventory::GetShieldItemType() const
1414 {
1415 	ieWord ret;
1416 	CREItem *Slot;
1417 	int slotNum = GetShieldSlot();
1418 
1419 	if (slotNum < 0) {
1420 		return 0xffff;
1421 	}
1422 	const Item *itm = GetItemPointer(slotNum, Slot);
1423 	if (!itm) return 0xffff;
1424 	ret = itm->ItemType;
1425 	gamedata->FreeItem(itm, Slot->ItemResRef);
1426 	return ret;
1427 }
1428 
GetArmorItemType() const1429 ieWord Inventory::GetArmorItemType() const
1430 {
1431 	ieWord ret;
1432 	CREItem *Slot;
1433 	int slotNum = GetArmorSlot();
1434 
1435 	if (slotNum < 0) {
1436 		return 0xffff;
1437 	}
1438 	const Item *itm = GetItemPointer(slotNum, Slot);
1439 	if (!itm) return 0xffff;
1440 	ret = itm->ItemType;
1441 	gamedata->FreeItem(itm, Slot->ItemResRef);
1442 	return ret;
1443 }
1444 
BreakItemSlot(ieDword slot)1445 void Inventory::BreakItemSlot(ieDword slot)
1446 {
1447 	ieResRef newItem;
1448 	CREItem *Slot;
1449 
1450 	const Item *itm = GetItemPointer(slot, Slot);
1451 	if (!itm) return;
1452 	//if it is the magic weapon slot, don't break it, just remove it, because it couldn't be removed
1453 	//or for pst, just remove it as there is no breaking (the replacement item is a sound)
1454 	if (slot == (unsigned int) SLOT_MAGIC || core->HasFeature(GF_HAS_PICK_SOUND)) {
1455 		newItem[0]=0;
1456 	} else {
1457 		memcpy(newItem, itm->ReplacementItem,sizeof(newItem) );
1458 	}
1459 	gamedata->FreeItem( itm, Slot->ItemResRef, true );
1460 	//this depends on setslotitemres using setslotitem
1461 	SetSlotItemRes(newItem, slot, 0,0,0);
1462 }
1463 
dump() const1464 void Inventory::dump() const
1465 {
1466 	StringBuffer buffer;
1467 	dump(buffer);
1468 	Log(DEBUG, "Inventory", buffer);
1469 }
1470 
dump(StringBuffer & buffer) const1471 void Inventory::dump(StringBuffer& buffer) const
1472 {
1473 	buffer.append( "INVENTORY:\n" );
1474 	for (unsigned int i = 0; i < Slots.size(); i++) {
1475 		CREItem* itm = Slots[i];
1476 
1477 		if (!itm) {
1478 			continue;
1479 		}
1480 
1481 		buffer.appendFormatted( "%2u: %8.8s - (%d %d %d) Fl:0x%x Wt: %d x %dLb\n", i, itm->ItemResRef, itm->Usages[0], itm->Usages[1], itm->Usages[2], itm->Flags, itm->MaxStackAmount, itm->Weight );
1482 	}
1483 
1484 	buffer.appendFormatted("Equipped: %d       EquippedHeader: %d\n", Equipped, EquippedHeader);
1485 	buffer.appendFormatted( "Total weight: %d\n", Weight );
1486 }
1487 
EquipBestWeapon(int flags)1488 void Inventory::EquipBestWeapon(int flags)
1489 {
1490 	int i;
1491 	int damage = -1;
1492 	ieDword best_slot = SLOT_FIST;
1493 	ITMExtHeader *header;
1494 	CREItem *Slot;
1495 	char AnimationType[2]={0,0};
1496 	ieWord MeleeAnimation[3]={100,0,0};
1497 
1498 	//cannot change equipment when holding magic weapons
1499 	if (Equipped == SLOT_MAGIC-SLOT_MELEE) {
1500 		return;
1501 	}
1502 
1503 	if (flags&EQUIP_RANGED) {
1504 		for(i=SLOT_RANGED;i<LAST_RANGED;i++) {
1505 			const Item *itm = GetItemPointer(i, Slot);
1506 			if (!itm) continue;
1507 			//cannot change equipment when holding a cursed weapon
1508 			if (Slot->Flags & IE_INV_ITEM_CURSED) {
1509 				return;
1510 			}
1511 			//best ranged
1512 			int tmp = itm->GetDamagePotential(true, header);
1513 			if (tmp>damage) {
1514 				best_slot = i;
1515 				damage = tmp;
1516 				memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1517 				memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
1518 			}
1519 			gamedata->FreeItem( itm, Slot->ItemResRef, false );
1520 		}
1521 
1522 		//ranged melee weapons like throwing daggers (not bows!)
1523 		for(i=SLOT_MELEE;i<=LAST_MELEE;i++) {
1524 			const Item *itm = GetItemPointer(i, Slot);
1525 			if (!itm) continue;
1526 			//cannot change equipment when holding a cursed weapon
1527 			if (Slot->Flags & IE_INV_ITEM_CURSED) {
1528 				return;
1529 			}
1530 			//best ranged
1531 			int tmp = itm->GetDamagePotential(true, header);
1532 			if (tmp>damage) {
1533 				best_slot = i;
1534 				damage = tmp;
1535 				memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1536 				memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
1537 			}
1538 			gamedata->FreeItem( itm, Slot->ItemResRef, false );
1539 		}
1540 	}
1541 
1542 	if (flags&EQUIP_MELEE) {
1543 		for(i=SLOT_MELEE;i<=LAST_MELEE;i++) {
1544 			const Item *itm = GetItemPointer(i, Slot);
1545 			if (!itm) continue;
1546 			//cannot change equipment when holding a cursed weapon
1547 			if (Slot->Flags & IE_INV_ITEM_CURSED) {
1548 				return;
1549 			}
1550 			//the Slot flag is enough for this
1551 			//though we need animation type/damagepotential anyway
1552 			if (Slot->Flags&IE_INV_ITEM_BOW) continue;
1553 			//best melee
1554 			int tmp = itm->GetDamagePotential(false, header);
1555 			if (tmp>damage) {
1556 				best_slot = i;
1557 				damage = tmp;
1558 				memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1559 				memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
1560 			}
1561 			gamedata->FreeItem( itm, Slot->ItemResRef, false );
1562 		}
1563 	}
1564 
1565 	EquipItem(best_slot);
1566 	UpdateWeaponAnimation();
1567 }
1568 
1569 #define ID_NONEED  0   //id is not important
1570 #define ID_NEED    1   //id is important
1571 #define ID_NO      2   //shouldn't id
1572 
1573 /* returns true if there are more item usages not fitting in given array */
GetEquipmentInfo(ItemExtHeader * array,int startindex,int count)1574 bool Inventory::GetEquipmentInfo(ItemExtHeader *array, int startindex, int count)
1575 {
1576 	int pos = 0;
1577 	int actual = 0;
1578 	memset(array, 0, count * sizeof(ItemExtHeader) );
1579 	for(unsigned int idx=0;idx<Slots.size();idx++) {
1580 		if (!core->QuerySlotEffects(idx)) {
1581 			continue;
1582 		}
1583 		CREItem *slot;
1584 
1585 		const Item *itm = GetItemPointer(idx, slot);
1586 		if (!itm) {
1587 			continue;
1588 		}
1589 		for(int ehc=0;ehc<itm->ExtHeaderCount;ehc++) {
1590 			ITMExtHeader *ext_header = itm->ext_headers+ehc;
1591 			if (ext_header->Location!=ITEM_LOC_EQUIPMENT) {
1592 				continue;
1593 			}
1594 			//skipping if we cannot use the item
1595 			int idreq1 = (slot->Flags&IE_INV_ITEM_IDENTIFIED);
1596 			int idreq2 = ext_header->IDReq;
1597 			switch (idreq2) {
1598 				case ID_NO:
1599 					if (idreq1) continue;
1600 					break;
1601 				case ID_NEED:
1602 					if (!idreq1) continue;
1603 					break;
1604 				default:;
1605 			}
1606 
1607 			actual++;
1608 			if (actual>startindex) {
1609 
1610 				//store the item, return if we can't store more
1611 				if (!count) {
1612 					gamedata->FreeItem(itm, slot->ItemResRef, false);
1613 					return true;
1614 				}
1615 				count--;
1616 				memcpy(array[pos].itemname, slot->ItemResRef, sizeof(ieResRef) );
1617 				array[pos].slot = idx;
1618 				array[pos].headerindex = ehc;
1619 				array[pos].Tooltip = ext_header->Tooltip;
1620 				int slen = ((char *) &(array[pos].itemname)) -((char *) &(array[pos].AttackType));
1621 				memcpy(&(array[pos].AttackType), &(ext_header->AttackType), slen);
1622 				if (ext_header->Charges) {
1623 					//don't modify ehc, it is a counter
1624 					if (ehc>=CHARGE_COUNTERS) {
1625 						array[pos].Charges=slot->Usages[0];
1626 					} else {
1627 						array[pos].Charges=slot->Usages[ehc];
1628 					}
1629 				} else {
1630 					array[pos].Charges=0xffff;
1631 				}
1632 				pos++;
1633 			}
1634 		}
1635 		gamedata->FreeItem(itm, slot->ItemResRef, false);
1636 	}
1637 
1638 	return false;
1639 }
1640 
1641 //The slot index value is optional, if you supply it,
1642 // then ItemExcl will be returned as if the item was already unequipped
GetEquipExclusion(int index) const1643 ieDword Inventory::GetEquipExclusion(int index) const
1644 {
1645 	if (index<0) {
1646 		return ItemExcl;
1647 	}
1648 	CREItem *slot;
1649 	const Item *itm = GetItemPointer(index, slot);
1650 	if (!itm) {
1651 		return ItemExcl;
1652 	}
1653 	ieDword ret = ItemExcl&~itm->ItemExcl;
1654 	gamedata->FreeItem(itm, slot->ItemResRef, false);
1655 	return ret;
1656 }
1657 
UpdateShieldAnimation(const Item * it)1658 void Inventory::UpdateShieldAnimation(const Item *it)
1659 {
1660 	char AnimationType[2]={0,0};
1661 	int WeaponType;
1662 
1663 	if (it) {
1664 		memcpy(AnimationType, it->AnimationType, 2);
1665 		if (core->CanUseItemType(SLOT_WEAPON, it))
1666 			WeaponType = IE_ANI_WEAPON_2W;
1667 		else
1668 			WeaponType = IE_ANI_WEAPON_1H;
1669 	} else {
1670 		WeaponType = IE_ANI_WEAPON_1H;
1671 	}
1672 	Owner->SetUsedShield(AnimationType, WeaponType);
1673 }
1674 
UpdateWeaponAnimation()1675 void Inventory::UpdateWeaponAnimation()
1676 {
1677 	int slot = GetEquippedSlot();
1678 	int effect = core->QuerySlotEffects( slot );
1679 	if (effect == SLOT_EFFECT_MISSILE) {
1680 		// ranged weapon
1681 		slot = FindRangedWeapon();
1682 	}
1683 	int WeaponType = -1;
1684 
1685 	char AnimationType[2]={0,0};
1686 	ieWord MeleeAnimation[3]={100,0,0};
1687 	CREItem *Slot;
1688 
1689 	// TODO: fix bows?
1690 
1691 	ITMExtHeader *header = 0;
1692 	const Item *itm = GetItemPointer(slot, Slot);
1693 	if (itm) {
1694 		itm->GetDamagePotential(false, header);
1695 		memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1696 		//for twohanded flag, you don't need itm
1697 		if (Slot->Flags & IE_INV_ITEM_TWOHANDED)
1698 			WeaponType = IE_ANI_WEAPON_2H;
1699 		else {
1700 
1701 			// Examine shield slot to check if we're using two weapons
1702 			// TODO: for consistency, use same Item* access method as above
1703 			bool twoweapon = false;
1704 			int slot = GetShieldSlot();
1705 			CREItem* si = NULL;
1706 			if (slot>0) {
1707 				si = GetSlotItem( (ieDword) slot );
1708 			}
1709 			if (si) {
1710 				Item* it = gamedata->GetItem(si->ItemResRef, true);
1711 				assert(it);
1712 				if (core->CanUseItemType(SLOT_WEAPON, it))
1713 					twoweapon = true;
1714 				gamedata->FreeItem(it, si->ItemResRef, false);
1715 			}
1716 
1717 			if (twoweapon)
1718 				WeaponType = IE_ANI_WEAPON_2W;
1719 			else
1720 				WeaponType = IE_ANI_WEAPON_1H;
1721 		}
1722 	}
1723 
1724 	if (header)
1725 		memcpy(MeleeAnimation,header->MeleeAnimation, sizeof(MeleeAnimation) );
1726 	if (itm)
1727 		gamedata->FreeItem( itm, Slot->ItemResRef, false );
1728 	Owner->SetUsedWeapon(AnimationType, MeleeAnimation, WeaponType);
1729 }
1730 
1731 //this function will also check disabled slots (if that feature will be imped)
IsSlotBlocked(int slot) const1732 bool Inventory::IsSlotBlocked(int slot) const
1733 {
1734 	if (slot<SLOT_MELEE) return false;
1735 	if (slot>LAST_MELEE) return false;
1736 	int otherslot;
1737 	if (IWD2) {
1738 		otherslot = slot+1;
1739 	} else {
1740 		otherslot = SLOT_LEFT;
1741 	}
1742 	return HasItemInSlot("",otherslot);
1743 }
1744 
TwoHandedInSlot(int slot) const1745 inline bool Inventory::TwoHandedInSlot(int slot) const
1746 {
1747 	CREItem *item;
1748 
1749 	item = GetSlotItem(slot);
1750 	if (!item) return false;
1751 	if (item->Flags&IE_INV_ITEM_TWOHANDED) {
1752 		return true;
1753 	}
1754 	return false;
1755 }
1756 
WhyCantEquip(int slot,int twohanded,bool ranged) const1757 int Inventory::WhyCantEquip(int slot, int twohanded, bool ranged) const
1758 {
1759 	// check only for hand slots
1760 	if ((slot<SLOT_MELEE || slot>LAST_MELEE) && (slot != SLOT_LEFT) ) {
1761 		return 0;
1762 	}
1763 
1764 	//magic items have the highest priority
1765 	if (MagicSlotEquipped()) {
1766 		//magic weapon is in use
1767 		return STR_MAGICWEAPON;
1768 	}
1769 
1770 	//can't equip in shield slot if a weapon slot is twohanded or ranged
1771 	for (int i=SLOT_MELEE; i<=LAST_MELEE;i++) {
1772 		//see GetShieldSlot
1773 		int otherslot;
1774 		if (IWD2) {
1775 			otherslot = i+1;
1776 		} else {
1777 			otherslot = SLOT_LEFT;
1778 		}
1779 		if (slot==otherslot) {
1780 			if (TwoHandedInSlot(i)) {
1781 				return STR_TWOHANDED_USED;
1782 			}
1783 			if (ranged) {
1784 				return STR_NO_RANGED_OFFHAND;
1785 			}
1786 		}
1787 	}
1788 
1789 	if (twohanded) {
1790 		if (IWD2) {
1791 			if (slot>=SLOT_MELEE&&slot<=LAST_MELEE && (slot-SLOT_MELEE)&1) {
1792 				return STR_NOT_IN_OFFHAND;
1793 			}
1794 		} else if (slot == SLOT_LEFT) {
1795 			return STR_NOT_IN_OFFHAND;
1796 		}
1797 		if (IsSlotBlocked(slot)) {
1798 		//cannot equip two handed while shield slot is in use?
1799 			return STR_OFFHAND_USED;
1800 		}
1801 	}
1802 	return 0;
1803 }
1804 
1805 //recharge items on rest, if rest was partial, recharge only 'hours'
1806 //if this latter functionality is unwanted, then simply don't recharge if
1807 //hours != 0
ChargeAllItems(int hours)1808 void Inventory::ChargeAllItems(int hours)
1809 {
1810 	//this loop is going from start
1811 	for (size_t i = 0; i < Slots.size(); i++) {
1812 		CREItem *item = Slots[i];
1813 		if (!item) {
1814 			continue;
1815 		}
1816 
1817 		Item *itm = gamedata->GetItem(item->ItemResRef, true);
1818 		if (!itm)
1819 			continue;
1820 		for(int h=0;h<CHARGE_COUNTERS;h++) {
1821 			ITMExtHeader *header = itm->GetExtHeader(h);
1822 			if (header && (header->RechargeFlags&IE_ITEM_RECHARGE)) {
1823 				unsigned short add = header->Charges;
1824 				if (hours && add>hours) add=hours;
1825 				add+=item->Usages[h];
1826 				if(add>header->Charges) add=header->Charges;
1827 				item->Usages[h]=add;
1828 			}
1829 		}
1830 		gamedata->FreeItem( itm, item->ItemResRef, false );
1831 	}
1832 }
1833 
1834 #define ITM_STEALING (IE_INV_ITEM_UNSTEALABLE | IE_INV_ITEM_MOVABLE | IE_INV_ITEM_EQUIPPED) //0x442
FindStealableItem()1835 int Inventory::FindStealableItem()
1836 {
1837 	unsigned int slotcnt = Slots.size();
1838 	unsigned int start = core->Roll(1, slotcnt, -1);
1839 	int inc = start & 1 ? 1 : -1;
1840 
1841 	Log(DEBUG, "Inventory", "Start Slot: %d, increment: %d", start, inc);
1842 	for (unsigned int i = 0; i < slotcnt; ++i) {
1843 		int slot = (slotcnt - 1 + start + i * inc) % slotcnt;
1844 		CREItem *item = Slots[slot];
1845 		//can't steal empty slot
1846 		if (!item) continue;
1847 		//bit 1 is stealable slot
1848 		if (!(core->QuerySlotFlags(slot)&1) ) continue;
1849 		//can't steal equipped weapon
1850 		int realslot = core->QuerySlot(slot);
1851 		if (GetEquippedSlot() == realslot) continue;
1852 		if (GetShieldSlot() == realslot) continue;
1853 		//can't steal flagged items
1854 		if ((item->Flags & ITM_STEALING) != IE_INV_ITEM_MOVABLE) continue;
1855 		return slot;
1856 	}
1857 	return -1;
1858 }
1859 
1860 // extension to allow more or less than head gear to avert critical hits:
1861 // If an item with bit 25 set is equipped in a non-helmet slot, aversion is enabled
1862 // If an item with bit 25 set is equipped in a helmet slot, aversion is disabled
ProvidesCriticalAversion()1863 bool Inventory::ProvidesCriticalAversion()
1864 {
1865 	int maxSlot = (int) Slots.size();
1866 	for (int i = 0; i < maxSlot; i++) {
1867 		CREItem *item = Slots[i];
1868 		if (!item || ((i>=SLOT_INV) && (i<=LAST_INV))) { // ignore items in the backpack
1869 			continue;
1870 		}
1871 		// weapon, but not equipped
1872 		if (!((i == SLOT_ARMOR) || (i == SLOT_HEAD)) && !(item->Flags & IE_INV_ITEM_EQUIPPED)) {
1873 			continue;
1874 		}
1875 
1876 		Item *itm = gamedata->GetItem(item->ItemResRef, true);
1877 		if (!itm) {
1878 			continue;
1879 		}
1880 		//if the item is worn on head, toggle crits must be 0, otherwise it must be 1
1881 		//this flag is only stored in the item header, so we need to make some efforts
1882 		//to get to it (TODO convince ToBEx to move this bit into the accessible range?) - low 24 bits
1883 		ieDword flag = itm->Flags;
1884 		gamedata->FreeItem( itm, item->ItemResRef, false );
1885 		bool togglesCrits = (flag&IE_ITEM_TOGGLE_CRITS);
1886 		bool isHelmet = (i == SLOT_HEAD);
1887 		if (togglesCrits ^ isHelmet) return true;
1888 	}
1889 	return false;
1890 }
1891 
MergeItems(int slot,CREItem * item)1892 int Inventory::MergeItems(int slot, CREItem *item)
1893 {
1894 	CREItem *slotitem = Slots[slot];
1895 	if (slotitem->MaxStackAmount && ItemsAreCompatible(slotitem, item)) {
1896 		//calculate with the max movable stock
1897 		int chunk = item->Usages[0];
1898 		if (slotitem->Usages[0] + chunk > slotitem->MaxStackAmount) {
1899 			chunk = slotitem->MaxStackAmount - slotitem->Usages[0];
1900 		}
1901 		if (chunk<=0) {
1902 			return ASI_FAILED;
1903 		}
1904 
1905 		slotitem->Flags |= IE_INV_ITEM_ACQUIRED;
1906 		slotitem->Usages[0] = (ieWord) (slotitem->Usages[0] + chunk);
1907 		item->Usages[0] = (ieWord) (item->Usages[0] - chunk);
1908 		EquipItem(slot);
1909 		CalculateWeight();
1910 		if (item->Usages[0] == 0) {
1911 			delete item;
1912 			return ASI_SUCCESS;
1913 		}
1914 		return ASI_PARTIAL;
1915 	}
1916 	return ASI_FAILED;
1917 }
1918 
1919 }
1920