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)
24 #include "Inventory.h"
29 #include "DisplayMessage.h"
32 #include "Interface.h"
34 #include "ScriptEngine.h"
35 #include "Scriptable/Actor.h"
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;
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
);
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
)
84 //TODO: set this correctly
89 Inventory::Inventory()
92 InventoryType
= INVENTORY_HEAP
;
95 Equipped
= IW_NO_EQUIPPED
;
98 memset(ItemTypes
, 0, sizeof(ItemTypes
));
101 Inventory::~Inventory()
103 for (size_t i
= 0; i
< Slots
.size(); i
++) {
111 // duplicates the source inventory into the current one
112 // also changes the items to not drop, so simulacrum and similar don't become factories
113 void Inventory::CopyFrom(const Actor
*source
)
119 SetSlotCount(source
->inventory
.GetSlotCount());
121 // allocate the items and mark them undroppable
123 for (size_t i
= 0; i
< source
->inventory
.Slots
.size(); i
++) {
124 item
= source
->inventory
.Slots
[i
];
127 memcpy(tmp
, item
, sizeof(CREItem
));
128 tmp
->Flags
|= IE_INV_ITEM_UNDROPPABLE
;
129 int ret
= AddSlotItem(tmp
, i
);
130 if (ret
!= ASI_SUCCESS
) {
136 // preserve the equipped status
137 Equipped
= source
->inventory
.GetEquipped();
138 EquippedHeader
= source
->inventory
.GetEquippedHeader();
144 CREItem
*Inventory::GetItem(unsigned int slot
)
146 if (slot
>= Slots
.size() ) {
150 CREItem
*item
= Slots
[slot
];
151 Slots
.erase(Slots
.begin()+slot
);
155 //This hack sets the charge counters for non-rechargeable items,
156 //if their charge is zero
157 inline void HackCharges(CREItem
*item
)
159 Item
*itm
= gamedata
->GetItem( item
->ItemResRef
);
161 for (int i
=0;i
<3;i
++) {
162 if (item
->Usages
[i
]) {
165 ITMExtHeader
*h
= itm
->GetExtHeader(i
);
166 if (h
&& !(h
->RechargeFlags
&IE_ITEM_RECHARGE
)) {
167 //HACK: the original (bg2) allows for 0 charged gems
169 item
->Usages
[i
] = h
->Charges
;
175 gamedata
->FreeItem( itm
, item
->ItemResRef
, false );
179 void Inventory::AddItem(CREItem
*item
)
181 if (!item
) return; //invalid items get no slot
182 Slots
.push_back(item
);
184 //this will update the flags (needed for unmovable items in containers)
185 //but those *can* be picked up (like the bg2 portal key), so we skip it
189 void Inventory::CalculateWeight()
195 for (size_t i
= 0; i
< Slots
.size(); i
++) {
196 CREItem
*slot
= Slots
[i
];
200 if (slot
->Weight
== -1) {
201 Item
*itm
= gamedata
->GetItem( slot
->ItemResRef
);
203 //simply adding the item flags to the slot
204 slot
->Flags
|= (itm
->Flags
<<8);
205 //some slot flags might be affected by the item flags
206 if (!(slot
->Flags
& IE_INV_ITEM_CRITICAL
)) {
207 slot
->Flags
|= IE_INV_ITEM_DESTRUCTIBLE
;
209 //this is for converting IWD items magic flag
211 if (slot
->Flags
&IE_INV_ITEM_UNDROPPABLE
) {
212 slot
->Flags
|=IE_INV_ITEM_MAGICAL
;
213 slot
->Flags
&=~IE_INV_ITEM_UNDROPPABLE
;
217 if (!(slot
->Flags
& IE_INV_ITEM_MOVABLE
)) {
218 slot
->Flags
|= IE_INV_ITEM_UNDROPPABLE
;
221 if (slot
->Flags
& IE_INV_ITEM_STOLEN2
) {
222 slot
->Flags
|= IE_INV_ITEM_STOLEN
;
225 //auto identify basic items
226 if (!itm
->LoreToID
) {
227 slot
->Flags
|= IE_INV_ITEM_IDENTIFIED
;
230 //if item is stacked mark it as so
231 if (itm
->StackAmount
) {
232 slot
->Flags
|= IE_INV_ITEM_STACKED
;
235 slot
->Weight
= itm
->Weight
;
236 slot
->StackAmount
= itm
->StackAmount
;
237 gamedata
->FreeItem( itm
, slot
->ItemResRef
, false );
240 printMessage( "Inventory", " ", LIGHT_RED
);
241 printf("Invalid item: %s!\n", slot
->ItemResRef
);
245 slot
->Flags
&= ~IE_INV_ITEM_ACQUIRED
;
247 if (slot
->Weight
> 0) {
248 Weight
+= slot
->Weight
* ((slot
->Usages
[0] && slot
->StackAmount
> 1) ? slot
->Usages
[0] : 1);
254 void Inventory::AddSlotEffects(ieDword index
)
258 const Item
*itm
= GetItemPointer(index
, slot
);
260 printMessage("Inventory","Invalid item equipped...\n",LIGHT_RED
);
263 ItemExcl
|=itm
->ItemExcl
;
264 ieDword pos
= itm
->ItemType
/32;
265 ieDword bit
= itm
->ItemType
%32;
267 ItemTypes
[pos
]|=1<<bit
;
270 ieWord gradient
= itm
->GetWieldedGradient();
271 if (gradient
!=0xffff) {
272 Owner
->SetBase(IE_COLORS
, gradient
);
275 //get the equipping effects
276 EffectQueue
*eqfx
= itm
->GetEffectBlock(Owner
, Owner
->Pos
, -1, index
, 0);
277 gamedata
->FreeItem( itm
, slot
->ItemResRef
, false );
279 Owner
->RefreshEffects(eqfx
);
280 //call gui for possible paperdoll animation changes
281 if (Owner
->InParty
) {
282 core
->SetEventFlag(EF_UPDATEANIM
);
286 //no need to know the item effects 'personally', the equipping slot
288 void Inventory::RemoveSlotEffects(ieDword index
)
290 Owner
->fxqueue
.RemoveEquippingEffects(index
);
291 Owner
->RefreshEffects(NULL
);
292 //call gui for possible paperdoll animation changes
293 if (Owner
->InParty
) {
294 core
->SetEventFlag(EF_UPDATEANIM
);
298 void Inventory::SetInventoryType(int arg
)
303 void Inventory::SetSlotCount(unsigned int size
)
306 printf("Inventory size changed???\n");
307 //we don't allow reassignment,
308 //if you want this, delete the previous Slots here
311 Slots
.assign((size_t) size
, NULL
);
314 /** if you supply a "" string, then it checks if the slot is empty */
315 bool Inventory::HasItemInSlot(const char *resref
, unsigned int slot
) const
317 if (slot
>= Slots
.size()) {
320 const CREItem
*item
= Slots
[slot
];
327 if (strnicmp( item
->ItemResRef
, resref
, 8 )==0) {
333 bool Inventory::HasItemType(ieDword type
) const
335 if (type
>255) return false;
338 return (ItemTypes
[idx
] & (1<<bit
) )!=0;
341 /** counts the items in the inventory, if stacks == 1 then stacks are
342 accounted for their heap size */
343 int Inventory::CountItems(const char *resref
, bool stacks
) const
346 size_t slot
= Slots
.size();
348 const CREItem
*item
= Slots
[slot
];
352 if (resref
&& resref
[0]) {
353 if (!strnicmp(resref
, item
->ItemResRef
, 8) )
356 if (stacks
&& (item
->Flags
&IE_INV_ITEM_STACKED
) ) {
357 count
+=item
->Usages
[0];
366 /** this function can look for stolen, equipped, identified, destructible
367 etc, items. You just have to specify the flags in the bitmask
368 specifying 1 in a bit signifies a requirement */
369 bool Inventory::HasItem(const char *resref
, ieDword flags
) const
371 size_t slot
= Slots
.size();
373 const CREItem
*item
= Slots
[slot
];
377 if ( (flags
&item
->Flags
)!=flags
) {
380 if (resref
[0] && strnicmp(item
->ItemResRef
, resref
,8) ) {
388 void Inventory::KillSlot(unsigned int index
)
390 if (InventoryType
==INVENTORY_HEAP
) {
391 Slots
.erase(Slots
.begin()+index
);
394 CREItem
*item
= Slots
[index
];
399 //the used up item vanishes from the quickslot bar
400 if (Owner
->IsSelected()) {
401 core
->SetEventFlag( EF_ACTION
);
405 int effect
= core
->QuerySlotEffects( index
);
409 RemoveSlotEffects( index
);
410 Item
*itm
= gamedata
->GetItem(item
->ItemResRef
);
411 //this cannot happen, but stuff happens!
415 ItemExcl
&= ~itm
->ItemExcl
;
418 case SLOT_EFFECT_LEFT
:
419 UpdateShieldAnimation(0);
421 case SLOT_EFFECT_MISSILE
:
422 //getting a new projectile of the same type
423 if (Equipped
+ SLOT_MELEE
== (int) index
) {
425 //always get the projectile weapon header (this quiver was equipped)
426 ITMExtHeader
*header
= itm
->GetWeaponHeader(true);
427 Equipped
= FindRangedProjectile(header
->ProjectileQualifier
);
428 if (Equipped
!=IW_NO_EQUIPPED
) {
429 EquipItem(Equipped
+SLOT_MELEE
);
431 EquipItem(SLOT_FIST
);
435 UpdateWeaponAnimation();
437 case SLOT_EFFECT_MELEE
:
438 // reset Equipped if it was the removed item
439 if (Equipped
+SLOT_MELEE
== (int)index
)
440 Equipped
= IW_NO_EQUIPPED
;
441 else if (Equipped
< 0) {
442 //always get the projectile weapon header (this is a bow, because Equipped is negative)
443 ITMExtHeader
*header
= itm
->GetWeaponHeader(true);
445 //find the equipped type
446 int type
= header
->ProjectileQualifier
;
447 int weaponslot
= FindTypedRangedWeapon(type
);
448 CREItem
*item2
= Slots
[weaponslot
];
450 Item
*itm2
= gamedata
->GetItem(item2
->ItemResRef
);
452 if (type
== header
->ProjectileQualifier
) {
453 Equipped
= FindRangedProjectile(header
->ProjectileQualifier
);
454 if (Equipped
!=IW_NO_EQUIPPED
) {
455 EquipItem(Equipped
+SLOT_MELEE
);
457 EquipItem(SLOT_FIST
);
460 gamedata
->FreeItem(itm2
, item2
->ItemResRef
, false);
465 // reset Equipped if it is a ranged weapon slot
466 // but not magic weapon slot!
468 UpdateWeaponAnimation();
470 case SLOT_EFFECT_HEAD
:
471 Owner
->SetUsedHelmet("");
473 case SLOT_EFFECT_ITEM
:
474 //remove the armor type only if this item is responsible for it
475 if ((ieDword
) (itm
->AnimationType
[0]-'1') == Owner
->GetBase(IE_ARMOR_TYPE
)) {
476 Owner
->SetBase(IE_ARMOR_TYPE
, 0);
480 gamedata
->FreeItem(itm
, item
->ItemResRef
, false);
482 /** if resref is "", then destroy ALL items
483 this function can look for stolen, equipped, identified, destructible
484 etc, items. You just have to specify the flags in the bitmask
485 specifying 1 in a bit signifies a requirement */
486 unsigned int Inventory::DestroyItem(const char *resref
, ieDword flags
, ieDword count
)
488 unsigned int destructed
= 0;
489 size_t slot
= Slots
.size();
492 //ignore the fist slot
493 if (slot
== (unsigned int)SLOT_FIST
) {
497 CREItem
*item
= Slots
[slot
];
501 // here you can simply destroy all items of a specific type
502 if ( (flags
&item
->Flags
)!=flags
) {
505 if (resref
[0] && strnicmp(item
->ItemResRef
, resref
, 8) ) {
508 //we need to acknowledge that the item was destroyed
509 //use unequip stuff, decrease encumbrance etc,
510 //until that, we simply erase it
513 if (item
->Flags
&IE_INV_ITEM_STACKED
) {
514 removed
=item
->Usages
[0];
515 if (count
&& (removed
+ destructed
> count
) ) {
516 removed
= count
- destructed
;
517 item
= RemoveItem( (unsigned int) slot
, removed
);
520 KillSlot( (unsigned int) slot
);
524 KillSlot( (unsigned int) slot
);
529 if (count
&& (destructed
>=count
) )
532 if (Changed
&& Owner
&& Owner
->InParty
) displaymsg
->DisplayConstantString(STR_LOSTITEM
, 0xbcefbc);
537 CREItem
*Inventory::RemoveItem(unsigned int slot
, unsigned int count
)
541 if (slot
>= Slots
.size() ) {
552 if (!count
|| !(item
->Flags
&IE_INV_ITEM_STACKED
) ) {
556 if (count
>= item
->Usages
[0]) {
561 CREItem
*returned
= new CREItem(*item
);
562 item
->Usages
[0]-=count
;
563 returned
->Usages
[0]=(ieWord
) count
;
567 //flags set disable item transfer
568 //except for undroppable and equipped, which are opposite (and shouldn't be set)
569 int Inventory::RemoveItem(const char *resref
, unsigned int flags
, CREItem
**res_item
)
571 size_t slot
= Slots
.size();
572 unsigned int mask
= (flags
^(IE_INV_ITEM_UNDROPPABLE
|IE_INV_ITEM_EQUIPPED
));
573 if (core
->HasFeature(GF_NO_DROP_CAN_MOVE
) ) {
574 mask
&= ~IE_INV_ITEM_UNDROPPABLE
;
577 CREItem
*item
= Slots
[slot
];
582 if (flags
&& (mask
&item
->Flags
)==flags
) {
585 if (!flags
&& (mask
&item
->Flags
)!=0) {
588 if (resref
[0] && strnicmp(item
->ItemResRef
, resref
, 8) ) {
591 *res_item
=RemoveItem( (unsigned int) slot
, 0);
598 void Inventory::SetSlotItem(CREItem
* item
, unsigned int slot
)
600 if (slot
>= Slots
.size() ) {
613 //update the action bar next time
614 if (Owner
->IsSelected()) {
615 core
->SetEventFlag( EF_ACTION
);
619 int Inventory::AddSlotItem(CREItem
* item
, int slot
, int slottype
)
621 int twohanded
= item
->Flags
&IE_INV_ITEM_TWOHANDED
;
623 if ((unsigned)slot
>= Slots
.size()) {
628 //check for equipping weapons
629 if (WhyCantEquip(slot
,twohanded
)) {
634 item
->Flags
|= IE_INV_ITEM_ACQUIRED
;
635 SetSlotItem(item
, slot
);
640 CREItem
*myslot
= Slots
[slot
];
641 if (ItemsAreCompatible( myslot
, item
)) {
642 //calculate with the max movable stock
643 int chunk
= item
->Usages
[0];
644 int newamount
= myslot
->Usages
[0]+chunk
;
645 if (newamount
>myslot
->StackAmount
) {
646 newamount
=myslot
->StackAmount
;
647 chunk
= item
->Usages
[0]-newamount
;
652 myslot
->Flags
|= IE_INV_ITEM_ACQUIRED
;
653 myslot
->Usages
[0] = (ieWord
) (myslot
->Usages
[0] + chunk
);
654 item
->Usages
[0] = (ieWord
) (item
->Usages
[0] - chunk
);
657 if (item
->Usages
[0] == 0) {
667 if (slot
==SLOT_AUTOEQUIP
) {
672 int res
= ASI_FAILED
;
673 int max
= (int) Slots
.size();
674 for (int i
= 0;i
<max
;i
++) {
675 //never autoequip in the magic slot!
678 if ((i
<SLOT_INV
|| i
>LAST_INV
)!=which
)
680 if (!(core
->QuerySlotType(i
)&slottype
))
682 //the slot has been disabled for this actor
683 if (i
>=SLOT_MELEE
&& i
<=LAST_MELEE
) {
684 if (Owner
->GetQuickSlot(i
-SLOT_MELEE
)==0xffff) {
688 int part_res
= AddSlotItem (item
, i
);
689 if (part_res
== ASI_SUCCESS
) return ASI_SUCCESS
;
690 else if (part_res
== ASI_PARTIAL
) res
= ASI_PARTIAL
;
697 void Inventory::TryEquipAll(int slot
)
699 for(int i
=SLOT_INV
;i
<=LAST_INV
;i
++) {
700 CREItem
*item
= Slots
[i
];
706 if (AddSlotItem(item
, slot
) == ASI_SUCCESS
) {
709 //try to stuff it back, it should work
710 if (AddSlotItem(item
, i
) != ASI_SUCCESS
) {
716 int Inventory::AddStoreItem(STOItem
* item
, int action
)
721 // item->PurchasedAmount is the number of items bought
722 // (you can still add grouped objects in a single step,
723 // just set up STOItem)
724 while (item
->PurchasedAmount
) {
725 //the first part of a STOItem is essentially a CREItem
726 temp
= new CREItem();
727 memcpy( temp
, item
, sizeof( CREItem
) );
728 //except the Expired flag
730 if (action
==STA_STEAL
) {
731 temp
->Flags
|= IE_INV_ITEM_STOLEN
;
733 temp
->Flags
&= ~IE_INV_ITEM_SELECTED
;
735 ret
= AddSlotItem( temp
, SLOT_ONLYINVENTORY
);
736 if (ret
!= ASI_SUCCESS
) {
740 if (item
->InfiniteSupply
!=-1) {
741 if (!item
->AmountInStock
) {
744 item
->AmountInStock
--;
746 item
->PurchasedAmount
--;
751 /* could the source item be dropped on the target item to merge them */
752 bool Inventory::ItemsAreCompatible(CREItem
* target
, CREItem
* source
) const
755 //this isn't always ok, please check!
756 printMessage("Inventory","Null item encountered by ItemsAreCompatible()",YELLOW
);
760 if (!(source
->Flags
&IE_INV_ITEM_STACKED
) ) {
764 if (!strnicmp( target
->ItemResRef
, source
->ItemResRef
,8 )) {
770 //depletes a magical item
771 //if flags==0 then magical weapons are not harmed
772 int Inventory::DepleteItem(ieDword flags
)
774 for (size_t i
= 0; i
< Slots
.size(); i
++) {
775 CREItem
*item
= Slots
[i
];
780 //don't harm critical items
781 //don't harm nonmagical items
782 //don't harm indestructible items
783 if ( (item
->Flags
&(IE_INV_ITEM_CRITICAL
|IE_INV_DEPLETABLE
)) != IE_INV_DEPLETABLE
) {
787 //if flags = 0 then weapons are not depleted
789 Item
*itm
= gamedata
->GetItem( item
->ItemResRef
);
792 //if the item is usable in weapon slot, then it is weapon
793 int weapon
= core
->CanUseItemType( SLOT_WEAPON
, itm
);
794 gamedata
->FreeItem( itm
, item
->ItemResRef
, false );
806 // if flags is 0, skips undroppable items
807 // if flags is IE_INV_ITEM_UNDROPPABLE, doesn't skip undroppable items
808 // TODO: once all callers have been checked, this can be reversed to make more sense
809 int Inventory::FindItem(const char *resref
, unsigned int flags
) const
811 unsigned int mask
= (flags
^IE_INV_ITEM_UNDROPPABLE
);
812 if (core
->HasFeature(GF_NO_DROP_CAN_MOVE
) ) {
813 mask
&= ~IE_INV_ITEM_UNDROPPABLE
;
815 for (size_t i
= 0; i
< Slots
.size(); i
++) {
816 const CREItem
*item
= Slots
[i
];
820 if ( mask
& item
->Flags
) {
823 if (resref
[0] && strnicmp(item
->ItemResRef
, resref
, 8) ) {
831 bool Inventory::DropItemAtLocation(unsigned int slot
, unsigned int flags
, Map
*map
, const Point
&loc
)
833 if (slot
>= Slots
.size()) {
836 //these slots will never 'drop' the item
837 if ((slot
==(unsigned int) SLOT_FIST
) || (slot
==(unsigned int) SLOT_MAGIC
)) {
841 CREItem
*item
= Slots
[slot
];
845 //if you want to drop undoppable items, simply set IE_INV_UNDROPPABLE
846 //by default, it won't drop them
847 if ( ((flags
^IE_INV_ITEM_UNDROPPABLE
)&item
->Flags
)!=flags
) {
853 map
->AddItemToLocation(loc
, item
);
859 bool Inventory::DropItemAtLocation(const char *resref
, unsigned int flags
, Map
*map
, const Point
&loc
)
861 bool dropped
= false;
867 //this loop is going from start
868 for (size_t i
= 0; i
< Slots
.size(); i
++) {
869 //these slots will never 'drop' the item
870 if ((i
==(unsigned int) SLOT_FIST
) || (i
==(unsigned int) SLOT_MAGIC
)) {
873 CREItem
*item
= Slots
[i
];
877 //if you want to drop undoppable items, simply set IE_INV_UNDROPPABLE
878 //by default, it won't drop them
879 if ( ((flags
^IE_INV_ITEM_UNDROPPABLE
)&item
->Flags
)!=flags
) {
882 if (resref
[0] && strnicmp(item
->ItemResRef
, resref
, 8) ) {
885 // mark it as unequipped, so it doesn't cause problems in stores
886 item
->Flags
&= ~ IE_INV_ITEM_EQUIPPED
;
887 map
->AddItemToLocation(loc
, item
);
890 KillSlot((unsigned int) i
);
891 //if it isn't all items then we stop here
898 CREItem
*Inventory::GetSlotItem(unsigned int slot
) const
900 if (slot
>= Slots
.size() ) {
907 ieDword
Inventory::GetItemFlag(unsigned int slot
) const
909 const CREItem
*item
= GetSlotItem(slot
);
916 bool Inventory::ChangeItemFlag(unsigned int slot
, ieDword arg
, int op
)
918 CREItem
*item
= GetSlotItem(slot
);
923 case BM_SET
: item
->Flags
= arg
; break;
924 case BM_OR
: item
->Flags
|= arg
; break;
925 case BM_NAND
: item
->Flags
&= ~arg
; break;
926 case BM_XOR
: item
->Flags
^= arg
; break;
927 case BM_AND
: item
->Flags
&= arg
; break;
932 //this is the low level equipping
933 //all checks have been made previously
934 bool Inventory::EquipItem(unsigned int slot
)
936 ITMExtHeader
*header
;
939 //maybe assertion too?
942 CREItem
*item
= GetSlotItem(slot
);
949 // add effects of an item just being equipped to actor's effect queue
950 int effect
= core
->QuerySlotEffects( slot
);
951 Item
*itm
= gamedata
->GetItem(item
->ItemResRef
);
953 printf("Invalid item Equipped: %s Slot: %d\n", item
->ItemResRef
, slot
);
957 case SLOT_EFFECT_LEFT
:
958 //no idea if the offhand weapon has style, or simply the right
959 //hand style is dominant
960 UpdateShieldAnimation(itm
);
962 case SLOT_EFFECT_MELEE
:
963 //if weapon is ranged, then find quarrel for it and equip that
967 header
= itm
->GetExtHeader(EquippedHeader
);
968 if (header
&& header
->AttackType
== ITEM_AT_BOW
) {
969 //find the ranged projectile associated with it.
970 slot
= FindRangedProjectile(header
->ProjectileQualifier
);
971 EquippedHeader
= itm
->GetWeaponHeaderNumber(true);
972 } else if (header
&& header
->AttackType
== ITEM_AT_PROJECTILE
) {
973 EquippedHeader
= itm
->GetWeaponHeaderNumber(true);
975 EquippedHeader
= itm
->GetWeaponHeaderNumber(false);
977 header
= itm
->GetExtHeader(EquippedHeader
);
979 SetEquippedSlot(slot
, EquippedHeader
);
980 if (slot
!= IW_NO_EQUIPPED
) {
981 Owner
->SetupQuickSlot(ACT_WEAPON1
+weaponslot
, slot
+SLOT_MELEE
, EquippedHeader
);
983 effect
= 0; // SetEquippedSlot will already call AddSlotEffects
984 UpdateWeaponAnimation();
987 case SLOT_EFFECT_MISSILE
:
988 //Get the ranged header of the projectile (so we theoretically allow shooting of daggers)
989 EquippedHeader
= itm
->GetWeaponHeaderNumber(true);
990 header
= itm
->GetExtHeader(EquippedHeader
);
992 weaponslot
= FindTypedRangedWeapon(header
->ProjectileQualifier
);
993 if (weaponslot
!= SLOT_FIST
) {
994 weaponslot
-= SLOT_MELEE
;
995 SetEquippedSlot((ieWordSigned
) (slot
-SLOT_MELEE
), EquippedHeader
);
996 //It is unsure if we can have multiple equipping headers for bows/arrow
997 //It is unclear which item's header index should go there
998 Owner
->SetupQuickSlot(ACT_WEAPON1
+weaponslot
, slot
, 0);
1000 UpdateWeaponAnimation();
1003 case SLOT_EFFECT_HEAD
:
1004 Owner
->SetUsedHelmet(itm
->AnimationType
);
1006 case SLOT_EFFECT_ITEM
:
1007 //adjusting armour level if needed
1009 int l
= itm
->AnimationType
[0]-'1';
1011 Owner
->SetBase(IE_ARMOR_TYPE
, l
);
1013 UpdateShieldAnimation(itm
);
1018 gamedata
->FreeItem(itm
, item
->ItemResRef
, false);
1020 if (item
->Flags
& IE_INV_ITEM_CURSED
) {
1021 item
->Flags
|=IE_INV_ITEM_UNDROPPABLE
;
1023 AddSlotEffects( slot
);
1028 //the removecurse flag will check if it is possible to move the item to the inventory
1029 //after a remove curse spell
1030 bool Inventory::UnEquipItem(unsigned int slot
, bool removecurse
)
1032 CREItem
*item
= GetSlotItem(slot
);
1037 if (item
->Flags
& IE_INV_ITEM_MOVABLE
) {
1038 item
->Flags
&=~IE_INV_ITEM_UNDROPPABLE
;
1040 if (FindCandidateSlot(SLOT_INVENTORY
,0,item
->ItemResRef
)<0) {
1044 if (!core
->HasFeature(GF_NO_DROP_CAN_MOVE
) || (item
->Flags
&IE_INV_ITEM_CURSED
) ) {
1045 if (item
->Flags
& IE_INV_ITEM_UNDROPPABLE
) {
1049 item
->Flags
&= ~IE_INV_ITEM_EQUIPPED
; //no idea if this is needed, won't hurt
1053 // find the projectile
1057 //returns equipped code
1058 int Inventory::FindRangedProjectile(unsigned int type
) const
1060 for(int i
=SLOT_RANGED
;i
<=LAST_RANGED
;i
++) {
1063 const Item
*itm
= GetItemPointer(i
, Slot
);
1065 ITMExtHeader
*ext_header
= itm
->GetExtHeader(0);
1066 unsigned int weapontype
= 0;
1068 weapontype
= ext_header
->ProjectileQualifier
;
1070 gamedata
->FreeItem(itm
, Slot
->ItemResRef
, false);
1071 if (weapontype
& type
) {
1072 return i
-SLOT_MELEE
;
1075 return IW_NO_EQUIPPED
;
1078 // find which bow is attached to the projectile marked by 'Equipped'
1080 int Inventory::FindRangedWeapon() const
1082 if (Equipped
>=0) return SLOT_FIST
;
1083 return FindSlotRangedWeapon(Equipped
+SLOT_MELEE
);
1086 int Inventory::FindSlotRangedWeapon(unsigned int slot
) const
1088 if ((int)slot
>= SLOT_MELEE
) return SLOT_FIST
;
1090 Item
*itm
= GetItemPointer(slot
, Slot
);
1091 if (!itm
) return SLOT_FIST
;
1093 //always look for a ranged header when looking for a projectile/projector
1094 ITMExtHeader
*ext_header
= itm
->GetWeaponHeader(true);
1095 unsigned int type
= 0;
1097 type
= ext_header
->ProjectileQualifier
;
1099 gamedata
->FreeItem(itm
, Slot
->ItemResRef
, false);
1100 return FindTypedRangedWeapon(type
);
1104 // find bow for a specific projectile type
1105 int Inventory::FindTypedRangedWeapon(unsigned int type
) const
1110 for(int i
=SLOT_MELEE
;i
<=LAST_MELEE
;i
++) {
1113 const Item
*itm
= GetItemPointer(i
, Slot
);
1115 //always look for a ranged header when looking for a projectile/projector
1116 ITMExtHeader
*ext_header
= itm
->GetWeaponHeader(true);
1119 weapontype
= ext_header
->ProjectileQualifier
;
1121 gamedata
->FreeItem(itm
, Slot
->ItemResRef
, false);
1122 if (weapontype
& type
) {
1129 void Inventory::SetHeadSlot(int arg
) { SLOT_HEAD
=arg
; }
1130 void Inventory::SetFistSlot(int arg
) { SLOT_FIST
=arg
; }
1131 void Inventory::SetMagicSlot(int arg
) { SLOT_MAGIC
=arg
; }
1132 void Inventory::SetWeaponSlot(int arg
)
1134 if (SLOT_MELEE
==-1) {
1140 //ranged slots should be before MELEE slots
1141 void Inventory::SetRangedSlot(int arg
)
1143 assert(SLOT_MELEE
!=-1);
1144 if (SLOT_RANGED
==-1) {
1150 void Inventory::SetQuickSlot(int arg
)
1152 if (SLOT_QUICK
==-1) {
1158 void Inventory::SetInventorySlot(int arg
)
1166 //multiple shield slots are allowed
1167 //but in this case they should be interspersed with melee slots
1168 void Inventory::SetShieldSlot(int arg
)
1170 if (SLOT_LEFT
!=-1) {
1171 assert(SLOT_MELEE
+1==SLOT_LEFT
);
1178 int Inventory::GetHeadSlot()
1183 int Inventory::GetFistSlot()
1188 int Inventory::GetMagicSlot()
1193 int Inventory::GetWeaponSlot()
1198 int Inventory::GetQuickSlot()
1203 int Inventory::GetInventorySlot()
1208 //if shield slot is empty, call again for fist slot!
1209 int Inventory::GetShieldSlot() const
1212 if (Equipped
>=0 && Equipped
<=3) {
1213 return Equipped
*2+SLOT_MELEE
+1;
1220 int Inventory::GetEquippedSlot() const
1222 if (Equipped
== IW_NO_EQUIPPED
) {
1225 if (IWD2
&& Equipped
>=0) {
1226 //i've absolutely NO idea what is this 4 (Avenger)
1227 //Equipped should be 0-3 in iWD2, no???
1228 if (Equipped
>= 4) {
1231 return Equipped
*2+SLOT_MELEE
;
1233 return Equipped
+SLOT_MELEE
;
1236 bool Inventory::SetEquippedSlot(ieWordSigned slotcode
, ieWord header
)
1238 EquippedHeader
= header
;
1240 //doesn't work if magic slot is used, refresh the magic slot just in case
1241 if (HasItemInSlot("",SLOT_MAGIC
) && (slotcode
!=SLOT_MAGIC
-SLOT_MELEE
)) {
1242 Equipped
= SLOT_MAGIC
-SLOT_MELEE
;
1243 UpdateWeaponAnimation();
1247 //if it is an illegal code, make it fist
1248 if ((size_t) (slotcode
+SLOT_MELEE
)>Slots
.size()) {
1249 slotcode
=IW_NO_EQUIPPED
;
1252 //unequipping (fist slot will be used now)
1253 if (slotcode
== IW_NO_EQUIPPED
|| !HasItemInSlot("",slotcode
+SLOT_MELEE
)) {
1254 if (Equipped
!= IW_NO_EQUIPPED
) {
1255 RemoveSlotEffects( SLOT_MELEE
+Equipped
);
1257 Equipped
= IW_NO_EQUIPPED
;
1258 //fist slot equipping effects
1259 AddSlotEffects(SLOT_FIST
);
1260 UpdateWeaponAnimation();
1264 //equipping a weapon, but remove its effects first
1265 if (Equipped
!= IW_NO_EQUIPPED
) {
1266 RemoveSlotEffects( SLOT_MELEE
+Equipped
);
1269 Equipped
= slotcode
;
1270 int effects
= core
->QuerySlotEffects( SLOT_MELEE
+Equipped
);
1272 CREItem
* item
= GetSlotItem(SLOT_MELEE
+Equipped
);
1273 item
->Flags
|=IE_INV_ITEM_EQUIPPED
;
1274 if (item
->Flags
& IE_INV_ITEM_CURSED
) {
1275 item
->Flags
|=IE_INV_ITEM_UNDROPPABLE
;
1277 AddSlotEffects( SLOT_MELEE
+Equipped
);
1279 UpdateWeaponAnimation();
1283 int Inventory::GetEquipped() const
1288 int Inventory::GetEquippedHeader() const
1290 return EquippedHeader
;
1293 //returns the fist weapon if there is nothing else
1294 //This will return the actual weapon, I mean the bow in the case of bow+arrow combination
1295 CREItem
*Inventory::GetUsedWeapon(bool leftorright
, int &slot
) const
1299 if (SLOT_MAGIC
!=-1) {
1301 ret
= GetSlotItem(slot
);
1302 if (ret
&& ret
->ItemResRef
[0]) {
1308 slot
= GetShieldSlot();
1310 ret
= GetSlotItem(slot
);
1314 //we don't want to return fist for shield slot
1319 slot
= GetEquippedSlot();
1320 if((core
->QuerySlotEffects(slot
) & SLOT_EFFECT_MISSILE
) == SLOT_EFFECT_MISSILE
) {
1321 slot
= FindRangedWeapon();
1323 ret
= GetSlotItem(slot
);
1325 //return fist weapon
1327 ret
= GetSlotItem(slot
);
1332 // Returns index of first empty slot or slot with the same
1333 // item and not full stack. On fail returns -1
1334 // Can be used to check for full inventory
1335 int Inventory::FindCandidateSlot(int slottype
, size_t first_slot
, const char *resref
)
1337 if (first_slot
>= Slots
.size())
1340 for (size_t i
= first_slot
; i
< Slots
.size(); i
++) {
1341 if (!(core
->QuerySlotType( (unsigned int) i
) & slottype
) ) {
1345 CREItem
*item
= Slots
[i
];
1348 return (int) i
; //this is a good empty slot
1353 if (!(item
->Flags
&IE_INV_ITEM_STACKED
) ) {
1356 if (strnicmp( item
->ItemResRef
, resref
, 8 )!=0) {
1359 // check if the item fits in this slot, we use the cached
1360 // stackamount value
1361 if (item
->Usages
[0]<item
->StackAmount
) {
1369 void Inventory::AddSlotItemRes(const ieResRef ItemResRef
, int SlotID
, int Charge0
, int Charge1
, int Charge2
)
1371 CREItem
*TmpItem
= new CREItem();
1372 strnlwrcpy(TmpItem
->ItemResRef
, ItemResRef
, 8);
1374 TmpItem
->Usages
[0]=(ieWord
) Charge0
;
1375 TmpItem
->Usages
[1]=(ieWord
) Charge1
;
1376 TmpItem
->Usages
[2]=(ieWord
) Charge2
;
1378 if (core
->ResolveRandomItem(TmpItem
) && gamedata
->Exists(TmpItem
->ItemResRef
, IE_ITM_CLASS_ID
)) {
1379 AddSlotItem( TmpItem
, SlotID
);
1386 void Inventory::SetSlotItemRes(const ieResRef ItemResRef
, int SlotID
, int Charge0
, int Charge1
, int Charge2
)
1389 CREItem
*TmpItem
= new CREItem();
1390 strnlwrcpy(TmpItem
->ItemResRef
, ItemResRef
, 8);
1392 TmpItem
->Usages
[0]=(ieWord
) Charge0
;
1393 TmpItem
->Usages
[1]=(ieWord
) Charge1
;
1394 TmpItem
->Usages
[2]=(ieWord
) Charge2
;
1396 if (core
->ResolveRandomItem(TmpItem
) && gamedata
->Exists(TmpItem
->ItemResRef
, IE_ITM_CLASS_ID
)) {
1397 SetSlotItem( TmpItem
, SlotID
);
1402 //if the item isn't creatable, we still destroy the old item
1408 void Inventory::BreakItemSlot(ieDword slot
)
1413 const Item
*itm
= GetItemPointer(slot
, Slot
);
1415 //if it is the magic weapon slot, don't break it, just remove it, because it couldn't be removed
1416 if(slot
==(unsigned int) SLOT_MAGIC
) {
1419 memcpy(newItem
, itm
->ReplacementItem
,sizeof(newItem
) );
1421 gamedata
->FreeItem( itm
, Slot
->ItemResRef
, true );
1422 //this depends on setslotitemres using setslotitem
1423 SetSlotItemRes(newItem
, slot
, 0,0,0);
1426 void Inventory::dump()
1428 printf( "INVENTORY:\n" );
1429 for (unsigned int i
= 0; i
< Slots
.size(); i
++) {
1430 CREItem
* itm
= Slots
[i
];
1436 printf ( "%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
->StackAmount
, itm
->Weight
);
1439 printf( "Equipped: %d\n", Equipped
);
1442 printf( "Total weight: %d\n", Weight
);
1445 void Inventory::EquipBestWeapon(int flags
)
1449 ieDword best_slot
= SLOT_FIST
;
1450 ITMExtHeader
*header
;
1452 char AnimationType
[2]={0,0};
1453 ieWord MeleeAnimation
[3]={100,0,0};
1455 //cannot change equipment when holding magic weapons
1456 if (Equipped
== SLOT_MAGIC
-SLOT_MELEE
) {
1460 if (flags
&EQUIP_RANGED
) {
1461 for(i
=SLOT_RANGED
;i
<LAST_RANGED
;i
++) {
1462 const Item
*itm
= GetItemPointer(i
, Slot
);
1465 int tmp
= itm
->GetDamagePotential(true, header
);
1469 memcpy(AnimationType
,itm
->AnimationType
,sizeof(AnimationType
) );
1470 memcpy(MeleeAnimation
,header
->MeleeAnimation
,sizeof(MeleeAnimation
) );
1472 gamedata
->FreeItem( itm
, Slot
->ItemResRef
, false );
1475 //ranged melee weapons like throwing daggers (not bows!)
1476 for(i
=SLOT_MELEE
;i
<=LAST_MELEE
;i
++) {
1477 const Item
*itm
= GetItemPointer(i
, Slot
);
1480 int tmp
= itm
->GetDamagePotential(true, header
);
1484 memcpy(AnimationType
,itm
->AnimationType
,sizeof(AnimationType
) );
1485 memcpy(MeleeAnimation
,header
->MeleeAnimation
,sizeof(MeleeAnimation
) );
1487 gamedata
->FreeItem( itm
, Slot
->ItemResRef
, false );
1491 if (flags
&EQUIP_MELEE
) {
1492 for(i
=SLOT_MELEE
;i
<=LAST_MELEE
;i
++) {
1493 const Item
*itm
= GetItemPointer(i
, Slot
);
1495 //the Slot flag is enough for this
1496 //though we need animation type/damagepotential anyway
1497 if (Slot
->Flags
&IE_INV_ITEM_BOW
) continue;
1499 int tmp
= itm
->GetDamagePotential(false, header
);
1503 memcpy(AnimationType
,itm
->AnimationType
,sizeof(AnimationType
) );
1504 memcpy(MeleeAnimation
,header
->MeleeAnimation
,sizeof(MeleeAnimation
) );
1506 gamedata
->FreeItem( itm
, Slot
->ItemResRef
, false );
1510 EquipItem(best_slot
);
1511 UpdateWeaponAnimation();
1514 #define ID_NONEED 0 //id is not important
1515 #define ID_NEED 1 //id is important
1516 #define ID_NO 2 //shouldn't id
1518 /* returns true if there are more item usages not fitting in given array */
1519 bool Inventory::GetEquipmentInfo(ItemExtHeader
*array
, int startindex
, int count
)
1523 memset(array
, 0, count
* sizeof(ItemExtHeader
) );
1524 for(unsigned int idx
=0;idx
<Slots
.size();idx
++) {
1525 if (!core
->QuerySlotEffects(idx
)) {
1530 const Item
*itm
= GetItemPointer(idx
, slot
);
1534 for(int ehc
=0;ehc
<itm
->ExtHeaderCount
;ehc
++) {
1535 ITMExtHeader
*ext_header
= itm
->ext_headers
+ehc
;
1536 if (ext_header
->Location
!=ITEM_LOC_EQUIPMENT
) {
1539 //skipping if we cannot use the item
1540 int idreq1
= (slot
->Flags
&IE_INV_ITEM_IDENTIFIED
);
1541 int idreq2
= ext_header
->IDReq
;
1544 if (idreq1
) continue;
1547 if (!idreq1
) continue;
1552 if (actual
>startindex
) {
1554 //store the item, return if we can't store more
1556 gamedata
->FreeItem(itm
, slot
->ItemResRef
, false);
1560 memcpy(array
[pos
].itemname
, slot
->ItemResRef
, sizeof(ieResRef
) );
1561 array
[pos
].slot
= idx
;
1562 array
[pos
].headerindex
= ehc
;
1563 int slen
= ((char *) &(array
[pos
].itemname
)) -((char *) &(array
[pos
].AttackType
));
1564 memcpy(&(array
[pos
].AttackType
), &(ext_header
->AttackType
), slen
);
1565 if (ext_header
->Charges
) {
1566 //don't modify ehc, it is a counter
1567 if (ehc
>=CHARGE_COUNTERS
) {
1568 array
[pos
].Charges
=slot
->Usages
[0];
1570 array
[pos
].Charges
=slot
->Usages
[ehc
];
1573 array
[pos
].Charges
=0xffff;
1578 gamedata
->FreeItem(itm
, slot
->ItemResRef
, false);
1584 //The slot index value is optional, if you supply it,
1585 // then ItemExcl will be returned as if the item was already unequipped
1586 ieDword
Inventory::GetEquipExclusion(int index
) const
1592 const Item
*itm
= GetItemPointer(index
, slot
);
1596 ieDword ret
= ItemExcl
&~itm
->ItemExcl
;
1597 gamedata
->FreeItem(itm
, slot
->ItemResRef
, false);
1601 void Inventory::UpdateShieldAnimation(Item
*it
)
1603 char AnimationType
[2]={0,0};
1604 int WeaponType
= -1;
1607 memcpy(AnimationType
, it
->AnimationType
, 2);
1608 if (core
->CanUseItemType(SLOT_WEAPON
, it
))
1609 WeaponType
= IE_ANI_WEAPON_2W
;
1611 WeaponType
= IE_ANI_WEAPON_1H
;
1613 WeaponType
= IE_ANI_WEAPON_1H
;
1615 Owner
->SetUsedShield(AnimationType
, WeaponType
);
1618 void Inventory::UpdateWeaponAnimation()
1620 int slot
= GetEquippedSlot();
1621 int effect
= core
->QuerySlotEffects( slot
);
1622 if (effect
== SLOT_EFFECT_MISSILE
) {
1624 slot
= FindRangedWeapon();
1626 int WeaponType
= -1;
1628 char AnimationType
[2]={0,0};
1629 ieWord MeleeAnimation
[3]={100,0,0};
1634 ITMExtHeader
*header
= 0;
1635 const Item
*itm
= GetItemPointer(slot
, Slot
);
1637 itm
->GetDamagePotential(false, header
);
1638 memcpy(AnimationType
,itm
->AnimationType
,sizeof(AnimationType
) );
1639 //for twohanded flag, you don't need itm
1640 if (Slot
->Flags
& IE_INV_ITEM_TWOHANDED
)
1641 WeaponType
= IE_ANI_WEAPON_2H
;
1644 // Examine shield slot to check if we're using two weapons
1645 // TODO: for consistency, use same Item* access method as above
1646 bool twoweapon
= false;
1647 int slot
= GetShieldSlot();
1650 si
= GetSlotItem( (ieDword
) slot
);
1653 Item
* it
= gamedata
->GetItem(si
->ItemResRef
);
1654 if (core
->CanUseItemType(SLOT_WEAPON
, it
))
1656 gamedata
->FreeItem(it
, si
->ItemResRef
, false);
1660 WeaponType
= IE_ANI_WEAPON_2W
;
1662 WeaponType
= IE_ANI_WEAPON_1H
;
1667 memcpy(MeleeAnimation
,header
->MeleeAnimation
, sizeof(MeleeAnimation
) );
1669 gamedata
->FreeItem( itm
, Slot
->ItemResRef
, false );
1670 Owner
->SetUsedWeapon(AnimationType
, MeleeAnimation
, WeaponType
);
1673 //this function will also check disabled slots (if that feature will be imped)
1674 bool Inventory::IsSlotBlocked(int slot
) const
1676 if (slot
<SLOT_MELEE
) return false;
1677 if (slot
>LAST_MELEE
) return false;
1682 otherslot
= SLOT_LEFT
;
1684 return HasItemInSlot("",otherslot
);
1687 inline bool Inventory::TwoHandedInSlot(int slot
) const
1691 item
= GetSlotItem(slot
);
1692 if (!item
) return false;
1693 if (item
->Flags
&IE_INV_ITEM_TWOHANDED
) {
1699 int Inventory::WhyCantEquip(int slot
, int twohanded
) const
1701 // check only for hand slots
1702 if ((slot
<SLOT_MELEE
|| slot
>LAST_MELEE
) && (slot
!= SLOT_LEFT
) ) {
1706 //magic items have the highest priority
1707 if ( HasItemInSlot("", SLOT_MAGIC
)) {
1708 //magic weapon is in use
1709 return STR_MAGICWEAPON
;
1712 //can't equip in shield slot if a weapon slot is twohanded
1713 for (int i
=SLOT_MELEE
; i
<=LAST_MELEE
;i
++) {
1719 otherslot
= SLOT_LEFT
;
1721 if (slot
==otherslot
) {
1722 if (TwoHandedInSlot(i
)) {
1723 return STR_TWOHANDED_USED
;
1730 if (slot
>=SLOT_MELEE
&&slot
<=LAST_MELEE
&& (slot
&1) ) {
1731 return STR_NOT_IN_OFFHAND
;
1734 if (slot
==SLOT_LEFT
) {
1735 return STR_NOT_IN_OFFHAND
;
1738 if (IsSlotBlocked(slot
)) {
1739 //cannot equip two handed while shield slot is in use?
1740 return STR_OFFHAND_USED
;
1746 //recharge items on rest, if rest was partial, recharge only 'hours'
1747 //if this latter functionality is unwanted, then simply don't recharge if
1749 void Inventory::ChargeAllItems(int hours
)
1751 //this loop is going from start
1752 for (size_t i
= 0; i
< Slots
.size(); i
++) {
1753 CREItem
*item
= Slots
[i
];
1758 Item
*itm
= gamedata
->GetItem( item
->ItemResRef
);
1761 for(int h
=0;h
<CHARGE_COUNTERS
;h
++) {
1762 ITMExtHeader
*header
= itm
->GetExtHeader(h
);
1763 if (header
&& (header
->RechargeFlags
&IE_ITEM_RECHARGE
)) {
1764 unsigned short add
= header
->Charges
;
1765 if (hours
&& add
>hours
) add
=hours
;
1766 add
+=item
->Usages
[h
];
1767 if(add
>header
->Charges
) add
=header
->Charges
;
1768 item
->Usages
[h
]=add
;
1771 gamedata
->FreeItem( itm
, item
->ItemResRef
, false );
1775 #define ITM_STEALING (IE_INV_ITEM_UNSTEALABLE | IE_INV_ITEM_MOVABLE | IE_INV_ITEM_EQUIPPED)
1776 unsigned int Inventory::FindStealableItem()
1781 slot
= core
->Roll(1, Slots
.size(),-1);
1784 printf("Start Slot: %d, increment: %d\n", slot
, inc
);
1785 //as the unsigned value underflows, it will be greater than Slots.size()
1786 for(;slot
<Slots
.size(); slot
+=inc
) {
1787 CREItem
*item
= Slots
[slot
];
1788 //can't steal empty slot
1789 if (!item
) continue;
1790 //bit 1 is stealable slot
1791 if (!(core
->QuerySlotFlags(slot
)&1) ) continue;
1792 //can't steal equipped weapon
1793 if ((unsigned int) (Equipped
+SLOT_MELEE
) == core
->QuerySlot(slot
)) continue;
1794 //can't steal flagged items
1795 if ((item
->Flags
& ITM_STEALING
) != IE_INV_ITEM_MOVABLE
) continue;
1801 // extension to allow more or less than head gear to avert critical hits:
1802 // If an item with bit 25 set is equipped in a non-helmet slot, aversion is enabled
1803 // If an item with bit 25 set is equipped in a helmet slot, aversion is disabled
1804 bool Inventory::ProvidesCriticalAversion()
1806 for (size_t i
= 0; i
< Slots
.size(); i
++) {
1807 CREItem
*item
= Slots
[i
];
1808 if (!item
|| ! (item
->Flags
& IE_INV_ITEM_EQUIPPED
)) {
1812 Item
*itm
= gamedata
->GetItem(item
->ItemResRef
);
1817 for (int h
= 0; h
< itm
->ExtHeaderCount
; h
++) {
1818 ITMExtHeader
*header
= itm
->GetExtHeader(h
);
1819 if ((int)i
== SLOT_HEAD
) {
1820 if (header
&& (header
->RechargeFlags
& IE_ITEM_TOGGLE_CRITS
)) {
1826 if (header
&& (header
->RechargeFlags
& IE_ITEM_TOGGLE_CRITS
)) {