diff options
Diffstat (limited to 'src/server/game/Handlers/ItemHandler.cpp')
-rw-r--r-- | src/server/game/Handlers/ItemHandler.cpp | 931 |
1 files changed, 613 insertions, 318 deletions
diff --git a/src/server/game/Handlers/ItemHandler.cpp b/src/server/game/Handlers/ItemHandler.cpp index 5aedc08a063..373c0d2a5e6 100644 --- a/src/server/game/Handlers/ItemHandler.cpp +++ b/src/server/game/Handlers/ItemHandler.cpp @@ -27,6 +27,7 @@ #include "UpdateData.h" #include "ObjectAccessor.h" #include "SpellInfo.h" +#include <vector> void WorldSession::HandleSplitItemOpcode(WorldPacket& recvData) { @@ -54,7 +55,7 @@ void WorldSession::HandleSplitItemOpcode(WorldPacket& recvData) if (!_player->IsValidPos(dstbag, dstslot, false)) // can be autostore pos { - _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL); + _player->SendEquipError(EQUIP_ERR_WRONG_SLOT, NULL, NULL); return; } @@ -81,7 +82,7 @@ void WorldSession::HandleSwapInvItemOpcode(WorldPacket& recvData) if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, dstslot, true)) { - _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL); + _player->SendEquipError(EQUIP_ERR_WRONG_SLOT, NULL, NULL); return; } @@ -133,7 +134,7 @@ void WorldSession::HandleSwapItem(WorldPacket& recvData) if (!_player->IsValidPos(dstbag, dstslot, true)) { - _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL); + _player->SendEquipError(EQUIP_ERR_WRONG_SLOT, NULL, NULL); return; } @@ -236,7 +237,7 @@ void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData) void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) { - //sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_DESTROYITEM"); + //sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_DESTROY_ITEM"); uint8 bag, slot, count, data1, data2, data3; recvData >> bag >> slot >> count >> data1 >> data2 >> data3; @@ -264,7 +265,7 @@ void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) if (pItem->GetTemplate()->Flags & ITEM_PROTO_FLAG_INDESTRUCTIBLE) { - _player->SendEquipError(EQUIP_ERR_CANT_DROP_SOULBOUND, NULL, NULL); + _player->SendEquipError(EQUIP_ERR_DROP_BOUND_ITEM, NULL, NULL); return; } @@ -277,174 +278,176 @@ void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData) _player->DestroyItem(bag, slot, true); } -// Only _static_ data send in this packet !!! -void WorldSession::HandleItemQuerySingleOpcode(WorldPacket& recvData) +void WorldSession::SendItemDb2Reply(uint32 entry) { - //sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_ITEM_QUERY_SINGLE"); - uint32 item; - recvData >> item; - - sLog->outInfo(LOG_FILTER_NETWORKIO, "STORAGE: Item Query = %u", item); - - ItemTemplate const* pProto = sObjectMgr->GetItemTemplate(item); - if (pProto) + WorldPacket data(SMSG_DB_REPLY, 44); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(entry); + if (!proto) { - std::string Name = pProto->Name1; - std::string Description = pProto->Description; - - int loc_idx = GetSessionDbLocaleIndex(); - if (loc_idx >= 0) - { - if (ItemLocale const* il = sObjectMgr->GetItemLocale(pProto->ItemId)) - { - ObjectMgr::GetLocaleString(il->Name, loc_idx, Name); - ObjectMgr::GetLocaleString(il->Description, loc_idx, Description); - } - } - // guess size - WorldPacket data(SMSG_ITEM_QUERY_SINGLE_RESPONSE, 600); - data << pProto->ItemId; - data << pProto->Class; - data << pProto->SubClass; - data << pProto->SoundOverrideSubclass; - data << Name; - data << uint8(0x00); //pProto->Name2; // blizz not send name there, just uint8(0x00); <-- \0 = empty string = empty name... - data << uint8(0x00); //pProto->Name3; // blizz not send name there, just uint8(0x00); - data << uint8(0x00); //pProto->Name4; // blizz not send name there, just uint8(0x00); - data << pProto->DisplayInfoID; - data << pProto->Quality; - data << pProto->Flags; - data << pProto->Flags2; - data << pProto->BuyPrice; - data << pProto->SellPrice; - data << pProto->InventoryType; - data << pProto->AllowableClass; - data << pProto->AllowableRace; - data << pProto->ItemLevel; - data << pProto->RequiredLevel; - data << pProto->RequiredSkill; - data << pProto->RequiredSkillRank; - data << pProto->RequiredSpell; - data << pProto->RequiredHonorRank; - data << pProto->RequiredCityRank; - data << pProto->RequiredReputationFaction; - data << pProto->RequiredReputationRank; - data << int32(pProto->MaxCount); - data << int32(pProto->Stackable); - data << pProto->ContainerSlots; - data << pProto->StatsCount; // item stats count - for (uint32 i = 0; i < pProto->StatsCount; ++i) - { - data << pProto->ItemStat[i].ItemStatType; - data << pProto->ItemStat[i].ItemStatValue; - } - data << pProto->ScalingStatDistribution; // scaling stats distribution - data << pProto->ScalingStatValue; // some kind of flags used to determine stat values column - for (int i = 0; i < MAX_ITEM_PROTO_DAMAGES; ++i) - { - data << pProto->Damage[i].DamageMin; - data << pProto->Damage[i].DamageMax; - data << pProto->Damage[i].DamageType; - } + data << uint32(-1); // entry + data << uint32(DB2_REPLY_ITEM); + data << uint32(time(NULL)); // hotfix date + data << uint32(0); // size of next block + return; + } - // resistances (7) - data << pProto->Armor; - data << pProto->HolyRes; - data << pProto->FireRes; - data << pProto->NatureRes; - data << pProto->FrostRes; - data << pProto->ShadowRes; - data << pProto->ArcaneRes; + data << uint32(entry); + data << uint32(DB2_REPLY_ITEM); + data << uint32(sObjectMgr->GetHotfixDate(entry, DB2_REPLY_ITEM)); - data << pProto->Delay; - data << pProto->AmmoType; - data << pProto->RangedModRange; + ByteBuffer buff; + buff << uint32(entry); + buff << uint32(proto->Class); + buff << uint32(proto->SubClass); + buff << int32(proto->SoundOverrideSubclass); + buff << uint32(proto->Material); + buff << uint32(proto->DisplayInfoID); + buff << uint32(proto->InventoryType); + buff << uint32(proto->Sheath); - for (int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s) - { - // send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown - // use `item_template` or if not set then only use spell cooldowns - SpellInfo const* spell = sSpellMgr->GetSpellInfo(pProto->Spells[s].SpellId); - if (spell) - { - bool db_data = pProto->Spells[s].SpellCooldown >= 0 || pProto->Spells[s].SpellCategoryCooldown >= 0; + data << uint32(buff.size()); + data.append(buff); - data << pProto->Spells[s].SpellId; - data << pProto->Spells[s].SpellTrigger; - data << uint32(-abs(pProto->Spells[s].SpellCharges)); + SendPacket(&data); +} - if (db_data) - { - data << uint32(pProto->Spells[s].SpellCooldown); - data << uint32(pProto->Spells[s].SpellCategory); - data << uint32(pProto->Spells[s].SpellCategoryCooldown); - } - else - { - data << uint32(spell->RecoveryTime); - data << uint32(spell->Category); - data << uint32(spell->CategoryRecoveryTime); - } - } - else - { - data << uint32(0); - data << uint32(0); - data << uint32(0); - data << uint32(-1); - data << uint32(0); - data << uint32(-1); - } - } - data << pProto->Bonding; - data << Description; - data << pProto->PageText; - data << pProto->LanguageID; - data << pProto->PageMaterial; - data << pProto->StartQuest; - data << pProto->LockID; - data << int32(pProto->Material); - data << pProto->Sheath; - data << pProto->RandomProperty; - data << pProto->RandomSuffix; - data << pProto->Block; - data << pProto->ItemSet; - data << pProto->MaxDurability; - data << pProto->Area; - data << pProto->Map; // Added in 1.12.x & 2.0.1 client branch - data << pProto->BagFamily; - data << pProto->TotemCategory; - for (int s = 0; s < MAX_ITEM_PROTO_SOCKETS; ++s) - { - data << pProto->Socket[s].Color; - data << pProto->Socket[s].Content; - } - data << pProto->socketBonus; - data << pProto->GemProperties; - data << pProto->RequiredDisenchantSkill; - data << pProto->ArmorDamageModifier; - data << pProto->Duration; // added in 2.4.2.8209, duration (seconds) - data << pProto->ItemLimitCategory; // WotLK, ItemLimitCategory - data << pProto->HolidayId; // Holiday.dbc? - SendPacket(&data); - } - else +void WorldSession::SendItemSparseDb2Reply(uint32 entry) +{ + WorldPacket data(SMSG_DB_REPLY, 526); + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(entry); + if (!proto) { - sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_ITEM_QUERY_SINGLE - NO item INFO! (ENTRY: %u)", item); - WorldPacket data(SMSG_ITEM_QUERY_SINGLE_RESPONSE, 4); - data << uint32(item | 0x80000000); - SendPacket(&data); + data << uint32(-1); // entry + data << uint32(DB2_REPLY_SPARSE); + data << uint32(time(NULL)); // hotfix date + data << uint32(0); // size of next block + return; } + + data << uint32(entry); + data << uint32(DB2_REPLY_SPARSE); + data << uint32(sObjectMgr->GetHotfixDate(entry, DB2_REPLY_SPARSE)); + + ByteBuffer buff; + buff << uint32(entry); + buff << uint32(proto->Quality); + buff << uint32(proto->Flags); + buff << uint32(proto->Flags2); + buff << float(proto->Unk430_1); + buff << float(proto->Unk430_2); + buff << uint32(proto->BuyCount); + buff << int32(proto->BuyPrice); + buff << uint32(proto->SellPrice); + buff << uint32(proto->InventoryType); + buff << int32(proto->AllowableClass); + buff << int32(proto->AllowableRace); + buff << uint32(proto->ItemLevel); + buff << uint32(proto->RequiredLevel); + buff << uint32(proto->RequiredSkill); + buff << uint32(proto->RequiredSkillRank); + buff << uint32(proto->RequiredSpell); + buff << uint32(proto->RequiredHonorRank); + buff << uint32(proto->RequiredCityRank); + buff << uint32(proto->RequiredReputationFaction); + buff << uint32(proto->RequiredReputationRank); + buff << int32(proto->MaxCount); + buff << int32(proto->Stackable); + buff << uint32(proto->ContainerSlots); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_STATS; ++x) + buff << uint32(proto->ItemStat[x].ItemStatType); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_STATS; ++x) + buff << int32(proto->ItemStat[x].ItemStatValue); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_STATS; ++x) + buff << int32(proto->ItemStat[x].ItemStatUnk1); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_STATS; ++x) + buff << int32(proto->ItemStat[x].ItemStatUnk2); + + buff << uint32(proto->ScalingStatDistribution); + buff << uint32(proto->DamageType); + buff << uint32(proto->Delay); + buff << float(proto->RangedModRange); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_SPELLS; ++x) + buff << int32(proto->Spells[x].SpellId); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_SPELLS; ++x) + buff << uint32(proto->Spells[x].SpellTrigger); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_SPELLS; ++x) + buff << int32(proto->Spells[x].SpellCharges); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_SPELLS; ++x) + buff << int32(proto->Spells[x].SpellCooldown); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_SPELLS; ++x) + buff << uint32(proto->Spells[x].SpellCategory); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_SPELLS; ++x) + buff << int32(proto->Spells[x].SpellCategoryCooldown); + + buff << uint32(proto->Bonding); + + // item name + std::string name = proto->Name1; + buff << uint16(name.length()); + if (name.length()) + buff << name; + + for (uint32 i = 0; i < 3; ++i) // other 3 names + buff << uint16(0); + + std::string desc = proto->Description; + buff << uint16(desc.length()); + if (desc.length()) + buff << desc; + + buff << uint32(proto->PageText); + buff << uint32(proto->LanguageID); + buff << uint32(proto->PageMaterial); + buff << uint32(proto->StartQuest); + buff << uint32(proto->LockID); + buff << int32(proto->Material); + buff << uint32(proto->Sheath); + buff << int32(proto->RandomProperty); + buff << int32(proto->RandomSuffix); + buff << uint32(proto->ItemSet); + + buff << uint32(proto->Area); + buff << uint32(proto->Map); + buff << uint32(proto->BagFamily); + buff << uint32(proto->TotemCategory); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_SOCKETS; ++x) + buff << uint32(proto->Socket[x].Color); + + for (uint32 x = 0; x < MAX_ITEM_PROTO_SOCKETS; ++x) + buff << uint32(proto->Socket[x].Content); + + buff << uint32(proto->socketBonus); + buff << uint32(proto->GemProperties); + buff << float(proto->ArmorDamageModifier); + buff << int32(proto->Duration); + buff << uint32(proto->ItemLimitCategory); + buff << uint32(proto->HolidayId); + buff << float(proto->StatScalingFactor); // StatScalingFactor + buff << uint32(proto->CurrencySubstitutionId); + buff << uint32(proto->CurrencySubstitutionCount); + + data << uint32(buff.size()); + data.append(buff); + + SendPacket(&data); } void WorldSession::HandleReadItem(WorldPacket& recvData) { - //sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_READ_ITEM"); - uint8 bag, slot; recvData >> bag >> slot; - //sLog->outInfo(LOG_FILTER_NETWORKIO, "STORAGE: Read bag = %u, slot = %u", bag, slot); Item* pItem = _player->GetItemByPos(bag, slot); if (pItem && pItem->GetTemplate()->PageText) @@ -454,7 +457,7 @@ void WorldSession::HandleReadItem(WorldPacket& recvData) InventoryResult msg = _player->CanUseItem(pItem); if (msg == EQUIP_ERR_OK) { - data.Initialize (SMSG_READ_ITEM_OK, 8); + data.Initialize(SMSG_READ_ITEM_OK, 8); sLog->outInfo(LOG_FILTER_NETWORKIO, "STORAGE: Item page sent"); } else @@ -470,19 +473,6 @@ void WorldSession::HandleReadItem(WorldPacket& recvData) _player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL); } -void WorldSession::HandlePageQuerySkippedOpcode(WorldPacket& recvData) -{ - sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_PAGE_TEXT_QUERY"); - - uint32 itemid; - uint64 guid; - - recvData >> itemid >> guid; - - sLog->outInfo(LOG_FILTER_NETWORKIO, "Packet Info: itemid: %u guidlow: %u guidentry: %u guidhigh: %u", - itemid, GUID_LOPART(guid), GUID_ENPART(guid), GUID_HIPART(guid)); -} - void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_SELL_ITEM"); @@ -498,7 +488,7 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) if (!creature) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleSellItemOpcode - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(vendorguid))); - _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid); return; } @@ -512,21 +502,21 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) // prevent sell not owner item if (_player->GetGUID() != pItem->GetOwnerGUID()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid); return; } // prevent sell non empty bag by drag-and-drop at vendor's item list if (pItem->IsNotEmptyBag()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid); return; } // prevent sell currently looted item if (_player->GetLootGUID() == pItem->GetGUID()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid); return; } @@ -544,7 +534,7 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) // prevent sell more items that exist in stack (possible only not from client) if (count > pItem->GetCount()) { - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid); return; } } @@ -560,7 +550,7 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) if (!pNewItem) { sLog->outError(LOG_FILTER_NETWORKIO, "WORLD: HandleSellItemOpcode - could not create clone of item %u; count = %u", pItem->GetEntry(), count); - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid); return; } @@ -587,11 +577,11 @@ void WorldSession::HandleSellItemOpcode(WorldPacket& recvData) _player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS, money); } else - _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid); return; } } - _player->SendSellError(SELL_ERR_CANT_FIND_ITEM, creature, itemguid, 0); + _player->SendSellError(SELL_ERR_CANT_FIND_ITEM, creature, itemguid); return; } @@ -607,7 +597,7 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData) if (!creature) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleBuybackItem - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(vendorguid))); - _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0); + _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, 0); return; } @@ -619,7 +609,7 @@ void WorldSession::HandleBuybackItem(WorldPacket& recvData) if (pItem) { uint32 price = _player->GetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START); - if (!_player->HasEnoughMoney(price)) + if (!_player->HasEnoughMoney(uint64(price))) { _player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, creature, pItem->GetEntry(), 0); return; @@ -659,24 +649,15 @@ void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData) return; // cheating uint8 bag = NULL_BAG; // init for case invalid bagGUID - + Item* bagItem = NULL; // find bag slot by bag guid if (bagguid == _player->GetGUID()) bag = INVENTORY_SLOT_BAG_0; else - { - for (int i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i) - { - if (Bag* pBag = _player->GetBagByPos(i)) - { - if (bagguid == pBag->GetGUID()) - { - bag = i; - break; - } - } - } - } + bagItem = _player->GetItemByGuid(bagguid); + + if (bagItem && bagItem->IsBag()) + bag = bagItem->GetSlot(); // bag not found, cheating? if (bag == NULL_BAG) @@ -688,11 +669,12 @@ void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData) void WorldSession::HandleBuyItemOpcode(WorldPacket& recvData) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_BUY_ITEM"); - uint64 vendorguid; + uint64 vendorguid, bagGuid; uint32 item, slot, count; - uint8 unk1; + uint8 itemType; // 1 = item, 2 = currency + uint8 bagSlot; - recvData >> vendorguid >> item >> slot >> count >> unk1; + recvData >> vendorguid >> itemType >> item >> slot >> count >> bagGuid >> bagSlot; // client expects count starting at 1, and we send vendorslot+1 to client already if (slot > 0) @@ -700,7 +682,22 @@ void WorldSession::HandleBuyItemOpcode(WorldPacket& recvData) else return; // cheating - GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, NULL_BAG, NULL_SLOT); + if (itemType == ITEM_VENDOR_TYPE_ITEM) + { + Item* bagItem = _player->GetItemByGuid(bagGuid); + + uint8 bag = NULL_BAG; + if (bagItem && bagItem->IsBag()) + bag = bagItem->GetSlot(); + else if (bagGuid == GetPlayer()->GetGUID()) // The client sends the player guid when trying to store an item in the default backpack + bag = INVENTORY_SLOT_BAG_0; + + GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, bag, bagSlot); + } + else if (itemType == ITEM_VENDOR_TYPE_CURRENCY) + GetPlayer()->BuyCurrencyFromVendorSlot(vendorguid, slot, item, count); + else + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: received wrong itemType (%u) in HandleBuyItemOpcode", itemType); } void WorldSession::HandleListInventoryOpcode(WorldPacket& recvData) @@ -725,7 +722,7 @@ void WorldSession::SendListInventory(uint64 vendorGuid) if (!vendor) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: SendListInventory - Unit (GUID: %u) not found or you can not interact with him.", uint32(GUID_LOPART(vendorGuid))); - _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0); + _player->SendSellError(SELL_ERR_CANT_FIND_VENDOR, NULL, 0); return; } @@ -737,78 +734,148 @@ void WorldSession::SendListInventory(uint64 vendorGuid) if (vendor->HasUnitState(UNIT_STATE_MOVING)) vendor->StopMoving(); - VendorItemData const* items = vendor->GetVendorItems(); - if (!items) - { - WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + 1); - data << uint64(vendorGuid); - data << uint8(0); // count == 0, next will be error code - data << uint8(0); // "Vendor has no inventory" - SendPacket(&data); - return; - } - - uint8 itemCount = items->GetItemCount(); - uint8 count = 0; - - WorldPacket data(SMSG_LIST_INVENTORY, 8 + 1 + itemCount * 8 * 4); - data << uint64(vendorGuid); + VendorItemData const* vendorItems = vendor->GetVendorItems(); + uint8 rawItemCount = vendorItems ? vendorItems->GetItemCount() : 0; - size_t countPos = data.wpos(); - data << uint8(count); + //if (rawItemCount > 300), + // rawItemCount = 300; // client cap but uint8 max value is 255 - float discountMod = _player->GetReputationPriceDiscount(vendor); + ByteBuffer itemsData(32 * rawItemCount); + std::vector<bool> enablers; + enablers.reserve(2 * rawItemCount); - for (uint8 slot = 0; slot < itemCount; ++slot) + const float discountMod = _player->GetReputationPriceDiscount(vendor); + uint8 count = 0; + for (uint8 slot = 0; slot < rawItemCount; ++slot) { - if (VendorItem const* item = items->GetItem(slot)) + VendorItem const* vendorItem = vendorItems->GetItem(slot); + if (!vendorItem) continue; + + if (vendorItem->Type == ITEM_VENDOR_TYPE_ITEM) { - if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(item->item)) + ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(vendorItem->item); + if (!itemTemplate) + continue; + + uint32 leftInStock = !vendorItem->maxcount ? 0xFFFFFFFF : vendor->GetVendorItemCurrentCount(vendorItem); + if (!_player->isGameMaster()) // ignore conditions if GM on { - if (!(itemTemplate->AllowableClass & _player->getClassMask()) && itemTemplate->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster()) + // Respect allowed class + if (!(itemTemplate->AllowableClass & _player->getClassMask()) && itemTemplate->Bonding == BIND_WHEN_PICKED_UP) continue; - // Only display items in vendor lists for the team the - // player is on. If GM on, display all items. - if (!_player->isGameMaster() && ((itemTemplate->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && _player->GetTeam() == ALLIANCE) || (itemTemplate->Flags2 == ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && _player->GetTeam() == HORDE))) + + // Only display items in vendor lists for the team the player is on + if ((itemTemplate->Flags2 & ITEM_FLAGS_EXTRA_HORDE_ONLY && _player->GetTeam() == ALLIANCE) || + (itemTemplate->Flags2 & ITEM_FLAGS_EXTRA_ALLIANCE_ONLY && _player->GetTeam() == HORDE)) continue; // Items sold out are not displayed in list - uint32 leftInStock = !item->maxcount ? 0xFFFFFFFF : vendor->GetVendorItemCurrentCount(item); - if (!_player->isGameMaster() && !leftInStock) + if (leftInStock == 0) continue; + } - ConditionList conditions = sConditionMgr->GetConditionsForNpcVendorEvent(vendor->GetEntry(), item->item); - if (!sConditionMgr->IsObjectMeetToConditions(_player, vendor, conditions)) - { - sLog->outDebug(LOG_FILTER_CONDITIONSYS, "SendListInventory: conditions not met for creature entry %u item %u", vendor->GetEntry(), item->item); - continue; - } + ConditionList conditions = sConditionMgr->GetConditionsForNpcVendorEvent(vendor->GetEntry(), vendorItem->item); + if (!sConditionMgr->IsObjectMeetToConditions(_player, vendor, conditions)) + { + sLog->outDebug(LOG_FILTER_CONDITIONSYS, "SendListInventory: conditions not met for creature entry %u item %u", vendor->GetEntry(), vendorItem->item); + continue; + } - ++count; + int32 price = vendorItem->IsGoldRequired(itemTemplate) ? uint32(floor(itemTemplate->BuyPrice * discountMod)) : 0; - // reputation discount - int32 price = item->IsGoldRequired(itemTemplate) ? uint32(floor(itemTemplate->BuyPrice * discountMod)) : 0; + if (int32 priceMod = _player->GetTotalAuraModifier(SPELL_AURA_MOD_VENDOR_ITEMS_PRICES)) + price -= CalculatePct(price, priceMod); - data << uint32(slot + 1); // client expects counting to start at 1 - data << uint32(item->item); - data << uint32(itemTemplate->DisplayInfoID); - data << int32(leftInStock); - data << uint32(price); - data << uint32(itemTemplate->MaxDurability); - data << uint32(itemTemplate->BuyCount); - data << uint32(item->ExtendedCost); + ++count; + itemsData << uint32(slot + 1); // client expects counting to start at 1 + itemsData << uint32(itemTemplate->MaxDurability); + + if (vendorItem->ExtendedCost != 0) + { + enablers.push_back(0); + itemsData << uint32(vendorItem->ExtendedCost); } + else + enablers.push_back(1); + enablers.push_back(1); // unk bit + + itemsData << uint32(vendorItem->item); + itemsData << uint32(vendorItem->Type); // 1 is items, 2 is currency + itemsData << uint32(price); + itemsData << uint32(itemTemplate->DisplayInfoID); + // if (!unk "enabler") data << uint32(something); + itemsData << int32(leftInStock); + itemsData << uint32(itemTemplate->BuyCount); } - } + else if (vendorItem->Type == ITEM_VENDOR_TYPE_CURRENCY) + { + CurrencyTypesEntry const* currencyTemplate = sCurrencyTypesStore.LookupEntry(vendorItem->item); + if (!currencyTemplate) + continue; - if (count == 0) - { - data << uint8(0); - SendPacket(&data); - return; + if (vendorItem->ExtendedCost == 0) + continue; // there's no price defined for currencies, only extendedcost is used + + ++count; + itemsData << uint32(slot + 1); // client expects counting to start at 1 + itemsData << uint32(0); // max durability + + if (vendorItem->ExtendedCost != 0) + { + enablers.push_back(0); + itemsData << uint32(vendorItem->ExtendedCost); + } + else + enablers.push_back(1); + + enablers.push_back(1); // unk bit + + itemsData << uint32(vendorItem->item); + itemsData << uint32(vendorItem->Type); // 1 is items, 2 is currency + itemsData << uint32(0); // price, only seen currency types that have Extended cost + itemsData << uint32(0); // displayId + // if (!unk "enabler") data << uint32(something); + itemsData << int32(-1); + itemsData << uint32(vendorItem->maxcount); + } + // else error } - data.put<uint8>(countPos, count); + ObjectGuid guid = vendorGuid; + + WorldPacket data(SMSG_LIST_INVENTORY, 12 + itemsData.size()); + + data.WriteBit(guid[1]); + data.WriteBit(guid[0]); + + data.WriteBits(count, 21); // item count + + data.WriteBit(guid[3]); + data.WriteBit(guid[6]); + data.WriteBit(guid[5]); + data.WriteBit(guid[2]); + data.WriteBit(guid[7]); + + for (std::vector<bool>::const_iterator itr = enablers.begin(); itr != enablers.end(); ++itr) + data.WriteBit(*itr); + + data.WriteBit(guid[4]); + + data.FlushBits(); + data.append(itemsData); + + data.WriteByteSeq(guid[5]); + data.WriteByteSeq(guid[4]); + data.WriteByteSeq(guid[1]); + data.WriteByteSeq(guid[0]); + data.WriteByteSeq(guid[6]); + + data << uint8(count == 0); // unk byte, item count 0: 1, item count != 0: 0 or some "random" value below 300 + + data.WriteByteSeq(guid[2]); + data.WriteByteSeq(guid[3]); + data.WriteByteSeq(guid[7]); + SendPacket(&data); } @@ -826,7 +893,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData) if (!_player->IsValidPos(dstbag, NULL_SLOT, false)) // can be autostore pos { - _player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL); + _player->SendEquipError(EQUIP_ERR_WRONG_SLOT, NULL, NULL); return; } @@ -855,7 +922,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData) if (dest.size() == 1 && dest[0].pos == src) { // just remove grey item state - _player->SendEquipError(EQUIP_ERR_NONE, pItem, NULL); + _player->SendEquipError(EQUIP_ERR_INTERNAL_BAG_ERROR, pItem, NULL); return; } @@ -900,7 +967,7 @@ void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& recvPacket) uint32 price = slotEntry->price; - if (!_player->HasEnoughMoney(price)) + if (!_player->HasEnoughMoney(uint64(price))) { data << uint32(ERR_BANKSLOT_INSUFFICIENT_FUNDS); SendPacket(&data); @@ -908,7 +975,7 @@ void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& recvPacket) } _player->SetBankBagSlotCount(slot); - _player->ModifyMoney(-int32(price)); + _player->ModifyMoney(-int64(price)); data << uint32(ERR_BANKSLOT_OK); SendPacket(&data); @@ -938,7 +1005,7 @@ void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket) if (dest.size() == 1 && dest[0].pos == pItem->GetPos()) { - _player->SendEquipError(EQUIP_ERR_NONE, pItem, NULL); + _player->SendEquipError(EQUIP_ERR_CANT_SWAP, pItem, NULL); return; } @@ -988,33 +1055,13 @@ void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket) } } -void WorldSession::HandleSetAmmoOpcode(WorldPacket& recvData) -{ - if (!GetPlayer()->isAlive()) - { - GetPlayer()->SendEquipError(EQUIP_ERR_YOU_ARE_DEAD, NULL, NULL); - return; - } - - sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SET_AMMO"); - uint32 item; - - recvData >> item; - - if (!item) - GetPlayer()->RemoveAmmo(); - else - GetPlayer()->SetAmmo(item); -} - void WorldSession::SendEnchantmentLog(uint64 Target, uint64 Caster, uint32 ItemID, uint32 SpellID) { - WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4+1)); // last check 2.0.10 - data << uint64(Target); - data << uint64(Caster); + WorldPacket data(SMSG_ENCHANTMENTLOG, (8+8+4+4+1)); // last check 4.3.4 + data.appendPackGUID(Target); + data.appendPackGUID(Caster); data << uint32(ItemID); data << uint32(SpellID); - data << uint8(0); SendPacket(&data); } @@ -1029,30 +1076,6 @@ void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid, SendPacket(&data); } -void WorldSession::HandleItemNameQueryOpcode(WorldPacket& recvData) -{ - uint32 itemid; - recvData >> itemid; - recvData.read_skip<uint64>(); // guid - - sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_ITEM_NAME_QUERY %u", itemid); - ItemSetNameEntry const* pName = sObjectMgr->GetItemSetNameEntry(itemid); - if (pName) - { - std::string Name = pName->name; - int loc_idx = GetSessionDbLocaleIndex(); - if (loc_idx >= 0) - if (ItemSetNameLocale const* isnl = sObjectMgr->GetItemSetNameLocale(itemid)) - ObjectMgr::GetLocaleString(isnl->Name, loc_idx, Name); - - WorldPacket data(SMSG_ITEM_NAME_QUERY_RESPONSE, (4+Name.size()+1+4)); - data << uint32(itemid); - data << Name; - data << uint32(pName->InventoryType); - SendPacket(&data); - } -} - void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) { sLog->outDebug(LOG_FILTER_NETWORKIO, "Received opcode CMSG_WRAP_ITEM"); @@ -1087,44 +1110,44 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recvData) if (item == gift) // not possable with pacjket from real client { - _player->SendEquipError(EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL); + _player->SendEquipError(EQUIP_ERR_CANT_WRAP_WRAPPED, item, NULL); return; } if (item->IsEquipped()) { - _player->SendEquipError(EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED, item, NULL); + _player->SendEquipError(EQUIP_ERR_CANT_WRAP_EQUIPPED, item, NULL); return; } if (item->GetUInt64Value(ITEM_FIELD_GIFTCREATOR)) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED); { - _player->SendEquipError(EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL); + _player->SendEquipError(EQUIP_ERR_CANT_WRAP_WRAPPED, item, NULL); return; } if (item->IsBag()) { - _player->SendEquipError(EQUIP_ERR_BAGS_CANT_BE_WRAPPED, item, NULL); + _player->SendEquipError(EQUIP_ERR_CANT_WRAP_BAGS, item, NULL); return; } if (item->IsSoulBound()) { - _player->SendEquipError(EQUIP_ERR_BOUND_CANT_BE_WRAPPED, item, NULL); + _player->SendEquipError(EQUIP_ERR_CANT_WRAP_BOUND, item, NULL); return; } if (item->GetMaxStackCount() != 1) { - _player->SendEquipError(EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED, item, NULL); + _player->SendEquipError(EQUIP_ERR_CANT_WRAP_STACKABLE, item, NULL); return; } // maybe not correct check (it is better than nothing) - if (item->GetTemplate()->MaxCount>0) + if (item->GetTemplate()->MaxCount > 0) { - _player->SendEquipError(EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED, item, NULL); + _player->SendEquipError(EQUIP_ERR_CANT_WRAP_UNIQUE, item, NULL); return; } @@ -1228,6 +1251,14 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData) // tried to put meta gem in normal socket if (itemProto->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META) return; + + // tried to put normal gem in cogwheel socket + if (itemProto->Socket[i].Color == SOCKET_COLOR_COGWHEEL && GemProps[i]->color != SOCKET_COLOR_COGWHEEL) + return; + + // tried to put cogwheel gem in normal socket + if (itemProto->Socket[i].Color != SOCKET_COLOR_COGWHEEL && GemProps[i]->color == SOCKET_COLOR_COGWHEEL) + return; } uint32 GemEnchants[MAX_GEM_SOCKETS]; @@ -1334,9 +1365,10 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recvData) { if (GemEnchants[i]) { + uint32 gemCount = 1; itemTarget->SetEnchantment(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i), GemEnchants[i], 0, 0); if (Item* guidItem = _player->GetItemByGuid(gem_guids[i])) - _player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true); + _player->DestroyItemCount(guidItem, gemCount, true); } } @@ -1362,15 +1394,15 @@ void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPacket& recvData) { sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT"); - uint32 eslot; + uint32 slot; - recvData >> eslot; + recvData >> slot; // apply only to equipped item - if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, eslot)) + if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, slot)) return; - Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot); + Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, slot); if (!item) return; @@ -1427,7 +1459,7 @@ void WorldSession::HandleItemTextQuery(WorldPacket& recvData ) sLog->outDebug(LOG_FILTER_NETWORKIO, "CMSG_ITEM_TEXT_QUERY item guid: %u", GUID_LOPART(itemGuid)); - WorldPacket data(SMSG_ITEM_TEXT_QUERY_RESPONSE, (4+10)); // guess size + WorldPacket data(SMSG_ITEM_TEXT_QUERY_RESPONSE, 14); // guess size if (Item* item = _player->GetItemByGuid(itemGuid)) { @@ -1442,3 +1474,266 @@ void WorldSession::HandleItemTextQuery(WorldPacket& recvData ) SendPacket(&data); } + +void WorldSession::HandleTransmogrifyItems(WorldPacket& recvData) +{ + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_TRANSMOGRIFY_ITEMS"); + Player* player = GetPlayer(); + + // Read data + uint32 count = recvData.ReadBits(22); + + if (count >= EQUIPMENT_SLOT_END) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) sent a wrong count (%u) when transmogrifying items.", player->GetGUIDLow(), player->GetName().c_str(), count); + recvData.rfinish(); + return; + } + + std::vector<ObjectGuid> itemGuids(count, ObjectGuid(0)); + std::vector<uint32> newEntries(count, 0); + std::vector<uint32> slots(count, 0); + + for (uint8 i = 0; i < count; ++i) + { + itemGuids[i][0] = recvData.ReadBit(); + itemGuids[i][5] = recvData.ReadBit(); + itemGuids[i][6] = recvData.ReadBit(); + itemGuids[i][2] = recvData.ReadBit(); + itemGuids[i][3] = recvData.ReadBit(); + itemGuids[i][7] = recvData.ReadBit(); + itemGuids[i][4] = recvData.ReadBit(); + itemGuids[i][1] = recvData.ReadBit(); + } + + ObjectGuid npcGuid; + npcGuid[7] = recvData.ReadBit(); + npcGuid[3] = recvData.ReadBit(); + npcGuid[5] = recvData.ReadBit(); + npcGuid[6] = recvData.ReadBit(); + npcGuid[1] = recvData.ReadBit(); + npcGuid[4] = recvData.ReadBit(); + npcGuid[0] = recvData.ReadBit(); + npcGuid[2] = recvData.ReadBit(); + + recvData.FlushBits(); + + for (uint32 i = 0; i < count; ++i) + { + recvData >> newEntries[i]; + + recvData.ReadByteSeq(itemGuids[i][1]); + recvData.ReadByteSeq(itemGuids[i][5]); + recvData.ReadByteSeq(itemGuids[i][0]); + recvData.ReadByteSeq(itemGuids[i][4]); + recvData.ReadByteSeq(itemGuids[i][6]); + recvData.ReadByteSeq(itemGuids[i][7]); + recvData.ReadByteSeq(itemGuids[i][3]); + recvData.ReadByteSeq(itemGuids[i][2]); + + recvData >> slots[i]; + } + + recvData.ReadByteSeq(npcGuid[7]); + recvData.ReadByteSeq(npcGuid[2]); + recvData.ReadByteSeq(npcGuid[5]); + recvData.ReadByteSeq(npcGuid[4]); + recvData.ReadByteSeq(npcGuid[3]); + recvData.ReadByteSeq(npcGuid[1]); + recvData.ReadByteSeq(npcGuid[6]); + recvData.ReadByteSeq(npcGuid[0]); + + // Validate + + if (!player->GetNPCIfCanInteractWith(npcGuid, UNIT_NPC_FLAG_TRANSMOGRIFIER)) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Unit (GUID: %u) not found or player can't interact with it.", GUID_LOPART(npcGuid)); + return; + } + + int32 cost = 0; + for (uint8 i = 0; i < count; ++i) + { + // slot of the transmogrified item + if (slots[i] >= EQUIPMENT_SLOT_END) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify an item (lowguid: %u) with a wrong slot (%u) when transmogrifying items.", player->GetGUIDLow(), player->GetName().c_str(), GUID_LOPART(itemGuids[i]), slots[i]); + return; + } + + // entry of the transmogrifier item, if it's not 0 + if (newEntries[i]) + { + ItemTemplate const* proto = sObjectMgr->GetItemTemplate(newEntries[i]); + if (!proto) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify to an invalid item (entry: %u).", player->GetGUIDLow(), player->GetName().c_str(), newEntries[i]); + return; + } + } + + Item* itemTransmogrifier = NULL; + // guid of the transmogrifier item, if it's not 0 + if (itemGuids[i]) + { + itemTransmogrifier = player->GetItemByGuid(itemGuids[i]); + if (!itemTransmogrifier) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify with an invalid item (lowguid: %u).", player->GetGUIDLow(), player->GetName().c_str(), GUID_LOPART(itemGuids[i])); + return; + } + } + + // transmogrified item + Item* itemTransmogrified = player->GetItemByPos(INVENTORY_SLOT_BAG_0, slots[i]); + if (!itemTransmogrified) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) tried to transmogrify an invalid item in a valid slot (slot: %u).", player->GetGUIDLow(), player->GetName().c_str(), slots[i]); + return; + } + + // uint16 tempDest; + //// has to be able to equip item transmogrified item + //if (!player->CanEquipItem(slots[i], tempDest, itemTransmogrified, true, true)) + //{ + // sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) can't equip the item to be transmogrified (slot: %u, entry: %u).", player->GetGUIDLow(), player->GetName().c_str(), slots[i], itemTransmogrified->GetEntry()); + // return; + //} + // + //// has to be able to equip item transmogrifier item + //if (!player->CanEquipItem(slots[i], tempDest, itemTransmogrifier, true, true)) + //{ + // sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) can't equip the transmogrifier item (slot: %u, entry: %u).", player->GetGUIDLow(), player->GetName().c_str(), slots[i], itemTransmogrifier->GetEntry()); + // return; + //} + + if (!newEntries[i]) // reset look + { + itemTransmogrified->ClearEnchantment(TRANSMOGRIFY_ENCHANTMENT_SLOT); + player->SetVisibleItemSlot(slots[i], itemTransmogrified); + } + else + { + if (!Item::CanTransmogrifyItemWithItem(itemTransmogrified, itemTransmogrifier)) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleTransmogrifyItems - Player (GUID: %u, name: %s) failed CanTransmogrifyItemWithItem (%u with %u).", player->GetGUIDLow(), player->GetName().c_str(), itemTransmogrified->GetEntry(), itemTransmogrifier->GetEntry()); + return; + } + + // All okay, proceed + itemTransmogrified->SetEnchantment(TRANSMOGRIFY_ENCHANTMENT_SLOT, newEntries[i], 0, 0); + player->SetVisibleItemSlot(slots[i], itemTransmogrified); + + itemTransmogrified->UpdatePlayedTime(player); + + itemTransmogrified->SetOwnerGUID(player->GetGUID()); + itemTransmogrified->SetNotRefundable(player); + itemTransmogrified->ClearSoulboundTradeable(player); + + if (itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_EQUIPED || itemTransmogrifier->GetTemplate()->Bonding == BIND_WHEN_USE) + itemTransmogrifier->SetBinding(true); + + itemTransmogrifier->SetOwnerGUID(player->GetGUID()); + itemTransmogrifier->SetNotRefundable(player); + itemTransmogrifier->ClearSoulboundTradeable(player); + + cost += itemTransmogrified->GetSpecialPrice(); + } + } + + // trusting the client, if it got here it has to have enough money + // ... unless client was modified + if (cost) // 0 cost if reverting look + player->ModifyMoney(-cost); +} + +void WorldSession::SendReforgeResult(bool success) +{ + WorldPacket data(SMSG_REFORGE_RESULT, 1); + data.WriteBit(success); + data.FlushBits(); + SendPacket(&data); +} + +void WorldSession::HandleReforgeItemOpcode(WorldPacket& recvData) +{ + uint32 slot, reforgeEntry; + ObjectGuid guid; + uint32 bag; + Player* player = GetPlayer(); + + recvData >> reforgeEntry >> slot >> bag; + + guid[2] = recvData.ReadBit(); + guid[6] = recvData.ReadBit(); + guid[3] = recvData.ReadBit(); + guid[4] = recvData.ReadBit(); + guid[1] = recvData.ReadBit(); + guid[0] = recvData.ReadBit(); + guid[7] = recvData.ReadBit(); + guid[5] = recvData.ReadBit(); + + recvData.ReadByteSeq(guid[2]); + recvData.ReadByteSeq(guid[3]); + recvData.ReadByteSeq(guid[6]); + recvData.ReadByteSeq(guid[4]); + recvData.ReadByteSeq(guid[1]); + recvData.ReadByteSeq(guid[0]); + recvData.ReadByteSeq(guid[7]); + recvData.ReadByteSeq(guid[5]); + + if (!player->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_REFORGER)) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleReforgeItemOpcode - Unit (GUID: %u) not found or player can't interact with it.", GUID_LOPART(guid)); + SendReforgeResult(false); + return; + } + + Item* item = player->GetItemByPos(bag, slot); + + if (!item) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleReforgeItemOpcode - Player (Guid: %u Name: %s) tried to reforge an invalid/non-existant item.", player->GetGUIDLow(), player->GetName().c_str()); + SendReforgeResult(false); + return; + } + + if (!reforgeEntry) + { + // Reset the item + if (item->IsEquipped()) + player->ApplyReforgeEnchantment(item, false); + item->ClearEnchantment(REFORGE_ENCHANTMENT_SLOT); + SendReforgeResult(true); + return; + } + + ItemReforgeEntry const* stats = sItemReforgeStore.LookupEntry(reforgeEntry); + if (!stats) + { + sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: HandleReforgeItemOpcode - Player (Guid: %u Name: %s) tried to reforge an item with invalid reforge entry (%u).", player->GetGUIDLow(), player->GetName().c_str(), reforgeEntry); + SendReforgeResult(false); + return; + } + + if (!item->GetReforgableStat(ItemModType(stats->SourceStat)) || item->GetReforgableStat(ItemModType(stats->FinalStat))) // Cheating, you cant reforge to a stat that the item already has, nor reforge from a stat that the item does not have + { + SendReforgeResult(false); + return; + } + + if (!player->HasEnoughMoney(uint64(item->GetSpecialPrice()))) // cheating + { + SendReforgeResult(false); + return; + } + + player->ModifyMoney(-int64(item->GetSpecialPrice())); + + item->SetEnchantment(REFORGE_ENCHANTMENT_SLOT, reforgeEntry, 0, 0); + + SendReforgeResult(true); + + if (item->IsEquipped()) + player->ApplyReforgeEnchantment(item, true); +} |