Base armor value from WDB data includes the ArmorDamageModifier. Items of same item level, required level, armor type and equiptment slot should have (nearly) the same armor values. See following links for further proofs: http://pastebin.com/fqNJLYj4 http://www.wowhead.com/item=50466#comments:id=889035:reply=444786 The shown item should have a base armor value of 177 as similar items and a bonus armor value of 560 which equals 737 - like in DB atm.
1445 lines
50 KiB
C++
1445 lines
50 KiB
C++
/*
|
|
* Copyright (C) 2008-2013 TrinityCore <http://www.trinitycore.org/>
|
|
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU General Public License as published by the
|
|
* Free Software Foundation; either version 2 of the License, or (at your
|
|
* option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along
|
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "Common.h"
|
|
#include "WorldPacket.h"
|
|
#include "WorldSession.h"
|
|
#include "Opcodes.h"
|
|
#include "Log.h"
|
|
#include "ObjectMgr.h"
|
|
#include "Player.h"
|
|
#include "Item.h"
|
|
#include "UpdateData.h"
|
|
#include "ObjectAccessor.h"
|
|
#include "SpellInfo.h"
|
|
|
|
void WorldSession::HandleSplitItemOpcode(WorldPacket& recvData)
|
|
{
|
|
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_SPLIT_ITEM");
|
|
uint8 srcbag, srcslot, dstbag, dstslot;
|
|
uint32 count;
|
|
|
|
recvData >> srcbag >> srcslot >> dstbag >> dstslot >> count;
|
|
//sLog->outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u, count = %u", srcbag, srcslot, dstbag, dstslot, count);
|
|
|
|
uint16 src = ((srcbag << 8) | srcslot);
|
|
uint16 dst = ((dstbag << 8) | dstslot);
|
|
|
|
if (src == dst)
|
|
return;
|
|
|
|
if (count == 0)
|
|
return; //check count - if zero it's fake packet
|
|
|
|
if (!_player->IsValidPos(srcbag, srcslot, true))
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
if (!_player->IsValidPos(dstbag, dstslot, false)) // can be autostore pos
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
_player->SplitItem(src, dst, count);
|
|
}
|
|
|
|
void WorldSession::HandleSwapInvItemOpcode(WorldPacket& recvData)
|
|
{
|
|
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_SWAP_INV_ITEM");
|
|
uint8 srcslot, dstslot;
|
|
|
|
recvData >> dstslot >> srcslot;
|
|
//sLog->outDebug("STORAGE: receive srcslot = %u, dstslot = %u", srcslot, dstslot);
|
|
|
|
// prevent attempt swap same item to current position generated by client at special checting sequence
|
|
if (srcslot == dstslot)
|
|
return;
|
|
|
|
if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, srcslot, true))
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
if (!_player->IsValidPos(INVENTORY_SLOT_BAG_0, dstslot, true))
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot);
|
|
uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot);
|
|
|
|
_player->SwapItem(src, dst);
|
|
}
|
|
|
|
void WorldSession::HandleAutoEquipItemSlotOpcode(WorldPacket& recvData)
|
|
{
|
|
uint64 itemguid;
|
|
uint8 dstslot;
|
|
recvData >> itemguid >> dstslot;
|
|
|
|
// cheating attempt, client should never send opcode in that case
|
|
if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, dstslot))
|
|
return;
|
|
|
|
Item* item = _player->GetItemByGuid(itemguid);
|
|
uint16 dstpos = dstslot | (INVENTORY_SLOT_BAG_0 << 8);
|
|
|
|
if (!item || item->GetPos() == dstpos)
|
|
return;
|
|
|
|
_player->SwapItem(item->GetPos(), dstpos);
|
|
}
|
|
|
|
void WorldSession::HandleSwapItem(WorldPacket& recvData)
|
|
{
|
|
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_SWAP_ITEM");
|
|
uint8 dstbag, dstslot, srcbag, srcslot;
|
|
|
|
recvData >> dstbag >> dstslot >> srcbag >> srcslot;
|
|
//sLog->outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u, dstslot = %u", srcbag, srcslot, dstbag, dstslot);
|
|
|
|
uint16 src = ((srcbag << 8) | srcslot);
|
|
uint16 dst = ((dstbag << 8) | dstslot);
|
|
|
|
// prevent attempt swap same item to current position generated by client at special checting sequence
|
|
if (src == dst)
|
|
return;
|
|
|
|
if (!_player->IsValidPos(srcbag, srcslot, true))
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
if (!_player->IsValidPos(dstbag, dstslot, true))
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
_player->SwapItem(src, dst);
|
|
}
|
|
|
|
void WorldSession::HandleAutoEquipItemOpcode(WorldPacket& recvData)
|
|
{
|
|
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_AUTOEQUIP_ITEM");
|
|
uint8 srcbag, srcslot;
|
|
|
|
recvData >> srcbag >> srcslot;
|
|
//sLog->outDebug("STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
|
|
|
|
Item* pSrcItem = _player->GetItemByPos(srcbag, srcslot);
|
|
if (!pSrcItem)
|
|
return; // only at cheat
|
|
|
|
uint16 dest;
|
|
InventoryResult msg = _player->CanEquipItem(NULL_SLOT, dest, pSrcItem, !pSrcItem->IsBag());
|
|
if (msg != EQUIP_ERR_OK)
|
|
{
|
|
_player->SendEquipError(msg, pSrcItem, NULL);
|
|
return;
|
|
}
|
|
|
|
uint16 src = pSrcItem->GetPos();
|
|
if (dest == src) // prevent equip in same slot, only at cheat
|
|
return;
|
|
|
|
Item* pDstItem = _player->GetItemByPos(dest);
|
|
if (!pDstItem) // empty slot, simple case
|
|
{
|
|
_player->RemoveItem(srcbag, srcslot, true);
|
|
_player->EquipItem(dest, pSrcItem, true);
|
|
_player->AutoUnequipOffhandIfNeed();
|
|
}
|
|
else // have currently equipped item, not simple case
|
|
{
|
|
uint8 dstbag = pDstItem->GetBagSlot();
|
|
uint8 dstslot = pDstItem->GetSlot();
|
|
|
|
msg = _player->CanUnequipItem(dest, !pSrcItem->IsBag());
|
|
if (msg != EQUIP_ERR_OK)
|
|
{
|
|
_player->SendEquipError(msg, pDstItem, NULL);
|
|
return;
|
|
}
|
|
|
|
// check dest->src move possibility
|
|
ItemPosCountVec sSrc;
|
|
uint16 eSrc = 0;
|
|
if (_player->IsInventoryPos(src))
|
|
{
|
|
msg = _player->CanStoreItem(srcbag, srcslot, sSrc, pDstItem, true);
|
|
if (msg != EQUIP_ERR_OK)
|
|
msg = _player->CanStoreItem(srcbag, NULL_SLOT, sSrc, pDstItem, true);
|
|
if (msg != EQUIP_ERR_OK)
|
|
msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, sSrc, pDstItem, true);
|
|
}
|
|
else if (_player->IsBankPos(src))
|
|
{
|
|
msg = _player->CanBankItem(srcbag, srcslot, sSrc, pDstItem, true);
|
|
if (msg != EQUIP_ERR_OK)
|
|
msg = _player->CanBankItem(srcbag, NULL_SLOT, sSrc, pDstItem, true);
|
|
if (msg != EQUIP_ERR_OK)
|
|
msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, sSrc, pDstItem, true);
|
|
}
|
|
else if (_player->IsEquipmentPos(src))
|
|
{
|
|
msg = _player->CanEquipItem(srcslot, eSrc, pDstItem, true);
|
|
if (msg == EQUIP_ERR_OK)
|
|
msg = _player->CanUnequipItem(eSrc, true);
|
|
}
|
|
|
|
if (msg != EQUIP_ERR_OK)
|
|
{
|
|
_player->SendEquipError(msg, pDstItem, pSrcItem);
|
|
return;
|
|
}
|
|
|
|
// now do moves, remove...
|
|
_player->RemoveItem(dstbag, dstslot, false);
|
|
_player->RemoveItem(srcbag, srcslot, false);
|
|
|
|
// add to dest
|
|
_player->EquipItem(dest, pSrcItem, true);
|
|
|
|
// add to src
|
|
if (_player->IsInventoryPos(src))
|
|
_player->StoreItem(sSrc, pDstItem, true);
|
|
else if (_player->IsBankPos(src))
|
|
_player->BankItem(sSrc, pDstItem, true);
|
|
else if (_player->IsEquipmentPos(src))
|
|
_player->EquipItem(eSrc, pDstItem, true);
|
|
|
|
_player->AutoUnequipOffhandIfNeed();
|
|
}
|
|
}
|
|
|
|
void WorldSession::HandleDestroyItemOpcode(WorldPacket& recvData)
|
|
{
|
|
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_DESTROYITEM");
|
|
uint8 bag, slot, count, data1, data2, data3;
|
|
|
|
recvData >> bag >> slot >> count >> data1 >> data2 >> data3;
|
|
//sLog->outDebug("STORAGE: receive bag = %u, slot = %u, count = %u", bag, slot, count);
|
|
|
|
uint16 pos = (bag << 8) | slot;
|
|
|
|
// prevent drop unequipable items (in combat, for example) and non-empty bags
|
|
if (_player->IsEquipmentPos(pos) || _player->IsBagPos(pos))
|
|
{
|
|
InventoryResult msg = _player->CanUnequipItem(pos, false);
|
|
if (msg != EQUIP_ERR_OK)
|
|
{
|
|
_player->SendEquipError(msg, _player->GetItemByPos(pos), NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
Item* pItem = _player->GetItemByPos(bag, slot);
|
|
if (!pItem)
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
if (pItem->GetTemplate()->Flags & ITEM_PROTO_FLAG_INDESTRUCTIBLE)
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_CANT_DROP_SOULBOUND, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
if (count)
|
|
{
|
|
uint32 i_count = count;
|
|
_player->DestroyItemCount(pItem, i_count, true);
|
|
}
|
|
else
|
|
_player->DestroyItem(bag, slot, true);
|
|
}
|
|
|
|
// Only _static_ data send in this packet !!!
|
|
void WorldSession::HandleItemQuerySingleOpcode(WorldPacket& recvData)
|
|
{
|
|
//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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
// resistances (7)
|
|
data << pProto->Armor;
|
|
data << pProto->HolyRes;
|
|
data << pProto->FireRes;
|
|
data << pProto->NatureRes;
|
|
data << pProto->FrostRes;
|
|
data << pProto->ShadowRes;
|
|
data << pProto->ArcaneRes;
|
|
|
|
data << pProto->Delay;
|
|
data << pProto->AmmoType;
|
|
data << pProto->RangedModRange;
|
|
|
|
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 << pProto->Spells[s].SpellId;
|
|
data << pProto->Spells[s].SpellTrigger;
|
|
data << uint32(-abs(pProto->Spells[s].SpellCharges));
|
|
|
|
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
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
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)
|
|
{
|
|
WorldPacket data;
|
|
|
|
InventoryResult msg = _player->CanUseItem(pItem);
|
|
if (msg == EQUIP_ERR_OK)
|
|
{
|
|
data.Initialize (SMSG_READ_ITEM_OK, 8);
|
|
sLog->outInfo(LOG_FILTER_NETWORKIO, "STORAGE: Item page sent");
|
|
}
|
|
else
|
|
{
|
|
data.Initialize(SMSG_READ_ITEM_FAILED, 8);
|
|
sLog->outInfo(LOG_FILTER_NETWORKIO, "STORAGE: Unable to read item");
|
|
_player->SendEquipError(msg, pItem, NULL);
|
|
}
|
|
data << pItem->GetGUID();
|
|
SendPacket(&data);
|
|
}
|
|
else
|
|
_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");
|
|
uint64 vendorguid, itemguid;
|
|
uint32 count;
|
|
|
|
recvData >> vendorguid >> itemguid >> count;
|
|
|
|
if (!itemguid)
|
|
return;
|
|
|
|
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
|
|
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);
|
|
return;
|
|
}
|
|
|
|
// remove fake death
|
|
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
|
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
|
|
|
Item* pItem = _player->GetItemByGuid(itemguid);
|
|
if (pItem)
|
|
{
|
|
// prevent sell not owner item
|
|
if (_player->GetGUID() != pItem->GetOwnerGUID())
|
|
{
|
|
_player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0);
|
|
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);
|
|
return;
|
|
}
|
|
|
|
// prevent sell currently looted item
|
|
if (_player->GetLootGUID() == pItem->GetGUID())
|
|
{
|
|
_player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0);
|
|
return;
|
|
}
|
|
|
|
// prevent selling item for sellprice when the item is still refundable
|
|
// this probably happens when right clicking a refundable item, the client sends both
|
|
// CMSG_SELL_ITEM and CMSG_REFUND_ITEM (unverified)
|
|
if (pItem->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAG_REFUNDABLE))
|
|
return; // Therefore, no feedback to client
|
|
|
|
// special case at auto sell (sell all)
|
|
if (count == 0)
|
|
count = pItem->GetCount();
|
|
else
|
|
{
|
|
// 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);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ItemTemplate const* pProto = pItem->GetTemplate();
|
|
if (pProto)
|
|
{
|
|
if (pProto->SellPrice > 0)
|
|
{
|
|
if (count < pItem->GetCount()) // need split items
|
|
{
|
|
Item* pNewItem = pItem->CloneItem(count, _player);
|
|
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);
|
|
return;
|
|
}
|
|
|
|
pItem->SetCount(pItem->GetCount() - count);
|
|
_player->ItemRemovedQuestCheck(pItem->GetEntry(), count);
|
|
if (_player->IsInWorld())
|
|
pItem->SendUpdateToPlayer(_player);
|
|
pItem->SetState(ITEM_CHANGED, _player);
|
|
|
|
_player->AddItemToBuyBackSlot(pNewItem);
|
|
if (_player->IsInWorld())
|
|
pNewItem->SendUpdateToPlayer(_player);
|
|
}
|
|
else
|
|
{
|
|
_player->ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
|
|
_player->RemoveItem(pItem->GetBagSlot(), pItem->GetSlot(), true);
|
|
pItem->RemoveFromUpdateQueueOf(_player);
|
|
_player->AddItemToBuyBackSlot(pItem);
|
|
}
|
|
|
|
uint32 money = pProto->SellPrice * count;
|
|
_player->ModifyMoney(money);
|
|
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_VENDORS, money);
|
|
}
|
|
else
|
|
_player->SendSellError(SELL_ERR_CANT_SELL_ITEM, creature, itemguid, 0);
|
|
return;
|
|
}
|
|
}
|
|
_player->SendSellError(SELL_ERR_CANT_FIND_ITEM, creature, itemguid, 0);
|
|
return;
|
|
}
|
|
|
|
void WorldSession::HandleBuybackItem(WorldPacket& recvData)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_BUYBACK_ITEM");
|
|
uint64 vendorguid;
|
|
uint32 slot;
|
|
|
|
recvData >> vendorguid >> slot;
|
|
|
|
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid, UNIT_NPC_FLAG_VENDOR);
|
|
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);
|
|
return;
|
|
}
|
|
|
|
// remove fake death
|
|
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
|
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
|
|
|
Item* pItem = _player->GetItemFromBuyBackSlot(slot);
|
|
if (pItem)
|
|
{
|
|
uint32 price = _player->GetUInt32Value(PLAYER_FIELD_BUYBACK_PRICE_1 + slot - BUYBACK_SLOT_START);
|
|
if (!_player->HasEnoughMoney(price))
|
|
{
|
|
_player->SendBuyError(BUY_ERR_NOT_ENOUGHT_MONEY, creature, pItem->GetEntry(), 0);
|
|
return;
|
|
}
|
|
|
|
ItemPosCountVec dest;
|
|
InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
|
|
if (msg == EQUIP_ERR_OK)
|
|
{
|
|
_player->ModifyMoney(-(int32)price);
|
|
_player->RemoveItemFromBuyBackSlot(slot, false);
|
|
_player->ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount());
|
|
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_RECEIVE_EPIC_ITEM, pItem->GetEntry(), pItem->GetCount());
|
|
_player->StoreItem(dest, pItem, true);
|
|
}
|
|
else
|
|
_player->SendEquipError(msg, pItem, NULL);
|
|
return;
|
|
}
|
|
else
|
|
_player->SendBuyError(BUY_ERR_CANT_FIND_ITEM, creature, 0, 0);
|
|
}
|
|
|
|
void WorldSession::HandleBuyItemInSlotOpcode(WorldPacket& recvData)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_BUY_ITEM_IN_SLOT");
|
|
uint64 vendorguid, bagguid;
|
|
uint32 item, slot, count;
|
|
uint8 bagslot;
|
|
|
|
recvData >> vendorguid >> item >> slot >> bagguid >> bagslot >> count;
|
|
|
|
// client expects count starting at 1, and we send vendorslot+1 to client already
|
|
if (slot > 0)
|
|
--slot;
|
|
else
|
|
return; // cheating
|
|
|
|
uint8 bag = NULL_BAG; // init for case invalid bagGUID
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// bag not found, cheating?
|
|
if (bag == NULL_BAG)
|
|
return;
|
|
|
|
GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, bag, bagslot);
|
|
}
|
|
|
|
void WorldSession::HandleBuyItemOpcode(WorldPacket& recvData)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Received CMSG_BUY_ITEM");
|
|
uint64 vendorguid;
|
|
uint32 item, slot, count;
|
|
uint8 unk1;
|
|
|
|
recvData >> vendorguid >> item >> slot >> count >> unk1;
|
|
|
|
// client expects count starting at 1, and we send vendorslot+1 to client already
|
|
if (slot > 0)
|
|
--slot;
|
|
else
|
|
return; // cheating
|
|
|
|
GetPlayer()->BuyItemFromVendorSlot(vendorguid, slot, item, count, NULL_BAG, NULL_SLOT);
|
|
}
|
|
|
|
void WorldSession::HandleListInventoryOpcode(WorldPacket& recvData)
|
|
{
|
|
uint64 guid;
|
|
|
|
recvData >> guid;
|
|
|
|
if (!GetPlayer()->isAlive())
|
|
return;
|
|
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Recvd CMSG_LIST_INVENTORY");
|
|
|
|
SendListInventory(guid);
|
|
}
|
|
|
|
void WorldSession::SendListInventory(uint64 vendorGuid)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: Sent SMSG_LIST_INVENTORY");
|
|
|
|
Creature* vendor = GetPlayer()->GetNPCIfCanInteractWith(vendorGuid, UNIT_NPC_FLAG_VENDOR);
|
|
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);
|
|
return;
|
|
}
|
|
|
|
// remove fake death
|
|
if (GetPlayer()->HasUnitState(UNIT_STATE_DIED))
|
|
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
|
|
|
|
// Stop the npc if moving
|
|
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);
|
|
|
|
size_t countPos = data.wpos();
|
|
data << uint8(count);
|
|
|
|
float discountMod = _player->GetReputationPriceDiscount(vendor);
|
|
|
|
for (uint8 slot = 0; slot < itemCount; ++slot)
|
|
{
|
|
if (VendorItem const* item = items->GetItem(slot))
|
|
{
|
|
if (ItemTemplate const* itemTemplate = sObjectMgr->GetItemTemplate(item->item))
|
|
{
|
|
if (!(itemTemplate->AllowableClass & _player->getClassMask()) && itemTemplate->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster())
|
|
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)))
|
|
continue;
|
|
|
|
// Items sold out are not displayed in list
|
|
uint32 leftInStock = !item->maxcount ? 0xFFFFFFFF : vendor->GetVendorItemCurrentCount(item);
|
|
if (!_player->isGameMaster() && !leftInStock)
|
|
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;
|
|
}
|
|
|
|
++count;
|
|
|
|
// reputation discount
|
|
int32 price = item->IsGoldRequired(itemTemplate) ? uint32(floor(itemTemplate->BuyPrice * discountMod)) : 0;
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (count == 0)
|
|
{
|
|
data << uint8(0);
|
|
SendPacket(&data);
|
|
return;
|
|
}
|
|
|
|
data.put<uint8>(countPos, count);
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::HandleAutoStoreBagItemOpcode(WorldPacket& recvData)
|
|
{
|
|
//sLog->outDebug(LOG_FILTER_PACKETIO, "WORLD: CMSG_AUTOSTORE_BAG_ITEM");
|
|
uint8 srcbag, srcslot, dstbag;
|
|
|
|
recvData >> srcbag >> srcslot >> dstbag;
|
|
//sLog->outDebug("STORAGE: receive srcbag = %u, srcslot = %u, dstbag = %u", srcbag, srcslot, dstbag);
|
|
|
|
Item* pItem = _player->GetItemByPos(srcbag, srcslot);
|
|
if (!pItem)
|
|
return;
|
|
|
|
if (!_player->IsValidPos(dstbag, NULL_SLOT, false)) // can be autostore pos
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL);
|
|
return;
|
|
}
|
|
|
|
uint16 src = pItem->GetPos();
|
|
|
|
// check unequip potability for equipped items and bank bags
|
|
if (_player->IsEquipmentPos (src) || _player->IsBagPos (src))
|
|
{
|
|
InventoryResult msg = _player->CanUnequipItem(src, !_player->IsBagPos (src));
|
|
if (msg != EQUIP_ERR_OK)
|
|
{
|
|
_player->SendEquipError(msg, pItem, NULL);
|
|
return;
|
|
}
|
|
}
|
|
|
|
ItemPosCountVec dest;
|
|
InventoryResult msg = _player->CanStoreItem(dstbag, NULL_SLOT, dest, pItem, false);
|
|
if (msg != EQUIP_ERR_OK)
|
|
{
|
|
_player->SendEquipError(msg, pItem, NULL);
|
|
return;
|
|
}
|
|
|
|
// no-op: placed in same slot
|
|
if (dest.size() == 1 && dest[0].pos == src)
|
|
{
|
|
// just remove grey item state
|
|
_player->SendEquipError(EQUIP_ERR_NONE, pItem, NULL);
|
|
return;
|
|
}
|
|
|
|
_player->RemoveItem(srcbag, srcslot, true);
|
|
_player->StoreItem(dest, pItem, true);
|
|
}
|
|
|
|
void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& recvPacket)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_BUY_BANK_SLOT");
|
|
|
|
uint64 guid;
|
|
recvPacket >> guid;
|
|
|
|
// cheating protection
|
|
/* not critical if "cheated", and check skip allow by slots in bank windows open by .bank command.
|
|
Creature* creature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_BANKER);
|
|
if (!creature)
|
|
{
|
|
sLog->outDebug("WORLD: HandleBuyBankSlotOpcode - Unit (GUID: %u) not found or you can't interact with him.", uint32(GUID_LOPART(guid)));
|
|
return;
|
|
}
|
|
*/
|
|
|
|
uint32 slot = _player->GetBankBagSlotCount();
|
|
|
|
// next slot
|
|
++slot;
|
|
|
|
sLog->outInfo(LOG_FILTER_NETWORKIO, "PLAYER: Buy bank bag slot, slot number = %u", slot);
|
|
|
|
BankBagSlotPricesEntry const* slotEntry = sBankBagSlotPricesStore.LookupEntry(slot);
|
|
|
|
WorldPacket data(SMSG_BUY_BANK_SLOT_RESULT, 4);
|
|
|
|
if (!slotEntry)
|
|
{
|
|
data << uint32(ERR_BANKSLOT_FAILED_TOO_MANY);
|
|
SendPacket(&data);
|
|
return;
|
|
}
|
|
|
|
uint32 price = slotEntry->price;
|
|
|
|
if (!_player->HasEnoughMoney(price))
|
|
{
|
|
data << uint32(ERR_BANKSLOT_INSUFFICIENT_FUNDS);
|
|
SendPacket(&data);
|
|
return;
|
|
}
|
|
|
|
_player->SetBankBagSlotCount(slot);
|
|
_player->ModifyMoney(-int32(price));
|
|
|
|
data << uint32(ERR_BANKSLOT_OK);
|
|
SendPacket(&data);
|
|
|
|
_player->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT);
|
|
}
|
|
|
|
void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOBANK_ITEM");
|
|
uint8 srcbag, srcslot;
|
|
|
|
recvPacket >> srcbag >> srcslot;
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
|
|
|
|
Item* pItem = _player->GetItemByPos(srcbag, srcslot);
|
|
if (!pItem)
|
|
return;
|
|
|
|
ItemPosCountVec dest;
|
|
InventoryResult msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
|
|
if (msg != EQUIP_ERR_OK)
|
|
{
|
|
_player->SendEquipError(msg, pItem, NULL);
|
|
return;
|
|
}
|
|
|
|
if (dest.size() == 1 && dest[0].pos == pItem->GetPos())
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_NONE, pItem, NULL);
|
|
return;
|
|
}
|
|
|
|
_player->RemoveItem(srcbag, srcslot, true);
|
|
_player->ItemRemovedQuestCheck(pItem->GetEntry(), pItem->GetCount());
|
|
_player->BankItem(dest, pItem, true);
|
|
}
|
|
|
|
void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_AUTOSTORE_BANK_ITEM");
|
|
uint8 srcbag, srcslot;
|
|
|
|
recvPacket >> srcbag >> srcslot;
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "STORAGE: receive srcbag = %u, srcslot = %u", srcbag, srcslot);
|
|
|
|
Item* pItem = _player->GetItemByPos(srcbag, srcslot);
|
|
if (!pItem)
|
|
return;
|
|
|
|
if (_player->IsBankPos(srcbag, srcslot)) // moving from bank to inventory
|
|
{
|
|
ItemPosCountVec dest;
|
|
InventoryResult msg = _player->CanStoreItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
|
|
if (msg != EQUIP_ERR_OK)
|
|
{
|
|
_player->SendEquipError(msg, pItem, NULL);
|
|
return;
|
|
}
|
|
|
|
_player->RemoveItem(srcbag, srcslot, true);
|
|
_player->StoreItem(dest, pItem, true);
|
|
_player->ItemAddedQuestCheck(pItem->GetEntry(), pItem->GetCount());
|
|
}
|
|
else // moving from inventory to bank
|
|
{
|
|
ItemPosCountVec dest;
|
|
InventoryResult msg = _player->CanBankItem(NULL_BAG, NULL_SLOT, dest, pItem, false);
|
|
if (msg != EQUIP_ERR_OK)
|
|
{
|
|
_player->SendEquipError(msg, pItem, NULL);
|
|
return;
|
|
}
|
|
|
|
_player->RemoveItem(srcbag, srcslot, true);
|
|
_player->BankItem(dest, pItem, true);
|
|
}
|
|
}
|
|
|
|
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);
|
|
data << uint32(ItemID);
|
|
data << uint32(SpellID);
|
|
data << uint8(0);
|
|
SendPacket(&data);
|
|
}
|
|
|
|
void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid, uint32 slot, uint32 Duration)
|
|
{
|
|
// last check 2.0.10
|
|
WorldPacket data(SMSG_ITEM_ENCHANT_TIME_UPDATE, (8+4+4+8));
|
|
data << uint64(Itemguid);
|
|
data << uint32(slot);
|
|
data << uint32(Duration);
|
|
data << uint64(Playerguid);
|
|
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");
|
|
|
|
uint8 gift_bag, gift_slot, item_bag, item_slot;
|
|
|
|
recvData >> gift_bag >> gift_slot; // paper
|
|
recvData >> item_bag >> item_slot; // item
|
|
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WRAP: receive gift_bag = %u, gift_slot = %u, item_bag = %u, item_slot = %u", gift_bag, gift_slot, item_bag, item_slot);
|
|
|
|
Item* gift = _player->GetItemByPos(gift_bag, gift_slot);
|
|
if (!gift)
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL);
|
|
return;
|
|
}
|
|
|
|
if (!(gift->GetTemplate()->Flags & ITEM_PROTO_FLAG_WRAPPER)) // cheating: non-wrapper wrapper
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, gift, NULL);
|
|
return;
|
|
}
|
|
|
|
Item* item = _player->GetItemByPos(item_bag, item_slot);
|
|
|
|
if (!item)
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, item, NULL);
|
|
return;
|
|
}
|
|
|
|
if (item == gift) // not possable with pacjket from real client
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_WRAPPED_CANT_BE_WRAPPED, item, NULL);
|
|
return;
|
|
}
|
|
|
|
if (item->IsEquipped())
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_EQUIPPED_CANT_BE_WRAPPED, 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);
|
|
return;
|
|
}
|
|
|
|
if (item->IsBag())
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_BAGS_CANT_BE_WRAPPED, item, NULL);
|
|
return;
|
|
}
|
|
|
|
if (item->IsSoulBound())
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_BOUND_CANT_BE_WRAPPED, item, NULL);
|
|
return;
|
|
}
|
|
|
|
if (item->GetMaxStackCount() != 1)
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_STACKABLE_CANT_BE_WRAPPED, item, NULL);
|
|
return;
|
|
}
|
|
|
|
// maybe not correct check (it is better than nothing)
|
|
if (item->GetTemplate()->MaxCount>0)
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_UNIQUE_CANT_BE_WRAPPED, item, NULL);
|
|
return;
|
|
}
|
|
|
|
SQLTransaction trans = CharacterDatabase.BeginTransaction();
|
|
|
|
PreparedStatement* stmt = CharacterDatabase.GetPreparedStatement(CHAR_INS_CHAR_GIFT);
|
|
stmt->setUInt32(0, GUID_LOPART(item->GetOwnerGUID()));
|
|
stmt->setUInt32(1, item->GetGUIDLow());
|
|
stmt->setUInt32(2, item->GetEntry());
|
|
stmt->setUInt32(3, item->GetUInt32Value(ITEM_FIELD_FLAGS));
|
|
trans->Append(stmt);
|
|
|
|
item->SetEntry(gift->GetEntry());
|
|
|
|
switch (item->GetEntry())
|
|
{
|
|
case 5042: item->SetEntry(5043); break;
|
|
case 5048: item->SetEntry(5044); break;
|
|
case 17303: item->SetEntry(17302); break;
|
|
case 17304: item->SetEntry(17305); break;
|
|
case 17307: item->SetEntry(17308); break;
|
|
case 21830: item->SetEntry(21831); break;
|
|
}
|
|
item->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, _player->GetGUID());
|
|
item->SetUInt32Value(ITEM_FIELD_FLAGS, ITEM_FLAG_WRAPPED);
|
|
item->SetState(ITEM_CHANGED, _player);
|
|
|
|
if (item->GetState() == ITEM_NEW) // save new item, to have alway for `character_gifts` record in `item_instance`
|
|
{
|
|
// after save it will be impossible to remove the item from the queue
|
|
item->RemoveFromUpdateQueueOf(_player);
|
|
item->SaveToDB(trans); // item gave inventory record unchanged and can be save standalone
|
|
}
|
|
CharacterDatabase.CommitTransaction(trans);
|
|
|
|
uint32 count = 1;
|
|
_player->DestroyItemCount(gift, count, true);
|
|
}
|
|
|
|
void WorldSession::HandleSocketOpcode(WorldPacket& recvData)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_SOCKET_GEMS");
|
|
|
|
uint64 item_guid;
|
|
uint64 gem_guids[MAX_GEM_SOCKETS];
|
|
|
|
recvData >> item_guid;
|
|
if (!item_guid)
|
|
return;
|
|
|
|
for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
|
|
recvData >> gem_guids[i];
|
|
|
|
//cheat -> tried to socket same gem multiple times
|
|
if ((gem_guids[0] && (gem_guids[0] == gem_guids[1] || gem_guids[0] == gem_guids[2])) ||
|
|
(gem_guids[1] && (gem_guids[1] == gem_guids[2])))
|
|
return;
|
|
|
|
Item* itemTarget = _player->GetItemByGuid(item_guid);
|
|
if (!itemTarget) //missing item to socket
|
|
return;
|
|
|
|
ItemTemplate const* itemProto = itemTarget->GetTemplate();
|
|
if (!itemProto)
|
|
return;
|
|
|
|
//this slot is excepted when applying / removing meta gem bonus
|
|
uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : uint8(NULL_SLOT);
|
|
|
|
Item* Gems[MAX_GEM_SOCKETS];
|
|
for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
|
|
Gems[i] = gem_guids[i] ? _player->GetItemByGuid(gem_guids[i]) : NULL;
|
|
|
|
GemPropertiesEntry const* GemProps[MAX_GEM_SOCKETS];
|
|
for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //get geminfo from dbc storage
|
|
GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetTemplate()->GemProperties) : NULL;
|
|
|
|
for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //check for hack maybe
|
|
{
|
|
if (!GemProps[i])
|
|
continue;
|
|
|
|
// tried to put gem in socket where no socket exists (take care about prismatic sockets)
|
|
if (!itemProto->Socket[i].Color)
|
|
{
|
|
// no prismatic socket
|
|
if (!itemTarget->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT))
|
|
return;
|
|
|
|
// not first not-colored (not normaly used) socket
|
|
if (i != 0 && !itemProto->Socket[i-1].Color && (i+1 >= MAX_GEM_SOCKETS || itemProto->Socket[i+1].Color))
|
|
return;
|
|
|
|
// ok, this is first not colored socket for item with prismatic socket
|
|
}
|
|
|
|
// tried to put normal gem in meta socket
|
|
if (itemProto->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META)
|
|
return;
|
|
|
|
// tried to put meta gem in normal socket
|
|
if (itemProto->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META)
|
|
return;
|
|
}
|
|
|
|
uint32 GemEnchants[MAX_GEM_SOCKETS];
|
|
uint32 OldEnchants[MAX_GEM_SOCKETS];
|
|
for (int i = 0; i < MAX_GEM_SOCKETS; ++i) //get new and old enchantments
|
|
{
|
|
GemEnchants[i] = (GemProps[i]) ? GemProps[i]->spellitemenchantement : 0;
|
|
OldEnchants[i] = itemTarget->GetEnchantmentId(EnchantmentSlot(SOCK_ENCHANTMENT_SLOT+i));
|
|
}
|
|
|
|
// check unique-equipped conditions
|
|
for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
|
|
{
|
|
if (!Gems[i])
|
|
continue;
|
|
|
|
// continue check for case when attempt add 2 similar unique equipped gems in one item.
|
|
ItemTemplate const* iGemProto = Gems[i]->GetTemplate();
|
|
|
|
// unique item (for new and already placed bit removed enchantments
|
|
if (iGemProto->Flags & ITEM_PROTO_FLAG_UNIQUE_EQUIPPED)
|
|
{
|
|
for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
|
|
{
|
|
if (i == j) // skip self
|
|
continue;
|
|
|
|
if (Gems[j])
|
|
{
|
|
if (iGemProto->ItemId == Gems[j]->GetEntry())
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
|
|
return;
|
|
}
|
|
}
|
|
else if (OldEnchants[j])
|
|
{
|
|
if (SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]))
|
|
{
|
|
if (iGemProto->ItemId == enchantEntry->GemID)
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// unique limit type item
|
|
int32 limit_newcount = 0;
|
|
if (iGemProto->ItemLimitCategory)
|
|
{
|
|
if (ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(iGemProto->ItemLimitCategory))
|
|
{
|
|
// NOTE: limitEntry->mode is not checked because if item has limit then it is applied in equip case
|
|
for (int j = 0; j < MAX_GEM_SOCKETS; ++j)
|
|
{
|
|
if (Gems[j])
|
|
{
|
|
// new gem
|
|
if (iGemProto->ItemLimitCategory == Gems[j]->GetTemplate()->ItemLimitCategory)
|
|
++limit_newcount;
|
|
}
|
|
else if (OldEnchants[j])
|
|
{
|
|
// existing gem
|
|
if (SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(OldEnchants[j]))
|
|
if (ItemTemplate const* jProto = sObjectMgr->GetItemTemplate(enchantEntry->GemID))
|
|
if (iGemProto->ItemLimitCategory == jProto->ItemLimitCategory)
|
|
++limit_newcount;
|
|
}
|
|
}
|
|
|
|
if (limit_newcount > 0 && uint32(limit_newcount) > limitEntry->maxCount)
|
|
{
|
|
_player->SendEquipError(EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// for equipped item check all equipment for duplicate equipped gems
|
|
if (itemTarget->IsEquipped())
|
|
{
|
|
if (InventoryResult res = _player->CanEquipUniqueItem(Gems[i], slot, std::max(limit_newcount, 0)))
|
|
{
|
|
_player->SendEquipError(res, itemTarget, NULL);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SocketBonusActivated = itemTarget->GemsFitSockets(); //save state of socketbonus
|
|
_player->ToggleMetaGemsActive(slot, false); //turn off all metagems (except for the target item)
|
|
|
|
//if a meta gem is being equipped, all information has to be written to the item before testing if the conditions for the gem are met
|
|
|
|
//remove ALL enchants
|
|
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT + MAX_GEM_SOCKETS; ++enchant_slot)
|
|
_player->ApplyEnchantment(itemTarget, EnchantmentSlot(enchant_slot), false);
|
|
|
|
for (int i = 0; i < MAX_GEM_SOCKETS; ++i)
|
|
{
|
|
if (GemEnchants[i])
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
for (uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot)
|
|
_player->ApplyEnchantment(itemTarget, EnchantmentSlot(enchant_slot), true);
|
|
|
|
bool SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state
|
|
if (SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change...
|
|
{
|
|
_player->ApplyEnchantment(itemTarget, BONUS_ENCHANTMENT_SLOT, false);
|
|
itemTarget->SetEnchantment(BONUS_ENCHANTMENT_SLOT, (SocketBonusToBeActivated ? itemTarget->GetTemplate()->socketBonus : 0), 0, 0);
|
|
_player->ApplyEnchantment(itemTarget, BONUS_ENCHANTMENT_SLOT, true);
|
|
//it is not displayed, client has an inbuilt system to determine if the bonus is activated
|
|
}
|
|
|
|
_player->ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item)
|
|
|
|
_player->RemoveTradeableItem(itemTarget);
|
|
itemTarget->ClearSoulboundTradeable(_player); // clear tradeable flag
|
|
}
|
|
|
|
void WorldSession::HandleCancelTempEnchantmentOpcode(WorldPacket& recvData)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT");
|
|
|
|
uint32 eslot;
|
|
|
|
recvData >> eslot;
|
|
|
|
// apply only to equipped item
|
|
if (!Player::IsEquipmentPos(INVENTORY_SLOT_BAG_0, eslot))
|
|
return;
|
|
|
|
Item* item = GetPlayer()->GetItemByPos(INVENTORY_SLOT_BAG_0, eslot);
|
|
|
|
if (!item)
|
|
return;
|
|
|
|
if (!item->GetEnchantmentId(TEMP_ENCHANTMENT_SLOT))
|
|
return;
|
|
|
|
GetPlayer()->ApplyEnchantment(item, TEMP_ENCHANTMENT_SLOT, false);
|
|
item->ClearEnchantment(TEMP_ENCHANTMENT_SLOT);
|
|
}
|
|
|
|
void WorldSession::HandleItemRefundInfoRequest(WorldPacket& recvData)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_ITEM_REFUND_INFO");
|
|
|
|
uint64 guid;
|
|
recvData >> guid; // item guid
|
|
|
|
Item* item = _player->GetItemByGuid(guid);
|
|
if (!item)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "Item refund: item not found!");
|
|
return;
|
|
}
|
|
|
|
GetPlayer()->SendRefundInfo(item);
|
|
}
|
|
|
|
void WorldSession::HandleItemRefund(WorldPacket &recvData)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "WORLD: CMSG_ITEM_REFUND");
|
|
uint64 guid;
|
|
recvData >> guid; // item guid
|
|
|
|
Item* item = _player->GetItemByGuid(guid);
|
|
if (!item)
|
|
{
|
|
sLog->outDebug(LOG_FILTER_NETWORKIO, "Item refund: item not found!");
|
|
return;
|
|
}
|
|
|
|
GetPlayer()->RefundItem(item);
|
|
}
|
|
|
|
/**
|
|
* Handles the packet sent by the client when requesting information about item text.
|
|
*
|
|
* This function is called when player clicks on item which has some flag set
|
|
*/
|
|
void WorldSession::HandleItemTextQuery(WorldPacket& recvData )
|
|
{
|
|
uint64 itemGuid;
|
|
recvData >> itemGuid;
|
|
|
|
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
|
|
|
|
if (Item* item = _player->GetItemByGuid(itemGuid))
|
|
{
|
|
data << uint8(0); // has text
|
|
data << uint64(itemGuid); // item guid
|
|
data << item->GetText();
|
|
}
|
|
else
|
|
{
|
|
data << uint8(1); // no text
|
|
}
|
|
|
|
SendPacket(&data);
|
|
}
|