Sort include order.
[gemrb.git] / gemrb / core / Inventory.cpp
blob4fd05c1e7693a1ed808da02bf7d94dca8dfc8fe6
1 /* GemRB - Infinity Engine Emulator
2 * Copyright (C) 2003 The GemRB Project
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.
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.
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.
21 //This class represents the inventory of stores (.sto), area containers (.are)
22 //or actors (.cre).
24 #include "Inventory.h"
26 #include "win32def.h"
27 #include "strrefs.h"
29 #include "Actor.h"
30 #include "Game.h"
31 #include "GameData.h"
32 #include "Interface.h"
33 #include "Item.h"
34 #include "ScriptEngine.h"
36 #include <cstdio>
38 static int SLOT_HEAD = -1;
39 static int SLOT_MAGIC = -1;
40 static int SLOT_FIST = -1;
41 static int SLOT_MELEE = -1;
42 static int LAST_MELEE = -1;
43 static int SLOT_RANGED = -1;
44 static int LAST_RANGED = -1;
45 static int SLOT_QUICK = -1;
46 static int LAST_QUICK = -1;
47 static int SLOT_INV = -1;
48 static int LAST_INV = -1;
49 static int SLOT_LEFT = -1;
51 //IWD2 style slots
52 static bool IWD2 = false;
53 static int MagicBit = 0;
55 static void InvalidSlot(int slot)
57 printMessage("Inventory"," ",LIGHT_RED);
58 printf("Invalid slot: %d!\n",slot);
59 abort();
62 //This inline function returns both an item pointer and the slot data.
63 //slot is a dynamic slot number (SLOT_*)
64 inline Item *Inventory::GetItemPointer(ieDword slot, CREItem *&item) const
66 item = GetSlotItem(slot);
67 if (!item) return NULL;
68 if (!item->ItemResRef[0]) return NULL;
69 return gamedata->GetItem(item->ItemResRef);
72 void Inventory::Init(int mb)
74 SLOT_MAGIC=-1;
75 SLOT_FIST=-1;
76 SLOT_MELEE=-1;
77 LAST_MELEE=-1;
78 SLOT_RANGED=-1;
79 LAST_RANGED=-1;
80 SLOT_QUICK=-1;
81 LAST_QUICK=-1;
82 SLOT_LEFT=-1;
83 //TODO: set this correctly
84 IWD2 = false;
85 MagicBit = mb;
88 Inventory::Inventory()
90 Owner = NULL;
91 InventoryType = INVENTORY_HEAP;
92 Changed = false;
93 Weight = 0;
94 Equipped = IW_NO_EQUIPPED;
95 EquippedHeader = 0;
96 ItemExcl = 0;
99 Inventory::~Inventory()
101 for (size_t i = 0; i < Slots.size(); i++) {
102 if (Slots[i]) {
103 delete( Slots[i] );
104 Slots[i] = NULL;
109 CREItem *Inventory::GetItem(unsigned int slot)
111 if (slot >= Slots.size() ) {
112 InvalidSlot(slot);
113 return NULL;
115 CREItem *item = Slots[slot];
116 Slots.erase(Slots.begin()+slot);
117 return item;
120 void Inventory::AddItem(CREItem *item)
122 if (!item) return; //invalid items get no slot
123 Slots.push_back(item);
124 //Changed=true; //probably not needed, chests got no encumbrance
127 void Inventory::CalculateWeight()
129 if (!Changed) {
130 return;
132 Weight = 0;
133 for (size_t i = 0; i < Slots.size(); i++) {
134 CREItem *slot = Slots[i];
135 if (!slot) {
136 continue;
138 //printf ("%2d: %8s : %d x %d\n", (int) i, slot->ItemResRef, slot->Weight, slot->Usages[0]);
139 if (slot->Weight == -1) {
140 Item *itm = gamedata->GetItem( slot->ItemResRef );
141 if (itm) {
142 //simply adding the item flags to the slot
143 slot->Flags |= (itm->Flags<<8);
144 //some slot flags might be affected by the item flags
145 if (!(slot->Flags & IE_INV_ITEM_CRITICAL)) {
146 slot->Flags |= IE_INV_ITEM_DESTRUCTIBLE;
148 //this is for converting IWD items magic flag
149 if (MagicBit) {
150 if (slot->Flags&IE_INV_ITEM_UNDROPPABLE) {
151 slot->Flags|=IE_INV_ITEM_MAGICAL;
152 slot->Flags&=~IE_INV_ITEM_UNDROPPABLE;
156 if (!(slot->Flags & IE_INV_ITEM_MOVABLE)) {
157 slot->Flags |= IE_INV_ITEM_UNDROPPABLE;
160 if (slot->Flags & IE_INV_ITEM_STOLEN2) {
161 slot->Flags |= IE_INV_ITEM_STOLEN;
164 //auto identify basic items
165 if (!itm->LoreToID) {
166 slot->Flags |= IE_INV_ITEM_IDENTIFIED;
169 //if item is stacked mark it as so
170 if (itm->StackAmount) {
171 slot->Flags |= IE_INV_ITEM_STACKED;
174 slot->Weight = itm->Weight;
175 slot->StackAmount = itm->StackAmount;
176 gamedata->FreeItem( itm, slot->ItemResRef, false );
178 else {
179 printMessage( "Inventory", " ", LIGHT_RED);
180 printf("Invalid item: %s!\n", slot->ItemResRef);
181 slot->Weight = 0;
183 } else {
184 slot->Flags &= ~IE_INV_ITEM_ACQUIRED;
186 if (slot->Weight > 0) {
187 Weight += slot->Weight * ((slot->Usages[0] && slot->StackAmount > 1) ? slot->Usages[0] : 1);
190 Changed = false;
193 void Inventory::AddSlotEffects(ieDword index)
195 CREItem* slot;
197 const Item *itm = GetItemPointer(index, slot);
198 if (!itm) {
199 printMessage("Inventory","Invalid item equipped...\n",LIGHT_RED);
200 return;
202 ItemExcl|=itm->ItemExcl;
204 ieWord gradient = itm->GetWieldedGradient();
205 if (gradient!=0xffff) {
206 Owner->SetBase(IE_COLORS, gradient);
209 //get the equipping effects
210 EffectQueue *eqfx = itm->GetEffectBlock(-1, index, 0);
211 gamedata->FreeItem( itm, slot->ItemResRef, false );
213 Owner->RefreshEffects(eqfx);
214 //call gui for possible paperdoll animation changes
215 if (Owner->InParty) {
216 core->SetEventFlag(EF_UPDATEANIM);
220 //no need to know the item effects 'personally', the equipping slot
221 //is stored in them
222 void Inventory::RemoveSlotEffects(ieDword index)
224 Owner->fxqueue.RemoveEquippingEffects(index);
225 Owner->RefreshEffects(NULL);
226 //call gui for possible paperdoll animation changes
227 if (Owner->InParty) {
228 core->SetEventFlag(EF_UPDATEANIM);
232 void Inventory::SetInventoryType(int arg)
234 InventoryType = arg;
237 void Inventory::SetSlotCount(unsigned int size)
239 if (Slots.size()) {
240 printf("Inventory size changed???\n");
241 //we don't allow reassignment,
242 //if you want this, delete the previous Slots here
243 abort();
245 Slots.assign((size_t) size, NULL);
248 /** if you supply a "" string, then it checks if the slot is empty */
249 bool Inventory::HasItemInSlot(const char *resref, unsigned int slot) const
251 if (slot >= Slots.size()) {
252 return false;
254 const CREItem *item = Slots[slot];
255 if (!item) {
256 return false;
258 if (!resref[0]) {
259 return true;
261 if (strnicmp( item->ItemResRef, resref, 8 )==0) {
262 return true;
264 return false;
267 /** counts the items in the inventory, if stacks == 1 then stacks are
268 accounted for their heap size */
269 int Inventory::CountItems(const char *resref, bool stacks) const
271 int count = 0;
272 size_t slot = Slots.size();
273 while(slot--) {
274 const CREItem *item = Slots[slot];
275 if (!item) {
276 continue;
278 if (resref && resref[0]) {
279 if (!strnicmp(resref, item->ItemResRef, 8) )
280 continue;
282 if (stacks && (item->Flags&IE_INV_ITEM_STACKED) ) {
283 count+=item->Usages[0];
285 else {
286 count++;
289 return count;
292 /** this function can look for stolen, equipped, identified, destructible
293 etc, items. You just have to specify the flags in the bitmask
294 specifying 1 in a bit signifies a requirement */
295 bool Inventory::HasItem(const char *resref, ieDword flags) const
297 size_t slot = Slots.size();
298 while(slot--) {
299 const CREItem *item = Slots[slot];
300 if (!item) {
301 continue;
303 if ( (flags&item->Flags)!=flags) {
304 continue;
306 if (resref[0] && strnicmp(item->ItemResRef, resref,8) ) {
307 continue;
309 return true;
311 return false;
314 void Inventory::KillSlot(unsigned int index)
316 if (InventoryType==INVENTORY_HEAP) {
317 Slots.erase(Slots.begin()+index);
318 return;
320 CREItem *item = Slots[index];
321 if (!item) {
322 return;
325 //the used up item vanishes from the quickslot bar
326 if (Owner->InParty) {
327 core->SetEventFlag( EF_ACTION );
330 Slots[index] = NULL;
331 int effect = core->QuerySlotEffects( index );
332 if (!effect) {
333 return;
335 RemoveSlotEffects( index );
336 Item *itm = gamedata->GetItem(item->ItemResRef);
337 //this cannot happen, but stuff happens!
338 if (!itm) {
339 return;
341 ItemExcl &= ~itm->ItemExcl;
343 switch (effect) {
344 case SLOT_EFFECT_LEFT:
345 UpdateShieldAnimation(0);
346 break;
347 case SLOT_EFFECT_MISSILE:
348 //getting a new projectile of the same type
349 if (Equipped + SLOT_MELEE == (int) index) {
350 if (Equipped < 0) {
351 //always get the projectile weapon header (this quiver was equipped)
352 ITMExtHeader *header = itm->GetWeaponHeader(true);
353 Equipped = FindRangedProjectile(header->ProjectileQualifier);
354 if (Equipped!=IW_NO_EQUIPPED) {
355 EquipItem(Equipped+SLOT_MELEE);
356 } else {
357 EquipItem(SLOT_FIST);
361 UpdateWeaponAnimation();
362 break;
363 case SLOT_EFFECT_MELEE:
364 // reset Equipped if it was the removed item
365 if (Equipped+SLOT_MELEE == (int)index)
366 Equipped = IW_NO_EQUIPPED;
367 else if (Equipped < 0) {
368 //always get the projectile weapon header (this is a bow, because Equipped is negative)
369 ITMExtHeader *header = itm->GetWeaponHeader(true);
370 if (header) {
371 //find the equipped type
372 int type = header->ProjectileQualifier;
373 int weaponslot = FindTypedRangedWeapon(type);
374 CREItem *item2 = Slots[weaponslot];
375 if (item2) {
376 Item *itm2 = gamedata->GetItem(item2->ItemResRef);
377 if (itm2) {
378 if (type == header->ProjectileQualifier) {
379 Equipped = FindRangedProjectile(header->ProjectileQualifier);
380 if (Equipped!=IW_NO_EQUIPPED) {
381 EquipItem(Equipped+SLOT_MELEE);
382 } else {
383 EquipItem(SLOT_FIST);
386 gamedata->FreeItem(itm2, item2->ItemResRef, false);
391 // reset Equipped if it is a ranged weapon slot
392 // but not magic weapon slot!
394 UpdateWeaponAnimation();
395 break;
396 case SLOT_EFFECT_HEAD:
397 Owner->SetUsedHelmet("");
398 break;
399 case SLOT_EFFECT_ITEM:
400 //remove the armor type only if this item is responsible for it
401 if ((ieDword) (itm->AnimationType[0]-'1') == Owner->GetBase(IE_ARMOR_TYPE)) {
402 Owner->SetBase(IE_ARMOR_TYPE, 0);
404 break;
406 gamedata->FreeItem(itm, item->ItemResRef, false);
408 /** if resref is "", then destroy ALL items
409 this function can look for stolen, equipped, identified, destructible
410 etc, items. You just have to specify the flags in the bitmask
411 specifying 1 in a bit signifies a requirement */
412 unsigned int Inventory::DestroyItem(const char *resref, ieDword flags, ieDword count)
414 unsigned int destructed = 0;
415 size_t slot = Slots.size();
417 while(slot--) {
418 //ignore the fist slot
419 if (slot == (unsigned int)SLOT_FIST) {
420 continue;
423 CREItem *item = Slots[slot];
424 if (!item) {
425 continue;
427 // here you can simply destroy all items of a specific type
428 if ( (flags&item->Flags)!=flags) {
429 continue;
431 if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
432 continue;
434 //we need to acknowledge that the item was destroyed
435 //use unequip stuff, decrease encumbrance etc,
436 //until that, we simply erase it
437 ieDword removed;
439 if (item->Flags&IE_INV_ITEM_STACKED) {
440 removed=item->Usages[0];
441 if (count && (removed + destructed > count) ) {
442 removed = count - destructed;
443 item = RemoveItem( (unsigned int) slot, removed );
445 else {
446 KillSlot( (unsigned int) slot);
448 } else {
449 removed=1;
450 KillSlot( (unsigned int) slot);
452 delete item;
453 Changed = true;
454 destructed+=removed;
455 if (count && (destructed>=count) )
456 break;
458 if (Changed && Owner && Owner->InParty) core->DisplayConstantString(STR_LOSTITEM, 0xbcefbc);
460 return destructed;
463 CREItem *Inventory::RemoveItem(unsigned int slot, unsigned int count)
465 CREItem *item;
467 if (slot >= Slots.size() ) {
468 InvalidSlot(slot);
469 return NULL;
471 Changed = true;
472 item = Slots[slot];
473 if (!count || !(item->Flags&IE_INV_ITEM_STACKED) ) {
474 KillSlot(slot);
475 return item;
477 if (count >= item->Usages[0]) {
478 KillSlot(slot);
479 return item;
482 CREItem *returned = new CREItem(*item);
483 item->Usages[0]-=count;
484 returned->Usages[0]=(ieWord) count;
485 return returned;
488 //flags set disable item transfer
489 //except for undroppable and equipped, which are opposite (and shouldn't be set)
490 int Inventory::RemoveItem(const char *resref, unsigned int flags, CREItem **res_item)
492 size_t slot = Slots.size();
493 while(slot--) {
494 CREItem *item = Slots[slot];
495 if (!item) {
496 continue;
498 if ( ((flags^(IE_INV_ITEM_UNDROPPABLE|IE_INV_ITEM_EQUIPPED))&item->Flags)!=flags) {
499 continue;
501 if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
502 continue;
504 *res_item=RemoveItem( (unsigned int) slot, 0);
505 return (int) slot;
507 *res_item = NULL;
508 return -1;
511 void Inventory::SetSlotItem(CREItem* item, unsigned int slot)
513 if (slot >= Slots.size() ) {
514 InvalidSlot(slot);
515 return;
517 Changed = true;
518 if (Slots[slot]) {
519 delete Slots[slot];
521 Slots[slot] = item;
523 //update the action bar next time
524 if (Owner->InParty) {
525 core->SetEventFlag( EF_ACTION );
529 int Inventory::AddSlotItem(CREItem* item, int slot, int slottype)
531 int twohanded = item->Flags&IE_INV_ITEM_TWOHANDED;
532 if (slot >= 0) {
533 if ((unsigned)slot >= Slots.size()) {
534 InvalidSlot(slot);
535 return ASI_FAILED;
538 //check for equipping weapons
539 if (WhyCantEquip(slot,twohanded)) {
540 return ASI_FAILED;
543 if (!Slots[slot]) {
544 item->Flags |= IE_INV_ITEM_ACQUIRED;
545 Slots[slot] = item;
546 Changed = true;
547 EquipItem(slot);
548 return ASI_SUCCESS;
551 CREItem *myslot = Slots[slot];
552 if (ItemsAreCompatible( myslot, item )) {
553 //calculate with the max movable stock
554 int chunk = item->Usages[0];
555 int newamount = myslot->Usages[0]+chunk;
556 if (newamount>myslot->StackAmount) {
557 newamount=myslot->StackAmount;
558 chunk = item->Usages[0]-newamount;
560 if (!chunk) {
561 return -1;
563 myslot->Flags |= IE_INV_ITEM_ACQUIRED;
564 myslot->Usages[0] = (ieWord) (myslot->Usages[0] + chunk);
565 item->Usages[0] = (ieWord) (item->Usages[0] - chunk);
566 Changed = true;
567 EquipItem(slot);
568 if (item->Usages[0] == 0) {
569 delete item;
570 return ASI_SUCCESS;
572 return ASI_PARTIAL;
574 return ASI_FAILED;
577 bool which;
578 if (slot==SLOT_AUTOEQUIP) {
579 which=true;
580 } else {
581 which=false;
583 int res = ASI_FAILED;
584 int max = (int) Slots.size();
585 for (int i = 0;i<max;i++) {
586 //never autoequip in the magic slot!
587 if (i==SLOT_MAGIC)
588 continue;
589 if ((i<SLOT_INV || i>LAST_INV)!=which)
590 continue;
591 if (!(core->QuerySlotType(i)&slottype))
592 continue;
593 //the slot has been disabled for this actor
594 if (i>=SLOT_MELEE && i<=LAST_MELEE) {
595 if (Owner->GetQuickSlot(i-SLOT_MELEE)==0xffff) {
596 continue;
599 //this is called by AddSlotItem as well
600 //if (WhyCantEquip(i,twohanded))
601 // continue;
602 int part_res = AddSlotItem (item, i);
603 if (part_res == ASI_SUCCESS) return ASI_SUCCESS;
604 else if (part_res == ASI_PARTIAL) res = ASI_PARTIAL;
607 return res;
610 int Inventory::AddStoreItem(STOItem* item, int action)
612 CREItem *temp;
613 int ret = -1;
615 // item->PurchasedAmount is the number of items bought
616 // (you can still add grouped objects in a single step,
617 // just set up STOItem)
618 while (item->PurchasedAmount) {
619 //the first part of a STOItem is essentially a CREItem
620 temp = new CREItem();
621 memcpy( temp, item, sizeof( CREItem ) );
622 //except the Expired flag
623 temp->Expired=0;
624 if (action==STA_STEAL) {
625 temp->Flags |= IE_INV_ITEM_STOLEN;
627 temp->Flags &= ~IE_INV_ITEM_SELECTED;
629 ret = AddSlotItem( temp, SLOT_ONLYINVENTORY );
630 if (ret != ASI_SUCCESS) {
631 delete temp;
632 break;
634 if (item->InfiniteSupply!=-1) {
635 if (!item->AmountInStock) {
636 break;
638 item->AmountInStock--;
640 item->PurchasedAmount--;
642 return ret;
645 /* could the source item be dropped on the target item to merge them */
646 bool Inventory::ItemsAreCompatible(CREItem* target, CREItem* source) const
648 if (!target) {
649 //this isn't always ok, please check!
650 printMessage("Inventory","Null item encountered by ItemsAreCompatible()",YELLOW);
651 return true;
654 if (!(source->Flags&IE_INV_ITEM_STACKED) ) {
655 return false;
658 if (!strnicmp( target->ItemResRef, source->ItemResRef,8 )) {
659 return true;
661 return false;
664 //depletes a magical item
665 //if flags==0 then magical weapons are not harmed
666 int Inventory::DepleteItem(ieDword flags)
668 for (size_t i = 0; i < Slots.size(); i++) {
669 CREItem *item = Slots[i];
670 if (!item) {
671 continue;
674 //don't harm critical items
675 //don't harm nonmagical items
676 //don't harm indestructible items
677 if ( (item->Flags&(IE_INV_ITEM_CRITICAL|IE_INV_DEPLETABLE)) != IE_INV_DEPLETABLE) {
678 continue;
681 //if flags = 0 then weapons are not depleted
682 if (!flags) {
683 Item *itm = gamedata->GetItem( item->ItemResRef );
684 if (!itm)
685 continue;
686 //if the item is usable in weapon slot, then it is weapon
687 int weapon = core->CanUseItemType( SLOT_WEAPON, itm );
688 gamedata->FreeItem( itm, item->ItemResRef, false );
689 if (weapon)
690 continue;
692 //deplete item
693 item->Usages[0]=0;
694 item->Usages[1]=0;
695 item->Usages[2]=0;
697 return -1;
700 // if flags is 0, skips undroppable items
701 // if flags is IE_INV_ITEM_UNDROPPABLE, doesn't skip undroppable items
702 // TODO: once all callers have been checked, this can be reversed to make more sense
703 int Inventory::FindItem(const char *resref, unsigned int flags) const
705 for (size_t i = 0; i < Slots.size(); i++) {
706 const CREItem *item = Slots[i];
707 if (!item) {
708 continue;
710 if ( (flags^IE_INV_ITEM_UNDROPPABLE) & item->Flags ) {
711 continue;
713 if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
714 continue;
716 return (int) i;
718 return -1;
721 bool Inventory::DropItemAtLocation(unsigned int slot, unsigned int flags, Map *map, const Point &loc)
723 if (slot >= Slots.size()) {
724 return false;
726 //these slots will never 'drop' the item
727 if ((slot==(unsigned int) SLOT_FIST) || (slot==(unsigned int) SLOT_MAGIC)) {
728 return false;
731 CREItem *item = Slots[slot];
732 if (!item) {
733 return false;
735 //if you want to drop undoppable items, simply set IE_INV_UNDROPPABLE
736 //by default, it won't drop them
737 if ( ((flags^IE_INV_ITEM_UNDROPPABLE)&item->Flags)!=flags) {
738 return false;
740 if (!map) {
741 return false;
743 map->AddItemToLocation(loc, item);
744 Changed = true;
745 KillSlot(slot);
746 return true;
749 bool Inventory::DropItemAtLocation(const char *resref, unsigned int flags, Map *map, const Point &loc)
751 bool dropped = false;
753 if (!map) {
754 return false;
757 //this loop is going from start
758 for (size_t i = 0; i < Slots.size(); i++) {
759 //these slots will never 'drop' the item
760 if ((i==(unsigned int) SLOT_FIST) || (i==(unsigned int) SLOT_MAGIC)) {
761 continue;
763 CREItem *item = Slots[i];
764 if (!item) {
765 continue;
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 continue;
772 if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
773 continue;
775 map->AddItemToLocation(loc, item);
776 Changed = true;
777 dropped = true;
778 KillSlot((unsigned int) i);
779 //if it isn't all items then we stop here
780 if (resref[0])
781 break;
783 return dropped;
786 CREItem *Inventory::GetSlotItem(unsigned int slot) const
788 if (slot >= Slots.size() ) {
789 InvalidSlot(slot);
790 return NULL;
792 return Slots[slot];
795 ieDword Inventory::GetItemFlag(unsigned int slot) const
797 const CREItem *item = GetSlotItem(slot);
798 if (!item) {
799 return 0;
801 return item->Flags;
804 bool Inventory::ChangeItemFlag(unsigned int slot, ieDword arg, int op)
806 CREItem *item = GetSlotItem(slot);
807 if (!item) {
808 return false;
810 switch (op) {
811 case BM_SET: item->Flags = arg; break;
812 case BM_OR: item->Flags |= arg; break;
813 case BM_NAND: item->Flags &= ~arg; break;
814 case BM_XOR: item->Flags ^= arg; break;
815 case BM_AND: item->Flags &= arg; break;
817 return true;
820 //this is the low level equipping
821 //all checks have been made previously
822 bool Inventory::EquipItem(unsigned int slot)
824 ITMExtHeader *header;
826 if (!Owner) {
827 //maybe assertion too?
828 return false;
830 CREItem *item = GetSlotItem(slot);
831 if (!item) {
832 return false;
835 int weaponslot;
837 // add effects of an item just being equipped to actor's effect queue
838 int effect = core->QuerySlotEffects( slot );
839 Item *itm = gamedata->GetItem(item->ItemResRef);
840 if (!itm) {
841 printf("Invalid item Equipped: %s Slot: %d\n", item->ItemResRef, slot);
842 return false;
844 switch (effect) {
845 case SLOT_EFFECT_LEFT:
846 //no idea if the offhand weapon has style, or simply the right
847 //hand style is dominant
848 UpdateShieldAnimation(itm);
849 break;
850 case SLOT_EFFECT_MELEE:
851 //if weapon is ranged, then find quarrel for it and equip that
852 slot -= SLOT_MELEE;
853 weaponslot = slot;
854 EquippedHeader = 0;
855 header = itm->GetExtHeader(EquippedHeader);
856 if (header && header->AttackType == ITEM_AT_BOW) {
857 //find the ranged projectile associated with it.
858 slot = FindRangedProjectile(header->ProjectileQualifier);
859 EquippedHeader = itm->GetWeaponHeaderNumber(true);
860 } else if (header && header->AttackType == ITEM_AT_PROJECTILE) {
861 EquippedHeader = itm->GetWeaponHeaderNumber(true);
862 } else {
863 EquippedHeader = itm->GetWeaponHeaderNumber(false);
865 header = itm->GetExtHeader(EquippedHeader);
866 if (header) {
867 SetEquippedSlot(slot, EquippedHeader);
868 if (slot != IW_NO_EQUIPPED) {
869 Owner->SetupQuickSlot(ACT_WEAPON1+weaponslot, slot+SLOT_MELEE, EquippedHeader);
871 effect = 0; // SetEquippedSlot will already call AddSlotEffects
872 UpdateWeaponAnimation();
874 break;
875 case SLOT_EFFECT_MISSILE:
876 //Get the ranged header of the projectile (so we theoretically allow shooting of daggers)
877 EquippedHeader = itm->GetWeaponHeaderNumber(true);
878 header = itm->GetExtHeader(EquippedHeader);
879 if (header) {
880 weaponslot = FindTypedRangedWeapon(header->ProjectileQualifier);
881 if (weaponslot != SLOT_FIST) {
882 weaponslot -= SLOT_MELEE;
883 SetEquippedSlot((ieWordSigned) (slot-SLOT_MELEE), EquippedHeader);
884 //It is unsure if we can have multiple equipping headers for bows/arrow
885 //It is unclear which item's header index should go there
886 Owner->SetupQuickSlot(ACT_WEAPON1+weaponslot, slot, 0);
888 UpdateWeaponAnimation();
890 break;
891 case SLOT_EFFECT_HEAD:
892 Owner->SetUsedHelmet(itm->AnimationType);
893 break;
894 case SLOT_EFFECT_ITEM:
895 //adjusting armour level if needed
897 int l = itm->AnimationType[0]-'1';
898 if (l>=0 && l<=3) {
899 Owner->SetBase(IE_ARMOR_TYPE, l);
900 } else {
901 UpdateShieldAnimation(itm);
904 break;
906 gamedata->FreeItem(itm, item->ItemResRef, false);
907 if (effect) {
908 if (item->Flags & IE_INV_ITEM_CURSED) {
909 item->Flags|=IE_INV_ITEM_UNDROPPABLE;
911 AddSlotEffects( slot );
913 return true;
916 //the removecurse flag will check if it is possible to move the item to the inventory
917 //after a remove curse spell
918 bool Inventory::UnEquipItem(unsigned int slot, bool removecurse)
920 CREItem *item = GetSlotItem(slot);
921 if (!item) {
922 return false;
924 if (removecurse) {
925 if (item->Flags & IE_INV_ITEM_MOVABLE) {
926 item->Flags&=~IE_INV_ITEM_UNDROPPABLE;
928 if (FindCandidateSlot(SLOT_INVENTORY,0,item->ItemResRef)<0) {
929 return false;
932 if (item->Flags & IE_INV_ITEM_UNDROPPABLE) {
933 return false;
935 item->Flags &= ~IE_INV_ITEM_EQUIPPED; //no idea if this is needed, won't hurt
936 return true;
939 // find the projectile
940 // type = 1 - bow
941 // 2 - xbow
942 // 4 - sling
943 //returns equipped code
944 int Inventory::FindRangedProjectile(unsigned int type) const
946 for(int i=SLOT_RANGED;i<=LAST_RANGED;i++) {
947 CREItem *Slot;
949 const Item *itm = GetItemPointer(i, Slot);
950 if (!itm) continue;
951 ITMExtHeader *ext_header = itm->GetExtHeader(0);
952 unsigned int weapontype = 0;
953 if (ext_header) {
954 weapontype = ext_header->ProjectileQualifier;
956 gamedata->FreeItem(itm, Slot->ItemResRef, false);
957 if (weapontype & type) {
958 return i-SLOT_MELEE;
961 return IW_NO_EQUIPPED;
964 // find which bow is attached to the projectile marked by 'Equipped'
965 // returns slotcode
966 int Inventory::FindRangedWeapon() const
968 if (Equipped>=0) return SLOT_FIST;
969 return FindSlotRangedWeapon(Equipped+SLOT_MELEE);
972 int Inventory::FindSlotRangedWeapon(unsigned int slot) const
974 if ((int)slot >= SLOT_MELEE) return SLOT_FIST;
975 CREItem *Slot;
976 Item *itm = GetItemPointer(slot, Slot);
977 if (!itm) return SLOT_FIST;
979 //always look for a ranged header when looking for a projectile/projector
980 ITMExtHeader *ext_header = itm->GetWeaponHeader(true);
981 unsigned int type = 0;
982 if (ext_header) {
983 type = ext_header->ProjectileQualifier;
985 gamedata->FreeItem(itm, Slot->ItemResRef, false);
986 return FindTypedRangedWeapon(type);
990 // find bow for a specific projectile type
991 int Inventory::FindTypedRangedWeapon(unsigned int type) const
993 if (!type) {
994 return SLOT_FIST;
996 for(int i=SLOT_MELEE;i<=LAST_MELEE;i++) {
997 CREItem *Slot;
999 const Item *itm = GetItemPointer(i, Slot);
1000 if (!itm) continue;
1001 //always look for a ranged header when looking for a projectile/projector
1002 ITMExtHeader *ext_header = itm->GetWeaponHeader(true);
1003 int weapontype = 0;
1004 if (ext_header) {
1005 weapontype = ext_header->ProjectileQualifier;
1007 gamedata->FreeItem(itm, Slot->ItemResRef, false);
1008 if (weapontype & type) {
1009 return i;
1012 return SLOT_FIST;
1015 void Inventory::SetHeadSlot(int arg) { SLOT_HEAD=arg; }
1016 void Inventory::SetFistSlot(int arg) { SLOT_FIST=arg; }
1017 void Inventory::SetMagicSlot(int arg) { SLOT_MAGIC=arg; }
1018 void Inventory::SetWeaponSlot(int arg)
1020 if (SLOT_MELEE==-1) {
1021 SLOT_MELEE=arg;
1023 LAST_MELEE=arg;
1026 //ranged slots should be before MELEE slots
1027 void Inventory::SetRangedSlot(int arg)
1029 assert(SLOT_MELEE!=-1);
1030 if (SLOT_RANGED==-1) {
1031 SLOT_RANGED=arg;
1033 LAST_RANGED=arg;
1036 void Inventory::SetQuickSlot(int arg)
1038 if (SLOT_QUICK==-1) {
1039 SLOT_QUICK=arg;
1041 LAST_QUICK=arg;
1044 void Inventory::SetInventorySlot(int arg)
1046 if (SLOT_INV==-1) {
1047 SLOT_INV=arg;
1049 LAST_INV=arg;
1052 //multiple shield slots are allowed
1053 //but in this case they should be interspersed with melee slots
1054 void Inventory::SetShieldSlot(int arg)
1056 if (SLOT_LEFT!=-1) {
1057 assert(SLOT_MELEE+1==SLOT_LEFT);
1058 IWD2=true;
1059 return;
1061 SLOT_LEFT=arg;
1064 int Inventory::GetHeadSlot()
1066 return SLOT_HEAD;
1069 int Inventory::GetFistSlot()
1071 return SLOT_FIST;
1074 int Inventory::GetMagicSlot()
1076 return SLOT_MAGIC;
1079 int Inventory::GetWeaponSlot()
1081 return SLOT_MELEE;
1084 int Inventory::GetQuickSlot()
1086 return SLOT_QUICK;
1089 int Inventory::GetInventorySlot()
1091 return SLOT_INV;
1094 //if shield slot is empty, call again for fist slot!
1095 int Inventory::GetShieldSlot() const
1097 if (IWD2) {
1098 if (Equipped>=0 && Equipped<=3) {
1099 return Equipped*2+SLOT_MELEE+1;
1101 return -1;
1103 return SLOT_LEFT;
1106 int Inventory::GetEquippedSlot() const
1108 if (Equipped == IW_NO_EQUIPPED) {
1109 return SLOT_FIST;
1111 if (IWD2 && Equipped>=0) {
1112 //i've absolutely NO idea what is this 4 (Avenger)
1113 //Equipped should be 0-3 in iWD2, no???
1114 if (Equipped >= 4) {
1115 return SLOT_MELEE;
1117 return Equipped*2+SLOT_MELEE;
1119 return Equipped+SLOT_MELEE;
1122 bool Inventory::SetEquippedSlot(ieWordSigned slotcode, ieWord header)
1124 EquippedHeader = header;
1126 //doesn't work if magic slot is used, refresh the magic slot just in case
1127 if (HasItemInSlot("",SLOT_MAGIC) && (slotcode!=SLOT_MAGIC-SLOT_MELEE)) {
1128 Equipped = SLOT_MAGIC-SLOT_MELEE;
1129 UpdateWeaponAnimation();
1130 return false;
1133 //if it is an illegal code, make it fist
1134 if ((size_t) (slotcode+SLOT_MELEE)>Slots.size()) {
1135 slotcode=IW_NO_EQUIPPED;
1138 //unequipping (fist slot will be used now)
1139 if (slotcode == IW_NO_EQUIPPED || !HasItemInSlot("",slotcode+SLOT_MELEE)) {
1140 if (Equipped != IW_NO_EQUIPPED) {
1141 RemoveSlotEffects( SLOT_MELEE+Equipped);
1143 Equipped = IW_NO_EQUIPPED;
1144 //fist slot equipping effects
1145 AddSlotEffects(SLOT_FIST);
1146 UpdateWeaponAnimation();
1147 return true;
1150 //equipping a weapon, but remove its effects first
1151 if (Equipped != IW_NO_EQUIPPED) {
1152 RemoveSlotEffects( SLOT_MELEE+Equipped);
1155 Equipped = slotcode;
1156 int effects = core->QuerySlotEffects( SLOT_MELEE+Equipped );
1157 if (effects) {
1158 CREItem* item = GetSlotItem(SLOT_MELEE+Equipped);
1159 item->Flags|=IE_INV_ITEM_EQUIPPED;
1160 if (item->Flags & IE_INV_ITEM_CURSED) {
1161 item->Flags|=IE_INV_ITEM_UNDROPPABLE;
1163 AddSlotEffects( SLOT_MELEE+Equipped);
1165 UpdateWeaponAnimation();
1166 return true;
1169 int Inventory::GetEquipped() const
1171 return Equipped;
1174 int Inventory::GetEquippedHeader() const
1176 return EquippedHeader;
1179 //returns the fist weapon if there is nothing else
1180 //This will return the actual weapon, I mean the bow in the case of bow+arrow combination
1181 CREItem *Inventory::GetUsedWeapon(bool leftorright, int &slot) const
1183 CREItem *ret;
1185 if (SLOT_MAGIC!=-1) {
1186 slot = SLOT_MAGIC;
1187 ret = GetSlotItem(slot);
1188 if (ret && ret->ItemResRef[0]) {
1189 return ret;
1192 if (leftorright) {
1193 //no shield slot
1194 slot = GetShieldSlot();
1195 if (slot>=0) {
1196 ret = GetSlotItem(slot);
1197 if (ret) {
1198 return ret;
1199 } else {
1200 //we don't want to return fist for shield slot
1201 return NULL;
1205 slot = GetEquippedSlot();
1206 if((core->QuerySlotEffects(slot) & SLOT_EFFECT_MISSILE) == SLOT_EFFECT_MISSILE) {
1207 slot = FindRangedWeapon();
1209 ret = GetSlotItem(slot);
1210 if (!ret) {
1211 //return fist weapon
1212 slot = SLOT_FIST;
1213 ret = GetSlotItem(slot);
1215 return ret;
1218 // Returns index of first empty slot or slot with the same
1219 // item and not full stack. On fail returns -1
1220 // Can be used to check for full inventory
1221 int Inventory::FindCandidateSlot(int slottype, size_t first_slot, const char *resref)
1223 if (first_slot >= Slots.size())
1224 return -1;
1226 for (size_t i = first_slot; i < Slots.size(); i++) {
1227 if (!(core->QuerySlotType( (unsigned int) i) & slottype) ) {
1228 continue;
1231 CREItem *item = Slots[i];
1233 if (!item) {
1234 return (int) i; //this is a good empty slot
1236 if (!resref) {
1237 continue;
1239 if (!(item->Flags&IE_INV_ITEM_STACKED) ) {
1240 continue;
1242 if (strnicmp( item->ItemResRef, resref, 8 )!=0) {
1243 continue;
1245 // check if the item fits in this slot, we use the cached
1246 // stackamount value
1247 if (item->Usages[0]<item->StackAmount) {
1248 return (int) i;
1252 return -1;
1255 void Inventory::AddSlotItemRes(const ieResRef ItemResRef, int SlotID, int Charge0, int Charge1, int Charge2)
1257 CREItem *TmpItem = new CREItem();
1258 strnlwrcpy(TmpItem->ItemResRef, ItemResRef, 8);
1259 TmpItem->Expired=0;
1260 TmpItem->Usages[0]=(ieWord) Charge0;
1261 TmpItem->Usages[1]=(ieWord) Charge1;
1262 TmpItem->Usages[2]=(ieWord) Charge2;
1263 TmpItem->Flags=0;
1264 if (core->ResolveRandomItem(TmpItem) && gamedata->Exists(TmpItem->ItemResRef, IE_ITM_CLASS_ID)) {
1265 AddSlotItem( TmpItem, SlotID );
1266 } else {
1267 delete TmpItem;
1269 CalculateWeight();
1272 void Inventory::SetSlotItemRes(const ieResRef ItemResRef, int SlotID, int Charge0, int Charge1, int Charge2)
1274 if(ItemResRef[0]) {
1275 CREItem *TmpItem = new CREItem();
1276 strnlwrcpy(TmpItem->ItemResRef, ItemResRef, 8);
1277 TmpItem->Expired=0;
1278 TmpItem->Usages[0]=(ieWord) Charge0;
1279 TmpItem->Usages[1]=(ieWord) Charge1;
1280 TmpItem->Usages[2]=(ieWord) Charge2;
1281 TmpItem->Flags=0;
1282 if (core->ResolveRandomItem(TmpItem) && gamedata->Exists(TmpItem->ItemResRef, IE_ITM_CLASS_ID)) {
1283 SetSlotItem( TmpItem, SlotID );
1284 } else {
1285 delete TmpItem;
1287 } else {
1288 //if the item isn't creatable, we still destroy the old item
1289 KillSlot( SlotID );
1291 CalculateWeight();
1294 void Inventory::BreakItemSlot(ieDword slot)
1296 ieResRef newItem;
1297 CREItem *Slot;
1299 const Item *itm = GetItemPointer(slot, Slot);
1300 if (!itm) return;
1301 //if it is the magic weapon slot, don't break it, just remove it, because it couldn't be removed
1302 if(slot ==(unsigned int) SLOT_MAGIC) {
1303 newItem[0]=0;
1304 } else {
1305 memcpy(newItem, itm->ReplacementItem,sizeof(newItem) );
1307 gamedata->FreeItem( itm, Slot->ItemResRef, true );
1308 //this depends on setslotitemres using setslotitem
1309 SetSlotItemRes(newItem, slot, 0,0,0);
1312 void Inventory::dump()
1314 printf( "INVENTORY:\n" );
1315 for (unsigned int i = 0; i < Slots.size(); i++) {
1316 CREItem* itm = Slots[i];
1318 if (!itm) {
1319 continue;
1322 printf ( "%2u: %8.8s - (%d %d %d) %x Wt: %d x %dLb\n", i, itm->ItemResRef, itm->Usages[0], itm->Usages[1], itm->Usages[2], itm->Flags, itm->StackAmount, itm->Weight );
1325 printf( "Equipped: %d\n", Equipped );
1326 Changed = true;
1327 CalculateWeight();
1328 printf( "Total weight: %d\n", Weight );
1331 void Inventory::EquipBestWeapon(int flags)
1333 int i;
1334 int damage = -1;
1335 ieDword best_slot = SLOT_FIST;
1336 ITMExtHeader *header;
1337 CREItem *Slot;
1338 char AnimationType[2]={0,0};
1339 ieWord MeleeAnimation[3]={100,0,0};
1341 //cannot change equipment when holding magic weapons
1342 if (Equipped == SLOT_MAGIC-SLOT_MELEE) {
1343 return;
1346 if (flags&EQUIP_RANGED) {
1347 for(i=SLOT_RANGED;i<LAST_RANGED;i++) {
1348 const Item *itm = GetItemPointer(i, Slot);
1349 if (!itm) continue;
1350 //best ranged
1351 int tmp = itm->GetDamagePotential(true, header);
1352 if (tmp>damage) {
1353 best_slot = i;
1354 damage = tmp;
1355 memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1356 memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
1358 gamedata->FreeItem( itm, Slot->ItemResRef, false );
1361 //ranged melee weapons like throwing daggers (not bows!)
1362 for(i=SLOT_MELEE;i<LAST_MELEE;i++) {
1363 const Item *itm = GetItemPointer(i, Slot);
1364 if (!itm) continue;
1365 //best ranged
1366 int tmp = itm->GetDamagePotential(true, header);
1367 if (tmp>damage) {
1368 best_slot = i;
1369 damage = tmp;
1370 memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1371 memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
1373 gamedata->FreeItem( itm, Slot->ItemResRef, false );
1377 if (flags&EQUIP_MELEE) {
1378 for(i=SLOT_MELEE;i<LAST_MELEE;i++) {
1379 const Item *itm = GetItemPointer(i, Slot);
1380 if (!itm) continue;
1381 //the Slot flag is enough for this
1382 //though we need animation type/damagepotential anyway
1383 if (Slot->Flags&IE_INV_ITEM_BOW) continue;
1384 //best melee
1385 int tmp = itm->GetDamagePotential(false, header);
1386 if (tmp>damage) {
1387 best_slot = i;
1388 damage = tmp;
1389 memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1390 memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
1392 gamedata->FreeItem( itm, Slot->ItemResRef, false );
1396 EquipItem(best_slot);
1397 UpdateWeaponAnimation();
1400 #define ID_NONEED 0 //id is not important
1401 #define ID_NEED 1 //id is important
1402 #define ID_NO 2 //shouldn't id
1404 /* returns true if there are more item usages not fitting in given array */
1405 bool Inventory::GetEquipmentInfo(ItemExtHeader *array, int startindex, int count)
1407 int pos = 0;
1408 int actual = 0;
1409 memset(array, 0, count * sizeof(ItemExtHeader) );
1410 for(unsigned int idx=0;idx<Slots.size();idx++) {
1411 if (!core->QuerySlotEffects(idx)) {
1412 continue;
1414 CREItem *slot;
1416 const Item *itm = GetItemPointer(idx, slot);
1417 if (!itm) {
1418 continue;
1420 for(int ehc=0;ehc<itm->ExtHeaderCount;ehc++) {
1421 ITMExtHeader *ext_header = itm->ext_headers+ehc;
1422 if (ext_header->Location!=ITEM_LOC_EQUIPMENT) {
1423 continue;
1425 //skipping if we cannot use the item
1426 int idreq1 = (slot->Flags&IE_INV_ITEM_IDENTIFIED);
1427 int idreq2 = ext_header->IDReq;
1428 switch (idreq2) {
1429 case ID_NO:
1430 if (idreq1) continue;
1431 break;
1432 case ID_NEED:
1433 if (!idreq1) continue;
1434 default:;
1437 actual++;
1438 if (actual>startindex) {
1440 //store the item, return if we can't store more
1441 if (!count) {
1442 gamedata->FreeItem(itm, slot->ItemResRef, false);
1443 return true;
1445 count--;
1446 memcpy(array[pos].itemname, slot->ItemResRef, sizeof(ieResRef) );
1447 array[pos].slot = idx;
1448 array[pos].headerindex = ehc;
1449 int slen = ((char *) &(array[pos].itemname)) -((char *) &(array[pos].AttackType));
1450 memcpy(&(array[pos].AttackType), &(ext_header->AttackType), slen);
1451 if (ext_header->Charges) {
1452 //don't modify ehc, it is a counter
1453 if (ehc>=CHARGE_COUNTERS) {
1454 array[pos].Charges=slot->Usages[0];
1455 } else {
1456 array[pos].Charges=slot->Usages[ehc];
1458 } else {
1459 array[pos].Charges=0xffff;
1461 pos++;
1464 gamedata->FreeItem(itm, slot->ItemResRef, false);
1467 return false;
1470 //The slot index value is optional, if you supply it,
1471 // then ItemExcl will be returned as if the item was already unequipped
1472 ieDword Inventory::GetEquipExclusion(int index) const
1474 if (index<0) {
1475 return ItemExcl;
1477 CREItem *slot;
1478 const Item *itm = GetItemPointer(index, slot);
1479 if (!itm) {
1480 return ItemExcl;
1482 ieDword ret = ItemExcl&~itm->ItemExcl;
1483 gamedata->FreeItem(itm, slot->ItemResRef, false);
1484 return ret;
1487 void Inventory::UpdateShieldAnimation(Item *it)
1489 char AnimationType[2]={0,0};
1490 int WeaponType = -1;
1492 if (it) {
1493 memcpy(AnimationType, it->AnimationType, 2);
1494 if (core->CanUseItemType(SLOT_WEAPON, it))
1495 WeaponType = IE_ANI_WEAPON_2W;
1496 else
1497 WeaponType = IE_ANI_WEAPON_1H;
1498 } else {
1499 WeaponType = IE_ANI_WEAPON_1H;
1501 Owner->SetUsedShield(AnimationType, WeaponType);
1504 void Inventory::UpdateWeaponAnimation()
1506 int slot = GetEquippedSlot();
1507 int effect = core->QuerySlotEffects( slot );
1508 if (effect == SLOT_EFFECT_MISSILE) {
1509 // ranged weapon
1510 slot = FindRangedWeapon();
1512 int WeaponType = -1;
1514 char AnimationType[2]={0,0};
1515 ieWord MeleeAnimation[3]={100,0,0};
1516 CREItem *Slot;
1518 // TODO: fix bows?
1520 ITMExtHeader *header = 0;
1521 const Item *itm = GetItemPointer(slot, Slot);
1522 if (itm) {
1523 itm->GetDamagePotential(false, header);
1524 memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1525 //for twohanded flag, you don't need itm
1526 if (Slot->Flags & IE_INV_ITEM_TWOHANDED)
1527 WeaponType = IE_ANI_WEAPON_2H;
1528 else {
1530 // Examine shield slot to check if we're using two weapons
1531 // TODO: for consistency, use same Item* access method as above
1532 bool twoweapon = false;
1533 int slot = GetShieldSlot();
1534 CREItem* si = NULL;
1535 if (slot>0) {
1536 si = GetSlotItem( (ieDword) slot );
1538 if (si) {
1539 Item* it = gamedata->GetItem(si->ItemResRef);
1540 if (core->CanUseItemType(SLOT_WEAPON, it))
1541 twoweapon = true;
1542 gamedata->FreeItem(it, si->ItemResRef, false);
1545 if (twoweapon)
1546 WeaponType = IE_ANI_WEAPON_2W;
1547 else
1548 WeaponType = IE_ANI_WEAPON_1H;
1552 if (header)
1553 memcpy(MeleeAnimation,header->MeleeAnimation,
1554 sizeof(MeleeAnimation) );
1555 if (itm)
1556 gamedata->FreeItem( itm, Slot->ItemResRef, false );
1557 Owner->SetUsedWeapon(AnimationType, MeleeAnimation, WeaponType);
1560 //this function will also check disabled slots (if that feature will be imped)
1561 bool Inventory::IsSlotBlocked(int slot) const
1563 if (slot<SLOT_MELEE) return false;
1564 if (slot>LAST_MELEE) return false;
1565 int otherslot;
1566 if (IWD2) {
1567 otherslot = slot+1;
1568 } else {
1569 otherslot = SLOT_LEFT;
1571 return HasItemInSlot("",otherslot);
1574 inline bool Inventory::TwoHandedInSlot(int slot) const
1576 CREItem *item;
1578 item = GetSlotItem(slot);
1579 if (!item) return false;
1580 if (item->Flags&IE_INV_ITEM_TWOHANDED) {
1581 return true;
1583 return false;
1586 int Inventory::WhyCantEquip(int slot, int twohanded) const
1588 // check only for hand slots
1589 if ((slot<SLOT_MELEE || slot>LAST_MELEE) && (slot != SLOT_LEFT) ) {
1590 return 0;
1593 //magic items have the highest priority
1594 if ( HasItemInSlot("", SLOT_MAGIC)) {
1595 //magic weapon is in use
1596 return STR_MAGICWEAPON;
1599 //can't equip in shield slot if a weapon slot is twohanded
1600 for (int i=SLOT_MELEE; i<=LAST_MELEE;i++) {
1601 //see GetShieldSlot
1602 int otherslot;
1603 if (IWD2) {
1604 otherslot = ++i;
1605 } else {
1606 otherslot = SLOT_LEFT;
1608 if (slot==otherslot) {
1609 if (TwoHandedInSlot(i)) {
1610 return STR_TWOHANDED_USED;
1615 if (twohanded) {
1616 if (IWD2) {
1617 if (slot>=SLOT_MELEE&&slot<=LAST_MELEE && (slot&1) ) {
1618 return STR_NOT_IN_OFFHAND;
1620 } else {
1621 if (slot==SLOT_LEFT) {
1622 return STR_NOT_IN_OFFHAND;
1625 if (IsSlotBlocked(slot)) {
1626 //cannot equip two handed while shield slot is in use?
1627 return STR_OFFHAND_USED;
1630 return 0;
1633 //recharge items on rest, if rest was partial, recharge only 'hours'
1634 //if this latter functionality is unwanted, then simply don't recharge if
1635 //hours != 0
1636 void Inventory::ChargeAllItems(int hours)
1638 //this loop is going from start
1639 for (size_t i = 0; i < Slots.size(); i++) {
1640 CREItem *item = Slots[i];
1641 if (!item) {
1642 continue;
1645 Item *itm = gamedata->GetItem( item->ItemResRef );
1646 if (!itm)
1647 continue;
1648 for(int h=0;h<CHARGE_COUNTERS;h++) {
1649 ITMExtHeader *header = itm->GetExtHeader(h);
1650 if (header && (header->RechargeFlags&IE_ITEM_RECHARGE)) {
1651 unsigned short add = header->Charges;
1652 if (hours && add>hours) add=hours;
1653 add+=item->Usages[h];
1654 if(add>header->Charges) add=header->Charges;
1655 item->Usages[h]=add;
1658 gamedata->FreeItem( itm, item->ItemResRef, false );