aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2016-07-16 13:52:12 +0200
committerShauren <shauren.trinity@gmail.com>2016-07-16 13:52:12 +0200
commit64de4b38ef5eddb14d9d456740fee62595c89e06 (patch)
tree719944e12b7178ab85dbab0934f6e171c15ec5d9 /src
parent49202ba93c6f02a93aa5268536dbd053fd26925a (diff)
Core/Items: Implemented child equipment
Diffstat (limited to 'src')
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.cpp3
-rw-r--r--src/server/database/Database/Implementation/HotfixDatabase.h2
-rw-r--r--src/server/game/DataStores/DB2Stores.cpp17
-rw-r--r--src/server/game/DataStores/DB2Stores.h3
-rw-r--r--src/server/game/DataStores/DB2Structure.h8
-rw-r--r--src/server/game/Entities/Item/Item.cpp10
-rw-r--r--src/server/game/Entities/Item/Item.h4
-rw-r--r--src/server/game/Entities/Item/ItemTemplate.h7
-rw-r--r--src/server/game/Entities/Player/Player.cpp626
-rw-r--r--src/server/game/Entities/Player/Player.h16
-rw-r--r--src/server/game/Handlers/ItemHandler.cpp116
-rw-r--r--src/server/game/Handlers/MailHandler.cpp2
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);