diff --git a/Source/Game/Chat/Commands/ListCommands.cs b/Source/Game/Chat/Commands/ListCommands.cs index 64963bf0e..e3f3c2612 100644 --- a/Source/Game/Chat/Commands/ListCommands.cs +++ b/Source/Game/Chat/Commands/ListCommands.cs @@ -232,6 +232,8 @@ namespace Game.Chat.Commands itemPos = "[equipped]"; else if (Player.IsInventoryPos((byte)itemBag, itemSlot)) itemPos = "[in inventory]"; + else if (Player.IsReagentBankPos((byte)itemBag, itemSlot)) + itemPos = "[in reagent bank]"; else if (Player.IsBankPos((byte)itemBag, itemSlot)) itemPos = "[in bank]"; else diff --git a/Source/Game/Entities/Player/Player.Items.cs b/Source/Game/Entities/Player/Player.Items.cs index 26170b849..1c762f0d5 100644 --- a/Source/Game/Entities/Player/Player.Items.cs +++ b/Source/Game/Entities/Player/Player.Items.cs @@ -583,22 +583,6 @@ namespace Game.Entities return InventoryResult.ItemMaxCount; } - res = CanStoreItem_InInventorySlots(InventorySlots.ReagentStart, InventorySlots.ReagentEnd, dest, pProto, ref count, true, pItem, bag, slot); - if (res != InventoryResult.Ok) - { - no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return InventoryResult.Ok; - - no_space_count = count + no_similar_count; - return InventoryResult.ItemMaxCount; - } - res = CanStoreItem_InInventorySlots(InventorySlots.ItemStart, inventoryEnd, dest, pProto, ref count, true, pItem, bag, slot); if (res != InventoryResult.Ok) { @@ -661,24 +645,6 @@ namespace Game.Entities return InventoryResult.ItemMaxCount; } } - else if (pProto.IsCraftingReagent() && HasPlayerFlagEx(PlayerFlagsEx.ReagentBankUnlocked)) - { - res = CanStoreItem_InInventorySlots(InventorySlots.ReagentStart, InventorySlots.ReagentEnd, dest, pProto, ref count, false, pItem, bag, slot); - if (res != InventoryResult.Ok) - { - no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return InventoryResult.Ok; - - no_space_count = count + no_similar_count; - return InventoryResult.ItemMaxCount; - } - } res = CanStoreItem_InInventorySlots(InventorySlots.ItemStart, inventoryEnd, dest, pProto, ref count, false, pItem, bag, slot); if (res != InventoryResult.Ok) @@ -740,22 +706,6 @@ namespace Game.Entities return InventoryResult.ItemMaxCount; } - res = CanStoreItem_InInventorySlots(InventorySlots.ReagentStart, InventorySlots.ReagentEnd, dest, pProto, ref count, true, pItem, bag, slot); - if (res != InventoryResult.Ok) - { - no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return InventoryResult.Ok; - - no_space_count = count + no_similar_count; - return InventoryResult.ItemMaxCount; - } - res = CanStoreItem_InInventorySlots(InventorySlots.ItemStart, inventoryEnd, dest, pProto, ref count, true, pItem, bag, slot); if (res != InventoryResult.Ok) { @@ -849,24 +799,6 @@ namespace Game.Entities return InventoryResult.ItemMaxCount; } } - else if (pProto.IsCraftingReagent() && HasPlayerFlagEx(PlayerFlagsEx.ReagentBankUnlocked)) - { - res = CanStoreItem_InInventorySlots(InventorySlots.ReagentStart, InventorySlots.ReagentEnd, dest, pProto, ref count, false, pItem, bag, slot); - if (res != InventoryResult.Ok) - { - no_space_count = count + no_similar_count; - return res; - } - - if (count == 0) - { - if (no_similar_count == 0) - return InventoryResult.Ok; - - no_space_count = count + no_similar_count; - return InventoryResult.ItemMaxCount; - } - } // search free slot byte searchSlotStart = InventorySlots.ItemStart; @@ -2177,6 +2109,12 @@ namespace Game.Entities } } + if (IsReagentBankPos(dst) && !IsReagentBankUnlocked()) + { + SendEquipError(InventoryResult.ReagentBankLocked, pSrcItem, pDstItem); + return; + } + // NOW this is or item move (swap with empty), or swap with another item (including bags in bag possitions) // or swap empty bag with another empty or not empty bag (with items exchange) @@ -2210,7 +2148,8 @@ namespace Game.Entities RemoveItem(srcbag, srcslot, true); BankItem(dest, pSrcItem, true); - ItemRemovedQuestCheck(pSrcItem.GetEntry(), pSrcItem.GetCount()); + if (!IsReagentBankPos(dst)) + ItemRemovedQuestCheck(pSrcItem.GetEntry(), pSrcItem.GetCount()); } else if (IsEquipmentPos(dst)) { @@ -2660,6 +2599,21 @@ namespace Game.Entities return sum / count; } + + public List GetCraftingReagentItemsToDeposit() + { + List itemList = new(); + ForEachItem(ItemSearchLocation.Inventory, item => + { + if (item.GetTemplate().IsCraftingReagent()) + itemList.Add(item); + + return true; + }); + + return itemList; + } + public Item GetItemByGuid(ObjectGuid guid) { Item result = null; @@ -2817,6 +2771,10 @@ namespace Game.Entities if (slot >= InventorySlots.BankBagStart && slot < InventorySlots.BankBagEnd) return true; + // reagent bank bag slots + if (slot >= InventorySlots.ReagentStart && slot < InventorySlots.ReagentEnd) + return true; + return false; } @@ -3577,13 +3535,6 @@ namespace Game.Entities } } - for (byte i = InventorySlots.ReagentStart; i < InventorySlots.ReagentEnd; ++i) - { - Item item = GetUseableItemByPos(InventorySlots.Bag0, i); - if (item && Global.DB2Mgr.IsTotemCategoryCompatibleWith(item.GetTemplate().GetTotemCategory(), TotemCategory)) - return true; - } - for (byte i = InventorySlots.ChildEquipmentStart; i < InventorySlots.ChildEquipmentEnd; ++i) { Item item = GetUseableItemByPos(InventorySlots.Bag0, i); @@ -4227,6 +4178,10 @@ namespace Game.Entities // prevent cheating if ((slot >= InventorySlots.BuyBackStart && slot < InventorySlots.BuyBackEnd) || slot >= (byte)PlayerSlots.End) return InventoryResult.WrongBagType; + + // can't store anything else than crafting reagents in Reagent Bank + if (IsReagentBankPos(bag, slot) && (!IsReagentBankUnlocked() || !pProto.IsCraftingReagent())) + return InventoryResult.WrongBagType; } else { @@ -4329,13 +4284,27 @@ namespace Game.Entities return true; if (bag >= InventorySlots.BankBagStart && bag < InventorySlots.BankBagEnd) return true; + if (bag == InventorySlots.Bag0 && (slot >= InventorySlots.ReagentStart && slot < InventorySlots.ReagentEnd)) + return true; return false; } - public InventoryResult CanBankItem(byte bag, byte slot, List dest, Item pItem, bool swap, bool not_loading = true) + public InventoryResult CanBankItem(byte bag, byte slot, List dest, Item pItem, bool swap, bool not_loading = true, bool reagentBankOnly = false) { if (pItem == null) return swap ? InventoryResult.CantSwap : InventoryResult.ItemNotFound; + // different slots range if we're trying to store item in Reagent Bank + if (reagentBankOnly) + { + Cypher.Assert(bag == ItemConst.NullBag && slot == ItemConst.NullSlot); // when reagentBankOnly is true then bag & slot must be hardcoded constants, not client input + } + + if ((IsReagentBankPos(bag, slot) || reagentBankOnly) && !IsReagentBankUnlocked()) + return InventoryResult.ReagentBankLocked; + + byte slotStart = reagentBankOnly ? InventorySlots.ReagentStart : InventorySlots.BankItemStart; + byte slotEnd = reagentBankOnly ? InventorySlots.ReagentEnd : InventorySlots.BankItemEnd; + uint count = pItem.GetCount(); Log.outDebug(LogFilter.Player, "STORAGE: CanBankItem bag = {0}, slot = {1}, item = {2}, count = {3}", bag, slot, pItem.GetEntry(), count); @@ -4400,7 +4369,7 @@ namespace Game.Entities { if (bag == InventorySlots.Bag0) { - res = CanStoreItem_InInventorySlots(InventorySlots.BankItemStart, InventorySlots.BankItemEnd, dest, pProto, ref count, true, pItem, bag, slot); + res = CanStoreItem_InInventorySlots(slotStart, slotEnd, dest, pProto, ref count, true, pItem, bag, slot); if (res != InventoryResult.Ok) return res; @@ -4424,7 +4393,7 @@ namespace Game.Entities // search free slot in bag if (bag == InventorySlots.Bag0) { - res = CanStoreItem_InInventorySlots(InventorySlots.BankItemStart, InventorySlots.BankItemEnd, dest, pProto, ref count, false, pItem, bag, slot); + res = CanStoreItem_InInventorySlots(slotStart, slotEnd, dest, pProto, ref count, false, pItem, bag, slot); if (res != InventoryResult.Ok) return res; @@ -4451,19 +4420,34 @@ namespace Game.Entities if (pProto.GetMaxStackSize() != 1) { // in slots - res = CanStoreItem_InInventorySlots(InventorySlots.BankItemStart, InventorySlots.BankItemEnd, dest, pProto, ref count, true, pItem, bag, slot); + res = CanStoreItem_InInventorySlots(slotStart, slotEnd, dest, pProto, ref count, true, pItem, bag, slot); if (res != InventoryResult.Ok) return res; if (count == 0) return InventoryResult.Ok; - // in special bags - if (pProto.GetBagFamily() != BagFamilyMask.None) + // don't try to store reagents anywhere else than in Reagent Bank if we're on it + if (!reagentBankOnly) { + // in special bags + if (pProto.GetBagFamily() != BagFamilyMask.None) + { + for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; i++) + { + res = CanStoreItem_InBag(i, dest, pProto, ref count, true, false, pItem, bag, slot); + if (res != InventoryResult.Ok) + continue; + + if (count == 0) + return InventoryResult.Ok; + } + } + + // in regular bags for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; i++) { - res = CanStoreItem_InBag(i, dest, pProto, ref count, true, false, pItem, bag, slot); + res = CanStoreItem_InBag(i, dest, pProto, ref count, true, true, pItem, bag, slot); if (res != InventoryResult.Ok) continue; @@ -4471,22 +4455,12 @@ namespace Game.Entities return InventoryResult.Ok; } } - - for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; i++) - { - res = CanStoreItem_InBag(i, dest, pProto, ref count, true, true, pItem, bag, slot); - if (res != InventoryResult.Ok) - continue; - - if (count == 0) - return InventoryResult.Ok; - } } // search free place in special bag - if (pProto.GetBagFamily() != BagFamilyMask.None) + if (!reagentBankOnly && pProto.GetBagFamily() != BagFamilyMask.None) { - for (byte i = InventorySlots.BagStart; i < InventorySlots.BankBagEnd; i++) + for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; i++) { res = CanStoreItem_InBag(i, dest, pProto, ref count, false, false, pItem, bag, slot); if (res != InventoryResult.Ok) @@ -4498,24 +4472,29 @@ namespace Game.Entities } // search free space - res = CanStoreItem_InInventorySlots(InventorySlots.BankItemStart, InventorySlots.BankItemEnd, dest, pProto, ref count, false, pItem, bag, slot); + res = CanStoreItem_InInventorySlots(slotStart, slotEnd, dest, pProto, ref count, false, pItem, bag, slot); if (res != InventoryResult.Ok) return res; if (count == 0) return InventoryResult.Ok; - for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; i++) + // search free space in regular bags (don't try to store reagents anywhere else than in Reagent Bank if we're on it) + if (!reagentBankOnly) { - res = CanStoreItem_InBag(i, dest, pProto, ref count, false, true, pItem, bag, slot); - if (res != InventoryResult.Ok) - continue; + for (byte i = InventorySlots.BankBagStart; i < InventorySlots.BankBagEnd; i++) + { + res = CanStoreItem_InBag(i, dest, pProto, ref count, false, true, pItem, bag, slot); + if (res != InventoryResult.Ok) + continue; - if (count == 0) - return InventoryResult.Ok; + if (count == 0) + return InventoryResult.Ok; + } } - return InventoryResult.BankFull; + return reagentBankOnly ? InventoryResult.ReagentBankFull : InventoryResult.BankFull; } + public Item BankItem(List dest, Item pItem, bool update) { return StoreItem(dest, pItem, update); @@ -4597,6 +4576,15 @@ namespace Game.Entities return freeSpace; } + //Reagent + public static bool IsReagentBankPos(ushort pos) { return IsReagentBankPos((byte)(pos >> 8), (byte)(pos & 255)); } + public static bool IsReagentBankPos(byte bag, byte slot) + { + if (bag == InventorySlots.Bag0 && (slot >= InventorySlots.ReagentStart && slot < InventorySlots.ReagentEnd)) + return true; + return false; + } + //Bags public Bag GetBagByPos(byte bag) { diff --git a/Source/Game/Entities/Player/Player.cs b/Source/Game/Entities/Player/Player.cs index 80606a513..fa1fc9493 100644 --- a/Source/Game/Entities/Player/Player.cs +++ b/Source/Game/Entities/Player/Player.cs @@ -7174,6 +7174,9 @@ namespace Game.Entities public void SetSemaphoreTeleportNear(bool semphsetting) { mSemaphoreTeleport_Near = semphsetting; } public void SetSemaphoreTeleportFar(bool semphsetting) { mSemaphoreTeleport_Far = semphsetting; } + public bool IsReagentBankUnlocked() { return HasPlayerFlagEx(PlayerFlagsEx.ReagentBankUnlocked); } + public void UnlockReagentBank() { AddPlayerFlagEx(PlayerFlagsEx.ReagentBankUnlocked); } + //new public uint DoRandomRoll(uint minimum, uint maximum) { diff --git a/Source/Game/Handlers/BankHandler.cs b/Source/Game/Handlers/BankHandler.cs index a04ed7395..2c8811a9f 100644 --- a/Source/Game/Handlers/BankHandler.cs +++ b/Source/Game/Handlers/BankHandler.cs @@ -143,6 +143,158 @@ namespace Game GetPlayer().UpdateCriteria(CriteriaType.BankSlotsPurchased); } + [WorldPacketHandler(ClientOpcodes.BuyReagentBank)] + void HandleBuyReagentBank(ReagentBank reagentBank) + { + if (!CanUseBank(reagentBank.Banker)) + { + Log.outDebug(LogFilter.Network, $"WORLD: HandleBuyReagentBankOpcode - {reagentBank.Banker} not found or you can't interact with him."); + return; + } + + if (_player.IsReagentBankUnlocked()) + { + Log.outDebug(LogFilter.Network, $"WORLD: HandleBuyReagentBankOpcode - Player ({_player.GetGUID()}, name: {_player.GetName()}) tried to unlock reagent bank a 2nd time."); + return; + } + + long price = 100 * MoneyConstants.Gold; + + if (!_player.HasEnoughMoney(price)) + { + Log.outDebug(LogFilter.Network, $"WORLD: HandleBuyReagentBankOpcode - Player ({_player.GetGUID()}, name: {_player.GetName()}) without enough gold."); + return; + } + + _player.ModifyMoney(-price); + _player.UnlockReagentBank(); + } + + [WorldPacketHandler(ClientOpcodes.DepositReagentBank)] + void HandleReagentBankDeposit(ReagentBank reagentBank) + { + if (!CanUseBank(reagentBank.Banker)) + { + Log.outDebug(LogFilter.Network, $"WORLD: HandleReagentBankDepositOpcode - {reagentBank.Banker} not found or you can't interact with him."); + return; + } + + if (!_player.IsReagentBankUnlocked()) + { + _player.SendEquipError(InventoryResult.ReagentBankLocked); + return; + } + + // query all reagents from player's inventory + bool anyDeposited = false; + foreach (Item item in _player.GetCraftingReagentItemsToDeposit()) + { + List dest = new(); + InventoryResult msg = _player.CanBankItem(ItemConst.NullBag, ItemConst.NullSlot, dest, item, false, true, true); + if (msg != InventoryResult.Ok) + { + if (msg != InventoryResult.ReagentBankFull || !anyDeposited) + _player.SendEquipError(msg, item); + break; + } + + if (dest.Count == 1 && dest[0].pos == item.GetPos()) + { + _player.SendEquipError(InventoryResult.CantSwap, item); + continue; + } + + // store reagent + _player.RemoveItem(item.GetBagSlot(), item.GetSlot(), true); + _player.BankItem(dest, item, true); + anyDeposited = true; + } + } + + [WorldPacketHandler(ClientOpcodes.AutobankReagent)] + void HandleAutoBankReagent(AutoBankReagent autoBankReagent) + { + if (!CanUseBank()) + { + Log.outDebug(LogFilter.Network, $"WORLD: HandleAutoBankReagentOpcode - {m_currentBankerGUID} not found or you can't interact with him."); + return; + } + + if (!_player.IsReagentBankUnlocked()) + { + _player.SendEquipError(InventoryResult.ReagentBankLocked); + return; + } + + Item item = _player.GetItemByPos(autoBankReagent.PackSlot, autoBankReagent.Slot); + if (!item) + return; + + List dest = new(); + InventoryResult msg = _player.CanBankItem(ItemConst.NullBag, ItemConst.NullSlot, dest, item, false, true, true); + if (msg != InventoryResult.Ok) + { + _player.SendEquipError(msg, item); + return; + } + + if (dest.Count == 1 && dest[0].pos == item.GetPos()) + { + _player.SendEquipError(InventoryResult.CantSwap, item); + return; + } + + _player.RemoveItem(autoBankReagent.PackSlot, autoBankReagent.Slot, true); + _player.BankItem(dest, item, true); + } + + [WorldPacketHandler(ClientOpcodes.AutostoreBankReagent)] + void HandleAutoStoreBankReagent(AutoStoreBankReagent autoStoreBankReagent) + { + if (!CanUseBank()) + { + Log.outDebug(LogFilter.Network, $"WORLD: HandleAutoBankReagentOpcode - {m_currentBankerGUID} not found or you can't interact with him."); + return; + } + + if (!_player.IsReagentBankUnlocked()) + { + _player.SendEquipError(InventoryResult.ReagentBankLocked); + return; + } + + Item pItem = _player.GetItemByPos(autoStoreBankReagent.Slot, autoStoreBankReagent.PackSlot); + if (!pItem) + return; + + if (Player.IsReagentBankPos(autoStoreBankReagent.Slot, autoStoreBankReagent.PackSlot)) + { + List dest = new(); + InventoryResult msg = _player.CanStoreItem(ItemConst.NullBag, ItemConst.NullSlot, dest, pItem, false); + if (msg != InventoryResult.Ok) + { + _player.SendEquipError(msg, pItem); + return; + } + + _player.RemoveItem(autoStoreBankReagent.Slot, autoStoreBankReagent.PackSlot, true); + _player.StoreItem(dest, pItem, true); + } + else + { + List dest = new(); + InventoryResult msg = _player.CanBankItem(ItemConst.NullBag, ItemConst.NullSlot, dest, pItem, false, true, true); + if (msg != InventoryResult.Ok) + { + _player.SendEquipError(msg, pItem); + return; + } + + _player.RemoveItem(autoStoreBankReagent.Slot, autoStoreBankReagent.PackSlot, true); + _player.BankItem(dest, pItem, true); + } + } + public void SendShowBank(ObjectGuid guid) { m_currentBankerGUID = guid; diff --git a/Source/Game/Networking/Packets/BankPackets.cs b/Source/Game/Networking/Packets/BankPackets.cs index 5ce4c8c99..1cfb17f8f 100644 --- a/Source/Game/Networking/Packets/BankPackets.cs +++ b/Source/Game/Networking/Packets/BankPackets.cs @@ -62,4 +62,50 @@ namespace Game.Networking.Packets public ObjectGuid Guid; } + + class AutoBankReagent : ClientPacket + { + public AutoBankReagent(WorldPacket packet) : base(packet) { } + + public override void Read() + { + Inv = new(_worldPacket); + PackSlot = _worldPacket.ReadUInt8(); + Slot = _worldPacket.ReadUInt8(); + } + + public InvUpdate Inv; + public byte Slot; + public byte PackSlot; + } + + class AutoStoreBankReagent : ClientPacket + { + public AutoStoreBankReagent(WorldPacket packet) : base(packet) { } + + public override void Read() + { + Inv = new(_worldPacket); + Slot = _worldPacket.ReadUInt8(); + PackSlot = _worldPacket.ReadUInt8(); + } + + public InvUpdate Inv; + public byte Slot; + public byte PackSlot; + } + + // CMSG_BUY_REAGENT_BANK + // CMSG_REAGENT_BANK_DEPOSIT + class ReagentBank : ClientPacket + { + public ReagentBank(WorldPacket packet) : base(packet) { } + + public override void Read() + { + Banker = _worldPacket.ReadPackedGuid(); + } + + public ObjectGuid Banker; + } }