2b08c49bbf
Port From (https://github.com/TrinityCore/TrinityCore/commit/f8dda8a4fc696d07bb6007e49140cec1b7f60dd5)
1119 lines
47 KiB
C#
1119 lines
47 KiB
C#
/*
|
|
* Copyright (C) 2012-2020 CypherCore <http://github.com/CypherCore>
|
|
*
|
|
* 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 3 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/>.
|
|
*/
|
|
|
|
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 - {0} not found or you can't interact with him.", m_currentBankerGUID.ToString());
|
|
return;
|
|
}
|
|
|
|
if (Player.IsBankPos(InventorySlots.Bag0, swapInvItem.Slot2) && !CanUseBank())
|
|
{
|
|
Log.outDebug(LogFilter.Network, "WORLD: HandleSwapInvItemOpcode - {0} not found or you can't interact with him.", m_currentBankerGUID.ToString());
|
|
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 - {0} not found or you can't interact with him.", m_currentBankerGUID.ToString());
|
|
return;
|
|
}
|
|
|
|
if (Player.IsBankPos(swapItem.ContainerSlotB, swapItem.SlotB) && !CanUseBank())
|
|
{
|
|
Log.outDebug(LogFilter.Network, "WORLD: HandleSwapInvItemOpcode - {0} not found or you can't interact with him.", m_currentBankerGUID.ToString());
|
|
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<ItemPosCount> 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().GetFlags().HasAnyFlag(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.HasItemFlag(ItemFieldFlags.Refundable))
|
|
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.ItemRemovedQuestCheck(pItem.GetEntry(), pItem.GetCount());
|
|
pl.RemoveItem(pItem.GetBagSlot(), pItem.GetSlot(), true);
|
|
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<ItemPosCount> 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.MoveItemToInventory(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<ItemPosCount> 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().GetFlags().HasAnyFlag(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 = DB.Characters.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.SetItemFlags(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 < ((List<uint>)gem.m_itemData.BonusListIDs).Count && b < 16; ++b)
|
|
gemData[i].BonusListIDs[b] = (ushort)((List<uint>)gem.m_itemData.BonusListIDs)[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.GetFlags().HasAnyFlag(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 = m_currentBankerGUID;
|
|
|
|
bool isUsingBankCommand = (bankerGUID == GetPlayer().GetGUID() && bankerGUID == m_currentBankerGUID);
|
|
|
|
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;
|
|
|
|
if (item.GetBonus().EffectCount < 2)
|
|
return;
|
|
|
|
uint spellToLearn = (uint)item.GetEffect(1).SpellID;
|
|
|
|
var entry = Global.SpellMgr.GetBattlePetSpecies(spellToLearn);
|
|
if (entry != null)
|
|
{
|
|
GetBattlePetMgr().AddPet(entry.Id, entry.CreatureID, BattlePetMgr.RollPetBreed(entry.Id), BattlePetMgr.GetDefaultPetQuality(entry.Id));
|
|
_player.UpdateCriteria(CriteriaType.UniquePetsOwned);
|
|
}
|
|
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|