diff --git a/src/server/apps/worldserver/worldserver.conf.dist b/src/server/apps/worldserver/worldserver.conf.dist index 1e8ffb11c..16d333b21 100644 --- a/src/server/apps/worldserver/worldserver.conf.dist +++ b/src/server/apps/worldserver/worldserver.conf.dist @@ -2186,7 +2186,7 @@ Rate.MissChanceMultiplier.OnlyAffectsPlayer = 0 # # LevelReq.Trade -# Description: Level requirement for characters to be able to trade. +# Description: Level requirement for characters to be able to initiate a trade. # Default: 1 LevelReq.Trade = 1 diff --git a/src/server/game/Entities/Player/Player.h b/src/server/game/Entities/Player/Player.h index 0e75edaf6..d6cb920ee 100644 --- a/src/server/game/Entities/Player/Player.h +++ b/src/server/game/Entities/Player/Player.h @@ -1057,6 +1057,18 @@ struct EntryPointData [[nodiscard]] bool HasTaxiPath() const { return taxiPath[0] && taxiPath[1]; } }; +struct TradeStatusInfo +{ + TradeStatusInfo() = default; + + TradeStatus Status{TRADE_STATUS_BUSY}; + ObjectGuid TraderGuid{}; + InventoryResult Result{EQUIP_ERR_OK}; + bool IsTargetResult{false}; + uint32 ItemLimitedByLimitCategory{0}; + uint8 Slot{0}; +}; + struct PendingSpellCastRequest { uint32 spellId; @@ -1277,12 +1289,17 @@ public: bool CanNoReagentCast(SpellInfo const* spellInfo) const; [[nodiscard]] bool HasItemOrGemWithIdEquipped(uint32 item, uint32 count, uint8 except_slot = NULL_SLOT) const; [[nodiscard]] bool HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 count, uint8 except_slot = NULL_SLOT) const; - InventoryResult CanTakeMoreSimilarItems(Item* pItem) const { return CanTakeMoreSimilarItems(pItem->GetEntry(), pItem->GetCount(), pItem); } - [[nodiscard]] InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count) const { return CanTakeMoreSimilarItems(entry, count, nullptr); } + + InventoryResult CanTakeMoreSimilarItems(Item* item, uint32* itemLimitedByLimitCategory = nullptr) const + { + return CanTakeMoreSimilarItems(item->GetEntry(), item->GetCount(), item, nullptr, itemLimitedByLimitCategory); + } + InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count, uint32* itemLimitedByLimitCategory = nullptr) const { return CanTakeMoreSimilarItems(entry, count, nullptr, nullptr, itemLimitedByLimitCategory); } InventoryResult CanStoreNewItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 item, uint32 count, uint32* no_space_count = nullptr) const { return CanStoreItem(bag, slot, dest, item, count, nullptr, false, no_space_count); } + InventoryResult CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, Item* pItem, bool swap = false) const { if (!pItem) @@ -1290,7 +1307,7 @@ public: uint32 count = pItem->GetCount(); return CanStoreItem(bag, slot, dest, pItem->GetEntry(), count, pItem, swap, nullptr); } - InventoryResult CanStoreItems(Item** pItem, int32 count) const; + InventoryResult CanStoreItems(Item** items, int count, uint32* itemLimitedByLimitCategory) const; 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; @@ -1318,7 +1335,7 @@ public: void UpdateLootAchievements(LootItem* item, Loot* loot); void UpdateTitansGrip(); - InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count = nullptr) const; + InventoryResult CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* item, uint32* no_space_count = nullptr, uint32* itemLimitCategory = nullptr) const; InventoryResult CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& dest, uint32 entry, uint32 count, Item* pItem = nullptr, bool swap = false, uint32* no_space_count = nullptr) const; void AddRefundReference(ObjectGuid itemGUID); diff --git a/src/server/game/Entities/Player/PlayerStorage.cpp b/src/server/game/Entities/Player/PlayerStorage.cpp index e8117aa58..213c3dbd9 100644 --- a/src/server/game/Entities/Player/PlayerStorage.cpp +++ b/src/server/game/Entities/Player/PlayerStorage.cpp @@ -799,7 +799,7 @@ bool Player::HasItemOrGemWithLimitCategoryEquipped(uint32 limitCategory, uint32 return false; } -InventoryResult Player::CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count) const +InventoryResult Player::CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* item, uint32* no_space_count /*= nullptr*/, uint32* itemLimitedByLimitCategory /*= nullptr*/) const { ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(entry); if (!pProto) @@ -809,13 +809,16 @@ InventoryResult Player::CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS; } + if (item && item->m_lootGenerated) + return EQUIP_ERR_ALREADY_LOOTED; + // no maximum if ((pProto->MaxCount <= 0 && pProto->ItemLimitCategory == 0) || pProto->MaxCount == 2147483647) return EQUIP_ERR_OK; if (pProto->MaxCount > 0) { - uint32 curcount = GetItemCount(pProto->ItemId, true, pItem); + uint32 curcount = GetItemCount(pProto->ItemId, true, item); if (curcount + count > uint32(pProto->MaxCount)) { if (no_space_count) @@ -837,11 +840,13 @@ InventoryResult Player::CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item if (limitEntry->mode == ITEM_LIMIT_CATEGORY_MODE_HAVE) { - uint32 curcount = GetItemCountWithLimitCategory(pProto->ItemLimitCategory, pItem); + uint32 curcount = GetItemCountWithLimitCategory(pProto->ItemLimitCategory, item); if (curcount + count > uint32(limitEntry->maxCount)) { if (no_space_count) *no_space_count = count + curcount - limitEntry->maxCount; + if (itemLimitedByLimitCategory) + *itemLimitedByLimitCategory = pProto->ItemId; return EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED; } } @@ -1119,7 +1124,7 @@ InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& des if (pItem->IsBag() && pItem->IsNotEmptyBag()) return EQUIP_ERR_CAN_ONLY_DO_WITH_EMPTY_BAGS; - // Xinef: Removed next loot generated check + // swapping/merging with currently looted item if (pItem->GetGUID() == GetLootGUID()) { if (no_space_count) @@ -1538,79 +1543,102 @@ InventoryResult Player::CanStoreItem(uint8 bag, uint8 slot, ItemPosCountVec& des } ////////////////////////////////////////////////////////////////////////// -InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const +InventoryResult Player::CanStoreItems(Item** items, int count, uint32* itemLimitedByLimitCategory) const { - Item* pItem2; + Item* item2; - // fill space table - int inv_slot_items[INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START]; - int inv_bags[INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE]; - int inv_keys[KEYRING_SLOT_END - KEYRING_SLOT_START]; - int inv_tokens[CURRENCYTOKEN_SLOT_END - CURRENCYTOKEN_SLOT_START]; + // fill space tables, creating a mock-up of the player's inventory - memset(inv_slot_items, 0, sizeof(int) * (INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START)); - memset(inv_bags, 0, sizeof(int) * (INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START) * MAX_BAG_SIZE); - memset(inv_keys, 0, sizeof(int) * (KEYRING_SLOT_END - KEYRING_SLOT_START)); - memset(inv_tokens, 0, sizeof(int) * (CURRENCYTOKEN_SLOT_END - CURRENCYTOKEN_SLOT_START)); + // counts + uint32 inventoryCounts[INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START] = {}; + uint32 bagCounts[INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE] = {}; + uint32 keyringCounts[KEYRING_SLOT_END - KEYRING_SLOT_START] = {}; + uint32 currencyCounts[CURRENCYTOKEN_SLOT_END - CURRENCYTOKEN_SLOT_START] = {}; + // Item pointers + Item* inventoryPointers[INVENTORY_SLOT_ITEM_END - INVENTORY_SLOT_ITEM_START] = {}; + Item* bagPointers[INVENTORY_SLOT_BAG_END - INVENTORY_SLOT_BAG_START][MAX_BAG_SIZE] = {}; + Item* keyringPointers[KEYRING_SLOT_END - KEYRING_SLOT_START] = {}; + Item* currencyPointers[CURRENCYTOKEN_SLOT_END - CURRENCYTOKEN_SLOT_START] = {}; + + // filling inventory for (uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++) { - pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem2 && !pItem2->IsInTrade()) - inv_slot_items[i - INVENTORY_SLOT_ITEM_START] = pItem2->GetCount(); + // build items in stock backpack + item2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (item2 && !item2->IsInTrade()) + { + inventoryCounts[i - INVENTORY_SLOT_ITEM_START] = item2->GetCount(); + inventoryPointers[i - INVENTORY_SLOT_ITEM_START] = item2; + } } for (uint8 i = KEYRING_SLOT_START; i < KEYRING_SLOT_END; i++) { - pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem2 && !pItem2->IsInTrade()) - inv_keys[i - KEYRING_SLOT_START] = pItem2->GetCount(); + // build items in key ring 'bag' + item2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (item2 && !item2->IsInTrade()) + { + keyringCounts[i - KEYRING_SLOT_START] = item2->GetCount(); + keyringPointers[i - KEYRING_SLOT_START] = item2; + } } for (uint8 i = CURRENCYTOKEN_SLOT_START; i < CURRENCYTOKEN_SLOT_END; i++) { - pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i); - if (pItem2 && !pItem2->IsInTrade()) - inv_tokens[i - CURRENCYTOKEN_SLOT_START] = pItem2->GetCount(); + // build items in currency 'bag' + item2 = GetItemByPos(INVENTORY_SLOT_BAG_0, i); + if (item2 && !item2->IsInTrade()) + { + currencyCounts[i - CURRENCYTOKEN_SLOT_START] = item2->GetCount(); + currencyPointers[i - CURRENCYTOKEN_SLOT_START] = item2; + } } 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++) { - pItem2 = GetItemByPos(i, j); - if (pItem2 && !pItem2->IsInTrade()) - inv_bags[i - INVENTORY_SLOT_BAG_START][j] = pItem2->GetCount(); + // build item counts in equippable bags + item2 = GetItemByPos(i, j); + if (item2 && !item2->IsInTrade()) + { + bagCounts[i - INVENTORY_SLOT_BAG_START][j] = item2->GetCount(); + bagPointers[i - INVENTORY_SLOT_BAG_START][j] = item2; + } } - // check free space for all items + // check free space for all items that we wish to add for (int k = 0; k < count; ++k) { - Item* pItem = pItems[k]; + // Incoming item + Item* item = items[k]; // no item - if (!pItem) + if (!item) continue; - LOG_DEBUG("entities.player.items", "STORAGE: CanStoreItems {}. item = {}, count = {}", k + 1, pItem->GetEntry(), pItem->GetCount()); - ItemTemplate const* pProto = pItem->GetTemplate(); + uint32 remaining_count = item->GetCount(); + + LOG_DEBUG("entities.player.items", "STORAGE: CanStoreItems {}. item = {}, count = {}", k + 1, item->GetEntry(), item->GetCount()); + ItemTemplate const* pProto = item->GetTemplate(); // strange item if (!pProto) return EQUIP_ERR_ITEM_NOT_FOUND; - // Xinef: Removed next loot generated check - if (pItem->GetGUID() == GetLootGUID()) + /// swapping/merging with currently looted item + if (item->GetGUID() == GetLootGUID()) return EQUIP_ERR_ALREADY_LOOTED; // item it 'bind' - if (pItem->IsBindedNotWith(this)) + if (item->IsBindedNotWith(this)) return EQUIP_ERR_DONT_OWN_THAT_ITEM; ItemTemplate const* pBagProto; // item is 'one item only' - InventoryResult res = CanTakeMoreSimilarItems(pItem); + InventoryResult res = CanTakeMoreSimilarItems(item, itemLimitedByLimitCategory); if (res != EQUIP_ERR_OK) return res; @@ -1621,40 +1649,58 @@ InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const for (uint8 t = KEYRING_SLOT_START; t < KEYRING_SLOT_END; ++t) { - pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t); - if (pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_keys[t - KEYRING_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) + item2 = keyringPointers[t - KEYRING_SLOT_START]; + if (item2 && item2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && keyringCounts[t - KEYRING_SLOT_START] < pProto->GetMaxStackSize()) { - inv_keys[t - KEYRING_SLOT_START] += pItem->GetCount(); - b_found = true; - break; + keyringCounts[t - KEYRING_SLOT_START] += remaining_count; + remaining_count = keyringCounts[t - KEYRING_SLOT_START] < pProto->GetMaxStackSize() ? 0 : keyringCounts[t - KEYRING_SLOT_START] - pProto->GetMaxStackSize(); + + b_found = remaining_count == 0; + + // if no pieces of the stack remain, then stop checking keyring + if (b_found) + break; } } + if (b_found) continue; for (int t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t) { - pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t); - if (pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_tokens[t - CURRENCYTOKEN_SLOT_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) + item2 = currencyPointers[t - CURRENCYTOKEN_SLOT_START]; + if (item2 && item2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && currencyCounts[t - CURRENCYTOKEN_SLOT_START] < pProto->GetMaxStackSize()) { - inv_tokens[t - CURRENCYTOKEN_SLOT_START] += pItem->GetCount(); - b_found = true; - break; + currencyCounts[t - CURRENCYTOKEN_SLOT_START] += remaining_count; + remaining_count = + currencyCounts[t - CURRENCYTOKEN_SLOT_START] < pProto->GetMaxStackSize() ? 0 : currencyCounts[t - CURRENCYTOKEN_SLOT_START] - pProto->GetMaxStackSize(); + + b_found = remaining_count == 0; + // if no pieces of the stack remain, then stop checking currency 'bag' + if (b_found) + break; } } + if (b_found) continue; for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t) { - pItem2 = GetItemByPos(INVENTORY_SLOT_BAG_0, t); - if (pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_slot_items[t - INVENTORY_SLOT_ITEM_START] + pItem->GetCount() <= pProto->GetMaxStackSize()) + item2 = inventoryPointers[t - INVENTORY_SLOT_ITEM_START]; + if (item2 && item2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inventoryCounts[t - INVENTORY_SLOT_ITEM_START] < pProto->GetMaxStackSize()) { - inv_slot_items[t - INVENTORY_SLOT_ITEM_START] += pItem->GetCount(); - b_found = true; - break; + inventoryCounts[t - INVENTORY_SLOT_ITEM_START] += remaining_count; + remaining_count = + inventoryCounts[t - INVENTORY_SLOT_ITEM_START] < pProto->GetMaxStackSize() ? 0 : inventoryCounts[t - INVENTORY_SLOT_ITEM_START] - pProto->GetMaxStackSize(); + + b_found = remaining_count == 0; + // if no pieces of the stack remain, then stop checking stock bag + if (b_found) + break; } } + if (b_found) continue; @@ -1662,21 +1708,29 @@ InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const { if (Bag* bag = GetBagByPos(t)) { - if (ItemCanGoIntoBag(pItem->GetTemplate(), bag->GetTemplate())) + if (!ItemCanGoIntoBag(item->GetTemplate(), bag->GetTemplate())) + continue; + + for (uint32 j = 0; j < bag->GetBagSize(); j++) { - for (uint32 j = 0; j < bag->GetBagSize(); j++) + item2 = bagPointers[t - INVENTORY_SLOT_BAG_START][j]; + if (item2 && item2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && bagCounts[t - INVENTORY_SLOT_BAG_START][j] < pProto->GetMaxStackSize()) { - pItem2 = GetItemByPos(t, j); - if (pItem2 && pItem2->CanBeMergedPartlyWith(pProto) == EQUIP_ERR_OK && inv_bags[t - INVENTORY_SLOT_BAG_START][j] + pItem->GetCount() <= pProto->GetMaxStackSize()) - { - inv_bags[t - INVENTORY_SLOT_BAG_START][j] += pItem->GetCount(); - b_found = true; + // add count to stack so that later items in the list do not double-book + bagCounts[t - INVENTORY_SLOT_BAG_START][j] += remaining_count; + remaining_count = + bagCounts[t - INVENTORY_SLOT_BAG_START][j] < pProto->GetMaxStackSize() ? 0 : bagCounts[t - INVENTORY_SLOT_BAG_START][j] - pProto->GetMaxStackSize(); + + b_found = remaining_count == 0; + + // if no pieces of the stack remain, then stop checking equippable bags + if (b_found) break; - } } } } } + if (b_found) continue; } @@ -1690,9 +1744,11 @@ InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const uint32 keyringSize = GetMaxKeyringSize(); for (uint32 t = KEYRING_SLOT_START; t < KEYRING_SLOT_START + keyringSize; ++t) { - if (inv_keys[t - KEYRING_SLOT_START] == 0) + if (keyringCounts[t - KEYRING_SLOT_START] == 0) { - inv_keys[t - KEYRING_SLOT_START] = 1; + keyringCounts[t - KEYRING_SLOT_START] = remaining_count; + keyringPointers[t - KEYRING_SLOT_START] = item; + b_found = true; break; } @@ -1706,9 +1762,11 @@ InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const { for (uint32 t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t) { - if (inv_tokens[t - CURRENCYTOKEN_SLOT_START] == 0) + if (currencyCounts[t - CURRENCYTOKEN_SLOT_START] == 0) { - inv_tokens[t - CURRENCYTOKEN_SLOT_START] = 1; + currencyCounts[t - CURRENCYTOKEN_SLOT_START] = remaining_count; + currencyPointers[t - CURRENCYTOKEN_SLOT_START] = item; + b_found = true; break; } @@ -1725,14 +1783,15 @@ InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const pBagProto = bag->GetTemplate(); // not plain container check - if (pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER) && - ItemCanGoIntoBag(pProto, pBagProto)) + if (pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER) && ItemCanGoIntoBag(pProto, pBagProto)) { for (uint32 j = 0; j < bag->GetBagSize(); j++) { - if (inv_bags[t - INVENTORY_SLOT_BAG_START][j] == 0) + if (bagCounts[t - INVENTORY_SLOT_BAG_START][j] == 0) { - inv_bags[t - INVENTORY_SLOT_BAG_START][j] = 1; + bagCounts[t - INVENTORY_SLOT_BAG_START][j] = remaining_count; + bagPointers[t - INVENTORY_SLOT_BAG_START][j] = item; + b_found = true; break; } @@ -1740,6 +1799,7 @@ InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const } } } + if (b_found) continue; } @@ -1748,18 +1808,21 @@ InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const bool b_found = false; for (int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t) { - if (inv_slot_items[t - INVENTORY_SLOT_ITEM_START] == 0) + if (inventoryCounts[t - INVENTORY_SLOT_ITEM_START] == 0) { - inv_slot_items[t - INVENTORY_SLOT_ITEM_START] = 1; + inventoryCounts[t - INVENTORY_SLOT_ITEM_START] = remaining_count; + inventoryPointers[t - INVENTORY_SLOT_ITEM_START] = item; + b_found = true; break; } } + if (b_found) continue; // search free slot in bags - for (int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t) + for (uint8 t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t) { if (Bag* bag = GetBagByPos(t)) { @@ -1771,9 +1834,11 @@ InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const for (uint32 j = 0; j < bag->GetBagSize(); j++) { - if (inv_bags[t - INVENTORY_SLOT_BAG_START][j] == 0) + if (bagCounts[t - INVENTORY_SLOT_BAG_START][j] == 0) { - inv_bags[t - INVENTORY_SLOT_BAG_START][j] = 1; + bagCounts[t - INVENTORY_SLOT_BAG_START][j] = remaining_count; + bagPointers[t - INVENTORY_SLOT_BAG_START][j] = item; + b_found = true; break; } @@ -1781,9 +1846,9 @@ InventoryResult Player::CanStoreItems(Item** pItems, int32 count) const } } - // no free slot found? + // if no free slot found for all pieces of the item, then return an error if (!b_found) - return EQUIP_ERR_INVENTORY_FULL; + return EQUIP_ERR_BAG_FULL; } return EQUIP_ERR_OK; diff --git a/src/server/game/Entities/Player/PlayerUpdates.cpp b/src/server/game/Entities/Player/PlayerUpdates.cpp index b0c7a8521..4b0bd93a4 100644 --- a/src/server/game/Entities/Player/PlayerUpdates.cpp +++ b/src/server/game/Entities/Player/PlayerUpdates.cpp @@ -1177,9 +1177,6 @@ bool Player::UpdatePosition(float x, float y, float z, float orientation, if (GetGroup()) SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION); - if (GetTrader() && !IsWithinDistInMap(GetTrader(), INTERACTION_DISTANCE)) - GetSession()->SendCancelTrade(TRADE_STATUS_TRADE_CANCELED); - CheckAreaExploreAndOutdoor(); return true; diff --git a/src/server/game/Entities/Player/TradeData.cpp b/src/server/game/Entities/Player/TradeData.cpp index 7f0fca7f1..a8f1eedc0 100644 --- a/src/server/game/Entities/Player/TradeData.cpp +++ b/src/server/game/Entities/Player/TradeData.cpp @@ -20,12 +20,12 @@ TradeData* TradeData::GetTraderData() const { - return m_trader->GetTradeData(); + return _trader->GetTradeData(); } Item* TradeData::GetItem(TradeSlots slot) const { - return m_items[slot] ? m_player->GetItemByGuid(m_items[slot]) : nullptr; + return m_items[slot] ? _player->GetItemByGuid(m_items[slot]) : nullptr; } bool TradeData::HasItem(ObjectGuid itemGuid) const @@ -48,7 +48,7 @@ TradeSlots TradeData::GetTradeSlotForItem(ObjectGuid itemGuid) const Item* TradeData::GetSpellCastItem() const { - return m_spellCastItem ? m_player->GetItemByGuid(m_spellCastItem) : nullptr; + return m_spellCastItem ? _player->GetItemByGuid(m_spellCastItem) : nullptr; } void TradeData::SetItem(TradeSlots slot, Item* item) @@ -92,16 +92,19 @@ void TradeData::SetSpell(uint32 spell_id, Item* castItem /*= nullptr*/) void TradeData::SetMoney(uint32 money) { - if (m_money == money) + if (_money == money) return; - if (!m_player->HasEnoughMoney(money)) + if (!_player->HasEnoughMoney(money)) { - m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BUSY); + TradeStatusInfo info; + info.Status = TRADE_STATUS_CLOSE_WINDOW; + info.Result = EQUIP_ERR_NOT_ENOUGH_MONEY; + _player->GetSession()->SendTradeStatus(info); return; } - m_money = money; + _money = money; SetAccepted(false); GetTraderData()->SetAccepted(false); @@ -112,20 +115,22 @@ void TradeData::SetMoney(uint32 money) void TradeData::Update(bool forTarget /*= true*/) { if (forTarget) - m_trader->GetSession()->SendUpdateTrade(true); // player state for trader + _trader->GetSession()->SendUpdateTrade(true); // player state for trader else - m_player->GetSession()->SendUpdateTrade(false); // player state for player + _player->GetSession()->SendUpdateTrade(false); // player state for player } -void TradeData::SetAccepted(bool state, bool crosssend /*= false*/) +void TradeData::SetAccepted(bool state, bool forTrader /*= false*/) { - m_accepted = state; + _accepted = state; if (!state) { - if (crosssend) - m_trader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + TradeStatusInfo info; + info.Status = TRADE_STATUS_BACK_TO_TRADE; + if (forTrader) + _trader->GetSession()->SendTradeStatus(info); else - m_player->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE); + _player->GetSession()->SendTradeStatus(info); } } diff --git a/src/server/game/Entities/Player/TradeData.h b/src/server/game/Entities/Player/TradeData.h index 5eb57e5fb..83e5c116d 100644 --- a/src/server/game/Entities/Player/TradeData.h +++ b/src/server/game/Entities/Player/TradeData.h @@ -36,9 +36,9 @@ class AC_GAME_API TradeData { public: // constructors TradeData(Player* player, Player* trader) : - m_player(player), m_trader(trader), m_accepted(false), m_acceptProccess(false), m_money(0), m_spell(0) { } + _player(player), _trader(trader), _accepted(false), m_acceptProccess(false), _money(0), m_spell(0) { } - [[nodiscard]] Player* GetTrader() const { return m_trader; } + [[nodiscard]] Player* GetTrader() const { return _trader; } [[nodiscard]] TradeData* GetTraderData() const; [[nodiscard]] Item* GetItem(TradeSlots slot) const; @@ -52,11 +52,11 @@ public: // constructors [[nodiscard]] Item* GetSpellCastItem() const; [[nodiscard]] bool HasSpellCastItem() const { return m_spellCastItem; } - [[nodiscard]] uint32 GetMoney() const { return m_money; } + [[nodiscard]] uint32 GetMoney() const { return _money; } void SetMoney(uint32 money); - [[nodiscard]] bool IsAccepted() const { return m_accepted; } - void SetAccepted(bool state, bool crosssend = false); + [[nodiscard]] bool IsAccepted() const { return _accepted; } + void SetAccepted(bool state, bool forTrader = false); [[nodiscard]] bool IsInAcceptProcess() const { return m_acceptProccess; } void SetInAcceptProcess(bool state) { m_acceptProccess = state; } @@ -65,13 +65,13 @@ private: // internal functions void Update(bool for_trader = true); private: // fields - Player* m_player; // Player who own of this TradeData - Player* m_trader; // Player who trade with m_player + Player* _player; // Player who own of this TradeData + Player* _trader; // Player who trade with m_player - bool m_accepted; // m_player press accept for trade list + bool _accepted; // m_player press accept for trade list bool m_acceptProccess; // one from player/trader press accept and this processed - uint32 m_money; // m_player place money to trade + uint32 _money; // m_player place money to trade uint32 m_spell; // m_player apply spell to non-traded slot item ObjectGuid m_spellCastItem; // applied spell casted by item use diff --git a/src/server/game/Handlers/TradeHandler.cpp b/src/server/game/Handlers/TradeHandler.cpp index 6bef09eb0..d87207a36 100644 --- a/src/server/game/Handlers/TradeHandler.cpp +++ b/src/server/game/Handlers/TradeHandler.cpp @@ -30,38 +30,29 @@ #include "WorldPacket.h" #include "WorldSession.h" -void WorldSession::SendTradeStatus(TradeStatus status) +void WorldSession::SendTradeStatus(TradeStatusInfo const& info) { - WorldPacket data; + WorldPacket data(SMSG_TRADE_STATUS, 13); + data << uint32(info.Status); - switch (status) + switch (info.Status) { case TRADE_STATUS_BEGIN_TRADE: - data.Initialize(SMSG_TRADE_STATUS, 4 + 8); - data << uint32(status); - data << uint64(0); + data << info.TraderGuid; // CGTradeInfo::m_tradingPlayer break; case TRADE_STATUS_OPEN_WINDOW: - data.Initialize(SMSG_TRADE_STATUS, 4 + 4); - data << uint32(status); - data << uint32(0); // added in 2.4.0 + data << uint32(0); // CGTradeInfo::m_tradeID break; case TRADE_STATUS_CLOSE_WINDOW: - data.Initialize(SMSG_TRADE_STATUS, 4 + 4 + 1 + 4); - data << uint32(status); - data << uint32(0); - data << uint8(0); - data << uint32(0); + data << uint32(info.Result); // InventoryResult + data << uint8(info.IsTargetResult); // bool isTargetError; used for: EQUIP_ERR_BAG_FULL, EQUIP_ERR_CANT_CARRY_MORE_OF_THIS, EQUIP_ERR_MISSING_REAGENT, EQUIP_ERR_ITEM_MAX_LIMIT_CATEGORY_COUNT_EXCEEDED + data << uint32(info.ItemLimitedByLimitCategory);// when result 84 - Item Id that was limited by ItemLimitCategory.dbc break; - case TRADE_STATUS_ONLY_CONJURED: - case TRADE_STATUS_NOT_ELIGIBLE: - data.Initialize(SMSG_TRADE_STATUS, 4 + 1); - data << uint32(status); - data << uint8(0); + case TRADE_STATUS_WRONG_REALM: + case TRADE_STATUS_NOT_ON_TAPLIST: + data << uint8(info.Slot); // Trade slot; -1 here clears CGTradeInfo::m_tradeMoney break; default: - data.Initialize(SMSG_TRADE_STATUS, 4); - data << uint32(status); break; } @@ -84,7 +75,7 @@ void WorldSession::SendUpdateTrade(bool trader_data /*= true*/) WorldPacket data(SMSG_TRADE_STATUS_EXTENDED, 1 + 4 + 4 + 4 + 4 + 4 + 7 * (1 + 4 + 4 + 4 + 4 + 8 + 4 + 4 + 4 + 4 + 8 + 4 + 4 + 4 + 4 + 4 + 4)); data << uint8(trader_data); // 1 means traders data, 0 means own - data << uint32(0); // added in 2.4.0, this value must be equal to value from TRADE_STATUS_OPEN_WINDOW status packet (different value for different players to block multiple trades?) + data << uint32(0); // CGTradeInfo::m_tradeID data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = next field in most cases data << uint32(TRADE_SLOT_COUNT); // trade slots count/number?, = prev field in most cases data << uint32(view_trade->GetMoney()); // trader gold @@ -253,17 +244,27 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) if (!his_trade) return; - Item* myItems[TRADE_SLOT_TRADED_COUNT] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; - Item* hisItems[TRADE_SLOT_TRADED_COUNT] = { nullptr, nullptr, nullptr, nullptr, nullptr, nullptr }; - bool myCanCompleteTrade = true, hisCanCompleteTrade = true; + Item* myItems[TRADE_SLOT_TRADED_COUNT] = { }; + Item* hisItems[TRADE_SLOT_TRADED_COUNT] = { }; // set before checks for propertly undo at problems (it already set in to client) my_trade->SetAccepted(true); + TradeStatusInfo info; + if (!_player->IsWithinDistInMap(trader, TRADE_DISTANCE, false)) + { + info.Status = TRADE_STATUS_TARGET_TO_FAR; + SendTradeStatus(info); + my_trade->SetAccepted(false); + return; + } + // not accept case incorrect money amount if (!_player->HasEnoughMoney(my_trade->GetMoney())) { - ChatHandler(this).SendNotification(LANG_NOT_ENOUGH_GOLD); + info.Status = TRADE_STATUS_CLOSE_WINDOW; + info.Result = EQUIP_ERR_NOT_ENOUGH_MONEY; + SendTradeStatus(info); my_trade->SetAccepted(false, true); return; } @@ -271,21 +272,27 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) // not accept case incorrect money amount if (!trader->HasEnoughMoney(his_trade->GetMoney())) { - ChatHandler(trader->GetSession()).SendNotification(LANG_NOT_ENOUGH_GOLD); + info.Status = TRADE_STATUS_CLOSE_WINDOW; + info.Result = EQUIP_ERR_NOT_ENOUGH_MONEY; + trader->GetSession()->SendTradeStatus(info); his_trade->SetAccepted(false, true); return; } - if (_player->GetMoney() >= uint32(MAX_MONEY_AMOUNT) - his_trade->GetMoney()) + if (_player->GetMoney() >= MAX_MONEY_AMOUNT - his_trade->GetMoney()) { - _player->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, nullptr, nullptr); + info.Status = TRADE_STATUS_CLOSE_WINDOW; + info.Result = EQUIP_ERR_TOO_MUCH_GOLD; + SendTradeStatus(info); my_trade->SetAccepted(false, true); return; } - if (trader->GetMoney() >= uint32(MAX_MONEY_AMOUNT) - my_trade->GetMoney()) + if (trader->GetMoney() >= MAX_MONEY_AMOUNT - my_trade->GetMoney()) { - trader->SendEquipError(EQUIP_ERR_TOO_MUCH_GOLD, nullptr, nullptr); + info.Status = TRADE_STATUS_CLOSE_WINDOW; + info.Result = EQUIP_ERR_TOO_MUCH_GOLD; + trader->GetSession()->SendTradeStatus(info); his_trade->SetAccepted(false, true); return; } @@ -297,14 +304,16 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) { if (!item->CanBeTraded(false, true)) { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } if (item->IsBindedNotWith(trader)) { - SendTradeStatus(TRADE_STATUS_NOT_ELIGIBLE); - SendTradeStatus(TRADE_STATUS_CLOSE_WINDOW/*TRADE_STATUS_TRADE_CANCELED*/); + info.Status = TRADE_STATUS_CLOSE_WINDOW; + info.Result = EQUIP_ERR_CANNOT_TRADE_THAT; + SendTradeStatus(info); return; } } @@ -313,7 +322,8 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) { if (!item->CanBeTraded(false, true)) { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } //if (item->IsBindedNotWith(_player)) // dont mark as invalid when his item isnt good (not exploitable because if item is invalid trade will fail anyway later on the same check) @@ -408,33 +418,39 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) } // inform partner client - trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + info.Status = TRADE_STATUS_TRADE_ACCEPT; + trader->GetSession()->SendTradeStatus(info); // test if item will fit in each inventory - hisCanCompleteTrade = (trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); - myCanCompleteTrade = (_player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT) == EQUIP_ERR_OK); + TradeStatusInfo myCanCompleteInfo, hisCanCompleteInfo; + hisCanCompleteInfo.Result = trader->CanStoreItems(myItems, TRADE_SLOT_TRADED_COUNT, &hisCanCompleteInfo.ItemLimitedByLimitCategory); + myCanCompleteInfo.Result = _player->CanStoreItems(hisItems, TRADE_SLOT_TRADED_COUNT, &myCanCompleteInfo.ItemLimitedByLimitCategory); clearAcceptTradeMode(myItems, hisItems); // in case of missing space report error - if (!myCanCompleteTrade) + if (myCanCompleteInfo.Result != EQUIP_ERR_OK) { clearAcceptTradeMode(my_trade, his_trade); - ChatHandler(this).SendNotification(LANG_NOT_FREE_TRADE_SLOTS); - ChatHandler(trader->GetSession()).SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); + myCanCompleteInfo.Status = TRADE_STATUS_CLOSE_WINDOW; + trader->GetSession()->SendTradeStatus(myCanCompleteInfo); + myCanCompleteInfo.IsTargetResult = true; + SendTradeStatus(myCanCompleteInfo); my_trade->SetAccepted(false); his_trade->SetAccepted(false); delete my_spell; delete his_spell; return; } - else if (!hisCanCompleteTrade) + else if (hisCanCompleteInfo.Result != EQUIP_ERR_OK) { clearAcceptTradeMode(my_trade, his_trade); - ChatHandler(this).SendNotification(LANG_NOT_PARTNER_FREE_TRADE_SLOTS); - ChatHandler(trader->GetSession()).SendNotification(LANG_NOT_FREE_TRADE_SLOTS); + hisCanCompleteInfo.Status = TRADE_STATUS_CLOSE_WINDOW; + SendTradeStatus(hisCanCompleteInfo); + hisCanCompleteInfo.IsTargetResult = true; + trader->GetSession()->SendTradeStatus(hisCanCompleteInfo); my_trade->SetAccepted(false); his_trade->SetAccepted(false); delete my_spell; @@ -460,6 +476,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) // execute trade: 2. store moveItems(myItems, hisItems); + // logging money if (my_trade->GetMoney() >= 10 * GOLD ) { CharacterDatabase.Execute("INSERT INTO log_money VALUES({}, {}, \"{}\", \"{}\", {}, \"{}\", {}, \"goods\", NOW(), {})", @@ -496,12 +513,14 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/) trader->SaveInventoryAndGoldToDB(trans); CharacterDatabase.CommitTransaction(trans); - trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); - SendTradeStatus(TRADE_STATUS_TRADE_COMPLETE); + info.Status = TRADE_STATUS_TRADE_COMPLETE; + trader->GetSession()->SendTradeStatus(info); + SendTradeStatus(info); } else { - trader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT); + info.Status = TRADE_STATUS_TRADE_ACCEPT; + trader->GetSession()->SendTradeStatus(info); } } @@ -516,12 +535,14 @@ void WorldSession::HandleUnacceptTradeOpcode(WorldPacket& /*recvPacket*/) void WorldSession::HandleBeginTradeOpcode(WorldPacket& /*recvPacket*/) { - TradeData* my_trade = _player->m_trade; + TradeData* my_trade = _player->GetTradeData(); if (!my_trade) return; - my_trade->GetTrader()->GetSession()->SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); - SendTradeStatus(TRADE_STATUS_OPEN_WINDOW); + TradeStatusInfo info; + info.Status = TRADE_STATUS_OPEN_WINDOW; + my_trade->GetTrader()->GetSession()->SendTradeStatus(info); + SendTradeStatus(info); } void WorldSession::SendCancelTrade(TradeStatus status) @@ -529,7 +550,9 @@ void WorldSession::SendCancelTrade(TradeStatus status) if (PlayerRecentlyLoggedOut() || PlayerLogout()) return; - SendTradeStatus(status); + TradeStatusInfo info; + info.Status = status; + SendTradeStatus(info); } void WorldSession::HandleCancelTradeOpcode(WorldPacket& /*recvPacket*/) @@ -544,36 +567,43 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) ObjectGuid ID; recvPacket >> ID; - if (GetPlayer()->m_trade) + if (GetPlayer()->GetTradeData()) return; + TradeStatusInfo info; if (!GetPlayer()->IsAlive()) { - SendTradeStatus(TRADE_STATUS_YOU_DEAD); + info.Status = TRADE_STATUS_YOU_DEAD; + SendTradeStatus(info); return; } if (GetPlayer()->HasUnitState(UNIT_STATE_STUNNED)) { - SendTradeStatus(TRADE_STATUS_YOU_STUNNED); + info.Status = TRADE_STATUS_YOU_STUNNED; + SendTradeStatus(info); return; } if (isLogingOut()) { - SendTradeStatus(TRADE_STATUS_YOU_LOGOUT); + info.Status = TRADE_STATUS_YOU_LOGOUT; + SendTradeStatus(info); return; } if (GetPlayer()->IsInFlight()) { - SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); + info.Status = TRADE_STATUS_TARGET_TO_FAR; + SendTradeStatus(info); return; } if (GetPlayer()->GetLevel() < sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ)) { ChatHandler(this).SendNotification(LANG_TRADE_REQ, sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ)); + info.Status = TRADE_STATUS_CLOSE_WINDOW; + SendTradeStatus(info); return; } @@ -584,55 +614,57 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) if (!pOther) { - SendTradeStatus(TRADE_STATUS_NO_TARGET); + info.Status = TRADE_STATUS_NO_TARGET; + SendTradeStatus(info); return; } if (pOther == GetPlayer() || pOther->m_trade) { - SendTradeStatus(TRADE_STATUS_BUSY); + info.Status = TRADE_STATUS_BUSY; + SendTradeStatus(info); return; } if (!pOther->IsAlive()) { - SendTradeStatus(TRADE_STATUS_TARGET_DEAD); + info.Status = TRADE_STATUS_TARGET_DEAD; + SendTradeStatus(info); return; } if (pOther->IsInFlight()) { - SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); + info.Status = TRADE_STATUS_TARGET_TO_FAR; + SendTradeStatus(info); return; } if (pOther->HasUnitState(UNIT_STATE_STUNNED)) { - SendTradeStatus(TRADE_STATUS_TARGET_STUNNED); + info.Status = TRADE_STATUS_TARGET_STUNNED; + SendTradeStatus(info); return; } if (pOther->GetSession()->isLogingOut()) { - SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT); + info.Status = TRADE_STATUS_TARGET_LOGOUT; + SendTradeStatus(info); return; } if (!sWorld->getBoolConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeamId() != _player->GetTeamId()) { - SendTradeStatus(TRADE_STATUS_WRONG_FACTION); + info.Status = TRADE_STATUS_WRONG_FACTION; + SendTradeStatus(info); return; } - if (!pOther->IsWithinDistInMap(_player, 10.0f, false)) + if (!pOther->IsWithinDistInMap(_player, TRADE_DISTANCE, false)) { - SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR); - return; - } - - if (pOther->GetLevel() < sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ)) - { - ChatHandler(this).SendNotification(LANG_TRADE_OTHER_REQ, sWorld->getIntConfig(CONFIG_TRADE_LEVEL_REQ)); + info.Status = TRADE_STATUS_TARGET_TO_FAR; + SendTradeStatus(info); return; } @@ -643,10 +675,13 @@ void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket) _player->m_trade = new TradeData(_player, pOther); pOther->m_trade = new TradeData(pOther, _player); - WorldPacket data(SMSG_TRADE_STATUS, 12); - data << uint32(TRADE_STATUS_BEGIN_TRADE); - data << _player->GetGUID(); - pOther->SendDirectMessage(&data); + // WorldPacket data(SMSG_TRADE_STATUS, 12); + // data << uint32(TRADE_STATUS_BEGIN_TRADE); + // data << _player->GetGUID(); + // pOther->SendDirectMessage(&data); + info.Status = TRADE_STATUS_BEGIN_TRADE; + info.TraderGuid = _player->GetGUID(); + pOther->GetSession()->SendTradeStatus(info); } void WorldSession::HandleSetTradeGoldOpcode(WorldPacket& recvPacket) @@ -676,10 +711,12 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) if (!my_trade) return; + TradeStatusInfo info; // invalid slot number if (tradeSlot >= TRADE_SLOT_COUNT) { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } @@ -687,7 +724,8 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) Item* item = _player->GetItemByPos(bag, slot); if (!item || (tradeSlot != TRADE_SLOT_NONTRADED && !item->CanBeTraded(false, true))) { - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } @@ -697,7 +735,8 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) if (my_trade->HasItem(iGUID)) { // cheating attempt - SendTradeStatus(TRADE_STATUS_TRADE_CANCELED); + info.Status = TRADE_STATUS_TRADE_CANCELED; + SendTradeStatus(info); return; } @@ -706,7 +745,16 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket) { // Do not send TRADE_STATUS_TRADE_CANCELED because it will cause double display of "Transaction canceled" notification // On the trade initiator screen - SendTradeStatus(TRADE_STATUS_CLOSE_WINDOW); + info.Status = TRADE_STATUS_CLOSE_WINDOW; + SendTradeStatus(info); + return; + } + + if (tradeSlot != TRADE_SLOT_NONTRADED && item->IsBindedNotWith(my_trade->GetTrader())) + { + info.Status = TRADE_STATUS_NOT_ON_TAPLIST; + info.Slot = tradeSlot; + SendTradeStatus(info); return; } diff --git a/src/server/game/Server/WorldSession.h b/src/server/game/Server/WorldSession.h index 1ea355538..b00c1d6ec 100644 --- a/src/server/game/Server/WorldSession.h +++ b/src/server/game/Server/WorldSession.h @@ -57,6 +57,7 @@ struct AuctionEntry; struct DeclinedName; struct ItemTemplate; struct MovementInfo; +struct TradeStatusInfo; namespace lfg { @@ -510,7 +511,7 @@ public: void SendBattleGroundList(ObjectGuid guid, BattlegroundTypeId bgTypeId = BATTLEGROUND_RB); - void SendTradeStatus(TradeStatus status); + void SendTradeStatus(TradeStatusInfo const& info); void SendUpdateTrade(bool trader_data = true); void SendCancelTrade(TradeStatus status); diff --git a/src/server/shared/SharedDefines.h b/src/server/shared/SharedDefines.h index b5a979dea..d6b41138d 100644 --- a/src/server/shared/SharedDefines.h +++ b/src/server/shared/SharedDefines.h @@ -3812,7 +3812,7 @@ enum TradeStatus TRADE_STATUS_NO_TARGET = 6, TRADE_STATUS_BACK_TO_TRADE = 7, TRADE_STATUS_TRADE_COMPLETE = 8, - // 9? + TRADE_STATUS_TRADE_REJECTED = 9, TRADE_STATUS_TARGET_TO_FAR = 10, TRADE_STATUS_WRONG_FACTION = 11, TRADE_STATUS_CLOSE_WINDOW = 12, @@ -3825,8 +3825,8 @@ enum TradeStatus TRADE_STATUS_YOU_LOGOUT = 19, TRADE_STATUS_TARGET_LOGOUT = 20, TRADE_STATUS_TRIAL_ACCOUNT = 21, // Trial accounts can not perform that action - TRADE_STATUS_ONLY_CONJURED = 22, // You can only trade conjured items... (cross realm BG related). - TRADE_STATUS_NOT_ELIGIBLE = 23 // Related to trading soulbound loot items + TRADE_STATUS_WRONG_REALM = 22, // You can only trade conjured items... (cross realm BG related). + TRADE_STATUS_NOT_ON_TAPLIST = 23 // Related to trading soulbound loot items }; enum XPColorChar : uint8