/* =========================================================================== Copyright (C) 2015-2019 Project Meteor Dev Team This file is part of Project Meteor Server. Project Meteor Server is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Project Meteor Server 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 Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Project Meteor Server. If not, see . =========================================================================== */ using Meteor.Map.actors.chara.player; using Meteor.Map.Actors; using Meteor.Map.dataobjects; using Meteor.Map.packets.send.actor.inventory; using System.Collections.Generic; using System.Diagnostics; namespace Meteor.Map.actors.chara { class ReferencedItemPackage { const uint EMPTY = 0xFFFFFFFF; private readonly Player owner; private readonly InventoryItem[] referenceList; private readonly ushort itemPackageCode; private readonly ushort itemPackageCapacity; private bool writeToDB = false; public ReferencedItemPackage(Player owner, ushort capacity, ushort code) { this.owner = owner; itemPackageCode = code; itemPackageCapacity = capacity; referenceList = new InventoryItem[capacity]; if (code == ItemPackage.EQUIPMENT) writeToDB = true; } public void ToggleDBWrite(bool flag) { writeToDB = flag; } #region Package Management public void SetList(InventoryItem[] toSet) { Debug.Assert(referenceList.Length == itemPackageCapacity); toSet.CopyTo(referenceList, 0); } public void Set(ushort[] positions, ushort[] itemSlots, ushort itemPackage) { Debug.Assert(positions.Length == itemSlots.Length); for (int i = 0; i < positions.Length; i++) { InventoryItem item = owner.GetItemPackage(itemPackage)?.GetItemAtSlot(itemSlots[i]); if (item == null) continue; Database.EquipItem(owner, positions[i], item.uniqueId); referenceList[positions[i]] = item; } owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id)); SendUpdate(); owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id)); } public void Set(ushort position, ushort itemPackagePosition, ushort itemPackageCode) { InventoryItem item = owner.GetItemPackage(itemPackageCode).GetItemAtSlot(itemPackagePosition); if (item == null) return; Set(position, item); } public void Set(ushort position, InventoryItem item) { if (position >= referenceList.Length) return; if (writeToDB) Database.EquipItem(owner, position, item.uniqueId); ItemPackage newPackage = owner.GetItemPackage(item.itemPackage); ItemPackage oldPackage = null; if (referenceList[position] != null) { oldPackage = owner.GetItemPackage(referenceList[position].itemPackage); InventoryItem oldItem = referenceList[position]; oldPackage.MarkDirty(oldItem); } newPackage.MarkDirty(item); referenceList[position] = item; owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id)); if (oldPackage != null) oldPackage.SendUpdate(); newPackage.SendUpdate(); SendSingleUpdate(position); owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id)); } public void Clear(ushort position) { if (position >= referenceList.Length) return; if (writeToDB) Database.UnequipItem(owner, position); ItemPackage oldItemPackage = owner.GetItemPackage(referenceList[position].itemPackage); oldItemPackage.MarkDirty(referenceList[position]); referenceList[position] = null; owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id)); oldItemPackage.SendUpdate(); SendSingleUpdate(position); owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id)); } public void ClearAll() { List packagesToRefresh = new List(); for (int i = 0; i < referenceList.Length; i++) { if (referenceList[i] == null) continue; if (writeToDB) Database.UnequipItem(owner, (ushort)i); ItemPackage package = owner.GetItemPackage(referenceList[i].itemPackage); package.MarkDirty(referenceList[i]); packagesToRefresh.Add(package); referenceList[i] = null; } owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.Id)); for (int i = 0; i < packagesToRefresh.Count; i++) packagesToRefresh[i].SendUpdate(); SendUpdate(); owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.Id)); } #endregion #region Send Update Functions public void SendSingleUpdate(ushort position) { owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.Id, itemPackageCapacity, itemPackageCode)); SendSingleLinkedItemPacket(owner, position); owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.Id)); } public void SendUpdate() { SendUpdate(owner); } public void SendUpdate(Player targetPlayer) { List slotsToUpdate = new List(); for (ushort i = 0; i < referenceList.Length; i++) { if (referenceList[i] != null) slotsToUpdate.Add(i); } targetPlayer.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.Id, itemPackageCapacity, itemPackageCode)); SendLinkedItemPackets(targetPlayer, slotsToUpdate); targetPlayer.QueuePacket(InventorySetEndPacket.BuildPacket(owner.Id)); } public void SendUpdateAsItemPackage(Player targetPlayer) { SendUpdateAsItemPackage(targetPlayer, itemPackageCapacity, itemPackageCode); } public void SendUpdateAsItemPackage(Player targetPlayer, ushort destinationCapacity, ushort destinationCode) { List items = new List(); for (ushort i = 0; i < referenceList.Length; i++) { if (referenceList[i] == null) continue; InventoryItem item = referenceList[i]; item.linkSlot = i; //We have to set the linkSlot as this is the position in the Referenced IP, not the original IP it's linked from. items.Add(referenceList[i]); } targetPlayer.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.Id, destinationCapacity, destinationCode)); SendItemPackets(targetPlayer, items); targetPlayer.QueuePacket(InventorySetEndPacket.BuildPacket(owner.Id)); //Clean Up linkSlots for (ushort i = 0; i < referenceList.Length; i++) { if (referenceList[i] == null) continue; InventoryItem item = referenceList[i]; item.linkSlot = 0xFFFF; } } #endregion #region Packet Functions (Private) private void SendSingleLinkedItemPacket(Player targetPlayer, ushort position) { if (referenceList[position] == null) targetPlayer.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.Id, position)); else targetPlayer.QueuePacket(LinkedItemListX01Packet.BuildPacket(owner.Id, position, referenceList[position])); } private void SendLinkedItemPackets(Player targetPlayer, List slotsToUpdate) { int currentIndex = 0; while (true) { if (slotsToUpdate.Count - currentIndex >= 64) targetPlayer.QueuePacket(LinkedItemListX64Packet.BuildPacket(owner.Id, referenceList, slotsToUpdate, ref currentIndex)); else if (slotsToUpdate.Count - currentIndex >= 32) targetPlayer.QueuePacket(LinkedItemListX32Packet.BuildPacket(owner.Id, referenceList, slotsToUpdate, ref currentIndex)); else if (slotsToUpdate.Count - currentIndex >= 16) targetPlayer.QueuePacket(LinkedItemListX16Packet.BuildPacket(owner.Id, referenceList, slotsToUpdate, ref currentIndex)); else if (slotsToUpdate.Count - currentIndex > 1) targetPlayer.QueuePacket(LinkedItemListX08Packet.BuildPacket(owner.Id, referenceList, slotsToUpdate, ref currentIndex)); else if (slotsToUpdate.Count - currentIndex == 1) { targetPlayer.QueuePacket(LinkedItemListX01Packet.BuildPacket(owner.Id, slotsToUpdate[currentIndex], referenceList[slotsToUpdate[currentIndex]])); currentIndex++; } else break; } } private void SendItemPackets(Player player, List items) { int currentIndex = 0; while (true) { if (items.Count - currentIndex >= 64) player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.Id, items, ref currentIndex)); else if (items.Count - currentIndex >= 32) player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.Id, items, ref currentIndex)); else if (items.Count - currentIndex >= 16) player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.Id, items, ref currentIndex)); else if (items.Count - currentIndex > 1) player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.Id, items, ref currentIndex)); else if (items.Count - currentIndex == 1) { player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.Id, items[currentIndex])); currentIndex++; } else break; } } #endregion #region Getters/Setters public ushort GetCode() { return itemPackageCode; } public int GetCapacity() { return itemPackageCapacity; } public Player GetOwner() { return owner; } public InventoryItem GetItemAtSlot(ushort position) { if (position < referenceList.Length) return referenceList[position]; else return null; } #endregion } }