made ProvidesCriticalAversion more like tobex
[gemrb.git] / gemrb / core / Inventory.cpp
blobadbfed8ff8d25b25352c3dede083a8ead9bdb0b1
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 "DisplayMessage.h"
30 #include "Game.h"
31 #include "GameData.h"
32 #include "Interface.h"
33 #include "Item.h"
34 #include "ScriptEngine.h"
35 #include "Scriptable/Actor.h"
37 #include <cstdio>
39 static int SLOT_HEAD = -1;
40 static int SLOT_MAGIC = -1;
41 static int SLOT_FIST = -1;
42 static int SLOT_MELEE = -1;
43 static int LAST_MELEE = -1;
44 static int SLOT_RANGED = -1;
45 static int LAST_RANGED = -1;
46 static int SLOT_QUICK = -1;
47 static int LAST_QUICK = -1;
48 static int SLOT_INV = -1;
49 static int LAST_INV = -1;
50 static int SLOT_LEFT = -1;
52 //IWD2 style slots
53 static bool IWD2 = false;
54 static int MagicBit = 0;
56 static void InvalidSlot(int slot)
58 printMessage("Inventory"," ",LIGHT_RED);
59 printf("Invalid slot: %d!\n",slot);
60 abort();
63 //This inline function returns both an item pointer and the slot data.
64 //slot is a dynamic slot number (SLOT_*)
65 inline Item *Inventory::GetItemPointer(ieDword slot, CREItem *&item) const
67 item = GetSlotItem(slot);
68 if (!item) return NULL;
69 if (!item->ItemResRef[0]) return NULL;
70 return gamedata->GetItem(item->ItemResRef);
73 void Inventory::Init(int mb)
75 SLOT_MAGIC=-1;
76 SLOT_FIST=-1;
77 SLOT_MELEE=-1;
78 LAST_MELEE=-1;
79 SLOT_RANGED=-1;
80 LAST_RANGED=-1;
81 SLOT_QUICK=-1;
82 LAST_QUICK=-1;
83 SLOT_LEFT=-1;
84 //TODO: set this correctly
85 IWD2 = false;
86 MagicBit = mb;
89 Inventory::Inventory()
91 Owner = NULL;
92 InventoryType = INVENTORY_HEAP;
93 Changed = false;
94 Weight = 0;
95 Equipped = IW_NO_EQUIPPED;
96 EquippedHeader = 0;
97 ItemExcl = 0;
98 memset(ItemTypes, 0, sizeof(ItemTypes));
101 Inventory::~Inventory()
103 for (size_t i = 0; i < Slots.size(); i++) {
104 if (Slots[i]) {
105 delete( Slots[i] );
106 Slots[i] = NULL;
111 CREItem *Inventory::GetItem(unsigned int slot)
113 if (slot >= Slots.size() ) {
114 InvalidSlot(slot);
115 return NULL;
117 CREItem *item = Slots[slot];
118 Slots.erase(Slots.begin()+slot);
119 return item;
122 //This hack sets the charge counters for non-rechargeable items,
123 //if their charge is zero
124 inline void HackCharges(CREItem *item)
126 Item *itm = gamedata->GetItem( item->ItemResRef );
127 if (itm) {
128 for (int i=0;i<3;i++) {
129 if (item->Usages[i]) {
130 continue;
132 ITMExtHeader *h = itm->GetExtHeader(i);
133 if (h && !(h->RechargeFlags&IE_ITEM_RECHARGE)) {
134 //HACK: the original (bg2) allows for 0 charged gems
135 if (h->Charges) {
136 item->Usages[i] = h->Charges;
137 } else {
138 item->Usages[i] = 1;
142 gamedata->FreeItem( itm, item->ItemResRef, false );
146 void Inventory::AddItem(CREItem *item)
148 if (!item) return; //invalid items get no slot
149 Slots.push_back(item);
150 HackCharges(item);
151 //Changed=true; //probably not needed, chests got no encumbrance
154 void Inventory::CalculateWeight()
156 if (!Changed) {
157 return;
159 Weight = 0;
160 for (size_t i = 0; i < Slots.size(); i++) {
161 CREItem *slot = Slots[i];
162 if (!slot) {
163 continue;
165 if (slot->Weight == -1) {
166 Item *itm = gamedata->GetItem( slot->ItemResRef );
167 if (itm) {
168 //simply adding the item flags to the slot
169 slot->Flags |= (itm->Flags<<8);
170 //some slot flags might be affected by the item flags
171 if (!(slot->Flags & IE_INV_ITEM_CRITICAL)) {
172 slot->Flags |= IE_INV_ITEM_DESTRUCTIBLE;
174 //this is for converting IWD items magic flag
175 if (MagicBit) {
176 if (slot->Flags&IE_INV_ITEM_UNDROPPABLE) {
177 slot->Flags|=IE_INV_ITEM_MAGICAL;
178 slot->Flags&=~IE_INV_ITEM_UNDROPPABLE;
182 if (!(slot->Flags & IE_INV_ITEM_MOVABLE)) {
183 slot->Flags |= IE_INV_ITEM_UNDROPPABLE;
186 if (slot->Flags & IE_INV_ITEM_STOLEN2) {
187 slot->Flags |= IE_INV_ITEM_STOLEN;
190 //auto identify basic items
191 if (!itm->LoreToID) {
192 slot->Flags |= IE_INV_ITEM_IDENTIFIED;
195 //if item is stacked mark it as so
196 if (itm->StackAmount) {
197 slot->Flags |= IE_INV_ITEM_STACKED;
200 slot->Weight = itm->Weight;
201 slot->StackAmount = itm->StackAmount;
202 gamedata->FreeItem( itm, slot->ItemResRef, false );
204 else {
205 printMessage( "Inventory", " ", LIGHT_RED);
206 printf("Invalid item: %s!\n", slot->ItemResRef);
207 slot->Weight = 0;
209 } else {
210 slot->Flags &= ~IE_INV_ITEM_ACQUIRED;
212 if (slot->Weight > 0) {
213 Weight += slot->Weight * ((slot->Usages[0] && slot->StackAmount > 1) ? slot->Usages[0] : 1);
216 Changed = false;
219 void Inventory::AddSlotEffects(ieDword index)
221 CREItem* slot;
223 const Item *itm = GetItemPointer(index, slot);
224 if (!itm) {
225 printMessage("Inventory","Invalid item equipped...\n",LIGHT_RED);
226 return;
228 ItemExcl|=itm->ItemExcl;
229 ieDword pos = itm->ItemType/32;
230 ieDword bit = itm->ItemType%32;
231 if (pos<4) {
232 ItemTypes[pos]|=1<<bit;
235 ieWord gradient = itm->GetWieldedGradient();
236 if (gradient!=0xffff) {
237 Owner->SetBase(IE_COLORS, gradient);
240 //get the equipping effects
241 EffectQueue *eqfx = itm->GetEffectBlock(-1, index, 0);
242 gamedata->FreeItem( itm, slot->ItemResRef, false );
244 Owner->RefreshEffects(eqfx);
245 //call gui for possible paperdoll animation changes
246 if (Owner->InParty) {
247 core->SetEventFlag(EF_UPDATEANIM);
251 //no need to know the item effects 'personally', the equipping slot
252 //is stored in them
253 void Inventory::RemoveSlotEffects(ieDword index)
255 Owner->fxqueue.RemoveEquippingEffects(index);
256 Owner->RefreshEffects(NULL);
257 //call gui for possible paperdoll animation changes
258 if (Owner->InParty) {
259 core->SetEventFlag(EF_UPDATEANIM);
263 void Inventory::SetInventoryType(int arg)
265 InventoryType = arg;
268 void Inventory::SetSlotCount(unsigned int size)
270 if (Slots.size()) {
271 printf("Inventory size changed???\n");
272 //we don't allow reassignment,
273 //if you want this, delete the previous Slots here
274 abort();
276 Slots.assign((size_t) size, NULL);
279 /** if you supply a "" string, then it checks if the slot is empty */
280 bool Inventory::HasItemInSlot(const char *resref, unsigned int slot) const
282 if (slot >= Slots.size()) {
283 return false;
285 const CREItem *item = Slots[slot];
286 if (!item) {
287 return false;
289 if (!resref[0]) {
290 return true;
292 if (strnicmp( item->ItemResRef, resref, 8 )==0) {
293 return true;
295 return false;
298 bool Inventory::HasItemType(ieDword type) const
300 if (type>255) return false;
301 int idx = type/32;
302 int bit = type%32;
303 return (ItemTypes[idx] & (1<<bit) )!=0;
306 /** counts the items in the inventory, if stacks == 1 then stacks are
307 accounted for their heap size */
308 int Inventory::CountItems(const char *resref, bool stacks) const
310 int count = 0;
311 size_t slot = Slots.size();
312 while(slot--) {
313 const CREItem *item = Slots[slot];
314 if (!item) {
315 continue;
317 if (resref && resref[0]) {
318 if (!strnicmp(resref, item->ItemResRef, 8) )
319 continue;
321 if (stacks && (item->Flags&IE_INV_ITEM_STACKED) ) {
322 count+=item->Usages[0];
324 else {
325 count++;
328 return count;
331 /** this function can look for stolen, equipped, identified, destructible
332 etc, items. You just have to specify the flags in the bitmask
333 specifying 1 in a bit signifies a requirement */
334 bool Inventory::HasItem(const char *resref, ieDword flags) const
336 size_t slot = Slots.size();
337 while(slot--) {
338 const CREItem *item = Slots[slot];
339 if (!item) {
340 continue;
342 if ( (flags&item->Flags)!=flags) {
343 continue;
345 if (resref[0] && strnicmp(item->ItemResRef, resref,8) ) {
346 continue;
348 return true;
350 return false;
353 void Inventory::KillSlot(unsigned int index)
355 if (InventoryType==INVENTORY_HEAP) {
356 Slots.erase(Slots.begin()+index);
357 return;
359 CREItem *item = Slots[index];
360 if (!item) {
361 return;
364 //the used up item vanishes from the quickslot bar
365 if (Owner->InParty) {
366 core->SetEventFlag( EF_ACTION );
369 Slots[index] = NULL;
370 int effect = core->QuerySlotEffects( index );
371 if (!effect) {
372 return;
374 RemoveSlotEffects( index );
375 Item *itm = gamedata->GetItem(item->ItemResRef);
376 //this cannot happen, but stuff happens!
377 if (!itm) {
378 return;
380 ItemExcl &= ~itm->ItemExcl;
382 switch (effect) {
383 case SLOT_EFFECT_LEFT:
384 UpdateShieldAnimation(0);
385 break;
386 case SLOT_EFFECT_MISSILE:
387 //getting a new projectile of the same type
388 if (Equipped + SLOT_MELEE == (int) index) {
389 if (Equipped < 0) {
390 //always get the projectile weapon header (this quiver was equipped)
391 ITMExtHeader *header = itm->GetWeaponHeader(true);
392 Equipped = FindRangedProjectile(header->ProjectileQualifier);
393 if (Equipped!=IW_NO_EQUIPPED) {
394 EquipItem(Equipped+SLOT_MELEE);
395 } else {
396 EquipItem(SLOT_FIST);
400 UpdateWeaponAnimation();
401 break;
402 case SLOT_EFFECT_MELEE:
403 // reset Equipped if it was the removed item
404 if (Equipped+SLOT_MELEE == (int)index)
405 Equipped = IW_NO_EQUIPPED;
406 else if (Equipped < 0) {
407 //always get the projectile weapon header (this is a bow, because Equipped is negative)
408 ITMExtHeader *header = itm->GetWeaponHeader(true);
409 if (header) {
410 //find the equipped type
411 int type = header->ProjectileQualifier;
412 int weaponslot = FindTypedRangedWeapon(type);
413 CREItem *item2 = Slots[weaponslot];
414 if (item2) {
415 Item *itm2 = gamedata->GetItem(item2->ItemResRef);
416 if (itm2) {
417 if (type == header->ProjectileQualifier) {
418 Equipped = FindRangedProjectile(header->ProjectileQualifier);
419 if (Equipped!=IW_NO_EQUIPPED) {
420 EquipItem(Equipped+SLOT_MELEE);
421 } else {
422 EquipItem(SLOT_FIST);
425 gamedata->FreeItem(itm2, item2->ItemResRef, false);
430 // reset Equipped if it is a ranged weapon slot
431 // but not magic weapon slot!
433 UpdateWeaponAnimation();
434 break;
435 case SLOT_EFFECT_HEAD:
436 Owner->SetUsedHelmet("");
437 break;
438 case SLOT_EFFECT_ITEM:
439 //remove the armor type only if this item is responsible for it
440 if ((ieDword) (itm->AnimationType[0]-'1') == Owner->GetBase(IE_ARMOR_TYPE)) {
441 Owner->SetBase(IE_ARMOR_TYPE, 0);
443 break;
445 gamedata->FreeItem(itm, item->ItemResRef, false);
447 /** if resref is "", then destroy ALL items
448 this function can look for stolen, equipped, identified, destructible
449 etc, items. You just have to specify the flags in the bitmask
450 specifying 1 in a bit signifies a requirement */
451 unsigned int Inventory::DestroyItem(const char *resref, ieDword flags, ieDword count)
453 unsigned int destructed = 0;
454 size_t slot = Slots.size();
456 while(slot--) {
457 //ignore the fist slot
458 if (slot == (unsigned int)SLOT_FIST) {
459 continue;
462 CREItem *item = Slots[slot];
463 if (!item) {
464 continue;
466 // here you can simply destroy all items of a specific type
467 if ( (flags&item->Flags)!=flags) {
468 continue;
470 if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
471 continue;
473 //we need to acknowledge that the item was destroyed
474 //use unequip stuff, decrease encumbrance etc,
475 //until that, we simply erase it
476 ieDword removed;
478 if (item->Flags&IE_INV_ITEM_STACKED) {
479 removed=item->Usages[0];
480 if (count && (removed + destructed > count) ) {
481 removed = count - destructed;
482 item = RemoveItem( (unsigned int) slot, removed );
484 else {
485 KillSlot( (unsigned int) slot);
487 } else {
488 removed=1;
489 KillSlot( (unsigned int) slot);
491 delete item;
492 Changed = true;
493 destructed+=removed;
494 if (count && (destructed>=count) )
495 break;
497 if (Changed && Owner && Owner->InParty) displaymsg->DisplayConstantString(STR_LOSTITEM, 0xbcefbc);
499 return destructed;
502 CREItem *Inventory::RemoveItem(unsigned int slot, unsigned int count)
504 CREItem *item;
506 if (slot >= Slots.size() ) {
507 InvalidSlot(slot);
508 return NULL;
510 Changed = true;
511 item = Slots[slot];
513 if (!item) {
514 return NULL;
517 if (!count || !(item->Flags&IE_INV_ITEM_STACKED) ) {
518 KillSlot(slot);
519 return item;
521 if (count >= item->Usages[0]) {
522 KillSlot(slot);
523 return item;
526 CREItem *returned = new CREItem(*item);
527 item->Usages[0]-=count;
528 returned->Usages[0]=(ieWord) count;
529 return returned;
532 //flags set disable item transfer
533 //except for undroppable and equipped, which are opposite (and shouldn't be set)
534 int Inventory::RemoveItem(const char *resref, unsigned int flags, CREItem **res_item)
536 size_t slot = Slots.size();
537 while(slot--) {
538 CREItem *item = Slots[slot];
539 if (!item) {
540 continue;
542 unsigned int mask = (flags^(IE_INV_ITEM_UNDROPPABLE|IE_INV_ITEM_EQUIPPED));
543 if (flags && (mask&item->Flags)==flags) {
544 continue;
546 if (!flags && (mask&item->Flags)!=0) {
547 continue;
549 if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
550 continue;
552 *res_item=RemoveItem( (unsigned int) slot, 0);
553 return (int) slot;
555 *res_item = NULL;
556 return -1;
559 void Inventory::SetSlotItem(CREItem* item, unsigned int slot)
561 if (slot >= Slots.size() ) {
562 InvalidSlot(slot);
563 return;
565 Changed = true;
566 if (Slots[slot]) {
567 delete Slots[slot];
570 HackCharges(item);
572 Slots[slot] = item;
574 //update the action bar next time
575 if (Owner->InParty) {
576 core->SetEventFlag( EF_ACTION );
580 int Inventory::AddSlotItem(CREItem* item, int slot, int slottype)
582 int twohanded = item->Flags&IE_INV_ITEM_TWOHANDED;
583 if (slot >= 0) {
584 if ((unsigned)slot >= Slots.size()) {
585 InvalidSlot(slot);
586 return ASI_FAILED;
589 //check for equipping weapons
590 if (WhyCantEquip(slot,twohanded)) {
591 return ASI_FAILED;
594 if (!Slots[slot]) {
595 item->Flags |= IE_INV_ITEM_ACQUIRED;
596 SetSlotItem(item, slot);
597 EquipItem(slot);
598 return ASI_SUCCESS;
601 CREItem *myslot = Slots[slot];
602 if (ItemsAreCompatible( myslot, item )) {
603 //calculate with the max movable stock
604 int chunk = item->Usages[0];
605 int newamount = myslot->Usages[0]+chunk;
606 if (newamount>myslot->StackAmount) {
607 newamount=myslot->StackAmount;
608 chunk = item->Usages[0]-newamount;
610 if (!chunk) {
611 return -1;
613 myslot->Flags |= IE_INV_ITEM_ACQUIRED;
614 myslot->Usages[0] = (ieWord) (myslot->Usages[0] + chunk);
615 item->Usages[0] = (ieWord) (item->Usages[0] - chunk);
616 Changed = true;
617 EquipItem(slot);
618 if (item->Usages[0] == 0) {
619 delete item;
620 return ASI_SUCCESS;
622 return ASI_PARTIAL;
624 return ASI_FAILED;
627 bool which;
628 if (slot==SLOT_AUTOEQUIP) {
629 which=true;
630 } else {
631 which=false;
633 int res = ASI_FAILED;
634 int max = (int) Slots.size();
635 for (int i = 0;i<max;i++) {
636 //never autoequip in the magic slot!
637 if (i==SLOT_MAGIC)
638 continue;
639 if ((i<SLOT_INV || i>LAST_INV)!=which)
640 continue;
641 if (!(core->QuerySlotType(i)&slottype))
642 continue;
643 //the slot has been disabled for this actor
644 if (i>=SLOT_MELEE && i<=LAST_MELEE) {
645 if (Owner->GetQuickSlot(i-SLOT_MELEE)==0xffff) {
646 continue;
649 int part_res = AddSlotItem (item, i);
650 if (part_res == ASI_SUCCESS) return ASI_SUCCESS;
651 else if (part_res == ASI_PARTIAL) res = ASI_PARTIAL;
654 return res;
657 //Used by FillSlot
658 void Inventory::TryEquipAll(int slot)
660 for(int i=SLOT_INV;i<=LAST_INV;i++) {
661 CREItem *item = Slots[i];
662 if (!item) {
663 continue;
666 Slots[i]=NULL;
667 if (AddSlotItem(item, slot) == ASI_SUCCESS) {
668 return;
670 //try to stuff it back, it should work
671 if (AddSlotItem(item, i) != ASI_SUCCESS) {
672 delete item;
677 int Inventory::AddStoreItem(STOItem* item, int action)
679 CREItem *temp;
680 int ret = -1;
682 // item->PurchasedAmount is the number of items bought
683 // (you can still add grouped objects in a single step,
684 // just set up STOItem)
685 while (item->PurchasedAmount) {
686 //the first part of a STOItem is essentially a CREItem
687 temp = new CREItem();
688 memcpy( temp, item, sizeof( CREItem ) );
689 //except the Expired flag
690 temp->Expired=0;
691 if (action==STA_STEAL) {
692 temp->Flags |= IE_INV_ITEM_STOLEN;
694 temp->Flags &= ~IE_INV_ITEM_SELECTED;
696 ret = AddSlotItem( temp, SLOT_ONLYINVENTORY );
697 if (ret != ASI_SUCCESS) {
698 delete temp;
699 break;
701 if (item->InfiniteSupply!=-1) {
702 if (!item->AmountInStock) {
703 break;
705 item->AmountInStock--;
707 item->PurchasedAmount--;
709 return ret;
712 /* could the source item be dropped on the target item to merge them */
713 bool Inventory::ItemsAreCompatible(CREItem* target, CREItem* source) const
715 if (!target) {
716 //this isn't always ok, please check!
717 printMessage("Inventory","Null item encountered by ItemsAreCompatible()",YELLOW);
718 return true;
721 if (!(source->Flags&IE_INV_ITEM_STACKED) ) {
722 return false;
725 if (!strnicmp( target->ItemResRef, source->ItemResRef,8 )) {
726 return true;
728 return false;
731 //depletes a magical item
732 //if flags==0 then magical weapons are not harmed
733 int Inventory::DepleteItem(ieDword flags)
735 for (size_t i = 0; i < Slots.size(); i++) {
736 CREItem *item = Slots[i];
737 if (!item) {
738 continue;
741 //don't harm critical items
742 //don't harm nonmagical items
743 //don't harm indestructible items
744 if ( (item->Flags&(IE_INV_ITEM_CRITICAL|IE_INV_DEPLETABLE)) != IE_INV_DEPLETABLE) {
745 continue;
748 //if flags = 0 then weapons are not depleted
749 if (!flags) {
750 Item *itm = gamedata->GetItem( item->ItemResRef );
751 if (!itm)
752 continue;
753 //if the item is usable in weapon slot, then it is weapon
754 int weapon = core->CanUseItemType( SLOT_WEAPON, itm );
755 gamedata->FreeItem( itm, item->ItemResRef, false );
756 if (weapon)
757 continue;
759 //deplete item
760 item->Usages[0]=0;
761 item->Usages[1]=0;
762 item->Usages[2]=0;
764 return -1;
767 // if flags is 0, skips undroppable items
768 // if flags is IE_INV_ITEM_UNDROPPABLE, doesn't skip undroppable items
769 // TODO: once all callers have been checked, this can be reversed to make more sense
770 int Inventory::FindItem(const char *resref, unsigned int flags) const
772 for (size_t i = 0; i < Slots.size(); i++) {
773 const CREItem *item = Slots[i];
774 if (!item) {
775 continue;
777 if ( (flags^IE_INV_ITEM_UNDROPPABLE) & item->Flags ) {
778 continue;
780 if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
781 continue;
783 return (int) i;
785 return -1;
788 bool Inventory::DropItemAtLocation(unsigned int slot, unsigned int flags, Map *map, const Point &loc)
790 if (slot >= Slots.size()) {
791 return false;
793 //these slots will never 'drop' the item
794 if ((slot==(unsigned int) SLOT_FIST) || (slot==(unsigned int) SLOT_MAGIC)) {
795 return false;
798 CREItem *item = Slots[slot];
799 if (!item) {
800 return false;
802 //if you want to drop undoppable items, simply set IE_INV_UNDROPPABLE
803 //by default, it won't drop them
804 if ( ((flags^IE_INV_ITEM_UNDROPPABLE)&item->Flags)!=flags) {
805 return false;
807 if (!map) {
808 return false;
810 map->AddItemToLocation(loc, item);
811 Changed = true;
812 KillSlot(slot);
813 return true;
816 bool Inventory::DropItemAtLocation(const char *resref, unsigned int flags, Map *map, const Point &loc)
818 bool dropped = false;
820 if (!map) {
821 return false;
824 //this loop is going from start
825 for (size_t i = 0; i < Slots.size(); i++) {
826 //these slots will never 'drop' the item
827 if ((i==(unsigned int) SLOT_FIST) || (i==(unsigned int) SLOT_MAGIC)) {
828 continue;
830 CREItem *item = Slots[i];
831 if (!item) {
832 continue;
834 //if you want to drop undoppable items, simply set IE_INV_UNDROPPABLE
835 //by default, it won't drop them
836 if ( ((flags^IE_INV_ITEM_UNDROPPABLE)&item->Flags)!=flags) {
837 continue;
839 if (resref[0] && strnicmp(item->ItemResRef, resref, 8) ) {
840 continue;
842 // mark it as unequipped, so it doesn't cause problems in stores
843 item->Flags &= ~ IE_INV_ITEM_EQUIPPED;
844 map->AddItemToLocation(loc, item);
845 Changed = true;
846 dropped = true;
847 KillSlot((unsigned int) i);
848 //if it isn't all items then we stop here
849 if (resref[0])
850 break;
852 return dropped;
855 CREItem *Inventory::GetSlotItem(unsigned int slot) const
857 if (slot >= Slots.size() ) {
858 InvalidSlot(slot);
859 return NULL;
861 return Slots[slot];
864 ieDword Inventory::GetItemFlag(unsigned int slot) const
866 const CREItem *item = GetSlotItem(slot);
867 if (!item) {
868 return 0;
870 return item->Flags;
873 bool Inventory::ChangeItemFlag(unsigned int slot, ieDword arg, int op)
875 CREItem *item = GetSlotItem(slot);
876 if (!item) {
877 return false;
879 switch (op) {
880 case BM_SET: item->Flags = arg; break;
881 case BM_OR: item->Flags |= arg; break;
882 case BM_NAND: item->Flags &= ~arg; break;
883 case BM_XOR: item->Flags ^= arg; break;
884 case BM_AND: item->Flags &= arg; break;
886 return true;
889 //this is the low level equipping
890 //all checks have been made previously
891 bool Inventory::EquipItem(unsigned int slot)
893 ITMExtHeader *header;
895 if (!Owner) {
896 //maybe assertion too?
897 return false;
899 CREItem *item = GetSlotItem(slot);
900 if (!item) {
901 return false;
904 int weaponslot;
906 // add effects of an item just being equipped to actor's effect queue
907 int effect = core->QuerySlotEffects( slot );
908 Item *itm = gamedata->GetItem(item->ItemResRef);
909 if (!itm) {
910 printf("Invalid item Equipped: %s Slot: %d\n", item->ItemResRef, slot);
911 return false;
913 switch (effect) {
914 case SLOT_EFFECT_LEFT:
915 //no idea if the offhand weapon has style, or simply the right
916 //hand style is dominant
917 UpdateShieldAnimation(itm);
918 break;
919 case SLOT_EFFECT_MELEE:
920 //if weapon is ranged, then find quarrel for it and equip that
921 slot -= SLOT_MELEE;
922 weaponslot = slot;
923 EquippedHeader = 0;
924 header = itm->GetExtHeader(EquippedHeader);
925 if (header && header->AttackType == ITEM_AT_BOW) {
926 //find the ranged projectile associated with it.
927 slot = FindRangedProjectile(header->ProjectileQualifier);
928 EquippedHeader = itm->GetWeaponHeaderNumber(true);
929 } else if (header && header->AttackType == ITEM_AT_PROJECTILE) {
930 EquippedHeader = itm->GetWeaponHeaderNumber(true);
931 } else {
932 EquippedHeader = itm->GetWeaponHeaderNumber(false);
934 header = itm->GetExtHeader(EquippedHeader);
935 if (header) {
936 SetEquippedSlot(slot, EquippedHeader);
937 if (slot != IW_NO_EQUIPPED) {
938 Owner->SetupQuickSlot(ACT_WEAPON1+weaponslot, slot+SLOT_MELEE, EquippedHeader);
940 effect = 0; // SetEquippedSlot will already call AddSlotEffects
941 UpdateWeaponAnimation();
943 break;
944 case SLOT_EFFECT_MISSILE:
945 //Get the ranged header of the projectile (so we theoretically allow shooting of daggers)
946 EquippedHeader = itm->GetWeaponHeaderNumber(true);
947 header = itm->GetExtHeader(EquippedHeader);
948 if (header) {
949 weaponslot = FindTypedRangedWeapon(header->ProjectileQualifier);
950 if (weaponslot != SLOT_FIST) {
951 weaponslot -= SLOT_MELEE;
952 SetEquippedSlot((ieWordSigned) (slot-SLOT_MELEE), EquippedHeader);
953 //It is unsure if we can have multiple equipping headers for bows/arrow
954 //It is unclear which item's header index should go there
955 Owner->SetupQuickSlot(ACT_WEAPON1+weaponslot, slot, 0);
957 UpdateWeaponAnimation();
959 break;
960 case SLOT_EFFECT_HEAD:
961 Owner->SetUsedHelmet(itm->AnimationType);
962 break;
963 case SLOT_EFFECT_ITEM:
964 //adjusting armour level if needed
966 int l = itm->AnimationType[0]-'1';
967 if (l>=0 && l<=3) {
968 Owner->SetBase(IE_ARMOR_TYPE, l);
969 } else {
970 UpdateShieldAnimation(itm);
973 break;
975 gamedata->FreeItem(itm, item->ItemResRef, false);
976 if (effect) {
977 if (item->Flags & IE_INV_ITEM_CURSED) {
978 item->Flags|=IE_INV_ITEM_UNDROPPABLE;
980 AddSlotEffects( slot );
982 return true;
985 //the removecurse flag will check if it is possible to move the item to the inventory
986 //after a remove curse spell
987 bool Inventory::UnEquipItem(unsigned int slot, bool removecurse)
989 CREItem *item = GetSlotItem(slot);
990 if (!item) {
991 return false;
993 if (removecurse) {
994 if (item->Flags & IE_INV_ITEM_MOVABLE) {
995 item->Flags&=~IE_INV_ITEM_UNDROPPABLE;
997 if (FindCandidateSlot(SLOT_INVENTORY,0,item->ItemResRef)<0) {
998 return false;
1001 if (item->Flags & IE_INV_ITEM_UNDROPPABLE) {
1002 return false;
1004 item->Flags &= ~IE_INV_ITEM_EQUIPPED; //no idea if this is needed, won't hurt
1005 return true;
1008 // find the projectile
1009 // type = 1 - bow
1010 // 2 - xbow
1011 // 4 - sling
1012 //returns equipped code
1013 int Inventory::FindRangedProjectile(unsigned int type) const
1015 for(int i=SLOT_RANGED;i<=LAST_RANGED;i++) {
1016 CREItem *Slot;
1018 const Item *itm = GetItemPointer(i, Slot);
1019 if (!itm) continue;
1020 ITMExtHeader *ext_header = itm->GetExtHeader(0);
1021 unsigned int weapontype = 0;
1022 if (ext_header) {
1023 weapontype = ext_header->ProjectileQualifier;
1025 gamedata->FreeItem(itm, Slot->ItemResRef, false);
1026 if (weapontype & type) {
1027 return i-SLOT_MELEE;
1030 return IW_NO_EQUIPPED;
1033 // find which bow is attached to the projectile marked by 'Equipped'
1034 // returns slotcode
1035 int Inventory::FindRangedWeapon() const
1037 if (Equipped>=0) return SLOT_FIST;
1038 return FindSlotRangedWeapon(Equipped+SLOT_MELEE);
1041 int Inventory::FindSlotRangedWeapon(unsigned int slot) const
1043 if ((int)slot >= SLOT_MELEE) return SLOT_FIST;
1044 CREItem *Slot;
1045 Item *itm = GetItemPointer(slot, Slot);
1046 if (!itm) return SLOT_FIST;
1048 //always look for a ranged header when looking for a projectile/projector
1049 ITMExtHeader *ext_header = itm->GetWeaponHeader(true);
1050 unsigned int type = 0;
1051 if (ext_header) {
1052 type = ext_header->ProjectileQualifier;
1054 gamedata->FreeItem(itm, Slot->ItemResRef, false);
1055 return FindTypedRangedWeapon(type);
1059 // find bow for a specific projectile type
1060 int Inventory::FindTypedRangedWeapon(unsigned int type) const
1062 if (!type) {
1063 return SLOT_FIST;
1065 for(int i=SLOT_MELEE;i<=LAST_MELEE;i++) {
1066 CREItem *Slot;
1068 const Item *itm = GetItemPointer(i, Slot);
1069 if (!itm) continue;
1070 //always look for a ranged header when looking for a projectile/projector
1071 ITMExtHeader *ext_header = itm->GetWeaponHeader(true);
1072 int weapontype = 0;
1073 if (ext_header) {
1074 weapontype = ext_header->ProjectileQualifier;
1076 gamedata->FreeItem(itm, Slot->ItemResRef, false);
1077 if (weapontype & type) {
1078 return i;
1081 return SLOT_FIST;
1084 void Inventory::SetHeadSlot(int arg) { SLOT_HEAD=arg; }
1085 void Inventory::SetFistSlot(int arg) { SLOT_FIST=arg; }
1086 void Inventory::SetMagicSlot(int arg) { SLOT_MAGIC=arg; }
1087 void Inventory::SetWeaponSlot(int arg)
1089 if (SLOT_MELEE==-1) {
1090 SLOT_MELEE=arg;
1092 LAST_MELEE=arg;
1095 //ranged slots should be before MELEE slots
1096 void Inventory::SetRangedSlot(int arg)
1098 assert(SLOT_MELEE!=-1);
1099 if (SLOT_RANGED==-1) {
1100 SLOT_RANGED=arg;
1102 LAST_RANGED=arg;
1105 void Inventory::SetQuickSlot(int arg)
1107 if (SLOT_QUICK==-1) {
1108 SLOT_QUICK=arg;
1110 LAST_QUICK=arg;
1113 void Inventory::SetInventorySlot(int arg)
1115 if (SLOT_INV==-1) {
1116 SLOT_INV=arg;
1118 LAST_INV=arg;
1121 //multiple shield slots are allowed
1122 //but in this case they should be interspersed with melee slots
1123 void Inventory::SetShieldSlot(int arg)
1125 if (SLOT_LEFT!=-1) {
1126 assert(SLOT_MELEE+1==SLOT_LEFT);
1127 IWD2=true;
1128 return;
1130 SLOT_LEFT=arg;
1133 int Inventory::GetHeadSlot()
1135 return SLOT_HEAD;
1138 int Inventory::GetFistSlot()
1140 return SLOT_FIST;
1143 int Inventory::GetMagicSlot()
1145 return SLOT_MAGIC;
1148 int Inventory::GetWeaponSlot()
1150 return SLOT_MELEE;
1153 int Inventory::GetQuickSlot()
1155 return SLOT_QUICK;
1158 int Inventory::GetInventorySlot()
1160 return SLOT_INV;
1163 //if shield slot is empty, call again for fist slot!
1164 int Inventory::GetShieldSlot() const
1166 if (IWD2) {
1167 if (Equipped>=0 && Equipped<=3) {
1168 return Equipped*2+SLOT_MELEE+1;
1170 return -1;
1172 return SLOT_LEFT;
1175 int Inventory::GetEquippedSlot() const
1177 if (Equipped == IW_NO_EQUIPPED) {
1178 return SLOT_FIST;
1180 if (IWD2 && Equipped>=0) {
1181 //i've absolutely NO idea what is this 4 (Avenger)
1182 //Equipped should be 0-3 in iWD2, no???
1183 if (Equipped >= 4) {
1184 return SLOT_MELEE;
1186 return Equipped*2+SLOT_MELEE;
1188 return Equipped+SLOT_MELEE;
1191 bool Inventory::SetEquippedSlot(ieWordSigned slotcode, ieWord header)
1193 EquippedHeader = header;
1195 //doesn't work if magic slot is used, refresh the magic slot just in case
1196 if (HasItemInSlot("",SLOT_MAGIC) && (slotcode!=SLOT_MAGIC-SLOT_MELEE)) {
1197 Equipped = SLOT_MAGIC-SLOT_MELEE;
1198 UpdateWeaponAnimation();
1199 return false;
1202 //if it is an illegal code, make it fist
1203 if ((size_t) (slotcode+SLOT_MELEE)>Slots.size()) {
1204 slotcode=IW_NO_EQUIPPED;
1207 //unequipping (fist slot will be used now)
1208 if (slotcode == IW_NO_EQUIPPED || !HasItemInSlot("",slotcode+SLOT_MELEE)) {
1209 if (Equipped != IW_NO_EQUIPPED) {
1210 RemoveSlotEffects( SLOT_MELEE+Equipped);
1212 Equipped = IW_NO_EQUIPPED;
1213 //fist slot equipping effects
1214 AddSlotEffects(SLOT_FIST);
1215 UpdateWeaponAnimation();
1216 return true;
1219 //equipping a weapon, but remove its effects first
1220 if (Equipped != IW_NO_EQUIPPED) {
1221 RemoveSlotEffects( SLOT_MELEE+Equipped);
1224 Equipped = slotcode;
1225 int effects = core->QuerySlotEffects( SLOT_MELEE+Equipped );
1226 if (effects) {
1227 CREItem* item = GetSlotItem(SLOT_MELEE+Equipped);
1228 item->Flags|=IE_INV_ITEM_EQUIPPED;
1229 if (item->Flags & IE_INV_ITEM_CURSED) {
1230 item->Flags|=IE_INV_ITEM_UNDROPPABLE;
1232 AddSlotEffects( SLOT_MELEE+Equipped);
1234 UpdateWeaponAnimation();
1235 return true;
1238 int Inventory::GetEquipped() const
1240 return Equipped;
1243 int Inventory::GetEquippedHeader() const
1245 return EquippedHeader;
1248 //returns the fist weapon if there is nothing else
1249 //This will return the actual weapon, I mean the bow in the case of bow+arrow combination
1250 CREItem *Inventory::GetUsedWeapon(bool leftorright, int &slot) const
1252 CREItem *ret;
1254 if (SLOT_MAGIC!=-1) {
1255 slot = SLOT_MAGIC;
1256 ret = GetSlotItem(slot);
1257 if (ret && ret->ItemResRef[0]) {
1258 return ret;
1261 if (leftorright) {
1262 //no shield slot
1263 slot = GetShieldSlot();
1264 if (slot>=0) {
1265 ret = GetSlotItem(slot);
1266 if (ret) {
1267 return ret;
1268 } else {
1269 //we don't want to return fist for shield slot
1270 return NULL;
1274 slot = GetEquippedSlot();
1275 if((core->QuerySlotEffects(slot) & SLOT_EFFECT_MISSILE) == SLOT_EFFECT_MISSILE) {
1276 slot = FindRangedWeapon();
1278 ret = GetSlotItem(slot);
1279 if (!ret) {
1280 //return fist weapon
1281 slot = SLOT_FIST;
1282 ret = GetSlotItem(slot);
1284 return ret;
1287 // Returns index of first empty slot or slot with the same
1288 // item and not full stack. On fail returns -1
1289 // Can be used to check for full inventory
1290 int Inventory::FindCandidateSlot(int slottype, size_t first_slot, const char *resref)
1292 if (first_slot >= Slots.size())
1293 return -1;
1295 for (size_t i = first_slot; i < Slots.size(); i++) {
1296 if (!(core->QuerySlotType( (unsigned int) i) & slottype) ) {
1297 continue;
1300 CREItem *item = Slots[i];
1302 if (!item) {
1303 return (int) i; //this is a good empty slot
1305 if (!resref) {
1306 continue;
1308 if (!(item->Flags&IE_INV_ITEM_STACKED) ) {
1309 continue;
1311 if (strnicmp( item->ItemResRef, resref, 8 )!=0) {
1312 continue;
1314 // check if the item fits in this slot, we use the cached
1315 // stackamount value
1316 if (item->Usages[0]<item->StackAmount) {
1317 return (int) i;
1321 return -1;
1324 void Inventory::AddSlotItemRes(const ieResRef ItemResRef, int SlotID, int Charge0, int Charge1, int Charge2)
1326 CREItem *TmpItem = new CREItem();
1327 strnlwrcpy(TmpItem->ItemResRef, ItemResRef, 8);
1328 TmpItem->Expired=0;
1329 TmpItem->Usages[0]=(ieWord) Charge0;
1330 TmpItem->Usages[1]=(ieWord) Charge1;
1331 TmpItem->Usages[2]=(ieWord) Charge2;
1332 TmpItem->Flags=0;
1333 if (core->ResolveRandomItem(TmpItem) && gamedata->Exists(TmpItem->ItemResRef, IE_ITM_CLASS_ID)) {
1334 AddSlotItem( TmpItem, SlotID );
1335 } else {
1336 delete TmpItem;
1338 CalculateWeight();
1341 void Inventory::SetSlotItemRes(const ieResRef ItemResRef, int SlotID, int Charge0, int Charge1, int Charge2)
1343 if(ItemResRef[0]) {
1344 CREItem *TmpItem = new CREItem();
1345 strnlwrcpy(TmpItem->ItemResRef, ItemResRef, 8);
1346 TmpItem->Expired=0;
1347 TmpItem->Usages[0]=(ieWord) Charge0;
1348 TmpItem->Usages[1]=(ieWord) Charge1;
1349 TmpItem->Usages[2]=(ieWord) Charge2;
1350 TmpItem->Flags=0;
1351 if (core->ResolveRandomItem(TmpItem) && gamedata->Exists(TmpItem->ItemResRef, IE_ITM_CLASS_ID)) {
1352 SetSlotItem( TmpItem, SlotID );
1353 } else {
1354 delete TmpItem;
1356 } else {
1357 //if the item isn't creatable, we still destroy the old item
1358 KillSlot( SlotID );
1360 CalculateWeight();
1363 void Inventory::BreakItemSlot(ieDword slot)
1365 ieResRef newItem;
1366 CREItem *Slot;
1368 const Item *itm = GetItemPointer(slot, Slot);
1369 if (!itm) return;
1370 //if it is the magic weapon slot, don't break it, just remove it, because it couldn't be removed
1371 if(slot ==(unsigned int) SLOT_MAGIC) {
1372 newItem[0]=0;
1373 } else {
1374 memcpy(newItem, itm->ReplacementItem,sizeof(newItem) );
1376 gamedata->FreeItem( itm, Slot->ItemResRef, true );
1377 //this depends on setslotitemres using setslotitem
1378 SetSlotItemRes(newItem, slot, 0,0,0);
1381 void Inventory::dump()
1383 printf( "INVENTORY:\n" );
1384 for (unsigned int i = 0; i < Slots.size(); i++) {
1385 CREItem* itm = Slots[i];
1387 if (!itm) {
1388 continue;
1391 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 );
1394 printf( "Equipped: %d\n", Equipped );
1395 Changed = true;
1396 CalculateWeight();
1397 printf( "Total weight: %d\n", Weight );
1400 void Inventory::EquipBestWeapon(int flags)
1402 int i;
1403 int damage = -1;
1404 ieDword best_slot = SLOT_FIST;
1405 ITMExtHeader *header;
1406 CREItem *Slot;
1407 char AnimationType[2]={0,0};
1408 ieWord MeleeAnimation[3]={100,0,0};
1410 //cannot change equipment when holding magic weapons
1411 if (Equipped == SLOT_MAGIC-SLOT_MELEE) {
1412 return;
1415 if (flags&EQUIP_RANGED) {
1416 for(i=SLOT_RANGED;i<LAST_RANGED;i++) {
1417 const Item *itm = GetItemPointer(i, Slot);
1418 if (!itm) continue;
1419 //best ranged
1420 int tmp = itm->GetDamagePotential(true, header);
1421 if (tmp>damage) {
1422 best_slot = i;
1423 damage = tmp;
1424 memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1425 memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
1427 gamedata->FreeItem( itm, Slot->ItemResRef, false );
1430 //ranged melee weapons like throwing daggers (not bows!)
1431 for(i=SLOT_MELEE;i<=LAST_MELEE;i++) {
1432 const Item *itm = GetItemPointer(i, Slot);
1433 if (!itm) continue;
1434 //best ranged
1435 int tmp = itm->GetDamagePotential(true, header);
1436 if (tmp>damage) {
1437 best_slot = i;
1438 damage = tmp;
1439 memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1440 memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
1442 gamedata->FreeItem( itm, Slot->ItemResRef, false );
1446 if (flags&EQUIP_MELEE) {
1447 for(i=SLOT_MELEE;i<=LAST_MELEE;i++) {
1448 const Item *itm = GetItemPointer(i, Slot);
1449 if (!itm) continue;
1450 //the Slot flag is enough for this
1451 //though we need animation type/damagepotential anyway
1452 if (Slot->Flags&IE_INV_ITEM_BOW) continue;
1453 //best melee
1454 int tmp = itm->GetDamagePotential(false, header);
1455 if (tmp>damage) {
1456 best_slot = i;
1457 damage = tmp;
1458 memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1459 memcpy(MeleeAnimation,header->MeleeAnimation,sizeof(MeleeAnimation) );
1461 gamedata->FreeItem( itm, Slot->ItemResRef, false );
1465 EquipItem(best_slot);
1466 UpdateWeaponAnimation();
1469 #define ID_NONEED 0 //id is not important
1470 #define ID_NEED 1 //id is important
1471 #define ID_NO 2 //shouldn't id
1473 /* returns true if there are more item usages not fitting in given array */
1474 bool Inventory::GetEquipmentInfo(ItemExtHeader *array, int startindex, int count)
1476 int pos = 0;
1477 int actual = 0;
1478 memset(array, 0, count * sizeof(ItemExtHeader) );
1479 for(unsigned int idx=0;idx<Slots.size();idx++) {
1480 if (!core->QuerySlotEffects(idx)) {
1481 continue;
1483 CREItem *slot;
1485 const Item *itm = GetItemPointer(idx, slot);
1486 if (!itm) {
1487 continue;
1489 for(int ehc=0;ehc<itm->ExtHeaderCount;ehc++) {
1490 ITMExtHeader *ext_header = itm->ext_headers+ehc;
1491 if (ext_header->Location!=ITEM_LOC_EQUIPMENT) {
1492 continue;
1494 //skipping if we cannot use the item
1495 int idreq1 = (slot->Flags&IE_INV_ITEM_IDENTIFIED);
1496 int idreq2 = ext_header->IDReq;
1497 switch (idreq2) {
1498 case ID_NO:
1499 if (idreq1) continue;
1500 break;
1501 case ID_NEED:
1502 if (!idreq1) continue;
1503 default:;
1506 actual++;
1507 if (actual>startindex) {
1509 //store the item, return if we can't store more
1510 if (!count) {
1511 gamedata->FreeItem(itm, slot->ItemResRef, false);
1512 return true;
1514 count--;
1515 memcpy(array[pos].itemname, slot->ItemResRef, sizeof(ieResRef) );
1516 array[pos].slot = idx;
1517 array[pos].headerindex = ehc;
1518 int slen = ((char *) &(array[pos].itemname)) -((char *) &(array[pos].AttackType));
1519 memcpy(&(array[pos].AttackType), &(ext_header->AttackType), slen);
1520 if (ext_header->Charges) {
1521 //don't modify ehc, it is a counter
1522 if (ehc>=CHARGE_COUNTERS) {
1523 array[pos].Charges=slot->Usages[0];
1524 } else {
1525 array[pos].Charges=slot->Usages[ehc];
1527 } else {
1528 array[pos].Charges=0xffff;
1530 pos++;
1533 gamedata->FreeItem(itm, slot->ItemResRef, false);
1536 return false;
1539 //The slot index value is optional, if you supply it,
1540 // then ItemExcl will be returned as if the item was already unequipped
1541 ieDword Inventory::GetEquipExclusion(int index) const
1543 if (index<0) {
1544 return ItemExcl;
1546 CREItem *slot;
1547 const Item *itm = GetItemPointer(index, slot);
1548 if (!itm) {
1549 return ItemExcl;
1551 ieDword ret = ItemExcl&~itm->ItemExcl;
1552 gamedata->FreeItem(itm, slot->ItemResRef, false);
1553 return ret;
1556 void Inventory::UpdateShieldAnimation(Item *it)
1558 char AnimationType[2]={0,0};
1559 int WeaponType = -1;
1561 if (it) {
1562 memcpy(AnimationType, it->AnimationType, 2);
1563 if (core->CanUseItemType(SLOT_WEAPON, it))
1564 WeaponType = IE_ANI_WEAPON_2W;
1565 else
1566 WeaponType = IE_ANI_WEAPON_1H;
1567 } else {
1568 WeaponType = IE_ANI_WEAPON_1H;
1570 Owner->SetUsedShield(AnimationType, WeaponType);
1573 void Inventory::UpdateWeaponAnimation()
1575 int slot = GetEquippedSlot();
1576 int effect = core->QuerySlotEffects( slot );
1577 if (effect == SLOT_EFFECT_MISSILE) {
1578 // ranged weapon
1579 slot = FindRangedWeapon();
1581 int WeaponType = -1;
1583 char AnimationType[2]={0,0};
1584 ieWord MeleeAnimation[3]={100,0,0};
1585 CREItem *Slot;
1587 // TODO: fix bows?
1589 ITMExtHeader *header = 0;
1590 const Item *itm = GetItemPointer(slot, Slot);
1591 if (itm) {
1592 itm->GetDamagePotential(false, header);
1593 memcpy(AnimationType,itm->AnimationType,sizeof(AnimationType) );
1594 //for twohanded flag, you don't need itm
1595 if (Slot->Flags & IE_INV_ITEM_TWOHANDED)
1596 WeaponType = IE_ANI_WEAPON_2H;
1597 else {
1599 // Examine shield slot to check if we're using two weapons
1600 // TODO: for consistency, use same Item* access method as above
1601 bool twoweapon = false;
1602 int slot = GetShieldSlot();
1603 CREItem* si = NULL;
1604 if (slot>0) {
1605 si = GetSlotItem( (ieDword) slot );
1607 if (si) {
1608 Item* it = gamedata->GetItem(si->ItemResRef);
1609 if (core->CanUseItemType(SLOT_WEAPON, it))
1610 twoweapon = true;
1611 gamedata->FreeItem(it, si->ItemResRef, false);
1614 if (twoweapon)
1615 WeaponType = IE_ANI_WEAPON_2W;
1616 else
1617 WeaponType = IE_ANI_WEAPON_1H;
1621 if (header)
1622 memcpy(MeleeAnimation,header->MeleeAnimation,
1623 sizeof(MeleeAnimation) );
1624 if (itm)
1625 gamedata->FreeItem( itm, Slot->ItemResRef, false );
1626 Owner->SetUsedWeapon(AnimationType, MeleeAnimation, WeaponType);
1629 //this function will also check disabled slots (if that feature will be imped)
1630 bool Inventory::IsSlotBlocked(int slot) const
1632 if (slot<SLOT_MELEE) return false;
1633 if (slot>LAST_MELEE) return false;
1634 int otherslot;
1635 if (IWD2) {
1636 otherslot = slot+1;
1637 } else {
1638 otherslot = SLOT_LEFT;
1640 return HasItemInSlot("",otherslot);
1643 inline bool Inventory::TwoHandedInSlot(int slot) const
1645 CREItem *item;
1647 item = GetSlotItem(slot);
1648 if (!item) return false;
1649 if (item->Flags&IE_INV_ITEM_TWOHANDED) {
1650 return true;
1652 return false;
1655 int Inventory::WhyCantEquip(int slot, int twohanded) const
1657 // check only for hand slots
1658 if ((slot<SLOT_MELEE || slot>LAST_MELEE) && (slot != SLOT_LEFT) ) {
1659 return 0;
1662 //magic items have the highest priority
1663 if ( HasItemInSlot("", SLOT_MAGIC)) {
1664 //magic weapon is in use
1665 return STR_MAGICWEAPON;
1668 //can't equip in shield slot if a weapon slot is twohanded
1669 for (int i=SLOT_MELEE; i<=LAST_MELEE;i++) {
1670 //see GetShieldSlot
1671 int otherslot;
1672 if (IWD2) {
1673 otherslot = ++i;
1674 } else {
1675 otherslot = SLOT_LEFT;
1677 if (slot==otherslot) {
1678 if (TwoHandedInSlot(i)) {
1679 return STR_TWOHANDED_USED;
1684 if (twohanded) {
1685 if (IWD2) {
1686 if (slot>=SLOT_MELEE&&slot<=LAST_MELEE && (slot&1) ) {
1687 return STR_NOT_IN_OFFHAND;
1689 } else {
1690 if (slot==SLOT_LEFT) {
1691 return STR_NOT_IN_OFFHAND;
1694 if (IsSlotBlocked(slot)) {
1695 //cannot equip two handed while shield slot is in use?
1696 return STR_OFFHAND_USED;
1699 return 0;
1702 //recharge items on rest, if rest was partial, recharge only 'hours'
1703 //if this latter functionality is unwanted, then simply don't recharge if
1704 //hours != 0
1705 void Inventory::ChargeAllItems(int hours)
1707 //this loop is going from start
1708 for (size_t i = 0; i < Slots.size(); i++) {
1709 CREItem *item = Slots[i];
1710 if (!item) {
1711 continue;
1714 Item *itm = gamedata->GetItem( item->ItemResRef );
1715 if (!itm)
1716 continue;
1717 for(int h=0;h<CHARGE_COUNTERS;h++) {
1718 ITMExtHeader *header = itm->GetExtHeader(h);
1719 if (header && (header->RechargeFlags&IE_ITEM_RECHARGE)) {
1720 unsigned short add = header->Charges;
1721 if (hours && add>hours) add=hours;
1722 add+=item->Usages[h];
1723 if(add>header->Charges) add=header->Charges;
1724 item->Usages[h]=add;
1727 gamedata->FreeItem( itm, item->ItemResRef, false );
1731 #define ITM_STEALING (IE_INV_ITEM_UNSTEALABLE | IE_INV_ITEM_MOVABLE)
1732 unsigned int Inventory::FindStealableItem()
1734 unsigned int slot;
1735 int inc;
1737 slot = core->Roll(1, Slots.size(),-1);
1738 inc = slot&1?1:-1;
1740 printf("Start Slot: %d, increment: %d\n", slot, inc);
1741 //as the unsigned value underflows, it will be greater than Slots.size()
1742 for(;slot<Slots.size(); slot+=inc) {
1743 CREItem *item = Slots[slot];
1744 //can't steal empty slot
1745 if (!item) continue;
1746 //bit 1 is stealable slot
1747 if (!(core->QuerySlotFlags(slot)&1) ) continue;
1748 //can't steal equipped weapon
1749 if ((unsigned int) (Equipped+SLOT_MELEE) == core->QuerySlot(slot)) continue;
1750 //can't steal flagged items
1751 if ((item->Flags & ITM_STEALING) != IE_INV_ITEM_MOVABLE) continue;
1752 return slot;
1754 return 0;
1757 // extension to allow more or less than head gear to avert critical hits:
1758 // If an item with bit 25 set is equipped in a non-helmet slot, aversion is enabled
1759 // If an item with bit 25 set is equipped in a helmet slot, aversion is disabled
1760 bool Inventory::ProvidesCriticalAversion()
1762 for (size_t i = 0; i < Slots.size(); i++) {
1763 CREItem *item = Slots[i];
1764 if (!item || ! (item->Flags & IE_INV_ITEM_EQUIPPED)) {
1765 continue;
1768 Item *itm = gamedata->GetItem(item->ItemResRef);
1769 if (!itm) {
1770 continue;
1773 for (int h = 0; h < itm->ExtHeaderCount; h++) {
1774 ITMExtHeader *header = itm->GetExtHeader(h);
1775 if ((int)i == SLOT_HEAD) {
1776 if (header && (header->RechargeFlags & IE_ITEM_TOGGLE_CRITS)) {
1777 return false;
1778 } else {
1779 return true;
1781 } else {
1782 if (header && (header->RechargeFlags & IE_ITEM_TOGGLE_CRITS)) {
1783 return true;
1788 return false;