diff --git a/changelog.txt b/changelog.txt index 0d4412bbd..5806a984f 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,7 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50) ------------------------------------------------------- == 10/17/2016 == Uleat: Moved namespace ItemField from item_instance.h to shareddb.cpp - the only place it is used +Uleat: Separated class Inventory from item_instance files into inventory_profile files == 10/16/2016 == Uleat: Renamed struct EQEmu::ItemBase to EQEmu::ItemData and class ItemInst to EQEmu::ItemInstance diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index fbfc9305e..cecb47d12 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -35,6 +35,7 @@ SET(common_sources faction.cpp guild_base.cpp guilds.cpp + inventory_profile.cpp inventory_slot.cpp ipc_mutex.cpp item_data.cpp @@ -156,6 +157,7 @@ SET(common_headers global_define.h guild_base.h guilds.h + inventory_profile.h inventory_slot.h ipc_mutex.h item_data.h diff --git a/common/extprofile.h b/common/extprofile.h index e2dae7579..bdb746068 100644 --- a/common/extprofile.h +++ b/common/extprofile.h @@ -19,7 +19,7 @@ #define EXTENDED_PROFILE_H #include "eq_packet_structs.h" -#include "item_instance.h" +#include "inventory_profile.h" #pragma pack(1) diff --git a/common/inventory_profile.cpp b/common/inventory_profile.cpp new file mode 100644 index 000000000..9e4c4fc94 --- /dev/null +++ b/common/inventory_profile.cpp @@ -0,0 +1,1419 @@ +/* EQEMu: Everquest Server Emulator + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include "inventory_profile.h" +#include "textures.h" +#include "eqemu_logsys.h" +//#include "classes.h" +//#include "global_define.h" +//#include "item_instance.h" +//#include "races.h" +//#include "rulesys.h" +//#include "shareddb.h" +#include "string_util.h" + +#include "../common/light_source.h" + +//#include + +#include + +std::list dirty_inst; + + +// +// class ItemInstQueue +// +ItemInstQueue::~ItemInstQueue() +{ + for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) { + safe_delete(*iter); + } + m_list.clear(); +} + +// Put item onto back of queue +void ItemInstQueue::push(EQEmu::ItemInstance* inst) +{ + m_list.push_back(inst); +} + +// Put item onto front of queue +void ItemInstQueue::push_front(EQEmu::ItemInstance* inst) +{ + m_list.push_front(inst); +} + +// Remove item from front of queue +EQEmu::ItemInstance* ItemInstQueue::pop() +{ + if (m_list.empty()) + return nullptr; + + EQEmu::ItemInstance* inst = m_list.front(); + m_list.pop_front(); + return inst; +} + +// Remove item from back of queue +EQEmu::ItemInstance* ItemInstQueue::pop_back() +{ + if (m_list.empty()) + return nullptr; + + EQEmu::ItemInstance* inst = m_list.back(); + m_list.pop_back(); + return inst; +} + +// Look at item at front of queue +EQEmu::ItemInstance* ItemInstQueue::peek_front() const +{ + return (m_list.empty()) ? nullptr : m_list.front(); +} + + +// +// class Inventory +// +Inventory::~Inventory() +{ + for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { + safe_delete(iter->second); + } + m_worn.clear(); + + for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { + safe_delete(iter->second); + } + m_inv.clear(); + + for (auto iter = m_bank.begin(); iter != m_bank.end(); ++iter) { + safe_delete(iter->second); + } + m_bank.clear(); + + for (auto iter = m_shbank.begin(); iter != m_shbank.end(); ++iter) { + safe_delete(iter->second); + } + m_shbank.clear(); + + for (auto iter = m_trade.begin(); iter != m_trade.end(); ++iter) { + safe_delete(iter->second); + } + m_trade.clear(); +} + +void Inventory::CleanDirty() { + auto iter = dirty_inst.begin(); + while (iter != dirty_inst.end()) { + delete (*iter); + ++iter; + } + dirty_inst.clear(); +} + +void Inventory::MarkDirty(EQEmu::ItemInstance *inst) { + if (inst) { + dirty_inst.push_back(inst); + } +} + +// Retrieve item at specified slot; returns false if item not found +EQEmu::ItemInstance* Inventory::GetItem(int16 slot_id) const +{ + EQEmu::ItemInstance* result = nullptr; + + // Cursor + if (slot_id == EQEmu::inventory::slotCursor) { + // Cursor slot + result = m_cursor.peek_front(); + } + + // Non bag slots + else if (slot_id >= EQEmu::legacy::TRADE_BEGIN && slot_id <= EQEmu::legacy::TRADE_END) { + result = _GetItem(m_trade, slot_id); + } + else if (slot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_END) { + // Shared Bank slots + result = _GetItem(m_shbank, slot_id); + } + else if (slot_id >= EQEmu::legacy::BANK_BEGIN && slot_id <= EQEmu::legacy::BANK_END) { + // Bank slots + result = _GetItem(m_bank, slot_id); + } + else if ((slot_id >= EQEmu::legacy::GENERAL_BEGIN && slot_id <= EQEmu::legacy::GENERAL_END)) { + // Personal inventory slots + result = _GetItem(m_inv, slot_id); + } + else if ((slot_id >= EQEmu::legacy::EQUIPMENT_BEGIN && slot_id <= EQEmu::legacy::EQUIPMENT_END) || + (slot_id >= EQEmu::legacy::TRIBUTE_BEGIN && slot_id <= EQEmu::legacy::TRIBUTE_END) || (slot_id == EQEmu::inventory::slotPowerSource)) { + // Equippable slots (on body) + result = _GetItem(m_worn, slot_id); + } + + // Inner bag slots + else if (slot_id >= EQEmu::legacy::TRADE_BAGS_BEGIN && slot_id <= EQEmu::legacy::TRADE_BAGS_END) { + // Trade bag slots + EQEmu::ItemInstance* inst = _GetItem(m_trade, Inventory::CalcSlotId(slot_id)); + if (inst && inst->IsClassBag()) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + else if (slot_id >= EQEmu::legacy::SHARED_BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_BAGS_END) { + // Shared Bank bag slots + EQEmu::ItemInstance* inst = _GetItem(m_shbank, Inventory::CalcSlotId(slot_id)); + if (inst && inst->IsClassBag()) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + else if (slot_id >= EQEmu::legacy::BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::BANK_BAGS_END) { + // Bank bag slots + EQEmu::ItemInstance* inst = _GetItem(m_bank, Inventory::CalcSlotId(slot_id)); + if (inst && inst->IsClassBag()) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + else if (slot_id >= EQEmu::legacy::CURSOR_BAG_BEGIN && slot_id <= EQEmu::legacy::CURSOR_BAG_END) { + // Cursor bag slots + EQEmu::ItemInstance* inst = m_cursor.peek_front(); + if (inst && inst->IsClassBag()) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + else if (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END) { + // Personal inventory bag slots + EQEmu::ItemInstance* inst = _GetItem(m_inv, Inventory::CalcSlotId(slot_id)); + if (inst && inst->IsClassBag()) { + result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); + } + } + + return result; +} + +// Retrieve item at specified position within bag +EQEmu::ItemInstance* Inventory::GetItem(int16 slot_id, uint8 bagidx) const +{ + return GetItem(Inventory::CalcSlotId(slot_id, bagidx)); +} + +// Put an item snto specified slot +int16 Inventory::PutItem(int16 slot_id, const EQEmu::ItemInstance& inst) +{ + // Clean up item already in slot (if exists) + DeleteItem(slot_id); + + if (!inst) { + // User is effectively deleting the item + // in the slot, why hold a null ptr in map<>? + return slot_id; + } + + // Delegate to internal method + return _PutItem(slot_id, inst.Clone()); +} + +int16 Inventory::PushCursor(const EQEmu::ItemInstance& inst) +{ + m_cursor.push(inst.Clone()); + return EQEmu::inventory::slotCursor; +} + +EQEmu::ItemInstance* Inventory::GetCursorItem() +{ + return m_cursor.peek_front(); +} + +// Swap items in inventory +bool Inventory::SwapItem(int16 slot_a, int16 slot_b) +{ + // Temp holding areas for a and b + EQEmu::ItemInstance* inst_a = GetItem(slot_a); + EQEmu::ItemInstance* inst_b = GetItem(slot_b); + + if (inst_a) { if (!inst_a->IsSlotAllowed(slot_b)) { return false; } } + if (inst_b) { if (!inst_b->IsSlotAllowed(slot_a)) { return false; } } + + _PutItem(slot_a, inst_b); // Copy b->a + _PutItem(slot_b, inst_a); // Copy a->b + + return true; +} + +// Remove item from inventory (with memory delete) +bool Inventory::DeleteItem(int16 slot_id, uint8 quantity) +{ + // Pop item out of inventory map (or queue) + EQEmu::ItemInstance* item_to_delete = PopItem(slot_id); + + // Determine if object should be fully deleted, or + // just a quantity of charges of the item can be deleted + if (item_to_delete && (quantity > 0)) { + + item_to_delete->SetCharges(item_to_delete->GetCharges() - quantity); + + // If there are no charges left on the item, + if (item_to_delete->GetCharges() <= 0) { + // If the item is stackable (e.g arrows), or + // the item is not stackable, and is not a charged item, or is expendable, delete it + if (item_to_delete->IsStackable() || + (!item_to_delete->IsStackable() && + ((item_to_delete->GetItem()->MaxCharges == 0) || item_to_delete->IsExpendable())) + ) { + // Item can now be destroyed + Inventory::MarkDirty(item_to_delete); + return true; + } + } + + // Charges still exist, or it is a charged item that is not expendable. Put back into inventory + _PutItem(slot_id, item_to_delete); + return false; + } + + Inventory::MarkDirty(item_to_delete); + return true; + +} + +// Checks All items in a bag for No Drop +bool Inventory::CheckNoDrop(int16 slot_id) { + EQEmu::ItemInstance* inst = GetItem(slot_id); + if (!inst) return false; + if (!inst->GetItem()->NoDrop) return true; + if (inst->GetItem()->ItemClass == 1) { + for (uint8 i = EQEmu::inventory::containerBegin; i < EQEmu::inventory::ContainerCount; i++) { + EQEmu::ItemInstance* bagitem = GetItem(Inventory::CalcSlotId(slot_id, i)); + if (bagitem && !bagitem->GetItem()->NoDrop) + return true; + } + } + return false; +} + +// Remove item from bucket without memory delete +// Returns item pointer if full delete was successful +EQEmu::ItemInstance* Inventory::PopItem(int16 slot_id) +{ + EQEmu::ItemInstance* p = nullptr; + + if (slot_id == EQEmu::inventory::slotCursor) { + p = m_cursor.pop(); + } + else if ((slot_id >= EQEmu::legacy::EQUIPMENT_BEGIN && slot_id <= EQEmu::legacy::EQUIPMENT_END) || (slot_id == EQEmu::inventory::slotPowerSource)) { + p = m_worn[slot_id]; + m_worn.erase(slot_id); + } + else if ((slot_id >= EQEmu::legacy::GENERAL_BEGIN && slot_id <= EQEmu::legacy::GENERAL_END)) { + p = m_inv[slot_id]; + m_inv.erase(slot_id); + } + else if (slot_id >= EQEmu::legacy::TRIBUTE_BEGIN && slot_id <= EQEmu::legacy::TRIBUTE_END) { + p = m_worn[slot_id]; + m_worn.erase(slot_id); + } + else if (slot_id >= EQEmu::legacy::BANK_BEGIN && slot_id <= EQEmu::legacy::BANK_END) { + p = m_bank[slot_id]; + m_bank.erase(slot_id); + } + else if (slot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_END) { + p = m_shbank[slot_id]; + m_shbank.erase(slot_id); + } + else if (slot_id >= EQEmu::legacy::TRADE_BEGIN && slot_id <= EQEmu::legacy::TRADE_END) { + p = m_trade[slot_id]; + m_trade.erase(slot_id); + } + else { + // Is slot inside bag? + EQEmu::ItemInstance* baginst = GetItem(Inventory::CalcSlotId(slot_id)); + if (baginst != nullptr && baginst->IsClassBag()) { + p = baginst->PopItem(Inventory::CalcBagIdx(slot_id)); + } + } + + // Return pointer that needs to be deleted (or otherwise managed) + return p; +} + +bool Inventory::HasSpaceForItem(const EQEmu::ItemData *ItemToTry, int16 Quantity) { + + if (ItemToTry->Stackable) { + + for (int16 i = EQEmu::legacy::GENERAL_BEGIN; i <= EQEmu::legacy::GENERAL_END; i++) { + + EQEmu::ItemInstance* InvItem = GetItem(i); + + if (InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + + int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); + + if (Quantity <= ChargeSlotsLeft) + return true; + + Quantity -= ChargeSlotsLeft; + + } + if (InvItem && InvItem->IsClassBag()) { + + int16 BaseSlotID = Inventory::CalcSlotId(i, EQEmu::inventory::containerBegin); + uint8 BagSize = InvItem->GetItem()->BagSlots; + for (uint8 BagSlot = EQEmu::inventory::containerBegin; BagSlot < BagSize; BagSlot++) { + + InvItem = GetItem(BaseSlotID + BagSlot); + + if (InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && + (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { + + int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); + + if (Quantity <= ChargeSlotsLeft) + return true; + + Quantity -= ChargeSlotsLeft; + } + } + } + } + } + + for (int16 i = EQEmu::legacy::GENERAL_BEGIN; i <= EQEmu::legacy::GENERAL_END; i++) { + + EQEmu::ItemInstance* InvItem = GetItem(i); + + if (!InvItem) { + + if (!ItemToTry->Stackable) { + + if (Quantity == 1) + return true; + else + Quantity--; + } + else { + if (Quantity <= ItemToTry->StackSize) + return true; + else + Quantity -= ItemToTry->StackSize; + } + + } + else if (InvItem->IsClassBag() && CanItemFitInContainer(ItemToTry, InvItem->GetItem())) { + + int16 BaseSlotID = Inventory::CalcSlotId(i, EQEmu::inventory::containerBegin); + + uint8 BagSize = InvItem->GetItem()->BagSlots; + + for (uint8 BagSlot = EQEmu::inventory::containerBegin; BagSlotStackable) { + + if (Quantity == 1) + return true; + else + Quantity--; + } + else { + if (Quantity <= ItemToTry->StackSize) + return true; + else + Quantity -= ItemToTry->StackSize; + } + } + } + } + } + + return false; + +} + +// Checks that user has at least 'quantity' number of items in a given inventory slot +// Returns first slot it was found in, or SLOT_INVALID if not found + +//This function has a flaw in that it only returns the last stack that it looked at +//when quantity is greater than 1 and not all of quantity can be found in 1 stack. +int16 Inventory::HasItem(uint32 item_id, uint8 quantity, uint8 where) +{ + int16 slot_id = INVALID_INDEX; + + //Altered by Father Nitwit to support a specification of + //where to search, with a default value to maintain compatibility + + // Check each inventory bucket + if (where & invWhereWorn) { + slot_id = _HasItem(m_worn, item_id, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWherePersonal) { + slot_id = _HasItem(m_inv, item_id, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWhereBank) { + slot_id = _HasItem(m_bank, item_id, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWhereSharedBank) { + slot_id = _HasItem(m_shbank, item_id, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWhereTrading) { + slot_id = _HasItem(m_trade, item_id, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + // Behavioral change - Limbo is no longer checked due to improper handling of return value + if (where & invWhereCursor) { + // Check cursor queue + slot_id = _HasItem(m_cursor, item_id, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + return slot_id; +} + +//this function has the same quantity flaw mentioned above in HasItem() +int16 Inventory::HasItemByUse(uint8 use, uint8 quantity, uint8 where) +{ + int16 slot_id = INVALID_INDEX; + + // Check each inventory bucket + if (where & invWhereWorn) { + slot_id = _HasItemByUse(m_worn, use, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWherePersonal) { + slot_id = _HasItemByUse(m_inv, use, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWhereBank) { + slot_id = _HasItemByUse(m_bank, use, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWhereSharedBank) { + slot_id = _HasItemByUse(m_shbank, use, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWhereTrading) { + slot_id = _HasItemByUse(m_trade, use, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + // Behavioral change - Limbo is no longer checked due to improper handling of return value + if (where & invWhereCursor) { + // Check cursor queue + slot_id = _HasItemByUse(m_cursor, use, quantity); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + return slot_id; +} + +int16 Inventory::HasItemByLoreGroup(uint32 loregroup, uint8 where) +{ + int16 slot_id = INVALID_INDEX; + + // Check each inventory bucket + if (where & invWhereWorn) { + slot_id = _HasItemByLoreGroup(m_worn, loregroup); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWherePersonal) { + slot_id = _HasItemByLoreGroup(m_inv, loregroup); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWhereBank) { + slot_id = _HasItemByLoreGroup(m_bank, loregroup); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWhereSharedBank) { + slot_id = _HasItemByLoreGroup(m_shbank, loregroup); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + if (where & invWhereTrading) { + slot_id = _HasItemByLoreGroup(m_trade, loregroup); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + // Behavioral change - Limbo is no longer checked due to improper handling of return value + if (where & invWhereCursor) { + // Check cursor queue + slot_id = _HasItemByLoreGroup(m_cursor, loregroup); + if (slot_id != INVALID_INDEX) + return slot_id; + } + + return slot_id; +} + +// Locate an available inventory slot +// Returns slot_id when there's one available, else SLOT_INVALID +int16 Inventory::FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size, bool is_arrow) +{ + // Check basic inventory + for (int16 i = EQEmu::legacy::GENERAL_BEGIN; i <= EQEmu::legacy::GENERAL_END; i++) { + if (!GetItem(i)) + // Found available slot in personal inventory + return i; + } + + if (!for_bag) { + for (int16 i = EQEmu::legacy::GENERAL_BEGIN; i <= EQEmu::legacy::GENERAL_END; i++) { + const EQEmu::ItemInstance* inst = GetItem(i); + if (inst && inst->IsClassBag() && inst->GetItem()->BagSize >= min_size) + { + if (inst->GetItem()->BagType == EQEmu::item::BagTypeQuiver && inst->GetItem()->ItemType != EQEmu::item::ItemTypeArrow) + { + continue; + } + + int16 base_slot_id = Inventory::CalcSlotId(i, EQEmu::inventory::containerBegin); + + uint8 slots = inst->GetItem()->BagSlots; + uint8 j; + for (j = EQEmu::inventory::containerBegin; jGetID()) + return INVALID_INDEX; + + // step 1: find room for bags (caller should really ask for slots for bags first to avoid sending them to cursor..and bag item loss) + if (inst->IsClassBag()) { + for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { + if (!m_inv[free_slot]) + return free_slot; + } + + return EQEmu::inventory::slotCursor; // return cursor since bags do not stack and will not fit inside other bags..yet...) + } + + // step 2: find partial room for stackables + if (inst->IsStackable()) { + for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { + const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; + + if (!main_inst) + continue; + + if ((main_inst->GetID() == inst->GetID()) && (main_inst->GetCharges() < main_inst->GetItem()->StackSize)) + return free_slot; + } + + for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { + const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; + + if (!main_inst) + continue; + + if (main_inst->IsClassBag()) { // if item-specific containers already have bad items, we won't fix it here... + for (uint8 free_bag_slot = EQEmu::inventory::containerBegin; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EQEmu::inventory::ContainerCount); ++free_bag_slot) { + const EQEmu::ItemInstance* sub_inst = main_inst->GetItem(free_bag_slot); + + if (!sub_inst) + continue; + + if ((sub_inst->GetID() == inst->GetID()) && (sub_inst->GetCharges() < sub_inst->GetItem()->StackSize)) + return Inventory::CalcSlotId(free_slot, free_bag_slot); + } + } + } + } + + // step 3a: find room for container-specific items (ItemClassArrow) + if (inst->GetItem()->ItemType == EQEmu::item::ItemTypeArrow) { + for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { + const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; + + if (!main_inst || (main_inst->GetItem()->BagType != EQEmu::item::BagTypeQuiver) || !main_inst->IsClassBag()) + continue; + + for (uint8 free_bag_slot = EQEmu::inventory::containerBegin; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EQEmu::inventory::ContainerCount); ++free_bag_slot) { + if (!main_inst->GetItem(free_bag_slot)) + return Inventory::CalcSlotId(free_slot, free_bag_slot); + } + } + } + + // step 3b: find room for container-specific items (ItemClassSmallThrowing) + if (inst->GetItem()->ItemType == EQEmu::item::ItemTypeSmallThrowing) { + for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { + const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; + + if (!main_inst || (main_inst->GetItem()->BagType != EQEmu::item::BagTypeBandolier) || !main_inst->IsClassBag()) + continue; + + for (uint8 free_bag_slot = EQEmu::inventory::containerBegin; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EQEmu::inventory::ContainerCount); ++free_bag_slot) { + if (!main_inst->GetItem(free_bag_slot)) + return Inventory::CalcSlotId(free_slot, free_bag_slot); + } + } + } + + // step 4: just find an empty slot + for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { + const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; + + if (!main_inst) + return free_slot; + } + + for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { + const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; + + if (main_inst && main_inst->IsClassBag()) { + if ((main_inst->GetItem()->BagSize < inst->GetItem()->Size) || (main_inst->GetItem()->BagType == EQEmu::item::BagTypeBandolier) || (main_inst->GetItem()->BagType == EQEmu::item::BagTypeQuiver)) + continue; + + for (uint8 free_bag_slot = EQEmu::inventory::containerBegin; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EQEmu::inventory::ContainerCount); ++free_bag_slot) { + if (!main_inst->GetItem(free_bag_slot)) + return Inventory::CalcSlotId(free_slot, free_bag_slot); + } + } + } + + //return INVALID_INDEX; // everything else pushes to the cursor + return EQEmu::inventory::slotCursor; +} + +// Opposite of below: Get parent bag slot_id from a slot inside of bag +int16 Inventory::CalcSlotId(int16 slot_id) { + int16 parent_slot_id = INVALID_INDEX; + + // this is not a bag range... using this risks over-writing existing items + //else if (slot_id >= EmuConstants::BANK_BEGIN && slot_id <= EmuConstants::BANK_END) + // parent_slot_id = EmuConstants::BANK_BEGIN + (slot_id - EmuConstants::BANK_BEGIN) / EmuConstants::ITEM_CONTAINER_SIZE; + //else if (slot_id >= 3100 && slot_id <= 3179) should be {3031..3110}..where did this range come from!!? (verified db save range) + + if (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END) { + parent_slot_id = EQEmu::legacy::GENERAL_BEGIN + (slot_id - EQEmu::legacy::GENERAL_BAGS_BEGIN) / EQEmu::inventory::ContainerCount; + } + else if (slot_id >= EQEmu::legacy::CURSOR_BAG_BEGIN && slot_id <= EQEmu::legacy::CURSOR_BAG_END) { + parent_slot_id = EQEmu::inventory::slotCursor; + } + else if (slot_id >= EQEmu::legacy::BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::BANK_BAGS_END) { + parent_slot_id = EQEmu::legacy::BANK_BEGIN + (slot_id - EQEmu::legacy::BANK_BAGS_BEGIN) / EQEmu::inventory::ContainerCount; + } + else if (slot_id >= EQEmu::legacy::SHARED_BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_BAGS_END) { + parent_slot_id = EQEmu::legacy::SHARED_BANK_BEGIN + (slot_id - EQEmu::legacy::SHARED_BANK_BAGS_BEGIN) / EQEmu::inventory::ContainerCount; + } + else if (slot_id >= EQEmu::legacy::TRADE_BAGS_BEGIN && slot_id <= EQEmu::legacy::TRADE_BAGS_END) { + parent_slot_id = EQEmu::legacy::TRADE_BEGIN + (slot_id - EQEmu::legacy::TRADE_BAGS_BEGIN) / EQEmu::inventory::ContainerCount; + } + + return parent_slot_id; +} + +// Calculate slot_id for an item within a bag +int16 Inventory::CalcSlotId(int16 bagslot_id, uint8 bagidx) { + if (!Inventory::SupportsContainers(bagslot_id)) + return INVALID_INDEX; + + int16 slot_id = INVALID_INDEX; + + if (bagslot_id == EQEmu::inventory::slotCursor || bagslot_id == 8000) { + slot_id = EQEmu::legacy::CURSOR_BAG_BEGIN + bagidx; + } + else if (bagslot_id >= EQEmu::legacy::GENERAL_BEGIN && bagslot_id <= EQEmu::legacy::GENERAL_END) { + slot_id = EQEmu::legacy::GENERAL_BAGS_BEGIN + (bagslot_id - EQEmu::legacy::GENERAL_BEGIN) * EQEmu::inventory::ContainerCount + bagidx; + } + else if (bagslot_id >= EQEmu::legacy::BANK_BEGIN && bagslot_id <= EQEmu::legacy::BANK_END) { + slot_id = EQEmu::legacy::BANK_BAGS_BEGIN + (bagslot_id - EQEmu::legacy::BANK_BEGIN) * EQEmu::inventory::ContainerCount + bagidx; + } + else if (bagslot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && bagslot_id <= EQEmu::legacy::SHARED_BANK_END) { + slot_id = EQEmu::legacy::SHARED_BANK_BAGS_BEGIN + (bagslot_id - EQEmu::legacy::SHARED_BANK_BEGIN) * EQEmu::inventory::ContainerCount + bagidx; + } + else if (bagslot_id >= EQEmu::legacy::TRADE_BEGIN && bagslot_id <= EQEmu::legacy::TRADE_END) { + slot_id = EQEmu::legacy::TRADE_BAGS_BEGIN + (bagslot_id - EQEmu::legacy::TRADE_BEGIN) * EQEmu::inventory::ContainerCount + bagidx; + } + + return slot_id; +} + +uint8 Inventory::CalcBagIdx(int16 slot_id) { + uint8 index = 0; + + // this is not a bag range... using this risks over-writing existing items + //else if (slot_id >= EmuConstants::BANK_BEGIN && slot_id <= EmuConstants::BANK_END) + // index = (slot_id - EmuConstants::BANK_BEGIN) % EmuConstants::ITEM_CONTAINER_SIZE; + + if (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END) { + index = (slot_id - EQEmu::legacy::GENERAL_BAGS_BEGIN) % EQEmu::inventory::ContainerCount; + } + else if (slot_id >= EQEmu::legacy::CURSOR_BAG_BEGIN && slot_id <= EQEmu::legacy::CURSOR_BAG_END) { + index = (slot_id - EQEmu::legacy::CURSOR_BAG_BEGIN); // % EQEmu::legacy::ITEM_CONTAINER_SIZE; - not needed since range is 10 slots + } + else if (slot_id >= EQEmu::legacy::BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::BANK_BAGS_END) { + index = (slot_id - EQEmu::legacy::BANK_BAGS_BEGIN) % EQEmu::inventory::ContainerCount; + } + else if (slot_id >= EQEmu::legacy::SHARED_BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_BAGS_END) { + index = (slot_id - EQEmu::legacy::SHARED_BANK_BAGS_BEGIN) % EQEmu::inventory::ContainerCount; + } + else if (slot_id >= EQEmu::legacy::TRADE_BAGS_BEGIN && slot_id <= EQEmu::legacy::TRADE_BAGS_END) { + index = (slot_id - EQEmu::legacy::TRADE_BAGS_BEGIN) % EQEmu::inventory::ContainerCount; + } + else if (slot_id >= EQEmu::legacy::WORLD_BEGIN && slot_id <= EQEmu::legacy::WORLD_END) { + index = (slot_id - EQEmu::legacy::WORLD_BEGIN); // % EQEmu::legacy::ITEM_CONTAINER_SIZE; - not needed since range is 10 slots + } + + return index; +} + +int16 Inventory::CalcSlotFromMaterial(uint8 material) +{ + switch (material) + { + case EQEmu::textures::armorHead: + return EQEmu::inventory::slotHead; + case EQEmu::textures::armorChest: + return EQEmu::inventory::slotChest; + case EQEmu::textures::armorArms: + return EQEmu::inventory::slotArms; + case EQEmu::textures::armorWrist: + return EQEmu::inventory::slotWrist1; // there's 2 bracers, only one bracer material + case EQEmu::textures::armorHands: + return EQEmu::inventory::slotHands; + case EQEmu::textures::armorLegs: + return EQEmu::inventory::slotLegs; + case EQEmu::textures::armorFeet: + return EQEmu::inventory::slotFeet; + case EQEmu::textures::weaponPrimary: + return EQEmu::inventory::slotPrimary; + case EQEmu::textures::weaponSecondary: + return EQEmu::inventory::slotSecondary; + default: + return INVALID_INDEX; + } +} + +uint8 Inventory::CalcMaterialFromSlot(int16 equipslot) +{ + switch (equipslot) + { + case EQEmu::inventory::slotHead: + return EQEmu::textures::armorHead; + case EQEmu::inventory::slotChest: + return EQEmu::textures::armorChest; + case EQEmu::inventory::slotArms: + return EQEmu::textures::armorArms; + case EQEmu::inventory::slotWrist1: + //case SLOT_BRACER02: // non-live behavior + return EQEmu::textures::armorWrist; + case EQEmu::inventory::slotHands: + return EQEmu::textures::armorHands; + case EQEmu::inventory::slotLegs: + return EQEmu::textures::armorLegs; + case EQEmu::inventory::slotFeet: + return EQEmu::textures::armorFeet; + case EQEmu::inventory::slotPrimary: + return EQEmu::textures::weaponPrimary; + case EQEmu::inventory::slotSecondary: + return EQEmu::textures::weaponSecondary; + default: + return EQEmu::textures::materialInvalid; + } +} + +bool Inventory::CanItemFitInContainer(const EQEmu::ItemData *ItemToTry, const EQEmu::ItemData *Container) { + + if (!ItemToTry || !Container) + return false; + + if (ItemToTry->Size > Container->BagSize) + return false; + + if ((Container->BagType == EQEmu::item::BagTypeQuiver) && (ItemToTry->ItemType != EQEmu::item::ItemTypeArrow)) + return false; + + if ((Container->BagType == EQEmu::item::BagTypeBandolier) && (ItemToTry->ItemType != EQEmu::item::ItemTypeSmallThrowing)) + return false; + + return true; +} + +bool Inventory::SupportsClickCasting(int16 slot_id) +{ + // there are a few non-potion items that identify as ItemTypePotion..so, we still need to ubiquitously include the equipment range + if ((uint16)slot_id <= EQEmu::legacy::GENERAL_END || slot_id == EQEmu::inventory::slotPowerSource) + { + return true; + } + else if (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END) + { + if (EQEmu::inventory::Lookup(m_inventory_version)->AllowClickCastFromBag) + return true; + } + + return false; +} + +bool Inventory::SupportsPotionBeltCasting(int16 slot_id) +{ + if ((uint16)slot_id <= EQEmu::legacy::GENERAL_END || slot_id == EQEmu::inventory::slotPowerSource || (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END)) + return true; + + return false; +} + +// Test whether a given slot can support a container item +bool Inventory::SupportsContainers(int16 slot_id) +{ + if ((slot_id == EQEmu::inventory::slotCursor) || + (slot_id >= EQEmu::legacy::GENERAL_BEGIN && slot_id <= EQEmu::legacy::GENERAL_END) || + (slot_id >= EQEmu::legacy::BANK_BEGIN && slot_id <= EQEmu::legacy::BANK_END) || + (slot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_END) || + (slot_id >= EQEmu::legacy::TRADE_BEGIN && slot_id <= EQEmu::legacy::TRADE_END) + ) { + return true; + } + + return false; +} + +int Inventory::GetSlotByItemInst(EQEmu::ItemInstance *inst) { + if (!inst) + return INVALID_INDEX; + + int i = GetSlotByItemInstCollection(m_worn, inst); + if (i != INVALID_INDEX) { + return i; + } + + i = GetSlotByItemInstCollection(m_inv, inst); + if (i != INVALID_INDEX) { + return i; + } + + i = GetSlotByItemInstCollection(m_bank, inst); + if (i != INVALID_INDEX) { + return i; + } + + i = GetSlotByItemInstCollection(m_shbank, inst); + if (i != INVALID_INDEX) { + return i; + } + + i = GetSlotByItemInstCollection(m_trade, inst); + if (i != INVALID_INDEX) { + return i; + } + + if (m_cursor.peek_front() == inst) { + return EQEmu::inventory::slotCursor; + } + + return INVALID_INDEX; +} + +uint8 Inventory::FindBrightestLightType() +{ + uint8 brightest_light_type = 0; + + for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { + if ((iter->first < EQEmu::legacy::EQUIPMENT_BEGIN || iter->first > EQEmu::legacy::EQUIPMENT_END) && iter->first != EQEmu::inventory::slotPowerSource) { continue; } + if (iter->first == EQEmu::inventory::slotAmmo) { continue; } + + auto inst = iter->second; + if (inst == nullptr) { continue; } + auto item = inst->GetItem(); + if (item == nullptr) { continue; } + + if (EQEmu::lightsource::IsLevelGreater(item->Light, brightest_light_type)) + brightest_light_type = item->Light; + } + + uint8 general_light_type = 0; + for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { + if (iter->first < EQEmu::legacy::GENERAL_BEGIN || iter->first > EQEmu::legacy::GENERAL_END) { continue; } + + auto inst = iter->second; + if (inst == nullptr) { continue; } + auto item = inst->GetItem(); + if (item == nullptr) { continue; } + + if (!item->IsClassCommon()) { continue; } + if (item->Light < 9 || item->Light > 13) { continue; } + + if (EQEmu::lightsource::TypeToLevel(item->Light)) + general_light_type = item->Light; + } + + if (EQEmu::lightsource::IsLevelGreater(general_light_type, brightest_light_type)) + brightest_light_type = general_light_type; + + return brightest_light_type; +} + +void Inventory::dumpEntireInventory() { + + dumpWornItems(); + dumpInventory(); + dumpBankItems(); + dumpSharedBankItems(); + + std::cout << std::endl; +} + +void Inventory::dumpWornItems() { + std::cout << "Worn items:" << std::endl; + dumpItemCollection(m_worn); +} + +void Inventory::dumpInventory() { + std::cout << "Inventory items:" << std::endl; + dumpItemCollection(m_inv); +} + +void Inventory::dumpBankItems() { + + std::cout << "Bank items:" << std::endl; + dumpItemCollection(m_bank); +} + +void Inventory::dumpSharedBankItems() { + + std::cout << "Shared Bank items:" << std::endl; + dumpItemCollection(m_shbank); +} + +int Inventory::GetSlotByItemInstCollection(const std::map &collection, EQEmu::ItemInstance *inst) { + for (auto iter = collection.begin(); iter != collection.end(); ++iter) { + EQEmu::ItemInstance *t_inst = iter->second; + if (t_inst == inst) { + return iter->first; + } + + if (t_inst && !t_inst->IsClassBag()) { + for (auto b_iter = t_inst->_cbegin(); b_iter != t_inst->_cend(); ++b_iter) { + if (b_iter->second == inst) { + return Inventory::CalcSlotId(iter->first, b_iter->first); + } + } + } + } + + return -1; +} + +void Inventory::dumpItemCollection(const std::map &collection) +{ + for (auto it = collection.cbegin(); it != collection.cend(); ++it) { + auto inst = it->second; + if (!inst || !inst->GetItem()) + continue; + + std::string slot = StringFormat("Slot %d: %s (%d)", it->first, it->second->GetItem()->Name, (inst->GetCharges() <= 0) ? 1 : inst->GetCharges()); + std::cout << slot << std::endl; + + dumpBagContents(inst, &it); + } +} + +void Inventory::dumpBagContents(EQEmu::ItemInstance *inst, std::map::const_iterator *it) +{ + if (!inst || !inst->IsClassBag()) + return; + + // Go through bag, if bag + for (auto itb = inst->_cbegin(); itb != inst->_cend(); ++itb) { + EQEmu::ItemInstance* baginst = itb->second; + if (!baginst || !baginst->GetItem()) + continue; + + std::string subSlot = StringFormat(" Slot %d: %s (%d)", Inventory::CalcSlotId((*it)->first, itb->first), + baginst->GetItem()->Name, (baginst->GetCharges() <= 0) ? 1 : baginst->GetCharges()); + std::cout << subSlot << std::endl; + } + +} + +// Internal Method: Retrieves item within an inventory bucket +EQEmu::ItemInstance* Inventory::_GetItem(const std::map& bucket, int16 slot_id) const +{ + auto it = bucket.find(slot_id); + if (it != bucket.end()) { + return it->second; + } + + // Not found! + return nullptr; +} + +// Internal Method: "put" item into bucket, without regard for what is currently in bucket +// Assumes item has already been allocated +int16 Inventory::_PutItem(int16 slot_id, EQEmu::ItemInstance* inst) +{ + // What happens here when we _PutItem(MainCursor)? Bad things..really bad things... + // + // If putting a nullptr into slot, we need to remove slot without memory delete + if (inst == nullptr) { + //Why do we not delete the poped item here???? + PopItem(slot_id); + return slot_id; + } + + int16 result = INVALID_INDEX; + int16 parentSlot = INVALID_INDEX; + + if (slot_id == EQEmu::inventory::slotCursor) { + // Replace current item on cursor, if exists + m_cursor.pop(); // no memory delete, clients of this function know what they are doing + m_cursor.push_front(inst); + result = slot_id; + } + else if ((slot_id >= EQEmu::legacy::EQUIPMENT_BEGIN && slot_id <= EQEmu::legacy::EQUIPMENT_END) || (slot_id == EQEmu::inventory::slotPowerSource)) { + m_worn[slot_id] = inst; + result = slot_id; + } + else if ((slot_id >= EQEmu::legacy::GENERAL_BEGIN && slot_id <= EQEmu::legacy::GENERAL_END)) { + m_inv[slot_id] = inst; + result = slot_id; + } + else if (slot_id >= EQEmu::legacy::TRIBUTE_BEGIN && slot_id <= EQEmu::legacy::TRIBUTE_END) { + m_worn[slot_id] = inst; + result = slot_id; + } + else if (slot_id >= EQEmu::legacy::BANK_BEGIN && slot_id <= EQEmu::legacy::BANK_END) { + m_bank[slot_id] = inst; + result = slot_id; + } + else if (slot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_END) { + m_shbank[slot_id] = inst; + result = slot_id; + } + else if (slot_id >= EQEmu::legacy::TRADE_BEGIN && slot_id <= EQEmu::legacy::TRADE_END) { + m_trade[slot_id] = inst; + result = slot_id; + } + else { + // Slot must be within a bag + parentSlot = Inventory::CalcSlotId(slot_id); + EQEmu::ItemInstance* baginst = GetItem(parentSlot); // Get parent bag + if (baginst && baginst->IsClassBag()) + { + baginst->_PutItem(Inventory::CalcBagIdx(slot_id), inst); + result = slot_id; + } + } + + if (result == INVALID_INDEX) { + Log.Out(Logs::General, Logs::Error, "Inventory::_PutItem: Invalid slot_id specified (%i) with parent slot id (%i)", slot_id, parentSlot); + Inventory::MarkDirty(inst); // Slot not found, clean up + } + + return result; +} + +// Internal Method: Checks an inventory bucket for a particular item +int16 Inventory::_HasItem(std::map& bucket, uint32 item_id, uint8 quantity) +{ + uint32 quantity_found = 0; + + for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { + auto inst = iter->second; + if (inst == nullptr) { continue; } + + if (inst->GetID() == item_id) { + quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) + return iter->first; + } + + for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { + if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) + return EQEmu::legacy::SLOT_AUGMENT; + } + + if (!inst->IsClassBag()) { continue; } + + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { + auto bag_inst = bag_iter->second; + if (bag_inst == nullptr) { continue; } + + if (bag_inst->GetID() == item_id) { + quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); + if (quantity_found >= quantity) + return Inventory::CalcSlotId(iter->first, bag_iter->first); + } + + for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { + if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) + return EQEmu::legacy::SLOT_AUGMENT; + } + } + } + + return INVALID_INDEX; +} + +// Internal Method: Checks an inventory queue type bucket for a particular item +int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) +{ + // The downfall of this (these) queue procedure is that callers presume that when an item is + // found, it is presented as being available on the cursor. In cases of a parity check, this + // is sufficient. However, in cases where referential criteria is considered, this can lead + // to unintended results. Funtionality should be observed when referencing the return value + // of this query + + uint32 quantity_found = 0; + + for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { + auto inst = *iter; + if (inst == nullptr) { continue; } + + if (inst->GetID() == item_id) { + quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) + return EQEmu::inventory::slotCursor; + } + + for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { + if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) + return EQEmu::legacy::SLOT_AUGMENT; + } + + if (!inst->IsClassBag()) { continue; } + + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { + auto bag_inst = bag_iter->second; + if (bag_inst == nullptr) { continue; } + + if (bag_inst->GetID() == item_id) { + quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); + if (quantity_found >= quantity) + return Inventory::CalcSlotId(EQEmu::inventory::slotCursor, bag_iter->first); + } + + for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { + if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) + return EQEmu::legacy::SLOT_AUGMENT; + } + } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; + } + + return INVALID_INDEX; +} + +// Internal Method: Checks an inventory bucket for a particular item +int16 Inventory::_HasItemByUse(std::map& bucket, uint8 use, uint8 quantity) +{ + uint32 quantity_found = 0; + + for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { + auto inst = iter->second; + if (inst == nullptr) { continue; } + + if (inst->IsClassCommon() && inst->GetItem()->ItemType == use) { + quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) + return iter->first; + } + + if (!inst->IsClassBag()) { continue; } + + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { + auto bag_inst = bag_iter->second; + if (bag_inst == nullptr) { continue; } + + if (bag_inst->IsClassCommon() && bag_inst->GetItem()->ItemType == use) { + quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); + if (quantity_found >= quantity) + return Inventory::CalcSlotId(iter->first, bag_iter->first); + } + } + } + + return INVALID_INDEX; +} + +// Internal Method: Checks an inventory queue type bucket for a particular item +int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) +{ + uint32 quantity_found = 0; + + for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { + auto inst = *iter; + if (inst == nullptr) { continue; } + + if (inst->IsClassCommon() && inst->GetItem()->ItemType == use) { + quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); + if (quantity_found >= quantity) + return EQEmu::inventory::slotCursor; + } + + if (!inst->IsClassBag()) { continue; } + + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { + auto bag_inst = bag_iter->second; + if (bag_inst == nullptr) { continue; } + + if (bag_inst->IsClassCommon() && bag_inst->GetItem()->ItemType == use) { + quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); + if (quantity_found >= quantity) + return Inventory::CalcSlotId(EQEmu::inventory::slotCursor, bag_iter->first); + } + } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; + } + + return INVALID_INDEX; +} + +int16 Inventory::_HasItemByLoreGroup(std::map& bucket, uint32 loregroup) +{ + for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { + auto inst = iter->second; + if (inst == nullptr) { continue; } + + if (inst->GetItem()->LoreGroup == loregroup) + return iter->first; + + for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { + auto aug_inst = inst->GetAugment(index); + if (aug_inst == nullptr) { continue; } + + if (aug_inst->GetItem()->LoreGroup == loregroup) + return EQEmu::legacy::SLOT_AUGMENT; + } + + if (!inst->IsClassBag()) { continue; } + + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { + auto bag_inst = bag_iter->second; + if (bag_inst == nullptr) { continue; } + + if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) + return Inventory::CalcSlotId(iter->first, bag_iter->first); + + for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { + auto aug_inst = bag_inst->GetAugment(index); + if (aug_inst == nullptr) { continue; } + + if (aug_inst->GetItem()->LoreGroup == loregroup) + return EQEmu::legacy::SLOT_AUGMENT; + } + } + } + + return INVALID_INDEX; +} + +// Internal Method: Checks an inventory queue type bucket for a particular item +int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) +{ + for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { + auto inst = *iter; + if (inst == nullptr) { continue; } + + if (inst->GetItem()->LoreGroup == loregroup) + return EQEmu::inventory::slotCursor; + + for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { + auto aug_inst = inst->GetAugment(index); + if (aug_inst == nullptr) { continue; } + + if (aug_inst->GetItem()->LoreGroup == loregroup) + return EQEmu::legacy::SLOT_AUGMENT; + } + + if (!inst->IsClassBag()) { continue; } + + for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { + auto bag_inst = bag_iter->second; + if (bag_inst == nullptr) { continue; } + + if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) + return Inventory::CalcSlotId(EQEmu::inventory::slotCursor, bag_iter->first); + + for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { + auto aug_inst = bag_inst->GetAugment(index); + if (aug_inst == nullptr) { continue; } + + if (aug_inst->GetItem()->LoreGroup == loregroup) + return EQEmu::legacy::SLOT_AUGMENT; + } + } + + // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) + break; + } + + return INVALID_INDEX; +} diff --git a/common/inventory_profile.h b/common/inventory_profile.h new file mode 100644 index 000000000..3903f9812 --- /dev/null +++ b/common/inventory_profile.h @@ -0,0 +1,228 @@ +/* EQEMu: Everquest Server Emulator + + Copyright (C) 2001-2016 EQEMu Development Team (http://eqemulator.net) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; version 2 of the License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY except by those people which sell it, which + are required to give you total support for your newly bought product; + without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 04111-1307 USA +*/ + +// @merth notes: +// These classes could be optimized with database reads/writes by storing +// a status flag indicating how object needs to interact with database + +#ifndef COMMON_INVENTORY_PROFILE_H +#define COMMON_INVENTORY_PROFILE_H + + +#include "item_instance.h" + +#include + + +//FatherNitwit: location bits for searching specific +//places with HasItem() and HasItemByUse() +enum { + invWhereWorn = 0x01, + invWherePersonal = 0x02, //in the character's inventory + invWhereBank = 0x04, + invWhereSharedBank = 0x08, + invWhereTrading = 0x10, + invWhereCursor = 0x20 +}; + +// ######################################## +// Class: Queue +// Queue that allows a read-only iterator +class ItemInstQueue +{ +public: + ~ItemInstQueue(); + ///////////////////////// + // Public Methods + ///////////////////////// + + inline std::list::const_iterator cbegin() { return m_list.cbegin(); } + inline std::list::const_iterator cend() { return m_list.cend(); } + + inline int size() { return static_cast(m_list.size()); } // TODO: change to size_t + inline bool empty() { return m_list.empty(); } + + void push(EQEmu::ItemInstance* inst); + void push_front(EQEmu::ItemInstance* inst); + EQEmu::ItemInstance* pop(); + EQEmu::ItemInstance* pop_back(); + EQEmu::ItemInstance* peek_front() const; + +protected: + ///////////////////////// + // Protected Members + ///////////////////////// + + std::list m_list; +}; + +// ######################################## +// Class: Inventory +// Character inventory +class Inventory +{ + friend class EQEmu::ItemInstance; +public: + /////////////////////////////// + // Public Methods + /////////////////////////////// + + Inventory() { m_inventory_version = EQEmu::versions::InventoryVersion::Unknown; m_inventory_version_set = false; } + ~Inventory(); + + // inv2 creep + bool SetInventoryVersion(EQEmu::versions::InventoryVersion inventory_version) { + if (!m_inventory_version_set) { + m_inventory_version = EQEmu::versions::ValidateInventoryVersion(inventory_version); + return (m_inventory_version_set = true); + } + else { + return false; + } + } + bool SetInventoryVersion(EQEmu::versions::ClientVersion client_version) { return SetInventoryVersion(EQEmu::versions::ConvertClientVersionToInventoryVersion(client_version)); } + + EQEmu::versions::InventoryVersion InventoryVersion() { return m_inventory_version; } + + static void CleanDirty(); + static void MarkDirty(EQEmu::ItemInstance *inst); + + // Retrieve a writeable item at specified slot + EQEmu::ItemInstance* GetItem(int16 slot_id) const; + EQEmu::ItemInstance* GetItem(int16 slot_id, uint8 bagidx) const; + + inline std::list::const_iterator cursor_cbegin() { return m_cursor.cbegin(); } + inline std::list::const_iterator cursor_cend() { return m_cursor.cend(); } + + inline int CursorSize() { return m_cursor.size(); } + inline bool CursorEmpty() { return m_cursor.empty(); } + + // Retrieve a read-only item from inventory + inline const EQEmu::ItemInstance* operator[](int16 slot_id) const { return GetItem(slot_id); } + + // Add item to inventory + int16 PutItem(int16 slot_id, const EQEmu::ItemInstance& inst); + + // Add item to cursor queue + int16 PushCursor(const EQEmu::ItemInstance& inst); + + // Get cursor item in front of queue + EQEmu::ItemInstance* GetCursorItem(); + + // Swap items in inventory + bool SwapItem(int16 slot_a, int16 slot_b); + + // Remove item from inventory + bool DeleteItem(int16 slot_id, uint8 quantity=0); + + // Checks All items in a bag for No Drop + bool CheckNoDrop(int16 slot_id); + + // Remove item from inventory (and take control of memory) + EQEmu::ItemInstance* PopItem(int16 slot_id); + + // Check whether there is space for the specified number of the specified item. + bool HasSpaceForItem(const EQEmu::ItemData *ItemToTry, int16 Quantity); + + // Check whether item exists in inventory + // where argument specifies OR'd list of invWhere constants to look + int16 HasItem(uint32 item_id, uint8 quantity = 0, uint8 where = 0xFF); + + // Check whether item exists in inventory + // where argument specifies OR'd list of invWhere constants to look + int16 HasItemByUse(uint8 use, uint8 quantity=0, uint8 where=0xFF); + + // Check whether item exists in inventory + // where argument specifies OR'd list of invWhere constants to look + int16 HasItemByLoreGroup(uint32 loregroup, uint8 where=0xFF); + + // Locate an available inventory slot + int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false); + int16 FindFreeSlotForTradeItem(const EQEmu::ItemInstance* inst); + + // Calculate slot_id for an item within a bag + static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id + static int16 CalcSlotId(int16 bagslot_id, uint8 bagidx); // Calc slot_id for item inside bag + static uint8 CalcBagIdx(int16 slot_id); // Calc bagidx for slot_id + static int16 CalcSlotFromMaterial(uint8 material); + static uint8 CalcMaterialFromSlot(int16 equipslot); + + static bool CanItemFitInContainer(const EQEmu::ItemData *ItemToTry, const EQEmu::ItemData *Container); + + // Test for valid inventory casting slot + bool SupportsClickCasting(int16 slot_id); + bool SupportsPotionBeltCasting(int16 slot_id); + + // Test whether a given slot can support a container item + static bool SupportsContainers(int16 slot_id); + + int GetSlotByItemInst(EQEmu::ItemInstance *inst); + + uint8 FindBrightestLightType(); + + void dumpEntireInventory(); + void dumpWornItems(); + void dumpInventory(); + void dumpBankItems(); + void dumpSharedBankItems(); + + void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, std::string value); + void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, int value); + void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, float value); + void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, bool value); + std::string GetCustomItemData(int16 slot_id, std::string identifier); +protected: + /////////////////////////////// + // Protected Methods + /////////////////////////////// + + int GetSlotByItemInstCollection(const std::map &collection, EQEmu::ItemInstance *inst); + void dumpItemCollection(const std::map &collection); + void dumpBagContents(EQEmu::ItemInstance *inst, std::map::const_iterator *it); + + // Retrieves item within an inventory bucket + EQEmu::ItemInstance* _GetItem(const std::map& bucket, int16 slot_id) const; + + // Private "put" item into bucket, without regard for what is currently in bucket + int16 _PutItem(int16 slot_id, EQEmu::ItemInstance* inst); + + // Checks an inventory bucket for a particular item + int16 _HasItem(std::map& bucket, uint32 item_id, uint8 quantity); + int16 _HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity); + int16 _HasItemByUse(std::map& bucket, uint8 use, uint8 quantity); + int16 _HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity); + int16 _HasItemByLoreGroup(std::map& bucket, uint32 loregroup); + int16 _HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup); + + + // Player inventory + std::map m_worn; // Items worn by character + std::map m_inv; // Items in character personal inventory + std::map m_bank; // Items in character bank + std::map m_shbank; // Items in character shared bank + std::map m_trade; // Items in a trade session + ItemInstQueue m_cursor; // Items on cursor: FIFO + +private: + // Active inventory version + EQEmu::versions::InventoryVersion m_inventory_version; + bool m_inventory_version_set; +}; + +#endif /*COMMON_INVENTORY_PROFILE_H*/ diff --git a/common/item_instance.cpp b/common/item_instance.cpp index fbe2af543..de01c0893 100644 --- a/common/item_instance.cpp +++ b/common/item_instance.cpp @@ -16,21 +16,21 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include "classes.h" -#include "global_define.h" -#include "item_instance.h" -#include "races.h" +#include "inventory_profile.h" +//#include "classes.h" +//#include "global_define.h" +//#include "item_instance.h" +//#include "races.h" #include "rulesys.h" #include "shareddb.h" #include "string_util.h" -#include "../common/light_source.h" +//#include "../common/light_source.h" -#include +//#include -#include +//#include -std::list dirty_inst; int32 NextItemInstSerialNumber = 1; static inline int32 GetNextItemInstSerialNumber() { @@ -53,1390 +53,6 @@ static inline int32 GetNextItemInstSerialNumber() { return NextItemInstSerialNumber; } - -// -// class ItemInstQueue -// -ItemInstQueue::~ItemInstQueue() -{ - for (auto iter = m_list.begin(); iter != m_list.end(); ++iter) { - safe_delete(*iter); - } - m_list.clear(); -} - -// Put item onto back of queue -void ItemInstQueue::push(EQEmu::ItemInstance* inst) -{ - m_list.push_back(inst); -} - -// Put item onto front of queue -void ItemInstQueue::push_front(EQEmu::ItemInstance* inst) -{ - m_list.push_front(inst); -} - -// Remove item from front of queue -EQEmu::ItemInstance* ItemInstQueue::pop() -{ - if (m_list.empty()) - return nullptr; - - EQEmu::ItemInstance* inst = m_list.front(); - m_list.pop_front(); - return inst; -} - -// Remove item from back of queue -EQEmu::ItemInstance* ItemInstQueue::pop_back() -{ - if (m_list.empty()) - return nullptr; - - EQEmu::ItemInstance* inst = m_list.back(); - m_list.pop_back(); - return inst; -} - -// Look at item at front of queue -EQEmu::ItemInstance* ItemInstQueue::peek_front() const -{ - return (m_list.empty()) ? nullptr : m_list.front(); -} - - -// -// class Inventory -// -Inventory::~Inventory() -{ - for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { - safe_delete(iter->second); - } - m_worn.clear(); - - for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { - safe_delete(iter->second); - } - m_inv.clear(); - - for (auto iter = m_bank.begin(); iter != m_bank.end(); ++iter) { - safe_delete(iter->second); - } - m_bank.clear(); - - for (auto iter = m_shbank.begin(); iter != m_shbank.end(); ++iter) { - safe_delete(iter->second); - } - m_shbank.clear(); - - for (auto iter = m_trade.begin(); iter != m_trade.end(); ++iter) { - safe_delete(iter->second); - } - m_trade.clear(); -} - -void Inventory::CleanDirty() { - auto iter = dirty_inst.begin(); - while (iter != dirty_inst.end()) { - delete (*iter); - ++iter; - } - dirty_inst.clear(); -} - -void Inventory::MarkDirty(EQEmu::ItemInstance *inst) { - if (inst) { - dirty_inst.push_back(inst); - } -} - -// Retrieve item at specified slot; returns false if item not found -EQEmu::ItemInstance* Inventory::GetItem(int16 slot_id) const -{ - EQEmu::ItemInstance* result = nullptr; - - // Cursor - if (slot_id == EQEmu::inventory::slotCursor) { - // Cursor slot - result = m_cursor.peek_front(); - } - - // Non bag slots - else if (slot_id >= EQEmu::legacy::TRADE_BEGIN && slot_id <= EQEmu::legacy::TRADE_END) { - result = _GetItem(m_trade, slot_id); - } - else if (slot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_END) { - // Shared Bank slots - result = _GetItem(m_shbank, slot_id); - } - else if (slot_id >= EQEmu::legacy::BANK_BEGIN && slot_id <= EQEmu::legacy::BANK_END) { - // Bank slots - result = _GetItem(m_bank, slot_id); - } - else if ((slot_id >= EQEmu::legacy::GENERAL_BEGIN && slot_id <= EQEmu::legacy::GENERAL_END)) { - // Personal inventory slots - result = _GetItem(m_inv, slot_id); - } - else if ((slot_id >= EQEmu::legacy::EQUIPMENT_BEGIN && slot_id <= EQEmu::legacy::EQUIPMENT_END) || - (slot_id >= EQEmu::legacy::TRIBUTE_BEGIN && slot_id <= EQEmu::legacy::TRIBUTE_END) || (slot_id == EQEmu::inventory::slotPowerSource)) { - // Equippable slots (on body) - result = _GetItem(m_worn, slot_id); - } - - // Inner bag slots - else if (slot_id >= EQEmu::legacy::TRADE_BAGS_BEGIN && slot_id <= EQEmu::legacy::TRADE_BAGS_END) { - // Trade bag slots - EQEmu::ItemInstance* inst = _GetItem(m_trade, Inventory::CalcSlotId(slot_id)); - if (inst && inst->IsClassBag()) { - result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); - } - } - else if (slot_id >= EQEmu::legacy::SHARED_BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_BAGS_END) { - // Shared Bank bag slots - EQEmu::ItemInstance* inst = _GetItem(m_shbank, Inventory::CalcSlotId(slot_id)); - if (inst && inst->IsClassBag()) { - result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); - } - } - else if (slot_id >= EQEmu::legacy::BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::BANK_BAGS_END) { - // Bank bag slots - EQEmu::ItemInstance* inst = _GetItem(m_bank, Inventory::CalcSlotId(slot_id)); - if (inst && inst->IsClassBag()) { - result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); - } - } - else if (slot_id >= EQEmu::legacy::CURSOR_BAG_BEGIN && slot_id <= EQEmu::legacy::CURSOR_BAG_END) { - // Cursor bag slots - EQEmu::ItemInstance* inst = m_cursor.peek_front(); - if (inst && inst->IsClassBag()) { - result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); - } - } - else if (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END) { - // Personal inventory bag slots - EQEmu::ItemInstance* inst = _GetItem(m_inv, Inventory::CalcSlotId(slot_id)); - if (inst && inst->IsClassBag()) { - result = inst->GetItem(Inventory::CalcBagIdx(slot_id)); - } - } - - return result; -} - -// Retrieve item at specified position within bag -EQEmu::ItemInstance* Inventory::GetItem(int16 slot_id, uint8 bagidx) const -{ - return GetItem(Inventory::CalcSlotId(slot_id, bagidx)); -} - -// Put an item snto specified slot -int16 Inventory::PutItem(int16 slot_id, const EQEmu::ItemInstance& inst) -{ - // Clean up item already in slot (if exists) - DeleteItem(slot_id); - - if (!inst) { - // User is effectively deleting the item - // in the slot, why hold a null ptr in map<>? - return slot_id; - } - - // Delegate to internal method - return _PutItem(slot_id, inst.Clone()); -} - -int16 Inventory::PushCursor(const EQEmu::ItemInstance& inst) -{ - m_cursor.push(inst.Clone()); - return EQEmu::inventory::slotCursor; -} - -EQEmu::ItemInstance* Inventory::GetCursorItem() -{ - return m_cursor.peek_front(); -} - -// Swap items in inventory -bool Inventory::SwapItem(int16 slot_a, int16 slot_b) -{ - // Temp holding areas for a and b - EQEmu::ItemInstance* inst_a = GetItem(slot_a); - EQEmu::ItemInstance* inst_b = GetItem(slot_b); - - if (inst_a) { if (!inst_a->IsSlotAllowed(slot_b)) { return false; } } - if (inst_b) { if (!inst_b->IsSlotAllowed(slot_a)) { return false; } } - - _PutItem(slot_a, inst_b); // Copy b->a - _PutItem(slot_b, inst_a); // Copy a->b - - return true; -} - -// Remove item from inventory (with memory delete) -bool Inventory::DeleteItem(int16 slot_id, uint8 quantity) -{ - // Pop item out of inventory map (or queue) - EQEmu::ItemInstance* item_to_delete = PopItem(slot_id); - - // Determine if object should be fully deleted, or - // just a quantity of charges of the item can be deleted - if (item_to_delete && (quantity > 0)) { - - item_to_delete->SetCharges(item_to_delete->GetCharges() - quantity); - - // If there are no charges left on the item, - if (item_to_delete->GetCharges() <= 0) { - // If the item is stackable (e.g arrows), or - // the item is not stackable, and is not a charged item, or is expendable, delete it - if (item_to_delete->IsStackable() || - (!item_to_delete->IsStackable() && - ((item_to_delete->GetItem()->MaxCharges == 0) || item_to_delete->IsExpendable())) - ) { - // Item can now be destroyed - Inventory::MarkDirty(item_to_delete); - return true; - } - } - - // Charges still exist, or it is a charged item that is not expendable. Put back into inventory - _PutItem(slot_id, item_to_delete); - return false; - } - - Inventory::MarkDirty(item_to_delete); - return true; - -} - -// Checks All items in a bag for No Drop -bool Inventory::CheckNoDrop(int16 slot_id) { - EQEmu::ItemInstance* inst = GetItem(slot_id); - if (!inst) return false; - if (!inst->GetItem()->NoDrop) return true; - if (inst->GetItem()->ItemClass == 1) { - for (uint8 i = EQEmu::inventory::containerBegin; i < EQEmu::inventory::ContainerCount; i++) { - EQEmu::ItemInstance* bagitem = GetItem(Inventory::CalcSlotId(slot_id, i)); - if (bagitem && !bagitem->GetItem()->NoDrop) - return true; - } - } - return false; -} - -// Remove item from bucket without memory delete -// Returns item pointer if full delete was successful -EQEmu::ItemInstance* Inventory::PopItem(int16 slot_id) -{ - EQEmu::ItemInstance* p = nullptr; - - if (slot_id == EQEmu::inventory::slotCursor) { - p = m_cursor.pop(); - } - else if ((slot_id >= EQEmu::legacy::EQUIPMENT_BEGIN && slot_id <= EQEmu::legacy::EQUIPMENT_END) || (slot_id == EQEmu::inventory::slotPowerSource)) { - p = m_worn[slot_id]; - m_worn.erase(slot_id); - } - else if ((slot_id >= EQEmu::legacy::GENERAL_BEGIN && slot_id <= EQEmu::legacy::GENERAL_END)) { - p = m_inv[slot_id]; - m_inv.erase(slot_id); - } - else if (slot_id >= EQEmu::legacy::TRIBUTE_BEGIN && slot_id <= EQEmu::legacy::TRIBUTE_END) { - p = m_worn[slot_id]; - m_worn.erase(slot_id); - } - else if (slot_id >= EQEmu::legacy::BANK_BEGIN && slot_id <= EQEmu::legacy::BANK_END) { - p = m_bank[slot_id]; - m_bank.erase(slot_id); - } - else if (slot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_END) { - p = m_shbank[slot_id]; - m_shbank.erase(slot_id); - } - else if (slot_id >= EQEmu::legacy::TRADE_BEGIN && slot_id <= EQEmu::legacy::TRADE_END) { - p = m_trade[slot_id]; - m_trade.erase(slot_id); - } - else { - // Is slot inside bag? - EQEmu::ItemInstance* baginst = GetItem(Inventory::CalcSlotId(slot_id)); - if (baginst != nullptr && baginst->IsClassBag()) { - p = baginst->PopItem(Inventory::CalcBagIdx(slot_id)); - } - } - - // Return pointer that needs to be deleted (or otherwise managed) - return p; -} - -bool Inventory::HasSpaceForItem(const EQEmu::ItemData *ItemToTry, int16 Quantity) { - - if (ItemToTry->Stackable) { - - for (int16 i = EQEmu::legacy::GENERAL_BEGIN; i <= EQEmu::legacy::GENERAL_END; i++) { - - EQEmu::ItemInstance* InvItem = GetItem(i); - - if (InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { - - int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); - - if (Quantity <= ChargeSlotsLeft) - return true; - - Quantity -= ChargeSlotsLeft; - - } - if (InvItem && InvItem->IsClassBag()) { - - int16 BaseSlotID = Inventory::CalcSlotId(i, EQEmu::inventory::containerBegin); - uint8 BagSize = InvItem->GetItem()->BagSlots; - for (uint8 BagSlot = EQEmu::inventory::containerBegin; BagSlot < BagSize; BagSlot++) { - - InvItem = GetItem(BaseSlotID + BagSlot); - - if (InvItem && (InvItem->GetItem()->ID == ItemToTry->ID) && - (InvItem->GetCharges() < InvItem->GetItem()->StackSize)) { - - int ChargeSlotsLeft = InvItem->GetItem()->StackSize - InvItem->GetCharges(); - - if (Quantity <= ChargeSlotsLeft) - return true; - - Quantity -= ChargeSlotsLeft; - } - } - } - } - } - - for (int16 i = EQEmu::legacy::GENERAL_BEGIN; i <= EQEmu::legacy::GENERAL_END; i++) { - - EQEmu::ItemInstance* InvItem = GetItem(i); - - if (!InvItem) { - - if (!ItemToTry->Stackable) { - - if (Quantity == 1) - return true; - else - Quantity--; - } - else { - if (Quantity <= ItemToTry->StackSize) - return true; - else - Quantity -= ItemToTry->StackSize; - } - - } - else if (InvItem->IsClassBag() && CanItemFitInContainer(ItemToTry, InvItem->GetItem())) { - - int16 BaseSlotID = Inventory::CalcSlotId(i, EQEmu::inventory::containerBegin); - - uint8 BagSize = InvItem->GetItem()->BagSlots; - - for (uint8 BagSlot = EQEmu::inventory::containerBegin; BagSlotStackable) { - - if (Quantity == 1) - return true; - else - Quantity--; - } - else { - if (Quantity <= ItemToTry->StackSize) - return true; - else - Quantity -= ItemToTry->StackSize; - } - } - } - } - } - - return false; - -} - -// Checks that user has at least 'quantity' number of items in a given inventory slot -// Returns first slot it was found in, or SLOT_INVALID if not found - -//This function has a flaw in that it only returns the last stack that it looked at -//when quantity is greater than 1 and not all of quantity can be found in 1 stack. -int16 Inventory::HasItem(uint32 item_id, uint8 quantity, uint8 where) -{ - int16 slot_id = INVALID_INDEX; - - //Altered by Father Nitwit to support a specification of - //where to search, with a default value to maintain compatibility - - // Check each inventory bucket - if (where & invWhereWorn) { - slot_id = _HasItem(m_worn, item_id, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWherePersonal) { - slot_id = _HasItem(m_inv, item_id, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWhereBank) { - slot_id = _HasItem(m_bank, item_id, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWhereSharedBank) { - slot_id = _HasItem(m_shbank, item_id, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWhereTrading) { - slot_id = _HasItem(m_trade, item_id, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - // Behavioral change - Limbo is no longer checked due to improper handling of return value - if (where & invWhereCursor) { - // Check cursor queue - slot_id = _HasItem(m_cursor, item_id, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - return slot_id; -} - -//this function has the same quantity flaw mentioned above in HasItem() -int16 Inventory::HasItemByUse(uint8 use, uint8 quantity, uint8 where) -{ - int16 slot_id = INVALID_INDEX; - - // Check each inventory bucket - if (where & invWhereWorn) { - slot_id = _HasItemByUse(m_worn, use, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWherePersonal) { - slot_id = _HasItemByUse(m_inv, use, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWhereBank) { - slot_id = _HasItemByUse(m_bank, use, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWhereSharedBank) { - slot_id = _HasItemByUse(m_shbank, use, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWhereTrading) { - slot_id = _HasItemByUse(m_trade, use, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - // Behavioral change - Limbo is no longer checked due to improper handling of return value - if (where & invWhereCursor) { - // Check cursor queue - slot_id = _HasItemByUse(m_cursor, use, quantity); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - return slot_id; -} - -int16 Inventory::HasItemByLoreGroup(uint32 loregroup, uint8 where) -{ - int16 slot_id = INVALID_INDEX; - - // Check each inventory bucket - if (where & invWhereWorn) { - slot_id = _HasItemByLoreGroup(m_worn, loregroup); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWherePersonal) { - slot_id = _HasItemByLoreGroup(m_inv, loregroup); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWhereBank) { - slot_id = _HasItemByLoreGroup(m_bank, loregroup); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWhereSharedBank) { - slot_id = _HasItemByLoreGroup(m_shbank, loregroup); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - if (where & invWhereTrading) { - slot_id = _HasItemByLoreGroup(m_trade, loregroup); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - // Behavioral change - Limbo is no longer checked due to improper handling of return value - if (where & invWhereCursor) { - // Check cursor queue - slot_id = _HasItemByLoreGroup(m_cursor, loregroup); - if (slot_id != INVALID_INDEX) - return slot_id; - } - - return slot_id; -} - -// Locate an available inventory slot -// Returns slot_id when there's one available, else SLOT_INVALID -int16 Inventory::FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size, bool is_arrow) -{ - // Check basic inventory - for (int16 i = EQEmu::legacy::GENERAL_BEGIN; i <= EQEmu::legacy::GENERAL_END; i++) { - if (!GetItem(i)) - // Found available slot in personal inventory - return i; - } - - if (!for_bag) { - for (int16 i = EQEmu::legacy::GENERAL_BEGIN; i <= EQEmu::legacy::GENERAL_END; i++) { - const EQEmu::ItemInstance* inst = GetItem(i); - if (inst && inst->IsClassBag() && inst->GetItem()->BagSize >= min_size) - { - if (inst->GetItem()->BagType == EQEmu::item::BagTypeQuiver && inst->GetItem()->ItemType != EQEmu::item::ItemTypeArrow) - { - continue; - } - - int16 base_slot_id = Inventory::CalcSlotId(i, EQEmu::inventory::containerBegin); - - uint8 slots = inst->GetItem()->BagSlots; - uint8 j; - for (j = EQEmu::inventory::containerBegin; jGetID()) - return INVALID_INDEX; - - // step 1: find room for bags (caller should really ask for slots for bags first to avoid sending them to cursor..and bag item loss) - if (inst->IsClassBag()) { - for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { - if (!m_inv[free_slot]) - return free_slot; - } - - return EQEmu::inventory::slotCursor; // return cursor since bags do not stack and will not fit inside other bags..yet...) - } - - // step 2: find partial room for stackables - if (inst->IsStackable()) { - for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { - const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; - - if (!main_inst) - continue; - - if ((main_inst->GetID() == inst->GetID()) && (main_inst->GetCharges() < main_inst->GetItem()->StackSize)) - return free_slot; - } - - for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { - const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; - - if (!main_inst) - continue; - - if (main_inst->IsClassBag()) { // if item-specific containers already have bad items, we won't fix it here... - for (uint8 free_bag_slot = EQEmu::inventory::containerBegin; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EQEmu::inventory::ContainerCount); ++free_bag_slot) { - const EQEmu::ItemInstance* sub_inst = main_inst->GetItem(free_bag_slot); - - if (!sub_inst) - continue; - - if ((sub_inst->GetID() == inst->GetID()) && (sub_inst->GetCharges() < sub_inst->GetItem()->StackSize)) - return Inventory::CalcSlotId(free_slot, free_bag_slot); - } - } - } - } - - // step 3a: find room for container-specific items (ItemClassArrow) - if (inst->GetItem()->ItemType == EQEmu::item::ItemTypeArrow) { - for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { - const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; - - if (!main_inst || (main_inst->GetItem()->BagType != EQEmu::item::BagTypeQuiver) || !main_inst->IsClassBag()) - continue; - - for (uint8 free_bag_slot = EQEmu::inventory::containerBegin; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EQEmu::inventory::ContainerCount); ++free_bag_slot) { - if (!main_inst->GetItem(free_bag_slot)) - return Inventory::CalcSlotId(free_slot, free_bag_slot); - } - } - } - - // step 3b: find room for container-specific items (ItemClassSmallThrowing) - if (inst->GetItem()->ItemType == EQEmu::item::ItemTypeSmallThrowing) { - for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { - const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; - - if (!main_inst || (main_inst->GetItem()->BagType != EQEmu::item::BagTypeBandolier) || !main_inst->IsClassBag()) - continue; - - for (uint8 free_bag_slot = EQEmu::inventory::containerBegin; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EQEmu::inventory::ContainerCount); ++free_bag_slot) { - if (!main_inst->GetItem(free_bag_slot)) - return Inventory::CalcSlotId(free_slot, free_bag_slot); - } - } - } - - // step 4: just find an empty slot - for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { - const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; - - if (!main_inst) - return free_slot; - } - - for (int16 free_slot = EQEmu::legacy::GENERAL_BEGIN; free_slot <= EQEmu::legacy::GENERAL_END; ++free_slot) { - const EQEmu::ItemInstance* main_inst = m_inv[free_slot]; - - if (main_inst && main_inst->IsClassBag()) { - if ((main_inst->GetItem()->BagSize < inst->GetItem()->Size) || (main_inst->GetItem()->BagType == EQEmu::item::BagTypeBandolier) || (main_inst->GetItem()->BagType == EQEmu::item::BagTypeQuiver)) - continue; - - for (uint8 free_bag_slot = EQEmu::inventory::containerBegin; (free_bag_slot < main_inst->GetItem()->BagSlots) && (free_bag_slot < EQEmu::inventory::ContainerCount); ++free_bag_slot) { - if (!main_inst->GetItem(free_bag_slot)) - return Inventory::CalcSlotId(free_slot, free_bag_slot); - } - } - } - - //return INVALID_INDEX; // everything else pushes to the cursor - return EQEmu::inventory::slotCursor; -} - -// Opposite of below: Get parent bag slot_id from a slot inside of bag -int16 Inventory::CalcSlotId(int16 slot_id) { - int16 parent_slot_id = INVALID_INDEX; - - // this is not a bag range... using this risks over-writing existing items - //else if (slot_id >= EmuConstants::BANK_BEGIN && slot_id <= EmuConstants::BANK_END) - // parent_slot_id = EmuConstants::BANK_BEGIN + (slot_id - EmuConstants::BANK_BEGIN) / EmuConstants::ITEM_CONTAINER_SIZE; - //else if (slot_id >= 3100 && slot_id <= 3179) should be {3031..3110}..where did this range come from!!? (verified db save range) - - if (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END) { - parent_slot_id = EQEmu::legacy::GENERAL_BEGIN + (slot_id - EQEmu::legacy::GENERAL_BAGS_BEGIN) / EQEmu::inventory::ContainerCount; - } - else if (slot_id >= EQEmu::legacy::CURSOR_BAG_BEGIN && slot_id <= EQEmu::legacy::CURSOR_BAG_END) { - parent_slot_id = EQEmu::inventory::slotCursor; - } - else if (slot_id >= EQEmu::legacy::BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::BANK_BAGS_END) { - parent_slot_id = EQEmu::legacy::BANK_BEGIN + (slot_id - EQEmu::legacy::BANK_BAGS_BEGIN) / EQEmu::inventory::ContainerCount; - } - else if (slot_id >= EQEmu::legacy::SHARED_BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_BAGS_END) { - parent_slot_id = EQEmu::legacy::SHARED_BANK_BEGIN + (slot_id - EQEmu::legacy::SHARED_BANK_BAGS_BEGIN) / EQEmu::inventory::ContainerCount; - } - else if (slot_id >= EQEmu::legacy::TRADE_BAGS_BEGIN && slot_id <= EQEmu::legacy::TRADE_BAGS_END) { - parent_slot_id = EQEmu::legacy::TRADE_BEGIN + (slot_id - EQEmu::legacy::TRADE_BAGS_BEGIN) / EQEmu::inventory::ContainerCount; - } - - return parent_slot_id; -} - -// Calculate slot_id for an item within a bag -int16 Inventory::CalcSlotId(int16 bagslot_id, uint8 bagidx) { - if (!Inventory::SupportsContainers(bagslot_id)) - return INVALID_INDEX; - - int16 slot_id = INVALID_INDEX; - - if (bagslot_id == EQEmu::inventory::slotCursor || bagslot_id == 8000) { - slot_id = EQEmu::legacy::CURSOR_BAG_BEGIN + bagidx; - } - else if (bagslot_id >= EQEmu::legacy::GENERAL_BEGIN && bagslot_id <= EQEmu::legacy::GENERAL_END) { - slot_id = EQEmu::legacy::GENERAL_BAGS_BEGIN + (bagslot_id - EQEmu::legacy::GENERAL_BEGIN) * EQEmu::inventory::ContainerCount + bagidx; - } - else if (bagslot_id >= EQEmu::legacy::BANK_BEGIN && bagslot_id <= EQEmu::legacy::BANK_END) { - slot_id = EQEmu::legacy::BANK_BAGS_BEGIN + (bagslot_id - EQEmu::legacy::BANK_BEGIN) * EQEmu::inventory::ContainerCount + bagidx; - } - else if (bagslot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && bagslot_id <= EQEmu::legacy::SHARED_BANK_END) { - slot_id = EQEmu::legacy::SHARED_BANK_BAGS_BEGIN + (bagslot_id - EQEmu::legacy::SHARED_BANK_BEGIN) * EQEmu::inventory::ContainerCount + bagidx; - } - else if (bagslot_id >= EQEmu::legacy::TRADE_BEGIN && bagslot_id <= EQEmu::legacy::TRADE_END) { - slot_id = EQEmu::legacy::TRADE_BAGS_BEGIN + (bagslot_id - EQEmu::legacy::TRADE_BEGIN) * EQEmu::inventory::ContainerCount + bagidx; - } - - return slot_id; -} - -uint8 Inventory::CalcBagIdx(int16 slot_id) { - uint8 index = 0; - - // this is not a bag range... using this risks over-writing existing items - //else if (slot_id >= EmuConstants::BANK_BEGIN && slot_id <= EmuConstants::BANK_END) - // index = (slot_id - EmuConstants::BANK_BEGIN) % EmuConstants::ITEM_CONTAINER_SIZE; - - if (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END) { - index = (slot_id - EQEmu::legacy::GENERAL_BAGS_BEGIN) % EQEmu::inventory::ContainerCount; - } - else if (slot_id >= EQEmu::legacy::CURSOR_BAG_BEGIN && slot_id <= EQEmu::legacy::CURSOR_BAG_END) { - index = (slot_id - EQEmu::legacy::CURSOR_BAG_BEGIN); // % EQEmu::legacy::ITEM_CONTAINER_SIZE; - not needed since range is 10 slots - } - else if (slot_id >= EQEmu::legacy::BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::BANK_BAGS_END) { - index = (slot_id - EQEmu::legacy::BANK_BAGS_BEGIN) % EQEmu::inventory::ContainerCount; - } - else if (slot_id >= EQEmu::legacy::SHARED_BANK_BAGS_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_BAGS_END) { - index = (slot_id - EQEmu::legacy::SHARED_BANK_BAGS_BEGIN) % EQEmu::inventory::ContainerCount; - } - else if (slot_id >= EQEmu::legacy::TRADE_BAGS_BEGIN && slot_id <= EQEmu::legacy::TRADE_BAGS_END) { - index = (slot_id - EQEmu::legacy::TRADE_BAGS_BEGIN) % EQEmu::inventory::ContainerCount; - } - else if (slot_id >= EQEmu::legacy::WORLD_BEGIN && slot_id <= EQEmu::legacy::WORLD_END) { - index = (slot_id - EQEmu::legacy::WORLD_BEGIN); // % EQEmu::legacy::ITEM_CONTAINER_SIZE; - not needed since range is 10 slots - } - - return index; -} - -int16 Inventory::CalcSlotFromMaterial(uint8 material) -{ - switch (material) - { - case EQEmu::textures::armorHead: - return EQEmu::inventory::slotHead; - case EQEmu::textures::armorChest: - return EQEmu::inventory::slotChest; - case EQEmu::textures::armorArms: - return EQEmu::inventory::slotArms; - case EQEmu::textures::armorWrist: - return EQEmu::inventory::slotWrist1; // there's 2 bracers, only one bracer material - case EQEmu::textures::armorHands: - return EQEmu::inventory::slotHands; - case EQEmu::textures::armorLegs: - return EQEmu::inventory::slotLegs; - case EQEmu::textures::armorFeet: - return EQEmu::inventory::slotFeet; - case EQEmu::textures::weaponPrimary: - return EQEmu::inventory::slotPrimary; - case EQEmu::textures::weaponSecondary: - return EQEmu::inventory::slotSecondary; - default: - return INVALID_INDEX; - } -} - -uint8 Inventory::CalcMaterialFromSlot(int16 equipslot) -{ - switch (equipslot) - { - case EQEmu::inventory::slotHead: - return EQEmu::textures::armorHead; - case EQEmu::inventory::slotChest: - return EQEmu::textures::armorChest; - case EQEmu::inventory::slotArms: - return EQEmu::textures::armorArms; - case EQEmu::inventory::slotWrist1: - //case SLOT_BRACER02: // non-live behavior - return EQEmu::textures::armorWrist; - case EQEmu::inventory::slotHands: - return EQEmu::textures::armorHands; - case EQEmu::inventory::slotLegs: - return EQEmu::textures::armorLegs; - case EQEmu::inventory::slotFeet: - return EQEmu::textures::armorFeet; - case EQEmu::inventory::slotPrimary: - return EQEmu::textures::weaponPrimary; - case EQEmu::inventory::slotSecondary: - return EQEmu::textures::weaponSecondary; - default: - return EQEmu::textures::materialInvalid; - } -} - -bool Inventory::CanItemFitInContainer(const EQEmu::ItemData *ItemToTry, const EQEmu::ItemData *Container) { - - if (!ItemToTry || !Container) - return false; - - if (ItemToTry->Size > Container->BagSize) - return false; - - if ((Container->BagType == EQEmu::item::BagTypeQuiver) && (ItemToTry->ItemType != EQEmu::item::ItemTypeArrow)) - return false; - - if ((Container->BagType == EQEmu::item::BagTypeBandolier) && (ItemToTry->ItemType != EQEmu::item::ItemTypeSmallThrowing)) - return false; - - return true; -} - -bool Inventory::SupportsClickCasting(int16 slot_id) -{ - // there are a few non-potion items that identify as ItemTypePotion..so, we still need to ubiquitously include the equipment range - if ((uint16)slot_id <= EQEmu::legacy::GENERAL_END || slot_id == EQEmu::inventory::slotPowerSource) - { - return true; - } - else if (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END) - { - if (EQEmu::inventory::Lookup(m_inventory_version)->AllowClickCastFromBag) - return true; - } - - return false; -} - -bool Inventory::SupportsPotionBeltCasting(int16 slot_id) -{ - if ((uint16)slot_id <= EQEmu::legacy::GENERAL_END || slot_id == EQEmu::inventory::slotPowerSource || (slot_id >= EQEmu::legacy::GENERAL_BAGS_BEGIN && slot_id <= EQEmu::legacy::GENERAL_BAGS_END)) - return true; - - return false; -} - -// Test whether a given slot can support a container item -bool Inventory::SupportsContainers(int16 slot_id) -{ - if ((slot_id == EQEmu::inventory::slotCursor) || - (slot_id >= EQEmu::legacy::GENERAL_BEGIN && slot_id <= EQEmu::legacy::GENERAL_END) || - (slot_id >= EQEmu::legacy::BANK_BEGIN && slot_id <= EQEmu::legacy::BANK_END) || - (slot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_END) || - (slot_id >= EQEmu::legacy::TRADE_BEGIN && slot_id <= EQEmu::legacy::TRADE_END) - ) { - return true; - } - - return false; -} - -int Inventory::GetSlotByItemInst(EQEmu::ItemInstance *inst) { - if (!inst) - return INVALID_INDEX; - - int i = GetSlotByItemInstCollection(m_worn, inst); - if (i != INVALID_INDEX) { - return i; - } - - i = GetSlotByItemInstCollection(m_inv, inst); - if (i != INVALID_INDEX) { - return i; - } - - i = GetSlotByItemInstCollection(m_bank, inst); - if (i != INVALID_INDEX) { - return i; - } - - i = GetSlotByItemInstCollection(m_shbank, inst); - if (i != INVALID_INDEX) { - return i; - } - - i = GetSlotByItemInstCollection(m_trade, inst); - if (i != INVALID_INDEX) { - return i; - } - - if (m_cursor.peek_front() == inst) { - return EQEmu::inventory::slotCursor; - } - - return INVALID_INDEX; -} - -uint8 Inventory::FindBrightestLightType() -{ - uint8 brightest_light_type = 0; - - for (auto iter = m_worn.begin(); iter != m_worn.end(); ++iter) { - if ((iter->first < EQEmu::legacy::EQUIPMENT_BEGIN || iter->first > EQEmu::legacy::EQUIPMENT_END) && iter->first != EQEmu::inventory::slotPowerSource) { continue; } - if (iter->first == EQEmu::inventory::slotAmmo) { continue; } - - auto inst = iter->second; - if (inst == nullptr) { continue; } - auto item = inst->GetItem(); - if (item == nullptr) { continue; } - - if (EQEmu::lightsource::IsLevelGreater(item->Light, brightest_light_type)) - brightest_light_type = item->Light; - } - - uint8 general_light_type = 0; - for (auto iter = m_inv.begin(); iter != m_inv.end(); ++iter) { - if (iter->first < EQEmu::legacy::GENERAL_BEGIN || iter->first > EQEmu::legacy::GENERAL_END) { continue; } - - auto inst = iter->second; - if (inst == nullptr) { continue; } - auto item = inst->GetItem(); - if (item == nullptr) { continue; } - - if (!item->IsClassCommon()) { continue; } - if (item->Light < 9 || item->Light > 13) { continue; } - - if (EQEmu::lightsource::TypeToLevel(item->Light)) - general_light_type = item->Light; - } - - if (EQEmu::lightsource::IsLevelGreater(general_light_type, brightest_light_type)) - brightest_light_type = general_light_type; - - return brightest_light_type; -} - -void Inventory::dumpEntireInventory() { - - dumpWornItems(); - dumpInventory(); - dumpBankItems(); - dumpSharedBankItems(); - - std::cout << std::endl; -} - -void Inventory::dumpWornItems() { - std::cout << "Worn items:" << std::endl; - dumpItemCollection(m_worn); -} - -void Inventory::dumpInventory() { - std::cout << "Inventory items:" << std::endl; - dumpItemCollection(m_inv); -} - -void Inventory::dumpBankItems() { - - std::cout << "Bank items:" << std::endl; - dumpItemCollection(m_bank); -} - -void Inventory::dumpSharedBankItems() { - - std::cout << "Shared Bank items:" << std::endl; - dumpItemCollection(m_shbank); -} - -int Inventory::GetSlotByItemInstCollection(const std::map &collection, EQEmu::ItemInstance *inst) { - for (auto iter = collection.begin(); iter != collection.end(); ++iter) { - EQEmu::ItemInstance *t_inst = iter->second; - if (t_inst == inst) { - return iter->first; - } - - if (t_inst && !t_inst->IsClassBag()) { - for (auto b_iter = t_inst->_cbegin(); b_iter != t_inst->_cend(); ++b_iter) { - if (b_iter->second == inst) { - return Inventory::CalcSlotId(iter->first, b_iter->first); - } - } - } - } - - return -1; -} - -void Inventory::dumpItemCollection(const std::map &collection) -{ - for (auto it = collection.cbegin(); it != collection.cend(); ++it) { - auto inst = it->second; - if (!inst || !inst->GetItem()) - continue; - - std::string slot = StringFormat("Slot %d: %s (%d)", it->first, it->second->GetItem()->Name, (inst->GetCharges() <= 0) ? 1 : inst->GetCharges()); - std::cout << slot << std::endl; - - dumpBagContents(inst, &it); - } -} - -void Inventory::dumpBagContents(EQEmu::ItemInstance *inst, std::map::const_iterator *it) -{ - if (!inst || !inst->IsClassBag()) - return; - - // Go through bag, if bag - for (auto itb = inst->_cbegin(); itb != inst->_cend(); ++itb) { - EQEmu::ItemInstance* baginst = itb->second; - if (!baginst || !baginst->GetItem()) - continue; - - std::string subSlot = StringFormat(" Slot %d: %s (%d)", Inventory::CalcSlotId((*it)->first, itb->first), - baginst->GetItem()->Name, (baginst->GetCharges() <= 0) ? 1 : baginst->GetCharges()); - std::cout << subSlot << std::endl; - } - -} - -// Internal Method: Retrieves item within an inventory bucket -EQEmu::ItemInstance* Inventory::_GetItem(const std::map& bucket, int16 slot_id) const -{ - auto it = bucket.find(slot_id); - if (it != bucket.end()) { - return it->second; - } - - // Not found! - return nullptr; -} - -// Internal Method: "put" item into bucket, without regard for what is currently in bucket -// Assumes item has already been allocated -int16 Inventory::_PutItem(int16 slot_id, EQEmu::ItemInstance* inst) -{ - // What happens here when we _PutItem(MainCursor)? Bad things..really bad things... - // - // If putting a nullptr into slot, we need to remove slot without memory delete - if (inst == nullptr) { - //Why do we not delete the poped item here???? - PopItem(slot_id); - return slot_id; - } - - int16 result = INVALID_INDEX; - int16 parentSlot = INVALID_INDEX; - - if (slot_id == EQEmu::inventory::slotCursor) { - // Replace current item on cursor, if exists - m_cursor.pop(); // no memory delete, clients of this function know what they are doing - m_cursor.push_front(inst); - result = slot_id; - } - else if ((slot_id >= EQEmu::legacy::EQUIPMENT_BEGIN && slot_id <= EQEmu::legacy::EQUIPMENT_END) || (slot_id == EQEmu::inventory::slotPowerSource)) { - m_worn[slot_id] = inst; - result = slot_id; - } - else if ((slot_id >= EQEmu::legacy::GENERAL_BEGIN && slot_id <= EQEmu::legacy::GENERAL_END)) { - m_inv[slot_id] = inst; - result = slot_id; - } - else if (slot_id >= EQEmu::legacy::TRIBUTE_BEGIN && slot_id <= EQEmu::legacy::TRIBUTE_END) { - m_worn[slot_id] = inst; - result = slot_id; - } - else if (slot_id >= EQEmu::legacy::BANK_BEGIN && slot_id <= EQEmu::legacy::BANK_END) { - m_bank[slot_id] = inst; - result = slot_id; - } - else if (slot_id >= EQEmu::legacy::SHARED_BANK_BEGIN && slot_id <= EQEmu::legacy::SHARED_BANK_END) { - m_shbank[slot_id] = inst; - result = slot_id; - } - else if (slot_id >= EQEmu::legacy::TRADE_BEGIN && slot_id <= EQEmu::legacy::TRADE_END) { - m_trade[slot_id] = inst; - result = slot_id; - } - else { - // Slot must be within a bag - parentSlot = Inventory::CalcSlotId(slot_id); - EQEmu::ItemInstance* baginst = GetItem(parentSlot); // Get parent bag - if (baginst && baginst->IsClassBag()) - { - baginst->_PutItem(Inventory::CalcBagIdx(slot_id), inst); - result = slot_id; - } - } - - if (result == INVALID_INDEX) { - Log.Out(Logs::General, Logs::Error, "Inventory::_PutItem: Invalid slot_id specified (%i) with parent slot id (%i)", slot_id, parentSlot); - Inventory::MarkDirty(inst); // Slot not found, clean up - } - - return result; -} - -// Internal Method: Checks an inventory bucket for a particular item -int16 Inventory::_HasItem(std::map& bucket, uint32 item_id, uint8 quantity) -{ - uint32 quantity_found = 0; - - for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { - auto inst = iter->second; - if (inst == nullptr) { continue; } - - if (inst->GetID() == item_id) { - quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) - return iter->first; - } - - for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { - if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) - return EQEmu::legacy::SLOT_AUGMENT; - } - - if (!inst->IsClassBag()) { continue; } - - for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } - - if (bag_inst->GetID() == item_id) { - quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) - return Inventory::CalcSlotId(iter->first, bag_iter->first); - } - - for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { - if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) - return EQEmu::legacy::SLOT_AUGMENT; - } - } - } - - return INVALID_INDEX; -} - -// Internal Method: Checks an inventory queue type bucket for a particular item -int16 Inventory::_HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity) -{ - // The downfall of this (these) queue procedure is that callers presume that when an item is - // found, it is presented as being available on the cursor. In cases of a parity check, this - // is sufficient. However, in cases where referential criteria is considered, this can lead - // to unintended results. Funtionality should be observed when referencing the return value - // of this query - - uint32 quantity_found = 0; - - for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { - auto inst = *iter; - if (inst == nullptr) { continue; } - - if (inst->GetID() == item_id) { - quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) - return EQEmu::inventory::slotCursor; - } - - for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { - if (inst->GetAugmentItemID(index) == item_id && quantity <= 1) - return EQEmu::legacy::SLOT_AUGMENT; - } - - if (!inst->IsClassBag()) { continue; } - - for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } - - if (bag_inst->GetID() == item_id) { - quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) - return Inventory::CalcSlotId(EQEmu::inventory::slotCursor, bag_iter->first); - } - - for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { - if (bag_inst->GetAugmentItemID(index) == item_id && quantity <= 1) - return EQEmu::legacy::SLOT_AUGMENT; - } - } - - // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) - break; - } - - return INVALID_INDEX; -} - -// Internal Method: Checks an inventory bucket for a particular item -int16 Inventory::_HasItemByUse(std::map& bucket, uint8 use, uint8 quantity) -{ - uint32 quantity_found = 0; - - for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { - auto inst = iter->second; - if (inst == nullptr) { continue; } - - if (inst->IsClassCommon() && inst->GetItem()->ItemType == use) { - quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) - return iter->first; - } - - if (!inst->IsClassBag()) { continue; } - - for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } - - if (bag_inst->IsClassCommon() && bag_inst->GetItem()->ItemType == use) { - quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) - return Inventory::CalcSlotId(iter->first, bag_iter->first); - } - } - } - - return INVALID_INDEX; -} - -// Internal Method: Checks an inventory queue type bucket for a particular item -int16 Inventory::_HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity) -{ - uint32 quantity_found = 0; - - for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { - auto inst = *iter; - if (inst == nullptr) { continue; } - - if (inst->IsClassCommon() && inst->GetItem()->ItemType == use) { - quantity_found += (inst->GetCharges() <= 0) ? 1 : inst->GetCharges(); - if (quantity_found >= quantity) - return EQEmu::inventory::slotCursor; - } - - if (!inst->IsClassBag()) { continue; } - - for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } - - if (bag_inst->IsClassCommon() && bag_inst->GetItem()->ItemType == use) { - quantity_found += (bag_inst->GetCharges() <= 0) ? 1 : bag_inst->GetCharges(); - if (quantity_found >= quantity) - return Inventory::CalcSlotId(EQEmu::inventory::slotCursor, bag_iter->first); - } - } - - // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) - break; - } - - return INVALID_INDEX; -} - -int16 Inventory::_HasItemByLoreGroup(std::map& bucket, uint32 loregroup) -{ - for (auto iter = bucket.begin(); iter != bucket.end(); ++iter) { - auto inst = iter->second; - if (inst == nullptr) { continue; } - - if (inst->GetItem()->LoreGroup == loregroup) - return iter->first; - - for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { - auto aug_inst = inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } - - if (aug_inst->GetItem()->LoreGroup == loregroup) - return EQEmu::legacy::SLOT_AUGMENT; - } - - if (!inst->IsClassBag()) { continue; } - - for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } - - if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) - return Inventory::CalcSlotId(iter->first, bag_iter->first); - - for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { - auto aug_inst = bag_inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } - - if (aug_inst->GetItem()->LoreGroup == loregroup) - return EQEmu::legacy::SLOT_AUGMENT; - } - } - } - - return INVALID_INDEX; -} - -// Internal Method: Checks an inventory queue type bucket for a particular item -int16 Inventory::_HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup) -{ - for (auto iter = iqueue.cbegin(); iter != iqueue.cend(); ++iter) { - auto inst = *iter; - if (inst == nullptr) { continue; } - - if (inst->GetItem()->LoreGroup == loregroup) - return EQEmu::inventory::slotCursor; - - for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { - auto aug_inst = inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } - - if (aug_inst->GetItem()->LoreGroup == loregroup) - return EQEmu::legacy::SLOT_AUGMENT; - } - - if (!inst->IsClassBag()) { continue; } - - for (auto bag_iter = inst->_cbegin(); bag_iter != inst->_cend(); ++bag_iter) { - auto bag_inst = bag_iter->second; - if (bag_inst == nullptr) { continue; } - - if (bag_inst->IsClassCommon() && bag_inst->GetItem()->LoreGroup == loregroup) - return Inventory::CalcSlotId(EQEmu::inventory::slotCursor, bag_iter->first); - - for (int index = EQEmu::inventory::socketBegin; index < EQEmu::inventory::SocketCount; ++index) { - auto aug_inst = bag_inst->GetAugment(index); - if (aug_inst == nullptr) { continue; } - - if (aug_inst->GetItem()->LoreGroup == loregroup) - return EQEmu::legacy::SLOT_AUGMENT; - } - } - - // We only check the visible cursor due to lack of queue processing ability (client allows duplicate in limbo) - break; - } - - return INVALID_INDEX; -} - - // // class EQEmu::ItemInstance // diff --git a/common/item_instance.h b/common/item_instance.h index b83baa13f..2a4afdca1 100644 --- a/common/item_instance.h +++ b/common/item_instance.h @@ -35,7 +35,6 @@ class EvolveInfo; // Stores information about an evolving item family #include "../common/deity.h" #include "../common/memory_buffer.h" -#include #include @@ -52,208 +51,8 @@ typedef enum { byFlagNotSet //apply action if the flag is NOT set } byFlagSetting; - -//FatherNitwit: location bits for searching specific -//places with HasItem() and HasItemByUse() -enum { - invWhereWorn = 0x01, - invWherePersonal = 0x02, //in the character's inventory - invWhereBank = 0x04, - invWhereSharedBank = 0x08, - invWhereTrading = 0x10, - invWhereCursor = 0x20 -}; - -namespace EQEmu -{ - class ItemInstance; -} - -// ######################################## -// Class: Queue -// Queue that allows a read-only iterator -class ItemInstQueue -{ -public: - ~ItemInstQueue(); - ///////////////////////// - // Public Methods - ///////////////////////// - - inline std::list::const_iterator cbegin() { return m_list.cbegin(); } - inline std::list::const_iterator cend() { return m_list.cend(); } - - inline int size() { return static_cast(m_list.size()); } // TODO: change to size_t - inline bool empty() { return m_list.empty(); } - - void push(EQEmu::ItemInstance* inst); - void push_front(EQEmu::ItemInstance* inst); - EQEmu::ItemInstance* pop(); - EQEmu::ItemInstance* pop_back(); - EQEmu::ItemInstance* peek_front() const; - -protected: - ///////////////////////// - // Protected Members - ///////////////////////// - - std::list m_list; -}; - -// ######################################## -// Class: Inventory -// Character inventory -class Inventory -{ - friend class EQEmu::ItemInstance; -public: - /////////////////////////////// - // Public Methods - /////////////////////////////// - - Inventory() { m_inventory_version = EQEmu::versions::InventoryVersion::Unknown; m_inventory_version_set = false; } - ~Inventory(); - - // inv2 creep - bool SetInventoryVersion(EQEmu::versions::InventoryVersion inventory_version) { - if (!m_inventory_version_set) { - m_inventory_version = EQEmu::versions::ValidateInventoryVersion(inventory_version); - return (m_inventory_version_set = true); - } - else { - return false; - } - } - bool SetInventoryVersion(EQEmu::versions::ClientVersion client_version) { return SetInventoryVersion(EQEmu::versions::ConvertClientVersionToInventoryVersion(client_version)); } - - EQEmu::versions::InventoryVersion InventoryVersion() { return m_inventory_version; } - - static void CleanDirty(); - static void MarkDirty(EQEmu::ItemInstance *inst); - - // Retrieve a writeable item at specified slot - EQEmu::ItemInstance* GetItem(int16 slot_id) const; - EQEmu::ItemInstance* GetItem(int16 slot_id, uint8 bagidx) const; - - inline std::list::const_iterator cursor_cbegin() { return m_cursor.cbegin(); } - inline std::list::const_iterator cursor_cend() { return m_cursor.cend(); } - - inline int CursorSize() { return m_cursor.size(); } - inline bool CursorEmpty() { return m_cursor.empty(); } - - // Retrieve a read-only item from inventory - inline const EQEmu::ItemInstance* operator[](int16 slot_id) const { return GetItem(slot_id); } - - // Add item to inventory - int16 PutItem(int16 slot_id, const EQEmu::ItemInstance& inst); - - // Add item to cursor queue - int16 PushCursor(const EQEmu::ItemInstance& inst); - - // Get cursor item in front of queue - EQEmu::ItemInstance* GetCursorItem(); - - // Swap items in inventory - bool SwapItem(int16 slot_a, int16 slot_b); - - // Remove item from inventory - bool DeleteItem(int16 slot_id, uint8 quantity=0); - - // Checks All items in a bag for No Drop - bool CheckNoDrop(int16 slot_id); - - // Remove item from inventory (and take control of memory) - EQEmu::ItemInstance* PopItem(int16 slot_id); - - // Check whether there is space for the specified number of the specified item. - bool HasSpaceForItem(const EQEmu::ItemData *ItemToTry, int16 Quantity); - - // Check whether item exists in inventory - // where argument specifies OR'd list of invWhere constants to look - int16 HasItem(uint32 item_id, uint8 quantity = 0, uint8 where = 0xFF); - - // Check whether item exists in inventory - // where argument specifies OR'd list of invWhere constants to look - int16 HasItemByUse(uint8 use, uint8 quantity=0, uint8 where=0xFF); - - // Check whether item exists in inventory - // where argument specifies OR'd list of invWhere constants to look - int16 HasItemByLoreGroup(uint32 loregroup, uint8 where=0xFF); - - // Locate an available inventory slot - int16 FindFreeSlot(bool for_bag, bool try_cursor, uint8 min_size = 0, bool is_arrow = false); - int16 FindFreeSlotForTradeItem(const EQEmu::ItemInstance* inst); - - // Calculate slot_id for an item within a bag - static int16 CalcSlotId(int16 slot_id); // Calc parent bag's slot_id - static int16 CalcSlotId(int16 bagslot_id, uint8 bagidx); // Calc slot_id for item inside bag - static uint8 CalcBagIdx(int16 slot_id); // Calc bagidx for slot_id - static int16 CalcSlotFromMaterial(uint8 material); - static uint8 CalcMaterialFromSlot(int16 equipslot); - - static bool CanItemFitInContainer(const EQEmu::ItemData *ItemToTry, const EQEmu::ItemData *Container); - - // Test for valid inventory casting slot - bool SupportsClickCasting(int16 slot_id); - bool SupportsPotionBeltCasting(int16 slot_id); - - // Test whether a given slot can support a container item - static bool SupportsContainers(int16 slot_id); - - int GetSlotByItemInst(EQEmu::ItemInstance *inst); - - uint8 FindBrightestLightType(); - - void dumpEntireInventory(); - void dumpWornItems(); - void dumpInventory(); - void dumpBankItems(); - void dumpSharedBankItems(); - - void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, std::string value); - void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, int value); - void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, float value); - void SetCustomItemData(uint32 character_id, int16 slot_id, std::string identifier, bool value); - std::string GetCustomItemData(int16 slot_id, std::string identifier); -protected: - /////////////////////////////// - // Protected Methods - /////////////////////////////// - - int GetSlotByItemInstCollection(const std::map &collection, EQEmu::ItemInstance *inst); - void dumpItemCollection(const std::map &collection); - void dumpBagContents(EQEmu::ItemInstance *inst, std::map::const_iterator *it); - - // Retrieves item within an inventory bucket - EQEmu::ItemInstance* _GetItem(const std::map& bucket, int16 slot_id) const; - - // Private "put" item into bucket, without regard for what is currently in bucket - int16 _PutItem(int16 slot_id, EQEmu::ItemInstance* inst); - - // Checks an inventory bucket for a particular item - int16 _HasItem(std::map& bucket, uint32 item_id, uint8 quantity); - int16 _HasItem(ItemInstQueue& iqueue, uint32 item_id, uint8 quantity); - int16 _HasItemByUse(std::map& bucket, uint8 use, uint8 quantity); - int16 _HasItemByUse(ItemInstQueue& iqueue, uint8 use, uint8 quantity); - int16 _HasItemByLoreGroup(std::map& bucket, uint32 loregroup); - int16 _HasItemByLoreGroup(ItemInstQueue& iqueue, uint32 loregroup); - - - // Player inventory - std::map m_worn; // Items worn by character - std::map m_inv; // Items in character personal inventory - std::map m_bank; // Items in character bank - std::map m_shbank; // Items in character shared bank - std::map m_trade; // Items in a trade session - ItemInstQueue m_cursor; // Items on cursor: FIFO - -private: - // Active inventory version - EQEmu::versions::InventoryVersion m_inventory_version; - bool m_inventory_version_set; -}; - class SharedDatabase; +class Inventory; // ######################################## // Class: EQEmu::ItemInstance diff --git a/common/patches/rof.cpp b/common/patches/rof.cpp index d3b4456b7..cc1744a31 100644 --- a/common/patches/rof.cpp +++ b/common/patches/rof.cpp @@ -29,7 +29,7 @@ #include "../eq_packet_structs.h" #include "../misc_functions.h" #include "../string_util.h" -#include "../item_instance.h" +#include "../inventory_profile.h" #include "rof_structs.h" #include "../rulesys.h" diff --git a/common/patches/rof2.cpp b/common/patches/rof2.cpp index 676f4c89f..aadd0a90b 100644 --- a/common/patches/rof2.cpp +++ b/common/patches/rof2.cpp @@ -29,7 +29,7 @@ #include "../eq_packet_structs.h" #include "../misc_functions.h" #include "../string_util.h" -#include "../item_instance.h" +#include "../inventory_profile.h" #include "rof2_structs.h" #include "../rulesys.h" diff --git a/common/shareddb.cpp b/common/shareddb.cpp index 35369c758..8cd62f164 100644 --- a/common/shareddb.cpp +++ b/common/shareddb.cpp @@ -29,7 +29,7 @@ #include "faction.h" #include "features.h" #include "ipc_mutex.h" -#include "item_instance.h" +#include "inventory_profile.h" #include "loottable.h" #include "memory_mapped_file.h" #include "mysql.h" diff --git a/world/client.cpp b/world/client.cpp index ff3c0ce9a..5614d6317 100644 --- a/world/client.cpp +++ b/world/client.cpp @@ -25,7 +25,7 @@ #include "../common/eq_packet_structs.h" #include "../common/packet_dump.h" #include "../common/eq_stream_intf.h" -#include "../common/item_instance.h" +#include "../common/inventory_profile.h" #include "../common/races.h" #include "../common/classes.h" #include "../common/languages.h" diff --git a/world/worlddb.cpp b/world/worlddb.cpp index 0fa814417..65476190e 100644 --- a/world/worlddb.cpp +++ b/world/worlddb.cpp @@ -17,10 +17,9 @@ */ #include "worlddb.h" -//#include "../common/item_instance.h" #include "../common/string_util.h" #include "../common/eq_packet_structs.h" -#include "../common/item_instance.h" +#include "../common/inventory_profile.h" #include "../common/rulesys.h" #include #include diff --git a/zone/client.h b/zone/client.h index bfe9f96cd..82ee442d2 100644 --- a/zone/client.h +++ b/zone/client.h @@ -45,9 +45,9 @@ namespace EQEmu #include "../common/extprofile.h" #include "../common/races.h" #include "../common/seperator.h" -#include "../common/item_instance.h" +#include "../common/inventory_profile.h" #include "../common/guilds.h" -#include "../common/item_data.h" +//#include "../common/item_data.h" #include "common.h" #include "merc.h" diff --git a/zone/zonedump.h b/zone/zonedump.h index 20412ed70..80888d31e 100644 --- a/zone/zonedump.h +++ b/zone/zonedump.h @@ -28,7 +28,7 @@ spawn2 mediumblob, npcs mediumblob, npc_loot mediumblob, gmspawntype mediumblob, #define ZONEDUMP_H #include "../common/faction.h" #include "../common/eq_packet_structs.h" -#include "../common/item_instance.h" +#include "../common/inventory_profile.h" #pragma pack(1)