diff options
author | Shauren <shauren.trinity@gmail.com> | 2016-07-16 13:52:12 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2016-07-16 13:52:12 +0200 |
commit | 64de4b38ef5eddb14d9d456740fee62595c89e06 (patch) | |
tree | 719944e12b7178ab85dbab0934f6e171c15ec5d9 /src | |
parent | 49202ba93c6f02a93aa5268536dbd053fd26925a (diff) |
Core/Items: Implemented child equipment
Diffstat (limited to 'src')
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.cpp | 3 | ||||
-rw-r--r-- | src/server/database/Database/Implementation/HotfixDatabase.h | 2 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.cpp | 17 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Stores.h | 3 | ||||
-rw-r--r-- | src/server/game/DataStores/DB2Structure.h | 8 | ||||
-rw-r--r-- | src/server/game/Entities/Item/Item.cpp | 10 | ||||
-rw-r--r-- | src/server/game/Entities/Item/Item.h | 4 | ||||
-rw-r--r-- | src/server/game/Entities/Item/ItemTemplate.h | 7 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 626 | ||||
-rw-r--r-- | src/server/game/Entities/Player/Player.h | 16 | ||||
-rw-r--r-- | src/server/game/Handlers/ItemHandler.cpp | 116 | ||||
-rw-r--r-- | src/server/game/Handlers/MailHandler.cpp | 2 |
12 files changed, 751 insertions, 63 deletions
diff --git a/src/server/database/Database/Implementation/HotfixDatabase.cpp b/src/server/database/Database/Implementation/HotfixDatabase.cpp index 524392fb474..beff06ff757 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.cpp +++ b/src/server/database/Database/Implementation/HotfixDatabase.cpp @@ -382,6 +382,9 @@ void HotfixDatabaseConnection::DoPrepareStatements() PrepareStatement(HOTFIX_SEL_ITEM_CLASS, "SELECT ID, PriceMod, Name, Flags FROM item_class ORDER BY ID DESC", CONNECTION_SYNCH); PREPARE_LOCALE_STMT(HOTFIX_SEL_ITEM_CLASS, "SELECT ID, Name_lang FROM item_class_locale WHERE locale = ?", CONNECTION_SYNCH); + // ItemChildEquipment.db2 + PrepareStatement(HOTFIX_SEL_ITEM_CHILD_EQUIPMENT, "SELECT ID, ItemID, AltItemID, AltEquipmentSlot FROM item_child_equipment ORDER BY ID DESC", CONNECTION_SYNCH); + // ItemCurrencyCost.db2 PrepareStatement(HOTFIX_SEL_ITEM_CURRENCY_COST, "SELECT ID, ItemId FROM item_currency_cost ORDER BY ID DESC", CONNECTION_SYNCH); diff --git a/src/server/database/Database/Implementation/HotfixDatabase.h b/src/server/database/Database/Implementation/HotfixDatabase.h index 4221e05f0cc..4fc4d796f58 100644 --- a/src/server/database/Database/Implementation/HotfixDatabase.h +++ b/src/server/database/Database/Implementation/HotfixDatabase.h @@ -218,6 +218,8 @@ enum HotfixDatabaseStatements HOTFIX_SEL_ITEM_CLASS, HOTFIX_SEL_ITEM_CLASS_LOCALE, + HOTFIX_SEL_ITEM_CHILD_EQUIPMENT, + HOTFIX_SEL_ITEM_CURRENCY_COST, HOTFIX_SEL_ITEM_DAMAGE_AMMO, diff --git a/src/server/game/DataStores/DB2Stores.cpp b/src/server/game/DataStores/DB2Stores.cpp index 2d7c3003688..40eeae312f8 100644 --- a/src/server/game/DataStores/DB2Stores.cpp +++ b/src/server/game/DataStores/DB2Stores.cpp @@ -101,6 +101,7 @@ DB2Storage<ItemBagFamilyEntry> sItemBagFamilyStore("ItemBagFami DB2Storage<ItemBonusEntry> sItemBonusStore("ItemBonus.db2", ItemBonusMeta::Instance(), HOTFIX_SEL_ITEM_BONUS); DB2Storage<ItemBonusTreeNodeEntry> sItemBonusTreeNodeStore("ItemBonusTreeNode.db2", ItemBonusTreeNodeMeta::Instance(), HOTFIX_SEL_ITEM_BONUS_TREE_NODE); DB2Storage<ItemClassEntry> sItemClassStore("ItemClass.db2", ItemClassMeta::Instance(), HOTFIX_SEL_ITEM_CLASS); +DB2Storage<ItemChildEquipmentEntry> sItemChildEquipmentStore("ItemChildEquipment.db2", ItemChildEquipmentMeta::Instance(), HOTFIX_SEL_ITEM_CHILD_EQUIPMENT); DB2Storage<ItemCurrencyCostEntry> sItemCurrencyCostStore("ItemCurrencyCost.db2", ItemCurrencyCostMeta::Instance(), HOTFIX_SEL_ITEM_CURRENCY_COST); DB2Storage<ItemDamageAmmoEntry> sItemDamageAmmoStore("ItemDamageAmmo.db2", ItemDamageAmmoMeta::Instance(), HOTFIX_SEL_ITEM_DAMAGE_AMMO); DB2Storage<ItemDamageOneHandEntry> sItemDamageOneHandStore("ItemDamageOneHand.db2", ItemDamageOneHandMeta::Instance(), HOTFIX_SEL_ITEM_DAMAGE_ONE_HAND); @@ -362,6 +363,7 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) LOAD_DB2(sItemBonusStore); LOAD_DB2(sItemBonusTreeNodeStore); LOAD_DB2(sItemClassStore); + LOAD_DB2(sItemChildEquipmentStore); LOAD_DB2(sItemCurrencyCostStore); LOAD_DB2(sItemDamageAmmoStore); LOAD_DB2(sItemDamageOneHandStore); @@ -600,6 +602,12 @@ void DB2Manager::LoadStores(std::string const& dataPath, uint32 defaultLocale) } } + for (ItemChildEquipmentEntry const* itemChildEquipment : sItemChildEquipmentStore) + { + ASSERT(_itemChildEquipment.find(itemChildEquipment->ItemID) == _itemChildEquipment.end(), "Item must have max 1 child item."); + _itemChildEquipment[itemChildEquipment->ItemID] = itemChildEquipment; + } + for (ItemCurrencyCostEntry const* itemCurrencyCost : sItemCurrencyCostStore) _itemsWithCurrencyCost.insert(itemCurrencyCost->ItemId); @@ -1062,6 +1070,15 @@ std::set<uint32> DB2Manager::GetItemBonusTree(uint32 itemId, uint32 itemBonusTre return bonusListIDs; } +ItemChildEquipmentEntry const* DB2Manager::GetItemChildEquipment(uint32 itemId) const +{ + auto itr = _itemChildEquipment.find(itemId); + if (itr != _itemChildEquipment.end()) + return itr->second; + + return nullptr; +} + uint32 DB2Manager::GetItemDisplayId(uint32 itemId, uint32 appearanceModId) const { if (ItemModifiedAppearanceEntry const* modifiedAppearance = GetItemModifiedAppearance(itemId, appearanceModId)) diff --git a/src/server/game/DataStores/DB2Stores.h b/src/server/game/DataStores/DB2Stores.h index 9ee2bb3b553..fdf3b744c4c 100644 --- a/src/server/game/DataStores/DB2Stores.h +++ b/src/server/game/DataStores/DB2Stores.h @@ -235,6 +235,7 @@ public: typedef std::vector<ItemBonusEntry const*> ItemBonusList; typedef std::unordered_map<uint32 /*bonusListId*/, ItemBonusList> ItemBonusListContainer; typedef std::unordered_multimap<uint32 /*itemId*/, uint32 /*bonusTreeId*/> ItemToBonusTreeContainer; + typedef std::unordered_map<uint32 /*itemId*/, ItemChildEquipmentEntry const*> ItemChildEquipmentContainer; typedef std::unordered_map<uint32 /*itemId | appearanceMod << 24*/, ItemModifiedAppearanceEntry const*> ItemModifiedAppearanceByItemContainer; typedef std::unordered_map<uint32, std::set<ItemBonusTreeNodeEntry const*>> ItemBonusTreeContainer; typedef std::unordered_map<uint32, std::vector<ItemSetSpellEntry const*>> ItemSetSpellContainer; @@ -283,6 +284,7 @@ public: HeirloomEntry const* GetHeirloomByItemId(uint32 itemId) const; ItemBonusList const* GetItemBonusList(uint32 bonusListId) const; std::set<uint32> GetItemBonusTree(uint32 itemId, uint32 itemBonusTreeMod) const; + ItemChildEquipmentEntry const* GetItemChildEquipment(uint32 itemId) const; bool HasItemCurrencyCost(uint32 itemId) const { return _itemsWithCurrencyCost.count(itemId) > 0; } uint32 GetItemDisplayId(uint32 itemId, uint32 appearanceModId) const; ItemModifiedAppearanceEntry const* GetItemModifiedAppearance(uint32 itemId, uint32 appearanceModId) const; @@ -333,6 +335,7 @@ private: HeirloomCurvesContainer _heirloomCurvePoints; ItemBonusListContainer _itemBonusLists; ItemBonusTreeContainer _itemBonusTrees; + ItemChildEquipmentContainer _itemChildEquipment; std::unordered_set<uint32> _itemsWithCurrencyCost; ItemModifiedAppearanceByItemContainer _itemModifiedAppearancesByItem; ItemToBonusTreeContainer _itemToBonusTree; diff --git a/src/server/game/DataStores/DB2Structure.h b/src/server/game/DataStores/DB2Structure.h index f9a7c7116d8..2101661912a 100644 --- a/src/server/game/DataStores/DB2Structure.h +++ b/src/server/game/DataStores/DB2Structure.h @@ -1182,6 +1182,14 @@ struct ItemBonusTreeNodeEntry uint8 BonusTreeModID; }; +struct ItemChildEquipmentEntry +{ + uint32 ID; + uint32 ItemID; + uint32 AltItemID; + uint8 AltEquipmentSlot; +}; + struct ItemClassEntry { uint32 ID; diff --git a/src/server/game/Entities/Item/Item.cpp b/src/server/game/Entities/Item/Item.cpp index 5acfa65ee7b..18c61a4c094 100644 --- a/src/server/game/Entities/Item/Item.cpp +++ b/src/server/game/Entities/Item/Item.cpp @@ -551,9 +551,15 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie if (!ownerGuid.IsEmpty()) SetOwnerGUID(ownerGuid); + uint32 itemFlags = fields[7].GetUInt32(); bool need_save = false; // need explicit save data at load fixes if (uint64 creator = fields[2].GetUInt64()) - SetGuidValue(ITEM_FIELD_CREATOR, ObjectGuid::Create<HighGuid::Player>(creator)); + { + if (!(itemFlags & ITEM_FIELD_FLAG_CHILD)) + SetGuidValue(ITEM_FIELD_CREATOR, ObjectGuid::Create<HighGuid::Player>(creator)); + else + SetGuidValue(ITEM_FIELD_CREATOR, ObjectGuid::Create<HighGuid::Item>(creator)); + } if (uint64 giftCreator = fields[3].GetUInt64()) SetGuidValue(ITEM_FIELD_GIFTCREATOR, ObjectGuid::Create<HighGuid::Player>(giftCreator)); SetCount(fields[4].GetUInt32()); @@ -572,7 +578,7 @@ bool Item::LoadFromDB(ObjectGuid::LowType guid, ObjectGuid ownerGuid, Field* fie for (uint8 i = 0; i < proto->Effects.size(); ++i) SetSpellCharges(i, atoi(tokens[i])); - SetUInt32Value(ITEM_FIELD_FLAGS, fields[7].GetUInt32()); + SetUInt32Value(ITEM_FIELD_FLAGS, itemFlags); // Remove bind flag for items vs NO_BIND set if (IsSoulBound() && proto->GetBonding() == NO_BIND) { diff --git a/src/server/game/Entities/Item/Item.h b/src/server/game/Entities/Item/Item.h index 68d903f6a81..683353a7fe3 100644 --- a/src/server/game/Entities/Item/Item.h +++ b/src/server/game/Entities/Item/Item.h @@ -477,6 +477,9 @@ class TC_GAME_API Item : public Object uint32 GetModifier(ItemModifier modifier) const; void SetModifier(ItemModifier modifier, uint32 value); + ObjectGuid GetChildItem() const { return m_childItem; } + void SetChildItem(ObjectGuid childItem) { m_childItem = childItem; } + protected: BonusData _bonusData; @@ -492,5 +495,6 @@ class TC_GAME_API Item : public Object uint32 m_paidMoney; uint32 m_paidExtendedCost; GuidSet allowedGUIDs; + ObjectGuid m_childItem; }; #endif diff --git a/src/server/game/Entities/Item/ItemTemplate.h b/src/server/game/Entities/Item/ItemTemplate.h index a41c8d91a88..d95b361da89 100644 --- a/src/server/game/Entities/Item/ItemTemplate.h +++ b/src/server/game/Entities/Item/ItemTemplate.h @@ -152,7 +152,7 @@ enum ItemFieldFlags : uint32 ITEM_FIELD_FLAG_UNK11 = 0x00010000, ITEM_FIELD_FLAG_UNK12 = 0x00020000, ITEM_FIELD_FLAG_UNK13 = 0x00040000, - ITEM_FIELD_FLAG_UNK14 = 0x00080000, + ITEM_FIELD_FLAG_CHILD = 0x00080000, ITEM_FIELD_FLAG_UNK15 = 0x00100000, ITEM_FIELD_FLAG_UNK16 = 0x00200000, ITEM_FIELD_FLAG_UNK17 = 0x00400000, @@ -164,9 +164,7 @@ enum ItemFieldFlags : uint32 ITEM_FIELD_FLAG_UNK23 = 0x10000000, ITEM_FIELD_FLAG_UNK24 = 0x20000000, ITEM_FIELD_FLAG_UNK25 = 0x40000000, - ITEM_FIELD_FLAG_UNK26 = 0x80000000, - - ITEM_FIELD_FLAG_MAIL_TEXT_MASK = ITEM_FIELD_FLAG_READABLE | ITEM_FIELD_FLAG_UNK13 | ITEM_FIELD_FLAG_UNK14 + ITEM_FIELD_FLAG_UNK26 = 0x80000000 }; enum ItemFlags : uint32 @@ -789,6 +787,7 @@ struct TC_GAME_API ItemTemplate bool IsPotion() const { return GetClass() == ITEM_CLASS_CONSUMABLE && GetSubClass() == ITEM_SUBCLASS_POTION; } bool IsVellum() const { return GetClass() == ITEM_CLASS_TRADE_GOODS && GetSubClass() == ITEM_SUBCLASS_ENCHANTMENT; } bool IsConjuredConsumable() const { return GetClass() == ITEM_CLASS_CONSUMABLE && (GetFlags() & ITEM_FLAG_CONJURED); } + bool IsCraftingReagent() const { return (GetFlags2() & ITEM_FLAG2_CRAFTING_MATERIAL) != 0; } bool IsRangedWeapon() const { diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 045066cdedf..e0d958d431f 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -3581,6 +3581,22 @@ void Player::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) c m_items[i]->BuildCreateUpdateBlockForPlayer(data, target); } + + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i) + { + if (m_items[i] == nullptr) + continue; + + m_items[i]->BuildCreateUpdateBlockForPlayer(data, target); + } + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + { + if (m_items[i] == nullptr) + continue; + + m_items[i]->BuildCreateUpdateBlockForPlayer(data, target); + } } Unit::BuildCreateUpdateBlockForPlayer(data, target); @@ -3607,6 +3623,22 @@ void Player::DestroyForPlayer(Player* target) const m_items[i]->DestroyForPlayer(target); } + + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i) + { + if (m_items[i] == nullptr) + continue; + + m_items[i]->DestroyForPlayer(target); + } + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + { + if (m_items[i] == nullptr) + continue; + + m_items[i]->DestroyForPlayer(target); + } } } @@ -9296,6 +9328,24 @@ InventoryResult Player::CanUnequipItems(uint32 item, uint32 count) const } + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem->GetEntry() == item) + { + tempcount += pItem->GetCount(); + if (tempcount >= count) + return EQUIP_ERR_OK; + } + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem->GetEntry() == item) + { + tempcount += pItem->GetCount(); + if (tempcount >= count) + return EQUIP_ERR_OK; + } + for (uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) if (Bag* pBag = GetBagByPos(i)) for (uint32 j = 0; j < pBag->GetBagSize(); ++j) @@ -9348,6 +9398,28 @@ uint32 Player::GetItemCount(uint32 item, bool inBankAlso, Item* skipItem) const count += pItem->GetGemCountWithID(item); } + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem != skipItem && pItem->GetEntry() == item) + count += pItem->GetCount(); + + if (skipItem && skipItem->GetTemplate()->GetGemProperties()) + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem != skipItem && pItem->GetSocketColor(0)) + count += pItem->GetGemCountWithID(item); + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem != skipItem && pItem->GetEntry() == item) + count += pItem->GetCount(); + + if (skipItem && skipItem->GetTemplate()->GetGemProperties()) + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem != skipItem && pItem->GetSocketColor(0)) + count += pItem->GetGemCountWithID(item); + return count; } @@ -9376,6 +9448,20 @@ uint32 Player::GetItemCountWithLimitCategory(uint32 limitCategory, Item* skipIte if (Bag* pBag = GetBagByPos(i)) count += pBag->GetItemCountWithLimitCategory(limitCategory, skipItem); + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem != skipItem) + if (ItemTemplate const* pProto = pItem->GetTemplate()) + if (pProto->GetItemLimitCategory() == limitCategory) + count += pItem->GetCount(); + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem != skipItem) + if (ItemTemplate const* pProto = pItem->GetTemplate()) + if (pProto->GetItemLimitCategory() == limitCategory) + count += pItem->GetCount(); + return count; } @@ -9386,7 +9472,17 @@ Item* Player::GetItemByGuid(ObjectGuid guid) const if (pItem->GetGUID() == guid) return pItem; - for (int i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i) + for (uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_BAG_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem->GetGUID() == guid) + return pItem; + + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem->GetGUID() == guid) + return pItem; + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) if (pItem->GetGUID() == guid) return pItem; @@ -9417,7 +9513,7 @@ Item* Player::GetItemByPos(uint16 pos) const Item* Player::GetItemByPos(uint8 bag, uint8 slot) const { - if (bag == INVENTORY_SLOT_BAG_0 && slot < BANK_SLOT_BAG_END) + if (bag == INVENTORY_SLOT_BAG_0 && slot < PLAYER_SLOT_END && (slot < BUYBACK_SLOT_START || slot >= BUYBACK_SLOT_END)) return m_items[slot]; if (Bag* pBag = GetBagByPos(bag)) return pBag->GetItemByPos(slot); @@ -9494,6 +9590,21 @@ Item* Player::GetShield(bool useable) const return item; } +Item* Player::GetChildItemByGuid(ObjectGuid guid) const +{ + for (uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem->GetGUID() == guid) + return pItem; + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem->GetGUID() == guid) + return pItem; + + return nullptr; +} + uint8 Player::GetAttackBySlot(uint8 slot, InventoryType inventoryType) { switch (slot) @@ -9512,6 +9623,10 @@ bool Player::IsInventoryPos(uint8 bag, uint8 slot) return true; if (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END) return true; + if (bag == INVENTORY_SLOT_BAG_0 && (slot >= REAGENT_SLOT_START && slot < REAGENT_SLOT_END)) + return true; + if (bag == INVENTORY_SLOT_BAG_0 && (slot >= CHILD_EQUIPMENT_SLOT_START && slot < CHILD_EQUIPMENT_SLOT_END)) + return true; return false; } @@ -9546,6 +9661,11 @@ bool Player::IsBagPos(uint16 pos) return false; } +bool Player::IsChildEquipmentPos(uint8 bag, uint8 slot) +{ + return bag == INVENTORY_SLOT_BAG_0 && (slot >= CHILD_EQUIPMENT_SLOT_START && slot < CHILD_EQUIPMENT_SLOT_END); +} + bool Player::IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) const { // post selected @@ -9657,6 +9777,28 @@ bool Player::HasItemCount(uint32 item, uint32 count, bool inBankAlso) const } } + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; i++) + { + Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) + { + tempcount += pItem->GetCount(); + if (tempcount >= count) + return true; + } + } + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; i++) + { + Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (pItem && pItem->GetEntry() == item && !pItem->IsInTrade()) + { + tempcount += pItem->GetCount(); + if (tempcount >= count) + return true; + } + } + return false; } @@ -9826,6 +9968,20 @@ bool Player::HasItemTotemCategory(uint32 TotemCategory) const } } + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i) + { + item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i); + if (item && DB2Manager::IsTotemCategoryCompatibleWith(item->GetTemplate()->GetTotemCategory(), TotemCategory)) + return true; + } + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + { + item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, i); + if (item && DB2Manager::IsTotemCategoryCompatibleWith(item->GetTemplate()->GetTotemCategory(), TotemCategory)) + return true; + } + return false; } @@ -9839,8 +9995,17 @@ InventoryResult Player::CanStoreItem_InSpecificSlot(uint8 bag, uint8 slot, ItemP uint32 need_space; - if (pSrcItem && pSrcItem->IsNotEmptyBag() && !IsBagPos(uint16(bag) << 8 | slot)) - return EQUIP_ERR_DESTROY_NONEMPTY_BAG; + if (pSrcItem) + { + if (pSrcItem->IsNotEmptyBag() && !IsBagPos(uint16(bag) << 8 | slot)) + return EQUIP_ERR_DESTROY_NONEMPTY_BAG; + + if (pSrcItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD) && !IsEquipmentPos(bag, slot) && !IsChildEquipmentPos(bag, slot)) + return EQUIP_ERR_WRONG_BAG_TYPE_3; + + if (!pSrcItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD) && IsChildEquipmentPos(bag, slot)) + return EQUIP_ERR_WRONG_BAG_TYPE_3; + } // empty specific slot - check item fit to slot if (!pItem2 || swap) @@ -9906,8 +10071,14 @@ InventoryResult Player::CanStoreItem_InBag(uint8 bag, ItemPosCountVec &dest, Ite if (!pBag || pBag == pSrcItem) return EQUIP_ERR_WRONG_BAG_TYPE; - if (pSrcItem && pSrcItem->IsNotEmptyBag()) - return EQUIP_ERR_DESTROY_NONEMPTY_BAG; + if (pSrcItem) + { + if (pSrcItem->IsNotEmptyBag()) + return EQUIP_ERR_DESTROY_NONEMPTY_BAG; + + if (pSrcItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD)) + return EQUIP_ERR_WRONG_BAG_TYPE_3; + } ItemTemplate const* pBagProto = pBag->GetTemplate(); if (!pBagProto) @@ -10090,6 +10261,42 @@ InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &des { if (bag == INVENTORY_SLOT_BAG_0) // inventory { + res = CanStoreItem_InInventorySlots(CHILD_EQUIPMENT_SLOT_START, CHILD_EQUIPMENT_SLOT_END, dest, pProto, count, true, pItem, bag, slot); + if (res != EQUIP_ERR_OK) + { + if (no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if (count == 0) + { + if (no_similar_count == 0) + return EQUIP_ERR_OK; + + if (no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_ITEM_MAX_COUNT; + } + + res = CanStoreItem_InInventorySlots(REAGENT_SLOT_START, REAGENT_SLOT_END, dest, pProto, count, true, pItem, bag, slot); + if (res != EQUIP_ERR_OK) + { + if (no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if (count == 0) + { + if (no_similar_count == 0) + return EQUIP_ERR_OK; + + if (no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_ITEM_MAX_COUNT; + } + res = CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot); if (res != EQUIP_ERR_OK) { @@ -10137,6 +10344,47 @@ InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &des // search free slot in bag for place to if (bag == INVENTORY_SLOT_BAG_0) // inventory { + if (pItem && pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD)) + { + res = CanStoreItem_InInventorySlots(CHILD_EQUIPMENT_SLOT_START, CHILD_EQUIPMENT_SLOT_END, dest, pProto, count, false, pItem, bag, slot); + if (res != EQUIP_ERR_OK) + { + if (no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if (count == 0) + { + if (no_similar_count == 0) + return EQUIP_ERR_OK; + + if (no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_ITEM_MAX_COUNT; + } + } + else if (pProto->IsCraftingReagent() && HasFlag(PLAYER_FLAGS_EX, PLAYER_FLAGS_EX_REAGENT_BANK_UNLOCKED)) + { + res = CanStoreItem_InInventorySlots(REAGENT_SLOT_START, REAGENT_SLOT_END, dest, pProto, count, false, pItem, bag, slot); + if (res != EQUIP_ERR_OK) + { + if (no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if (count == 0) + { + if (no_similar_count == 0) + return EQUIP_ERR_OK; + + if (no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_ITEM_MAX_COUNT; + } + } + res = CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot); if (res != EQUIP_ERR_OK) { @@ -10185,6 +10433,42 @@ InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &des // search stack for merge to if (pProto->GetMaxStackSize() != 1) { + res = CanStoreItem_InInventorySlots(CHILD_EQUIPMENT_SLOT_START, CHILD_EQUIPMENT_SLOT_END, dest, pProto, count, true, pItem, bag, slot); + if (res != EQUIP_ERR_OK) + { + if (no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if (count == 0) + { + if (no_similar_count == 0) + return EQUIP_ERR_OK; + + if (no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_ITEM_MAX_COUNT; + } + + res = CanStoreItem_InInventorySlots(REAGENT_SLOT_START, REAGENT_SLOT_END, dest, pProto, count, true, pItem, bag, slot); + if (res != EQUIP_ERR_OK) + { + if (no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if (count == 0) + { + if (no_similar_count == 0) + return EQUIP_ERR_OK; + + if (no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_ITEM_MAX_COUNT; + } + res = CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, true, pItem, bag, slot); if (res != EQUIP_ERR_OK) { @@ -10265,6 +10549,47 @@ InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec &des if (pItem && pItem->IsNotEmptyBag()) return EQUIP_ERR_BAG_IN_BAG; + if (pItem && pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD)) + { + res = CanStoreItem_InInventorySlots(CHILD_EQUIPMENT_SLOT_START, CHILD_EQUIPMENT_SLOT_END, dest, pProto, count, false, pItem, bag, slot); + if (res != EQUIP_ERR_OK) + { + if (no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if (count == 0) + { + if (no_similar_count == 0) + return EQUIP_ERR_OK; + + if (no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_ITEM_MAX_COUNT; + } + } + else if (pProto->IsCraftingReagent() && HasFlag(PLAYER_FLAGS_EX, PLAYER_FLAGS_EX_REAGENT_BANK_UNLOCKED)) + { + res = CanStoreItem_InInventorySlots(REAGENT_SLOT_START, REAGENT_SLOT_END, dest, pProto, count, false, pItem, bag, slot); + if (res != EQUIP_ERR_OK) + { + if (no_space_count) + *no_space_count = count + no_similar_count; + return res; + } + + if (count == 0) + { + if (no_similar_count == 0) + return EQUIP_ERR_OK; + + if (no_space_count) + *no_space_count = count + no_similar_count; + return EQUIP_ERR_ITEM_MAX_COUNT; + } + } + // search free slot res = CanStoreItem_InInventorySlots(INVENTORY_SLOT_ITEM_START, INVENTORY_SLOT_ITEM_END, dest, pProto, count, false, pItem, bag, slot); if (res != EQUIP_ERR_OK) @@ -10662,6 +10987,46 @@ InventoryResult Player::CanEquipItem(uint8 slot, uint16 &dest, Item* pItem, bool return !swap ? EQUIP_ERR_ITEM_NOT_FOUND : EQUIP_ERR_CANT_SWAP; } +InventoryResult Player::CanEquipChildItem(Item* parentItem) const +{ + Item* childItem = GetChildItemByGuid(parentItem->GetChildItem()); + if (!childItem) + return EQUIP_ERR_OK; + + ItemChildEquipmentEntry const* childEquipement = sDB2Manager.GetItemChildEquipment(parentItem->GetEntry()); + if (!childItem) + return EQUIP_ERR_OK; + + Item* dstItem = GetItemByPos(INVENTORY_SLOT_BAG_0, childEquipement->AltEquipmentSlot); + if (!dstItem) + return EQUIP_ERR_OK; + + uint16 childDest = (INVENTORY_SLOT_BAG_0 << 8) | childEquipement->AltEquipmentSlot; + InventoryResult msg = CanUnequipItem(childDest, !childItem->IsBag()); + if (msg != EQUIP_ERR_OK) + return msg; + + // check dest->src move possibility + uint16 src = parentItem->GetPos(); + ItemPosCountVec dest; + if (IsInventoryPos(src)) + { + msg = CanStoreItem(parentItem->GetBagSlot(), NULL_SLOT, dest, dstItem, true); + if (msg != EQUIP_ERR_OK) + msg = CanStoreItem(NULL_BAG, NULL_SLOT, dest, dstItem, true); + } + else if (IsBankPos(src)) + { + msg = CanBankItem(parentItem->GetBagSlot(), NULL_SLOT, dest, dstItem, true); + if (msg != EQUIP_ERR_OK) + msg = CanBankItem(NULL_BAG, NULL_SLOT, dest, dstItem, true); + } + else if (IsEquipmentPos(src)) + return EQUIP_ERR_CANT_SWAP; + + return msg; +} + InventoryResult Player::CanUnequipItem(uint16 pos, bool swap) const { // Applied only to equipped items and bank bags @@ -11110,6 +11475,20 @@ Item* Player::StoreNewItem(ItemPosCountVec const& pos, uint32 itemId, bool updat if (addToCollection) GetSession()->GetCollectionMgr()->OnItemAdded(item); + + if (ItemChildEquipmentEntry const* childItemEntry = sDB2Manager.GetItemChildEquipment(itemId)) + { + if (ItemTemplate const* childTemplate = sObjectMgr->GetItemTemplate(childItemEntry->AltItemID)) + { + ItemPosCountVec childDest; + CanStoreItem_InInventorySlots(CHILD_EQUIPMENT_SLOT_START, CHILD_EQUIPMENT_SLOT_END, childDest, childTemplate, count, false, nullptr, NULL_BAG, NULL_SLOT); + if (Item* childItem = StoreNewItem(childDest, childTemplate->GetId(), update, 0, {}, {}, addToCollection)) + { + childItem->SetGuidValue(ITEM_FIELD_CREATOR, item->GetGUID()); + childItem->SetFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD); + } + } + } } return item; } @@ -11135,6 +11514,9 @@ Item* Player::StoreItem(ItemPosCountVec const& dest, Item* pItem, bool update) lastItem = _StoreItem(pos, pItem, count, true, update); } + + AutoUnequipChildItem(pItem); + return lastItem; } @@ -11368,6 +11750,105 @@ Item* Player::EquipItem(uint16 pos, Item* pItem, bool update) return pItem; } +void Player::EquipChildItem(uint8 parentBag, uint8 parentSlot, Item* parentItem) +{ + if (ItemChildEquipmentEntry const* itemChildEquipment = sDB2Manager.GetItemChildEquipment(parentItem->GetEntry())) + { + if (Item* childItem = GetChildItemByGuid(parentItem->GetChildItem())) + { + uint16 childDest = (INVENTORY_SLOT_BAG_0 << 8) | itemChildEquipment->AltEquipmentSlot; + if (childItem->GetPos() != childDest) + { + Item* dstItem = GetItemByPos(childDest); + if (!dstItem) // empty slot, simple case + { + RemoveItem(childItem->GetBagSlot(), childItem->GetSlot(), true); + EquipItem(childDest, childItem, true); + AutoUnequipOffhandIfNeed(); + } + else // have currently equipped item, not simple case + { + uint8 dstbag = dstItem->GetBagSlot(); + uint8 dstslot = dstItem->GetSlot(); + + InventoryResult msg = CanUnequipItem(childDest, !childItem->IsBag()); + if (msg != EQUIP_ERR_OK) + { + SendEquipError(msg, dstItem); + return; + } + + // check dest->src move possibility but try to store currently equipped item in the bag where the parent item is + ItemPosCountVec sSrc; + uint16 eSrc = 0; + if (IsInventoryPos(parentBag, parentSlot)) + { + msg = CanStoreItem(parentBag, NULL_SLOT, sSrc, dstItem, true); + if (msg != EQUIP_ERR_OK) + msg = CanStoreItem(NULL_BAG, NULL_SLOT, sSrc, dstItem, true); + } + else if (IsBankPos(parentBag, parentSlot)) + { + msg = CanBankItem(parentBag, NULL_SLOT, sSrc, dstItem, true); + if (msg != EQUIP_ERR_OK) + msg = CanBankItem(NULL_BAG, NULL_SLOT, sSrc, dstItem, true); + } + else if (IsEquipmentPos(parentBag, parentSlot)) + { + msg = CanEquipItem(parentSlot, eSrc, dstItem, true); + if (msg == EQUIP_ERR_OK) + msg = CanUnequipItem(eSrc, true); + } + + if (msg != EQUIP_ERR_OK) + { + SendEquipError(msg, dstItem, childItem); + return; + } + + // now do moves, remove... + RemoveItem(dstbag, dstslot, false); + RemoveItem(childItem->GetBagSlot(), childItem->GetSlot(), false); + + // add to dest + EquipItem(childDest, childItem, true); + + // add to src + if (IsInventoryPos(parentBag, parentSlot)) + StoreItem(sSrc, dstItem, true); + else if (IsBankPos(parentBag, parentSlot)) + BankItem(sSrc, dstItem, true); + else if (IsEquipmentPos(parentBag, parentSlot)) + EquipItem(eSrc, dstItem, true); + + AutoUnequipOffhandIfNeed(); + } + } + } + } +} + +void Player::AutoUnequipChildItem(Item* parentItem) +{ + if (ItemChildEquipmentEntry const* itemChildEquipment = sDB2Manager.GetItemChildEquipment(parentItem->GetEntry())) + { + if (Item* childItem = GetChildItemByGuid(parentItem->GetChildItem())) + { + if (IsChildEquipmentPos(childItem->GetPos())) + return; + + ItemPosCountVec dest; + uint32 count = childItem->GetCount(); + InventoryResult result = CanStoreItem_InInventorySlots(CHILD_EQUIPMENT_SLOT_START, CHILD_EQUIPMENT_SLOT_END, dest, childItem->GetTemplate(), count, false, childItem, NULL_BAG, NULL_SLOT); + if (result != EQUIP_ERR_OK) + return; + + RemoveItem(childItem->GetBagSlot(), childItem->GetSlot(), true); + StoreItem(dest, childItem, true); + } + } +} + void Player::QuickEquipItem(uint16 pos, Item* pItem) { if (pItem) @@ -11515,6 +11996,8 @@ void Player::RemoveItem(uint8 bag, uint8 slot, bool update) pItem->SetSlot(NULL_SLOT); if (IsInWorld() && update) pItem->SendUpdateToPlayer(this); + + AutoUnequipChildItem(pItem); } } @@ -11825,6 +12308,63 @@ void Player::DestroyItemCount(uint32 itemEntry, uint32 count, bool update, bool } } } + + for (uint8 i = REAGENT_SLOT_START; i < REAGENT_SLOT_END; ++i) + { + if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + if (item->GetEntry() == itemEntry && !item->IsInTrade()) + { + if (item->GetCount() + remcount <= count) + { + // all keys can be unequipped + remcount += item->GetCount(); + DestroyItem(INVENTORY_SLOT_BAG_0, i, update); + + if (remcount >= count) + return; + } + else + { + ItemRemovedQuestCheck(item->GetEntry(), count - remcount); + item->SetCount(item->GetCount() - count + remcount); + if (IsInWorld() && update) + item->SendUpdateToPlayer(this); + item->SetState(ITEM_CHANGED, this); + return; + } + } + } + } + + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + { + if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + { + if (item->GetEntry() == itemEntry && !item->IsInTrade()) + { + if (item->GetCount() + remcount <= count) + { + // all keys can be unequipped + remcount += item->GetCount(); + DestroyItem(INVENTORY_SLOT_BAG_0, i, update); + + if (remcount >= count) + return; + } + else + { + ItemRemovedQuestCheck(item->GetEntry(), count - remcount); + item->SetCount(item->GetCount() - count + remcount); + if (IsInWorld() && update) + item->SendUpdateToPlayer(this); + item->SetState(ITEM_CHANGED, this); + return; + } + } + } + } + } void Player::DestroyZoneLimitedItem(bool update, uint32 new_zone) @@ -11901,6 +12441,11 @@ Item* Player::GetItemByEntry(uint32 entry) const if (pItem->GetEntry() == entry) return pItem; + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (pItem->GetEntry() == entry) + return pItem; + return nullptr; } @@ -11933,6 +12478,11 @@ std::vector<Item*> Player::GetItemListByEntry(uint32 entry, bool inBankAlso) con itemList.push_back(item); } + for (uint8 i = CHILD_EQUIPMENT_SLOT_START; i < CHILD_EQUIPMENT_SLOT_END; ++i) + if (Item* item = GetItemByPos(INVENTORY_SLOT_BAG_0, i)) + if (item->GetEntry() == entry) + itemList.push_back(item); + return itemList; } @@ -12091,6 +12641,33 @@ void Player::SwapItem(uint16 src, uint16 dst) if (!pSrcItem) return; + if (pSrcItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD)) + { + if (Item* parentItem = GetItemByGuid(pSrcItem->GetGuidValue(ITEM_FIELD_CREATOR))) + { + if (IsEquipmentPos(src)) + { + AutoUnequipChildItem(parentItem); // we need to unequip child first since it cannot go into whatever is going to happen next + SwapItem(dst, src); // src is now empty + SwapItem(parentItem->GetPos(), dst);// dst is now empty + return; + } + } + } + else if (pDstItem && pDstItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD)) + { + if (Item* parentItem = GetItemByGuid(pDstItem->GetGuidValue(ITEM_FIELD_CREATOR))) + { + if (IsEquipmentPos(dst)) + { + AutoUnequipChildItem(parentItem); // we need to unequip child first since it cannot go into whatever is going to happen next + SwapItem(src, dst); // dst is now empty + SwapItem(parentItem->GetPos(), src);// src is now empty + return; + } + } + } + TC_LOG_DEBUG("entities.player.items", "Player::SwapItem: Player '%s' (%s), Bag: %u, Slot: %u, Item: %u", GetName().c_str(), GetGUID().ToString().c_str(), dstbag, dstslot, pSrcItem->GetEntry()); @@ -12129,7 +12706,6 @@ void Player::SwapItem(uint16 src, uint16 dst) } // DST checks - if (pDstItem) { // check unequip potability for equipped items and bank bags @@ -12213,6 +12789,9 @@ void Player::SwapItem(uint16 src, uint16 dst) else return; + if (msg == EQUIP_ERR_OK && IsEquipmentPos(dst) && !pSrcItem->GetChildItem().IsEmpty()) + msg = CanEquipChildItem(pSrcItem); + // can be merge/fill if (msg == EQUIP_ERR_OK) { @@ -12227,6 +12806,9 @@ void Player::SwapItem(uint16 src, uint16 dst) else if (IsEquipmentPos(dst)) { EquipItem(eDest, pSrcItem, true); + if (!pSrcItem->GetChildItem().IsEmpty()) + EquipChildItem(srcbag, srcslot, pSrcItem); + AutoUnequipOffhandIfNeed(); } } @@ -12284,6 +12866,9 @@ void Player::SwapItem(uint16 src, uint16 dst) msg = CanUnequipItem(eDest2, true); } + if (msg == EQUIP_ERR_OK && IsEquipmentPos(dst) && !pSrcItem->GetChildItem().IsEmpty()) + msg = CanEquipChildItem(pSrcItem); + if (msg != EQUIP_ERR_OK) { SendEquipError(msg, pDstItem, pSrcItem); @@ -12367,7 +12952,11 @@ void Player::SwapItem(uint16 src, uint16 dst) else if (IsBankPos(dst)) BankItem(sDest, pSrcItem, true); else if (IsEquipmentPos(dst)) + { EquipItem(eDest, pSrcItem, true); + if (!pSrcItem->GetChildItem().IsEmpty()) + EquipChildItem(srcbag, srcslot, pSrcItem); + } // add to src if (IsInventoryPos(src)) @@ -17353,6 +17942,7 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) std::map<ObjectGuid, Bag*> bagMap; // fast guid lookup for bags std::map<ObjectGuid, Item*> invalidBagMap; // fast guid lookup for bags + std::vector<Item*> childItems; std::list<Item*> problematicItems; SQLTransaction trans = CharacterDatabase.BeginTransaction(); @@ -17439,9 +18029,14 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) } + // Item's state may have changed after storing if (err == EQUIP_ERR_OK) + { item->SetState(ITEM_UNCHANGED, this); + if (item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD)) + childItems.push_back(item); + } else { TC_LOG_ERROR("entities.player", "Player::_LoadInventory: Player '%s' (%s) has item (%s, entry: %u) which can't be loaded into inventory (Bag %s, slot: %u) by reason %u. Item will be sent by mail.", @@ -17467,6 +18062,15 @@ void Player::_LoadInventory(PreparedQueryResult result, uint32 timeDiff) } draft.SendMailTo(trans, this, MailSender(this, MAIL_STATIONERY_GM), MAIL_CHECK_MASK_COPIED); } + + for (Item* childItem : childItems) + { + if (Item* parent = GetItemByGuid(childItem->GetGuidValue(ITEM_FIELD_CREATOR))) + parent->SetChildItem(childItem->GetGUID()); + else + childItem->SetState(ITEM_REMOVED, this); + } + CharacterDatabase.CommitTransaction(trans); } //if (IsAlive()) @@ -17516,14 +18120,6 @@ void Player::_LoadVoidStorage(PreparedQueryResult result) continue; } - std::string name; - if (!creatorGuid.IsEmpty() && !ObjectMgr::GetPlayerNameByGUID(creatorGuid, name)) - { - TC_LOG_ERROR("entities.player", "Player::_LoadVoidStorage: Player '%s' (%s) has an item with an invalid creator guid, set to 0 (item id: " UI64FMTD ", entry: %u, creator: %s).", - GetName().c_str(), GetGUID().ToString().c_str(), itemId, itemEntry, creatorGuid.ToString().c_str()); - creatorGuid.Clear(); - } - _voidStorageItems[slot] = new VoidStorageItem(itemId, itemEntry, creatorGuid, randomProperty, suffixFactor, upgradeId, bonusListIDs); WorldPackets::Item::ItemInstance voidInstance; diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index c836fda5435..99591ea5729 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -726,7 +726,7 @@ enum PlayerSlots // first slot for item stored (in any way in player m_items data) PLAYER_SLOT_START = 0, // last+1 slot for item stored (in any way in player m_items data) - PLAYER_SLOT_END = 184, + PLAYER_SLOT_END = 187, PLAYER_SLOTS_COUNT = (PLAYER_SLOT_END - PLAYER_SLOT_START) }; @@ -797,6 +797,12 @@ enum ReagentSlots REAGENT_SLOT_END = 184, }; +enum ChildEquipmentSlots +{ + CHILD_EQUIPMENT_SLOT_START = 184, + CHILD_EQUIPMENT_SLOT_END = 187, +}; + enum EquipmentSetUpdateState { EQUIPMENT_SET_UNCHANGED = 0, @@ -1325,6 +1331,7 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> Bag* GetBagByPos(uint8 slot) const; Item* GetWeaponForAttack(WeaponAttackType attackType, bool useable = false) const; Item* GetShield(bool useable = false) const; + Item* GetChildItemByGuid(ObjectGuid guid) const; static uint8 GetAttackBySlot(uint8 slot, InventoryType inventoryType); // MAX_ATTACK if not weapon slot std::vector<Item*> &GetItemUpdateQueue() { return m_itemUpdateQueue; } static bool IsInventoryPos(uint16 pos) { return IsInventoryPos(pos >> 8, pos & 255); } @@ -1334,6 +1341,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> static bool IsBagPos(uint16 pos); static bool IsBankPos(uint16 pos) { return IsBankPos(pos >> 8, pos & 255); } static bool IsBankPos(uint8 bag, uint8 slot); + static bool IsChildEquipmentPos(uint16 pos) { return IsChildEquipmentPos(pos >> 8, pos & 255); } + static bool IsChildEquipmentPos(uint8 bag, uint8 slot); bool IsValidPos(uint16 pos, bool explicit_pos) const { return IsValidPos(pos >> 8, pos & 255, explicit_pos); } bool IsValidPos(uint8 bag, uint8 slot, bool explicit_pos) const; uint8 GetBankBagSlotCount() const { return GetByteValue(PLAYER_BYTES_3, PLAYER_BYTES_3_OFFSET_BANK_BAG_SLOTS); } @@ -1351,6 +1360,9 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> InventoryResult CanEquipNewItem(uint8 slot, uint16& dest, uint32 item, bool swap) const; InventoryResult CanEquipItem(uint8 slot, uint16& dest, Item* pItem, bool swap, bool not_loading = true) const; + // This method must be called before equipping parent item! + InventoryResult CanEquipChildItem(Item* parentItem) const; + InventoryResult CanEquipUniqueItem(Item* pItem, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1) const; InventoryResult CanEquipUniqueItem(ItemTemplate const* itemProto, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1) const; InventoryResult CanUnequipItems(uint32 item, uint32 count) const; @@ -1365,6 +1377,8 @@ class TC_GAME_API Player : public Unit, public GridObject<Player> Item* EquipNewItem(uint16 pos, uint32 item, bool update); Item* EquipItem(uint16 pos, Item* pItem, bool update); void AutoUnequipOffhandIfNeed(bool force = false); + void EquipChildItem(uint8 parentBag, uint8 parentSlot, Item* parentItem); + void AutoUnequipChildItem(Item* parentItem); bool StoreNewItemInBestSlots(uint32 item_id, uint32 item_count); void AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast = false); void AutoStoreLoot(uint32 loot_id, LootStore const& store, bool broadcast = false) { AutoStoreLoot(NULL_BAG, NULL_SLOT, loot_id, store, broadcast); } diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index a4e35620bd2..26f88bc6528 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -201,8 +201,21 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPackets::Item::AutoEquipItem& Item* dstItem = _player->GetItemByPos(dest); if (!dstItem) // empty slot, simple case { + if (!srcItem->GetChildItem().IsEmpty()) + { + InventoryResult childEquipResult = _player->CanEquipChildItem(srcItem); + if (childEquipResult != EQUIP_ERR_OK) + { + _player->SendEquipError(msg, srcItem); + return; + } + } + _player->RemoveItem(autoEquipItem.PackSlot, autoEquipItem.Slot, true); _player->EquipItem(dest, srcItem, true); + if (!srcItem->GetChildItem().IsEmpty()) + _player->EquipChildItem(autoEquipItem.PackSlot, autoEquipItem.Slot, srcItem); + _player->AutoUnequipOffhandIfNeed(); } else // have currently equipped item, not simple case @@ -217,52 +230,75 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPackets::Item::AutoEquipItem& return; } - // check dest->src move possibility - ItemPosCountVec sSrc; - uint16 eSrc = 0; - if (_player->IsInventoryPos(src)) - { - msg = _player->CanStoreItem(autoEquipItem.PackSlot, autoEquipItem.Slot, sSrc, dstItem, true); - if (msg != EQUIP_ERR_OK) - msg = _player->CanStoreItem(autoEquipItem.PackSlot, NULL_SLOT, sSrc, dstItem, true); - if (msg != EQUIP_ERR_OK) - msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sSrc, dstItem, true); - } - else if (_player->IsBankPos(src)) + if (!dstItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_CHILD)) { - msg = _player->CanBankItem(autoEquipItem.PackSlot, autoEquipItem.Slot, sSrc, dstItem, true); - if (msg != EQUIP_ERR_OK) - msg = _player->CanBankItem(autoEquipItem.PackSlot, NULL_SLOT, sSrc, dstItem, true); + // check dest->src move possibility + ItemPosCountVec sSrc; + uint16 eSrc = 0; + if (_player->IsInventoryPos(src)) + { + msg = _player->CanStoreItem(autoEquipItem.PackSlot, autoEquipItem.Slot, sSrc, dstItem, true); + if (msg != EQUIP_ERR_OK) + msg = _player->CanStoreItem(autoEquipItem.PackSlot, NULL_SLOT, sSrc, dstItem, true); + if (msg != EQUIP_ERR_OK) + msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sSrc, dstItem, true); + } + else if (_player->IsBankPos(src)) + { + msg = _player->CanBankItem(autoEquipItem.PackSlot, autoEquipItem.Slot, sSrc, dstItem, true); + if (msg != EQUIP_ERR_OK) + msg = _player->CanBankItem(autoEquipItem.PackSlot, NULL_SLOT, sSrc, dstItem, true); + if (msg != EQUIP_ERR_OK) + msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, sSrc, dstItem, true); + } + else if (_player->IsEquipmentPos(src)) + { + msg = _player->CanEquipItem(autoEquipItem.Slot, eSrc, dstItem, true); + if (msg == EQUIP_ERR_OK) + msg = _player->CanUnequipItem(eSrc, true); + } + + if (msg == EQUIP_ERR_OK && Player::IsEquipmentPos(dest) && !srcItem->GetChildItem().IsEmpty()) + msg = _player->CanEquipChildItem(srcItem); + if (msg != EQUIP_ERR_OK) - msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, sSrc, dstItem, true); - } - else if (_player->IsEquipmentPos(src)) - { - msg = _player->CanEquipItem(autoEquipItem.Slot, eSrc, dstItem, true); - if (msg == EQUIP_ERR_OK) - msg = _player->CanUnequipItem(eSrc, true); - } + { + _player->SendEquipError(msg, dstItem, srcItem); + return; + } - if (msg != EQUIP_ERR_OK) - { - _player->SendEquipError(msg, dstItem, srcItem); - return; - } + // now do moves, remove... + _player->RemoveItem(dstbag, dstslot, false); + _player->RemoveItem(autoEquipItem.PackSlot, autoEquipItem.Slot, false); - // now do moves, remove... - _player->RemoveItem(dstbag, dstslot, false); - _player->RemoveItem(autoEquipItem.PackSlot, autoEquipItem.Slot, false); + // add to dest + _player->EquipItem(dest, srcItem, true); - // add to dest - _player->EquipItem(dest, srcItem, true); + // add to src + if (_player->IsInventoryPos(src)) + _player->StoreItem(sSrc, dstItem, true); + else if (_player->IsBankPos(src)) + _player->BankItem(sSrc, dstItem, true); + else if (_player->IsEquipmentPos(src)) + _player->EquipItem(eSrc, dstItem, true); - // add to src - if (_player->IsInventoryPos(src)) - _player->StoreItem(sSrc, dstItem, true); - else if (_player->IsBankPos(src)) - _player->BankItem(sSrc, dstItem, true); - else if (_player->IsEquipmentPos(src)) - _player->EquipItem(eSrc, dstItem, true); + if (Player::IsEquipmentPos(dest) && !srcItem->GetChildItem().IsEmpty()) + _player->EquipChildItem(autoEquipItem.PackSlot, autoEquipItem.Slot, srcItem); + } + else + { + if (Item* parentItem = _player->GetItemByGuid(dstItem->GetGuidValue(ITEM_FIELD_CREATOR))) + { + if (Player::IsEquipmentPos(dest)) + { + _player->AutoUnequipChildItem(parentItem); + // dest is now empty + _player->SwapItem(src, dest); + // src is now empty + _player->SwapItem(parentItem->GetPos(), src); + } + } + } _player->AutoUnequipOffhandIfNeed(); } diff --git a/src/server/game/Handlers/MailHandler.cpp b/src/server/game/Handlers/MailHandler.cpp index 69bafe9bc11..e86b304b554 100644 --- a/src/server/game/Handlers/MailHandler.cpp +++ b/src/server/game/Handlers/MailHandler.cpp @@ -635,7 +635,7 @@ void WorldSession::HandleMailCreateTextItem(WorldPackets::Mail::MailCreateTextIt if (m->messageType == MAIL_NORMAL) bodyItem->SetGuidValue(ITEM_FIELD_CREATOR, ObjectGuid::Create<HighGuid::Player>(m->sender)); - bodyItem->SetFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_MAIL_TEXT_MASK); + bodyItem->SetFlag(ITEM_FIELD_FLAGS, ITEM_FIELD_FLAG_READABLE); ItemPosCountVec dest; uint8 msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, bodyItem, false); |