// Copyright (c) CypherCore All rights reserved. // Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information. using Framework.Constants; using Framework.Database; using Game.BattlePets; using Game.DataStorage; using Game.Entities; using Game.Networking; using Game.Networking.Packets; using System; using System.Collections.Generic; namespace Game { public partial class WorldSession { [WorldPacketHandler(ClientOpcodes.SplitItem, Processing = PacketProcessing.Inplace)] void HandleSplitItem(SplitItem splitItem) { if (splitItem.Inv.Items.Count != 0) { Log.outError(LogFilter.Network, "WORLD: HandleSplitItemOpcode - Invalid itemCount ({0})", splitItem.Inv.Items.Count); return; } ushort src = (ushort)((splitItem.FromPackSlot << 8) | splitItem.FromSlot); ushort dst = (ushort)((splitItem.ToPackSlot << 8) | splitItem.ToSlot); if (src == dst) return; if (splitItem.Quantity == 0) return; //check count - if zero it's fake packet if (!_player.IsValidPos(splitItem.FromPackSlot, splitItem.FromSlot, true)) { _player.SendEquipError(InventoryResult.ItemNotFound); return; } if (!_player.IsValidPos(splitItem.ToPackSlot, splitItem.ToSlot, false)) // can be autostore pos { _player.SendEquipError(InventoryResult.WrongSlot); return; } _player.SplitItem(src, dst, (uint)splitItem.Quantity); } [WorldPacketHandler(ClientOpcodes.SwapInvItem, Processing = PacketProcessing.Inplace)] void HandleSwapInvenotryItem(SwapInvItem swapInvItem) { if (swapInvItem.Inv.Items.Count != 2) { Log.outError(LogFilter.Network, "WORLD: HandleSwapInvItemOpcode - Invalid itemCount ({0})", swapInvItem.Inv.Items.Count); return; } // prevent attempt swap same item to current position generated by client at special checting sequence if (swapInvItem.Slot1 == swapInvItem.Slot2) return; if (!GetPlayer().IsValidPos(InventorySlots.Bag0, swapInvItem.Slot1, true)) { GetPlayer().SendEquipError(InventoryResult.ItemNotFound); return; } if (!GetPlayer().IsValidPos(InventorySlots.Bag0, swapInvItem.Slot2, true)) { GetPlayer().SendEquipError(InventoryResult.WrongSlot); return; } if (Player.IsBankPos(InventorySlots.Bag0, swapInvItem.Slot1) && !CanUseBank()) { Log.outDebug(LogFilter.Network, $"WORLD: HandleSwapInvItemOpcode - {_player.PlayerTalkClass.GetInteractionData().SourceGuid} not found or you can't interact with him."); return; } if (Player.IsBankPos(InventorySlots.Bag0, swapInvItem.Slot2) && !CanUseBank()) { Log.outDebug(LogFilter.Network, $"WORLD: HandleSwapInvItemOpcode - {_player.PlayerTalkClass.GetInteractionData().SourceGuid} not found or you can't interact with him."); return; } ushort src = (ushort)((InventorySlots.Bag0 << 8) | swapInvItem.Slot1); ushort dst = (ushort)((InventorySlots.Bag0 << 8) | swapInvItem.Slot2); GetPlayer().SwapItem(src, dst); } [WorldPacketHandler(ClientOpcodes.AutoEquipItemSlot, Processing = PacketProcessing.Inplace)] void HandleAutoEquipItemSlot(AutoEquipItemSlot packet) { // cheating attempt, client should never send opcode in that case if (packet.Inv.Items.Count != 1 || !Player.IsEquipmentPos(InventorySlots.Bag0, packet.ItemDstSlot)) return; Item item = GetPlayer().GetItemByGuid(packet.Item); ushort dstPos = (ushort)(packet.ItemDstSlot | (InventorySlots.Bag0 << 8)); ushort srcPos = (ushort)(packet.Inv.Items[0].Slot | (packet.Inv.Items[0].ContainerSlot << 8)); if (item == null || item.GetPos() != srcPos || srcPos == dstPos) return; GetPlayer().SwapItem(srcPos, dstPos); } [WorldPacketHandler(ClientOpcodes.SwapItem, Processing = PacketProcessing.Inplace)] void HandleSwapItem(SwapItem swapItem) { if (swapItem.Inv.Items.Count != 2) { Log.outError(LogFilter.Network, "WORLD: HandleSwapItem - Invalid itemCount ({0})", swapItem.Inv.Items.Count); return; } ushort src = (ushort)((swapItem.ContainerSlotA << 8) | swapItem.SlotA); ushort dst = (ushort)((swapItem.ContainerSlotB << 8) | swapItem.SlotB); var pl = GetPlayer(); // prevent attempt swap same item to current position generated by client at special checting sequence if (src == dst) return; if (!pl.IsValidPos(swapItem.ContainerSlotA, swapItem.SlotA, true)) { pl.SendEquipError(InventoryResult.ItemNotFound); return; } if (!pl.IsValidPos(swapItem.ContainerSlotB, swapItem.SlotB, true)) { pl.SendEquipError(InventoryResult.WrongSlot); return; } if (Player.IsBankPos(swapItem.ContainerSlotA, swapItem.SlotA) && !CanUseBank()) { Log.outDebug(LogFilter.Network, $"WORLD: HandleSwapInvItemOpcode - {_player.PlayerTalkClass.GetInteractionData().SourceGuid} not found or you can't interact with him."); return; } if (Player.IsBankPos(swapItem.ContainerSlotB, swapItem.SlotB) && !CanUseBank()) { Log.outDebug(LogFilter.Network, $"WORLD: HandleSwapInvItemOpcode - {_player.PlayerTalkClass.GetInteractionData().SourceGuid} not found or you can't interact with him."); return; } pl.SwapItem(src, dst); } [WorldPacketHandler(ClientOpcodes.AutoEquipItem, Processing = PacketProcessing.Inplace)] void HandleAutoEquipItem(AutoEquipItem autoEquipItem) { if (autoEquipItem.Inv.Items.Count != 1) { Log.outError(LogFilter.Network, "WORLD: HandleAutoEquipItem - Invalid itemCount ({0})", autoEquipItem.Inv.Items.Count); return; } var pl = GetPlayer(); Item srcItem = pl.GetItemByPos(autoEquipItem.PackSlot, autoEquipItem.Slot); if (srcItem == null) return; // only at cheat ushort dest; InventoryResult msg = pl.CanEquipItem(ItemConst.NullSlot, out dest, srcItem, !srcItem.IsBag()); if (msg != InventoryResult.Ok) { pl.SendEquipError(msg, srcItem); return; } ushort src = srcItem.GetPos(); if (dest == src) // prevent equip in same slot, only at cheat return; Item dstItem = pl.GetItemByPos(dest); if (dstItem == null) // empty slot, simple case { if (!srcItem.GetChildItem().IsEmpty()) { InventoryResult childEquipResult = _player.CanEquipChildItem(srcItem); if (childEquipResult != InventoryResult.Ok) { _player.SendEquipError(msg, srcItem); return; } } pl.RemoveItem(autoEquipItem.PackSlot, autoEquipItem.Slot, true); pl.EquipItem(dest, srcItem, true); if (!srcItem.GetChildItem().IsEmpty()) _player.EquipChildItem(autoEquipItem.PackSlot, autoEquipItem.Slot, srcItem); pl.AutoUnequipOffhandIfNeed(); } else // have currently equipped item, not simple case { byte dstbag = dstItem.GetBagSlot(); byte dstslot = dstItem.GetSlot(); msg = pl.CanUnequipItem(dest, !srcItem.IsBag()); if (msg != InventoryResult.Ok) { pl.SendEquipError(msg, dstItem); return; } if (!dstItem.HasItemFlag(ItemFieldFlags.Child)) { // check dest.src move possibility List sSrc = new(); ushort eSrc = 0; if (pl.IsInventoryPos(src)) { msg = pl.CanStoreItem(autoEquipItem.PackSlot, autoEquipItem.Slot, sSrc, dstItem, true); if (msg != InventoryResult.Ok) msg = pl.CanStoreItem(autoEquipItem.PackSlot, ItemConst.NullSlot, sSrc, dstItem, true); if (msg != InventoryResult.Ok) msg = pl.CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, sSrc, dstItem, true); } else if (Player.IsBankPos(src)) { msg = pl.CanBankItem(autoEquipItem.PackSlot, autoEquipItem.Slot, sSrc, dstItem, true); if (msg != InventoryResult.Ok) msg = pl.CanBankItem(autoEquipItem.PackSlot, ItemConst.NullSlot, sSrc, dstItem, true); if (msg != InventoryResult.Ok) msg = pl.CanBankItem(ItemConst.NullBag, ItemConst.NullSlot, sSrc, dstItem, true); } else if (Player.IsEquipmentPos(src)) { msg = pl.CanEquipItem(autoEquipItem.Slot, out eSrc, dstItem, true); if (msg == InventoryResult.Ok) msg = pl.CanUnequipItem(eSrc, true); } if (msg == InventoryResult.Ok && Player.IsEquipmentPos(dest) && !srcItem.GetChildItem().IsEmpty()) msg = _player.CanEquipChildItem(srcItem); if (msg != InventoryResult.Ok) { pl.SendEquipError(msg, dstItem, srcItem); return; } // now do moves, remove... pl.RemoveItem(dstbag, dstslot, false); pl.RemoveItem(autoEquipItem.PackSlot, autoEquipItem.Slot, false); // add to dest pl.EquipItem(dest, srcItem, true); // add to src if (pl.IsInventoryPos(src)) pl.StoreItem(sSrc, dstItem, true); else if (Player.IsBankPos(src)) pl.BankItem(sSrc, dstItem, true); else if (Player.IsEquipmentPos(src)) pl.EquipItem(eSrc, dstItem, true); if (Player.IsEquipmentPos(dest) && !srcItem.GetChildItem().IsEmpty()) _player.EquipChildItem(autoEquipItem.PackSlot, autoEquipItem.Slot, srcItem); } else { Item parentItem = _player.GetItemByGuid(dstItem.GetCreator()); if (parentItem) { if (Player.IsEquipmentPos(dest)) { _player.AutoUnequipChildItem(parentItem); // dest is now empty _player.SwapItem(src, dest); // src is now empty _player.SwapItem(parentItem.GetPos(), src); } } } pl.AutoUnequipOffhandIfNeed(); // if inventory item was moved, check if we can remove dependent auras, because they were not removed in Player::RemoveItem (update was set to false) // do this after swaps are done, we pass nullptr because both weapons could be swapped and none of them should be ignored if ((autoEquipItem.PackSlot == InventorySlots.Bag0 && autoEquipItem.Slot < InventorySlots.BagEnd) || (dstbag == InventorySlots.Bag0 && dstslot < InventorySlots.BagEnd)) pl.ApplyItemDependentAuras(null, false); } } [WorldPacketHandler(ClientOpcodes.DestroyItem, Processing = PacketProcessing.Inplace)] void HandleDestroyItem(DestroyItem destroyItem) { ushort pos = (ushort)((destroyItem.ContainerId << 8) | destroyItem.SlotNum); // 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 != InventoryResult.Ok) { _player.SendEquipError(msg, _player.GetItemByPos(pos)); return; } } Item pItem = _player.GetItemByPos(destroyItem.ContainerId, destroyItem.SlotNum); if (pItem == null) { _player.SendEquipError(InventoryResult.ItemNotFound); return; } if (pItem.GetTemplate().HasFlag(ItemFlags.NoUserDestroy)) { _player.SendEquipError(InventoryResult.DropBoundItem); return; } if (destroyItem.Count != 0) { uint i_count = destroyItem.Count; _player.DestroyItemCount(pItem, ref i_count, true); } else _player.DestroyItem(destroyItem.ContainerId, destroyItem.SlotNum, true); } [WorldPacketHandler(ClientOpcodes.ReadItem, Processing = PacketProcessing.Inplace)] void HandleReadItem(ReadItem readItem) { Item item = _player.GetItemByPos(readItem.PackSlot, readItem.Slot); if (item != null && item.GetTemplate().GetPageText() != 0) { InventoryResult msg = _player.CanUseItem(item); if (msg == InventoryResult.Ok) { ReadItemResultOK packet = new(); packet.Item = item.GetGUID(); SendPacket(packet); } else { // @todo: 6.x research new values /*WorldPackets.Item.ReadItemResultFailed packet; packet.Item = item.GetGUID(); packet.Subcode = ??; packet.Delay = ??; SendPacket(packet);*/ Log.outInfo(LogFilter.Network, "STORAGE: Unable to read item"); _player.SendEquipError(msg, item); } } else _player.SendEquipError(InventoryResult.ItemNotFound); } [WorldPacketHandler(ClientOpcodes.SellItem, Processing = PacketProcessing.Inplace)] void HandleSellItem(SellItem packet) { if (packet.ItemGUID.IsEmpty()) return; var pl = GetPlayer(); Creature creature = pl.GetNPCIfCanInteractWith(packet.VendorGUID, NPCFlags.Vendor, NPCFlags2.None); if (creature == null) { Log.outDebug(LogFilter.Network, "WORLD: HandleSellItemOpcode - {0} not found or you can not interact with him.", packet.VendorGUID.ToString()); pl.SendSellError(SellResult.CantFindVendor, null, packet.ItemGUID); return; } if (creature.GetCreatureTemplate().FlagsExtra.HasFlag(CreatureFlagsExtra.NoSellVendor)) { _player.SendSellError(SellResult.CantSellToThisMerchant, creature, packet.ItemGUID); return; } // remove fake death if (pl.HasUnitState(UnitState.Died)) pl.RemoveAurasByType(AuraType.FeignDeath); Item pItem = pl.GetItemByGuid(packet.ItemGUID); if (pItem != null) { // prevent sell not owner item if (pl.GetGUID() != pItem.GetOwnerGUID()) { pl.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); return; } // prevent sell non empty bag by drag-and-drop at vendor's item list if (pItem.IsNotEmptyBag()) { pl.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); return; } // prevent sell currently looted item if (pl.GetLootGUID() == pItem.GetGUID()) { pl.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); 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.IsRefundable()) return; // Therefore, no feedback to client // special case at auto sell (sell all) if (packet.Amount == 0) packet.Amount = pItem.GetCount(); else { // prevent sell more items that exist in stack (possible only not from client) if (packet.Amount > pItem.GetCount()) { pl.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); return; } } ItemTemplate pProto = pItem.GetTemplate(); if (pProto != null) { if (pProto.GetSellPrice() > 0) { ulong money = pProto.GetSellPrice() * packet.Amount; if (!_player.ModifyMoney((long)money)) // ensure player doesn't exceed gold limit { _player.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); return; } _player.UpdateCriteria(CriteriaType.MoneyEarnedFromSales, money); _player.UpdateCriteria(CriteriaType.SellItemsToVendors, 1); if (packet.Amount < pItem.GetCount()) // need split items { Item pNewItem = pItem.CloneItem(packet.Amount, pl); if (pNewItem == null) { Log.outError(LogFilter.Network, "WORLD: HandleSellItemOpcode - could not create clone of item {0}; count = {1}", pItem.GetEntry(), packet.Amount); pl.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); return; } pItem.SetCount(pItem.GetCount() - packet.Amount); pl.ItemRemovedQuestCheck(pItem.GetEntry(), packet.Amount); if (pl.IsInWorld) pItem.SendUpdateToPlayer(pl); pItem.SetState(ItemUpdateState.Changed, pl); pl.AddItemToBuyBackSlot(pNewItem); if (pl.IsInWorld) pNewItem.SendUpdateToPlayer(pl); } else { pl.RemoveItem(pItem.GetBagSlot(), pItem.GetSlot(), true); pl.ItemRemovedQuestCheck(pItem.GetEntry(), pItem.GetCount()); Item.RemoveItemFromUpdateQueueOf(pItem, pl); pl.AddItemToBuyBackSlot(pItem); } } else pl.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); return; } } pl.SendSellError(SellResult.CantSellItem, creature, packet.ItemGUID); return; } [WorldPacketHandler(ClientOpcodes.BuyBackItem, Processing = PacketProcessing.Inplace)] void HandleBuybackItem(BuyBackItem packet) { Creature creature = _player.GetNPCIfCanInteractWith(packet.VendorGUID, NPCFlags.Vendor, NPCFlags2.None); if (creature == null) { Log.outDebug(LogFilter.Network, "WORLD: HandleBuybackItem - {0} not found or you can not interact with him.", packet.VendorGUID.ToString()); _player.SendSellError(SellResult.CantFindVendor, null, ObjectGuid.Empty); return; } // remove fake death if (_player.HasUnitState(UnitState.Died)) _player.RemoveAurasByType(AuraType.FeignDeath); Item pItem = _player.GetItemFromBuyBackSlot(packet.Slot); if (pItem != null) { uint price = _player.m_activePlayerData.BuybackPrice[(int)(packet.Slot - InventorySlots.BuyBackStart)]; if (!_player.HasEnoughMoney(price)) { _player.SendBuyError(BuyResult.NotEnoughtMoney, creature, pItem.GetEntry()); return; } List dest = new(); InventoryResult msg = _player.CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, dest, pItem, false); if (msg == InventoryResult.Ok) { _player.ModifyMoney(-price); _player.RemoveItemFromBuyBackSlot(packet.Slot, false); _player.ItemAddedQuestCheck(pItem.GetEntry(), pItem.GetCount()); _player.StoreItem(dest, pItem, true); } else _player.SendEquipError(msg, pItem); return; } else _player.SendBuyError(BuyResult.CantFindItem, creature, 0); } [WorldPacketHandler(ClientOpcodes.BuyItem, Processing = PacketProcessing.Inplace)] void HandleBuyItem(BuyItem packet) { // client expects count starting at 1, and we send vendorslot+1 to client already if (packet.Muid > 0) --packet.Muid; else return; // cheating switch (packet.ItemType) { case ItemVendorType.Item: Item bagItem = GetPlayer().GetItemByGuid(packet.ContainerGUID); byte bag = ItemConst.NullBag; if (bagItem != null && bagItem.IsBag()) bag = bagItem.GetSlot(); else if (packet.ContainerGUID == GetPlayer().GetGUID()) // The client sends the player guid when trying to store an item in the default backpack bag = InventorySlots.Bag0; GetPlayer().BuyItemFromVendorSlot(packet.VendorGUID, packet.Muid, packet.Item.ItemID, (byte)packet.Quantity, bag, (byte)packet.Slot); break; case ItemVendorType.Currency: GetPlayer().BuyCurrencyFromVendorSlot(packet.VendorGUID, packet.Muid, packet.Item.ItemID, (byte)packet.Quantity); break; default: Log.outDebug(LogFilter.Network, "WORLD: received wrong itemType {0} in HandleBuyItem", packet.ItemType); break; } } [WorldPacketHandler(ClientOpcodes.AutoStoreBagItem, Processing = PacketProcessing.Inplace)] void HandleAutoStoreBagItem(AutoStoreBagItem packet) { if (!packet.Inv.Items.Empty()) { Log.outError(LogFilter.Network, "HandleAutoStoreBagItemOpcode - Invalid itemCount ({0})", packet.Inv.Items.Count); return; } Item item = GetPlayer().GetItemByPos(packet.ContainerSlotA, packet.SlotA); if (!item) return; if (!GetPlayer().IsValidPos(packet.ContainerSlotB, ItemConst.NullSlot, false)) // can be autostore pos { GetPlayer().SendEquipError(InventoryResult.WrongSlot); return; } ushort src = item.GetPos(); InventoryResult msg; // check unequip potability for equipped items and bank bags if (Player.IsEquipmentPos(src) || Player.IsBagPos(src)) { msg = GetPlayer().CanUnequipItem(src, !Player.IsBagPos(src)); if (msg != InventoryResult.Ok) { GetPlayer().SendEquipError(msg, item); return; } } List dest = new(); msg = GetPlayer().CanStoreItem(packet.ContainerSlotB, ItemConst.NullSlot, dest, item, false); if (msg != InventoryResult.Ok) { GetPlayer().SendEquipError(msg, item); return; } // no-op: placed in same slot if (dest.Count == 1 && dest[0].pos == src) { // just remove grey item state GetPlayer().SendEquipError(InventoryResult.InternalBagError, item); return; } GetPlayer().RemoveItem(packet.ContainerSlotA, packet.SlotA, true); GetPlayer().StoreItem(dest, item, true); } public void SendEnchantmentLog(ObjectGuid owner, ObjectGuid caster, ObjectGuid itemGuid, uint itemId, uint enchantId, uint enchantSlot) { EnchantmentLog packet = new(); packet.Owner = owner; packet.Caster = caster; packet.ItemGUID = itemGuid; packet.ItemID = itemId; packet.Enchantment = enchantId; packet.EnchantSlot = enchantSlot; GetPlayer().SendMessageToSet(packet, true); } public void SendItemEnchantTimeUpdate(ObjectGuid Playerguid, ObjectGuid Itemguid, uint slot, uint Duration) { ItemEnchantTimeUpdate data = new(); data.ItemGuid = Itemguid; data.DurationLeft = Duration; data.Slot = slot; data.OwnerGuid = Playerguid; SendPacket(data); } [WorldPacketHandler(ClientOpcodes.WrapItem)] void HandleWrapItem(WrapItem packet) { if (packet.Inv.Items.Count != 2) { Log.outError(LogFilter.Network, "HandleWrapItem - Invalid itemCount ({0})", packet.Inv.Items.Count); return; } // Gift byte giftContainerSlot = packet.Inv.Items[0].ContainerSlot; byte giftSlot = packet.Inv.Items[0].Slot; // Item byte itemContainerSlot = packet.Inv.Items[1].ContainerSlot; byte itemSlot = packet.Inv.Items[1].Slot; Item gift = GetPlayer().GetItemByPos(giftContainerSlot, giftSlot); if (!gift) { GetPlayer().SendEquipError(InventoryResult.ItemNotFound, gift); return; } if (!gift.GetTemplate().HasFlag(ItemFlags.IsWrapper)) // cheating: non-wrapper wrapper { GetPlayer().SendEquipError(InventoryResult.ItemNotFound, gift); return; } Item item = GetPlayer().GetItemByPos(itemContainerSlot, itemSlot); if (!item) { GetPlayer().SendEquipError(InventoryResult.ItemNotFound, item); return; } if (item == gift) // not possable with pacjket from real client { GetPlayer().SendEquipError(InventoryResult.CantWrapWrapped, item); return; } if (item.IsEquipped()) { GetPlayer().SendEquipError(InventoryResult.CantWrapEquipped, item); return; } if (!item.GetGiftCreator().IsEmpty()) // HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED); { GetPlayer().SendEquipError(InventoryResult.CantWrapWrapped, item); return; } if (item.IsBag()) { GetPlayer().SendEquipError(InventoryResult.CantWrapBags, item); return; } if (item.IsSoulBound()) { GetPlayer().SendEquipError(InventoryResult.CantWrapBound, item); return; } if (item.GetMaxStackCount() != 1) { GetPlayer().SendEquipError(InventoryResult.CantWrapStackable, item); return; } // maybe not correct check (it is better than nothing) if (item.GetTemplate().GetMaxCount() > 0) { GetPlayer().SendEquipError(InventoryResult.CantWrapUnique, item); return; } SQLTransaction trans = new(); PreparedStatement stmt = CharacterDatabase.GetPreparedStatement(CharStatements.INS_CHAR_GIFT); stmt.AddValue(0, item.GetOwnerGUID().GetCounter()); stmt.AddValue(1, item.GetGUID().GetCounter()); stmt.AddValue(2, item.GetEntry()); stmt.AddValue(3, (uint)item.m_itemData.DynamicFlags); 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.SetGiftCreator(GetPlayer().GetGUID()); item.ReplaceAllItemFlags(ItemFieldFlags.Wrapped); item.SetState(ItemUpdateState.Changed, GetPlayer()); if (item.GetState() == ItemUpdateState.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.RemoveItemFromUpdateQueueOf(item, GetPlayer()); item.SaveToDB(trans); // item gave inventory record unchanged and can be save standalone } DB.Characters.CommitTransaction(trans); uint count = 1; GetPlayer().DestroyItemCount(gift, ref count, true); } [WorldPacketHandler(ClientOpcodes.SocketGems, Processing = PacketProcessing.Inplace)] void HandleSocketGems(SocketGems socketGems) { if (socketGems.ItemGuid.IsEmpty()) return; //cheat . tried to socket same gem multiple times if ((!socketGems.GemItem[0].IsEmpty() && (socketGems.GemItem[0] == socketGems.GemItem[1] || socketGems.GemItem[0] == socketGems.GemItem[2])) || (!socketGems.GemItem[1].IsEmpty() && (socketGems.GemItem[1] == socketGems.GemItem[2]))) return; Item itemTarget = GetPlayer().GetItemByGuid(socketGems.ItemGuid); if (!itemTarget) //missing item to socket return; ItemTemplate itemProto = itemTarget.GetTemplate(); if (itemProto == null) return; //this slot is excepted when applying / removing meta gem bonus byte slot = itemTarget.IsEquipped() ? itemTarget.GetSlot() : ItemConst.NullSlot; Item[] gems = new Item[ItemConst.MaxGemSockets]; ItemDynamicFieldGems[] gemData = new ItemDynamicFieldGems[ItemConst.MaxGemSockets]; GemPropertiesRecord[] gemProperties = new GemPropertiesRecord[ItemConst.MaxGemSockets]; SocketedGem[] oldGemData = new SocketedGem[ItemConst.MaxGemSockets]; for (int i = 0; i < ItemConst.MaxGemSockets; ++i) { Item gem = _player.GetItemByGuid(socketGems.GemItem[i]); if (gem) { gems[i] = gem; gemData[i].ItemId = gem.GetEntry(); gemData[i].Context = (byte)gem.m_itemData.Context; for (int b = 0; b < gem.GetBonusListIDs().Count && b < 16; ++b) gemData[i].BonusListIDs[b] = (ushort)gem.GetBonusListIDs()[b]; gemProperties[i] = CliDB.GemPropertiesStorage.LookupByKey(gem.GetTemplate().GetGemProperties()); } oldGemData[i] = itemTarget.GetGem((ushort)i); } // Find first prismatic socket uint firstPrismatic = 0; while (firstPrismatic < ItemConst.MaxGemSockets && itemTarget.GetSocketColor(firstPrismatic) != 0) ++firstPrismatic; for (uint i = 0; i < ItemConst.MaxGemSockets; ++i) //check for hack maybe { if (gemProperties[i] == null) continue; // tried to put gem in socket where no socket exists (take care about prismatic sockets) if (itemTarget.GetSocketColor(i) == 0) { // no prismatic socket if (itemTarget.GetEnchantmentId(EnchantmentSlot.Prismatic) == 0) return; if (i != firstPrismatic) return; } // Gem must match socket color if (ItemConst.SocketColorToGemTypeMask[(int)itemTarget.GetSocketColor(i)] != gemProperties[i].Type) { // unless its red, blue, yellow or prismatic if (!ItemConst.SocketColorToGemTypeMask[(int)itemTarget.GetSocketColor(i)].HasAnyFlag(SocketColor.Prismatic) || !gemProperties[i].Type.HasAnyFlag(SocketColor.Prismatic)) return; } } // check unique-equipped conditions for (int i = 0; i < ItemConst.MaxGemSockets; ++i) { if (!gems[i]) continue; // continue check for case when attempt add 2 similar unique equipped gems in one item. ItemTemplate iGemProto = gems[i].GetTemplate(); // unique item (for new and already placed bit removed enchantments if (iGemProto.HasFlag(ItemFlags.UniqueEquippable)) { for (int j = 0; j < ItemConst.MaxGemSockets; ++j) { if (i == j) // skip self continue; if (gems[j]) { if (iGemProto.GetId() == gems[j].GetEntry()) { GetPlayer().SendEquipError(InventoryResult.ItemUniqueEquippableSocketed, itemTarget); return; } } else if (oldGemData[j] != null) { if (iGemProto.GetId() == oldGemData[j].ItemId) { GetPlayer().SendEquipError(InventoryResult.ItemUniqueEquippableSocketed, itemTarget); return; } } } } // unique limit type item int limit_newcount = 0; if (iGemProto.GetItemLimitCategory() != 0) { ItemLimitCategoryRecord limitEntry = CliDB.ItemLimitCategoryStorage.LookupByKey(iGemProto.GetItemLimitCategory()); if (limitEntry != null) { // NOTE: limitEntry.mode is not checked because if item has limit then it is applied in equip case for (int j = 0; j < ItemConst.MaxGemSockets; ++j) { if (gems[j]) { // new gem if (iGemProto.GetItemLimitCategory() == gems[j].GetTemplate().GetItemLimitCategory()) ++limit_newcount; } else if (oldGemData[j] != null) { // existing gem ItemTemplate jProto = Global.ObjectMgr.GetItemTemplate(oldGemData[j].ItemId); if (jProto != null) if (iGemProto.GetItemLimitCategory() == jProto.GetItemLimitCategory()) ++limit_newcount; } } if (limit_newcount > 0 && limit_newcount > _player.GetItemLimitCategoryQuantity(limitEntry)) { GetPlayer().SendEquipError(InventoryResult.ItemUniqueEquippableSocketed, itemTarget); return; } } } // for equipped item check all equipment for duplicate equipped gems if (itemTarget.IsEquipped()) { InventoryResult res = GetPlayer().CanEquipUniqueItem(gems[i], slot, (uint)Math.Max(limit_newcount, 0)); if (res != 0) { GetPlayer().SendEquipError(res, itemTarget); return; } } } bool SocketBonusActivated = itemTarget.GemsFitSockets(); //save state of socketbonus GetPlayer().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 mods - gem can change item level if (itemTarget.IsEquipped()) _player._ApplyItemMods(itemTarget, itemTarget.GetSlot(), false); for (ushort i = 0; i < ItemConst.MaxGemSockets; ++i) { if (gems[i]) { uint gemScalingLevel = _player.GetLevel(); uint fixedLevel = gems[i].GetModifier(ItemModifier.TimewalkerLevel); if (fixedLevel != 0) gemScalingLevel = fixedLevel; itemTarget.SetGem(i, gemData[i], gemScalingLevel); if (gemProperties[i] != null && gemProperties[i].EnchantId != 0) itemTarget.SetEnchantment(EnchantmentSlot.Sock1 + i, gemProperties[i].EnchantId, 0, 0, GetPlayer().GetGUID()); uint gemCount = 1; GetPlayer().DestroyItemCount(gems[i], ref gemCount, true); } } if (itemTarget.IsEquipped()) _player._ApplyItemMods(itemTarget, itemTarget.GetSlot(), true); Item childItem = _player.GetChildItemByGuid(itemTarget.GetChildItem()); if (childItem) { if (childItem.IsEquipped()) _player._ApplyItemMods(childItem, childItem.GetSlot(), false); childItem.CopyArtifactDataFromParent(itemTarget); if (childItem.IsEquipped()) _player._ApplyItemMods(childItem, childItem.GetSlot(), true); } bool SocketBonusToBeActivated = itemTarget.GemsFitSockets();//current socketbonus state if (SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change... { GetPlayer().ApplyEnchantment(itemTarget, EnchantmentSlot.Bonus, false); itemTarget.SetEnchantment(EnchantmentSlot.Bonus, SocketBonusToBeActivated ? itemTarget.GetTemplate().GetSocketBonus() : 0, 0, 0, GetPlayer().GetGUID()); GetPlayer().ApplyEnchantment(itemTarget, EnchantmentSlot.Bonus, true); //it is not displayed, client has an inbuilt system to determine if the bonus is activated } GetPlayer().ToggleMetaGemsActive(slot, true); //turn on all metagems (except for target item) GetPlayer().RemoveTradeableItem(itemTarget); itemTarget.ClearSoulboundTradeable(GetPlayer()); // clear tradeable flag itemTarget.SendUpdateSockets(); } [WorldPacketHandler(ClientOpcodes.CancelTempEnchantment, Processing = PacketProcessing.Inplace)] void HandleCancelTempEnchantment(CancelTempEnchantment packet) { // apply only to equipped item if (!Player.IsEquipmentPos(InventorySlots.Bag0, (byte)packet.Slot)) return; Item item = GetPlayer().GetItemByPos(InventorySlots.Bag0, (byte)packet.Slot); if (!item) return; if (item.GetEnchantmentId(EnchantmentSlot.Temp) == 0) return; GetPlayer().ApplyEnchantment(item, EnchantmentSlot.Temp, false); item.ClearEnchantment(EnchantmentSlot.Temp); } [WorldPacketHandler(ClientOpcodes.GetItemPurchaseData, Processing = PacketProcessing.Inplace)] void HandleGetItemPurchaseData(GetItemPurchaseData packet) { Item item = GetPlayer().GetItemByGuid(packet.ItemGUID); if (!item) { Log.outDebug(LogFilter.Network, "HandleGetItemPurchaseData: Item {0} not found!", packet.ItemGUID.ToString()); return; } GetPlayer().SendRefundInfo(item); } [WorldPacketHandler(ClientOpcodes.ItemPurchaseRefund, Processing = PacketProcessing.Inplace)] void HandleItemRefund(ItemPurchaseRefund packet) { Item item = GetPlayer().GetItemByGuid(packet.ItemGUID); if (!item) { Log.outDebug(LogFilter.Network, "WorldSession.HandleItemRefund: Item {0} not found!", packet.ItemGUID.ToString()); return; } // Don't try to refund item currently being disenchanted if (GetPlayer().GetLootGUID() == packet.ItemGUID) return; GetPlayer().RefundItem(item); } bool CanUseBank(ObjectGuid bankerGUID = default) { // bankerGUID parameter is optional, set to 0 by default. if (bankerGUID.IsEmpty()) bankerGUID = _player.PlayerTalkClass.GetInteractionData().SourceGuid; bool isUsingBankCommand = bankerGUID == GetPlayer().GetGUID() && bankerGUID == _player.PlayerTalkClass.GetInteractionData().SourceGuid; if (!isUsingBankCommand) { Creature creature = GetPlayer().GetNPCIfCanInteractWith(bankerGUID, NPCFlags.Banker, NPCFlags2.None); if (!creature) return false; } return true; } [WorldPacketHandler(ClientOpcodes.UseCritterItem)] void HandleUseCritterItem(UseCritterItem useCritterItem) { Item item = GetPlayer().GetItemByGuid(useCritterItem.ItemGuid); if (!item) return; foreach (var itemEffect in item.GetEffects()) { if (itemEffect.TriggerType != ItemSpelltriggerType.OnLearn) continue; var speciesEntry = BattlePetMgr.GetBattlePetSpeciesBySpell((uint)itemEffect.SpellID); if (speciesEntry != null) GetBattlePetMgr().AddPet(speciesEntry.Id, BattlePetMgr.SelectPetDisplay(speciesEntry), BattlePetMgr.RollPetBreed(speciesEntry.Id), BattlePetMgr.GetDefaultPetQuality(speciesEntry.Id)); } GetPlayer().DestroyItem(item.GetBagSlot(), item.GetSlot(), true); } [WorldPacketHandler(ClientOpcodes.SortBags, Processing = PacketProcessing.Inplace)] void HandleSortBags(SortBags sortBags) { // TODO: Implement sorting // Placeholder to prevent completely locking out bags clientside SendPacket(new BagCleanupFinished()); } [WorldPacketHandler(ClientOpcodes.SortBankBags, Processing = PacketProcessing.Inplace)] void HandleSortBankBags(SortBankBags sortBankBags) { // TODO: Implement sorting // Placeholder to prevent completely locking out bags clientside SendPacket(new BagCleanupFinished()); } [WorldPacketHandler(ClientOpcodes.SortReagentBankBags, Processing = PacketProcessing.Inplace)] void HandleSortReagentBankBags(SortReagentBankBags sortReagentBankBags) { // TODO: Implement sorting // Placeholder to prevent completely locking out bags clientside SendPacket(new BagCleanupFinished()); } [WorldPacketHandler(ClientOpcodes.RemoveNewItem, Processing = PacketProcessing.Inplace)] void HandleRemoveNewItem(RemoveNewItem removeNewItem) { Item item = _player.GetItemByGuid(removeNewItem.ItemGuid); if (!item) { Log.outDebug(LogFilter.Network, $"WorldSession.HandleRemoveNewItem: Item ({removeNewItem.ItemGuid}) not found for {GetPlayerInfo()}!"); return; } if (item.HasItemFlag(ItemFieldFlags.NewItem)) { item.RemoveItemFlag(ItemFieldFlags.NewItem); item.SetState(ItemUpdateState.Changed, _player); } } } }