From 02e1b8a82f7881e9ac021f56849eb93d883bb1b3 Mon Sep 17 00:00:00 2001 From: Filip Maj Date: Sun, 12 May 2019 16:16:44 -0400 Subject: [PATCH] Renamed the Inventory class file to ItemPackage (class was already renamed). Moved ItemPackage and Equipment classes to Character folder. Fixed unequip bug introduced by accidently removing InventoryBeginChangePacket. Added documentation to the Equipment class. Cleaned up Equipment packet code. --- .../FFXIVClassic Map Server.csproj | 4 +- .../actors/chara/Equipment.cs | 271 +++ .../{player/Inventory.cs => ItemPackage.cs} | 1488 ++++++++--------- .../actors/chara/player/Equipment.cs | 212 --- .../actors/chara/player/Player.cs | 2 +- 5 files changed, 1018 insertions(+), 959 deletions(-) create mode 100644 FFXIVClassic Map Server/actors/chara/Equipment.cs rename FFXIVClassic Map Server/actors/chara/{player/Inventory.cs => ItemPackage.cs} (97%) delete mode 100644 FFXIVClassic Map Server/actors/chara/player/Equipment.cs diff --git a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj index 14905e16..00b528b2 100644 --- a/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj +++ b/FFXIVClassic Map Server/FFXIVClassic Map Server.csproj @@ -139,8 +139,8 @@ - - + + diff --git a/FFXIVClassic Map Server/actors/chara/Equipment.cs b/FFXIVClassic Map Server/actors/chara/Equipment.cs new file mode 100644 index 00000000..fae856ef --- /dev/null +++ b/FFXIVClassic Map Server/actors/chara/Equipment.cs @@ -0,0 +1,271 @@ +using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server.dataobjects; +using FFXIVClassic_Map_Server.packets.send.actor.inventory; +using System.Collections.Generic; +using System.Diagnostics; + +namespace FFXIVClassic_Map_Server.actors.chara.player +{ + /// + /// This class stores the current equipment that a Player has equipped on themselves. Technically + /// it is an ItemPackage like other inventories, however due to how this one operates, it is in + /// it's own class. While on the server this is stored as a list of InventoryItems like other + /// ItemPackages, on the client it exists as a link to the "normal inventory" package. The Equipment list + /// therefore is a list of slot values (the slots in the inventory), with each position in the list being + /// a position on the paper doll (in the game's Gear menu). + /// + class Equipment + { + public const int SLOT_MAINHAND = 0; + public const int SLOT_OFFHAND = 1; + public const int SLOT_THROWINGWEAPON = 4; + public const int SLOT_PACK = 5; + public const int SLOT_POUCH = 6; + public const int SLOT_HEAD = 8; + public const int SLOT_UNDERSHIRT = 9; + public const int SLOT_BODY = 10; + public const int SLOT_UNDERGARMENT = 11; + public const int SLOT_LEGS = 12; + public const int SLOT_HANDS = 13; + public const int SLOT_BOOTS = 14; + public const int SLOT_WAIST = 15; + public const int SLOT_NECK = 16; + public const int SLOT_EARS = 17; + public const int SLOT_WRISTS = 19; + public const int SLOT_RIGHTFINGER = 21; + public const int SLOT_LEFTFINGER = 22; + + private const ushort EQUIP_ITEMPACKAGE_CAPACITY = 0x23; + private const ushort EQUIP_ITEMPACKAGE_CODE = ItemPackage.EQUIPMENT; + + readonly private InventoryItem[] list = new InventoryItem[EQUIP_ITEMPACKAGE_CAPACITY]; + + readonly private Player owner; + readonly private ItemPackage normalInventory; + + private bool writeToDB = true; + + /// The player client that owns this ItemPackage. + /// A reference to the normal inventory ItemPackage this equipment ItemPackage links to. + public Equipment(Player ownerPlayer, ItemPackage normalInventory) + { + owner = ownerPlayer; + this.normalInventory = normalInventory; + } + + /// + /// Sets the full equipment ItemPackage to the given list. 's length must + /// equal EQUIP_ITEMPACKAGE_CAPACITY. Used to initialize the list when loading from the DB. + /// + /// The list of inventory items to set the full list to. + public void SetEquipment(InventoryItem[] toEquip) + { + Debug.Assert(toEquip.Length == EQUIP_ITEMPACKAGE_CAPACITY); + toEquip.CopyTo(list, 0); + } + + /// + /// Sets the given equipSlots to link to the given itemSlots. The lengths of both must be equal. + /// + /// The list of equipmentSlots that get linked. + /// The list of itemSlots that the equipSlots will link to. + public void SetEquipment(ushort[] equipSlots, ushort[] itemSlots) + { + if (equipSlots.Length != itemSlots.Length) + return; + + for (int i = 0; i < equipSlots.Length; i++) + { + InventoryItem item = normalInventory.GetItemAtSlot(itemSlots[i]); + + if (item == null) + continue; + + Database.EquipItem(owner, equipSlots[i], item.uniqueId); + list[equipSlots[i]] = normalInventory.GetItemAtSlot(itemSlots[i]); + } + + owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); + SendFullEquipment(owner); + owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); + } + + /// + /// Equips the item at the given item slot to the given equipment slot. + /// + /// The equipment slot being equipped. + /// The inventory slot of where the equipped item is. + public void Equip(ushort equipSlot, ushort invSlot) + { + InventoryItem item = normalInventory.GetItemAtSlot(invSlot); + + if (item == null) + return; + + Equip(equipSlot, item); + } + + /// + /// Equips the given inventory item to the given equipment slot. + /// + /// The equipment slot being equipped. + /// The inventory item being equiped. + public void Equip(ushort equipSlot, InventoryItem item) + { + if (equipSlot >= list.Length) + return; + + if (writeToDB) + Database.EquipItem(owner, equipSlot, item.uniqueId); + + owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); + + if (list[equipSlot] != null) + normalInventory.RefreshItem(owner, list[equipSlot], item); + else + normalInventory.RefreshItem(owner, item); + + list[equipSlot] = item; + + owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, EQUIP_ITEMPACKAGE_CAPACITY, EQUIP_ITEMPACKAGE_CODE)); + SendSingleEquipmentUpdatePacket(equipSlot); + owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + + owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); + + owner.CalculateBaseStats();// RecalculateStats(); + } + + /// + /// Toggles if equipment changes are saved to the DB. + /// + /// If true, equipment changes are saved to the db. + public void ToggleDBWrite(bool flag) + { + writeToDB = flag; + } + + /// + /// Removes the linked item at the given . + /// + /// The slot that is being cleared. + public void Unequip(ushort equipSlot) + { + if (equipSlot >= list.Length) + return; + + if (writeToDB) + Database.UnequipItem(owner, equipSlot); + + owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); + normalInventory.RefreshItem(owner, list[equipSlot]); + list[equipSlot] = null; + SendSingleEquipmentUpdatePacket(equipSlot); + owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); + + owner.RecalculateStats(); + } + + /// + /// Returns the item equipped at the given . + /// + /// The slot to retrieve from. + public InventoryItem GetItemAtSlot(ushort equipSlot) + { + if (equipSlot < list.Length) + return list[equipSlot]; + else + return null; + } + + /// + /// Returns the capacity of this ItemPackage. + /// + public int GetCapacity() + { + return list.Length; + } + + #region Packet Functions + + /// + /// Syncs the client the link status of a single equipment slot. If the item was null, + /// sends a delete packet instead. + /// + /// The slot we are updating the client about. + private void SendSingleEquipmentUpdatePacket(ushort equipSlot) + { + owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, EQUIP_ITEMPACKAGE_CAPACITY, EQUIP_ITEMPACKAGE_CODE)); + if (list[equipSlot] == null) + owner.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, equipSlot)); + else + owner.QueuePacket(EquipmentListX01Packet.BuildPacket(owner.actorId, equipSlot, list[equipSlot].slot)); + owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + } + + /// + /// Syncs the full list of equipped items to the client owner of this ItemPackage. Used on login/zone in. + /// + public void SendFullEquipment() + { + SendFullEquipment(owner); + } + + /// + /// Syncs the full list of equipped items to a given target. Used to send the equipment list of this ItemPackage to the owner, + /// or in the case of examining another player, sends the list of this ItemPackage to the player client examining. A different + /// ItemPackage Code is used for /checking. + /// + /// The player client that is being synced. + public void SendFullEquipment(Player targetPlayer) + { + List slotsToUpdate = new List(); + + for (ushort i = 0; i < list.Length; i++) + { + if (list[i] != null) + slotsToUpdate.Add(i); + } + + if (targetPlayer.Equals(owner)) + targetPlayer.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, EQUIP_ITEMPACKAGE_CAPACITY, EQUIP_ITEMPACKAGE_CODE)); + else + targetPlayer.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, 0x23, ItemPackage.EQUIPMENT_OTHERPLAYER)); + + SendEquipmentPackets(slotsToUpdate, targetPlayer); + + targetPlayer.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + } + + /// + /// Main sync function. Syncs the given targetPlayer client the link status of multiple equipment slots. + /// + /// The slots that will be synced. + /// The player client that is being synced. + private void SendEquipmentPackets(List slotsToUpdate, Player targetPlayer) + { + int currentIndex = 0; + + while (true) + { + if (slotsToUpdate.Count - currentIndex >= 64) + targetPlayer.QueuePacket(EquipmentListX64Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex)); + else if (slotsToUpdate.Count - currentIndex >= 32) + targetPlayer.QueuePacket(EquipmentListX32Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex)); + else if (slotsToUpdate.Count - currentIndex >= 16) + targetPlayer.QueuePacket(EquipmentListX16Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex)); + else if (slotsToUpdate.Count - currentIndex > 1) + targetPlayer.QueuePacket(EquipmentListX08Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex)); + else if (slotsToUpdate.Count - currentIndex == 1) + { + targetPlayer.QueuePacket(EquipmentListX01Packet.BuildPacket(owner.actorId, slotsToUpdate[currentIndex], list[slotsToUpdate[currentIndex]].slot)); + currentIndex++; + } + else + break; + } + } + + #endregion + } +} diff --git a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs b/FFXIVClassic Map Server/actors/chara/ItemPackage.cs similarity index 97% rename from FFXIVClassic Map Server/actors/chara/player/Inventory.cs rename to FFXIVClassic Map Server/actors/chara/ItemPackage.cs index 9573bb0f..dd543dc6 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Inventory.cs +++ b/FFXIVClassic Map Server/actors/chara/ItemPackage.cs @@ -1,744 +1,744 @@ -using FFXIVClassic.Common; -using FFXIVClassic_Map_Server.actors.chara.npc; -using FFXIVClassic_Map_Server.Actors; -using FFXIVClassic_Map_Server.dataobjects; -using FFXIVClassic_Map_Server.packets.send.actor.inventory; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; - -namespace FFXIVClassic_Map_Server.actors.chara.player -{ - class ItemPackage - { - public const ushort NORMAL = 0; //Max 0xC8 - public const ushort UNKNOWN = 1; //Max 0x96 - public const ushort LOOT = 4; //Max 0xA - public const ushort MELDREQUEST = 5; //Max 0x04 - public const ushort BAZAAR = 7; //Max 0x0A - public const ushort CURRENCY_CRYSTALS = 99; //Max 0x140 - public const ushort KEYITEMS = 100; //Max 0x500 - public const ushort EQUIPMENT = 0x00FE; //Max 0x23 - public const ushort TRADE = 0x00FD; //Max 0x04 - public const ushort EQUIPMENT_OTHERPLAYER = 0x00F9; //Max 0x23 - - public enum INV_ERROR { - SUCCESS = 0, - INVENTORY_FULL, - ALREADY_HAS_UNIQUE, - SYSTEM_ERROR - }; - - private Character owner; - private ushort itemPackageCapacity; - private ushort itemPackageCode; - private bool isTemporary; - private InventoryItem[] list; - private bool[] isDirty; - private bool holdingUpdates = false; - - private int endOfListIndex = 0; - - public ItemPackage(Character ownerPlayer, ushort capacity, ushort code, bool temporary = false) - { - owner = ownerPlayer; - itemPackageCapacity = capacity; - itemPackageCode = code; - isTemporary = temporary; - list = new InventoryItem[capacity]; - isDirty = new bool[capacity]; - } - - #region Inventory Management - public void InitList(List itemsFromDB) - { - int i = 0; - foreach (InventoryItem item in itemsFromDB) - { - item.RefreshPositioning(owner, itemPackageCode, (ushort) i); - list[i++] = item; - } - endOfListIndex = i; - } - - public InventoryItem GetItemAtSlot(ushort slot) - { - if (slot < list.Length) - return list[slot]; - else - return null; - } - - public InventoryItem GetItemByUniqueId(ulong uniqueItemId) - { - for (int i = 0; i < endOfListIndex; i++) - { - InventoryItem item = list[i]; - - Debug.Assert(item != null, "Item slot was null!!!"); - - if (item.uniqueId == uniqueItemId) - return item; - } - return null; - } - - public InventoryItem GetItemByCatelogId(ulong catelogId) - { - for (int i = 0; i < endOfListIndex; i++) - { - InventoryItem item = list[i]; - - Debug.Assert(item != null, "Item slot was null!!!"); - - if (item.itemId == catelogId) - return item; - } - return null; - } - - public InventoryItem GetItemAttachedTo(InventoryItem attachedTo) - { - for (int i = 0; i < endOfListIndex; i++) - { - InventoryItem item = list[i]; - - Debug.Assert(item != null, "Item slot was null!!!"); - - if (attachedTo.GetAttached() == item.uniqueId) - return item; - } - return null; - } - - public INV_ERROR AddItem(uint itemId) - { - return AddItem(itemId, 1, 1); - } - - public INV_ERROR AddItem(uint itemId, int quantity) - { - return AddItem(itemId, quantity, 1); - } - - public bool AddItems(uint[] itemIds) - { - bool canAdd = GetFreeSlots() - itemIds.Length >= 0; - if (canAdd) - { - foreach (uint id in itemIds) - { - ItemData gItem = Server.GetItemGamedata(id); - InventoryItem.ItemModifier modifiers = null; - if (gItem.durability != 0) - { - modifiers = new InventoryItem.ItemModifier(); - modifiers.durability = (uint)gItem.durability; - } - - InventoryItem addedItem = Database.CreateItem(id, Math.Min(1, gItem.maxStack), 0, modifiers); - addedItem.RefreshPositioning(owner, itemPackageCode, (ushort)endOfListIndex); - isDirty[endOfListIndex] = true; - list[endOfListIndex++] = addedItem; - DoDatabaseAdd(addedItem); - } - } - return canAdd; - } - - public INV_ERROR AddItem(InventoryItem itemRef) - { - //If it isn't a single item (ie: armor) just add like normal (not valid for BAZAAR) - if (itemPackageCode != BAZAAR && itemRef.GetItemData().maxStack > 1) - return AddItem(itemRef.itemId, itemRef.quantity, itemRef.quality); - - if (!IsSpaceForAdd(itemRef.itemId, itemRef.quantity, itemRef.quality)) - return INV_ERROR.INVENTORY_FULL; - - ItemData gItem = Server.GetItemGamedata(itemRef.itemId); - - if (gItem == null) - { - Program.Log.Error("Inventory.AddItem: unable to find item %u", itemRef.itemId); - return INV_ERROR.SYSTEM_ERROR; - } - - itemRef.RefreshPositioning(owner, itemPackageCode, (ushort)endOfListIndex); - - isDirty[endOfListIndex] = true; - list[endOfListIndex++] = itemRef; - DoDatabaseAdd(itemRef); - - SendUpdatePackets(); - - return INV_ERROR.SUCCESS; - } - - public INV_ERROR AddItem(uint itemId, int quantity, byte quality) - { - if (!IsSpaceForAdd(itemId, quantity, quality)) - return INV_ERROR.INVENTORY_FULL; - - ItemData gItem = Server.GetItemGamedata(itemId); - - //If it's unique, abort - if (HasItem(itemId) && gItem.isExclusive) - return INV_ERROR.ALREADY_HAS_UNIQUE; - - if (gItem == null) - { - Program.Log.Error("Inventory.AddItem: unable to find item %u", itemId); - return INV_ERROR.SYSTEM_ERROR; - } - - //Check if item id exists - int quantityCount = quantity; - for (int i = 0; i < endOfListIndex; i++) - { - InventoryItem item = list[i]; - - Debug.Assert(item != null, "Item slot was null!!!"); - - if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack) - { - int oldQuantity = item.quantity; - item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack); - isDirty[i] = true; - quantityCount -= (gItem.maxStack - oldQuantity); - - DoDatabaseQuantity(item.uniqueId, item.quantity); - - if (quantityCount <= 0) - break; - } - } - - //New item that spilled over - while (quantityCount > 0) - { - InventoryItem.ItemModifier modifiers = null; - if (gItem.durability != 0) - { - modifiers = new InventoryItem.ItemModifier(); - modifiers.durability = (uint)gItem.durability; - } - - InventoryItem addedItem = Database.CreateItem(itemId, Math.Min(quantityCount, gItem.maxStack), quality, modifiers); - addedItem.RefreshPositioning(owner, itemPackageCode, (ushort)endOfListIndex); - isDirty[endOfListIndex] = true; - list[endOfListIndex++] = addedItem; - quantityCount -= gItem.maxStack; - - DoDatabaseAdd(addedItem); - } - - SendUpdatePackets(); - - return INV_ERROR.SUCCESS; - } - - public void SetItem(ushort slot, InventoryItem item) - { - list[slot] = item; - SendUpdatePackets(); - item.RefreshPositioning(owner, itemPackageCode, slot); - } - - public void RemoveItem(uint itemId) - { - RemoveItem(itemId, 1); - } - - public void RemoveItem(uint itemId, int quantity) - { - RemoveItem(itemId, quantity, 1); - } - - public void RemoveItem(uint itemId, int quantity, int quality) - { - if (!HasItem(itemId, quantity, quality)) - return; - - List slotsToUpdate = new List(); - List itemsToRemove = new List(); - List slotsToRemove = new List(); - List AddItemPackets = new List(); - - //Remove as we go along - int quantityCount = quantity; - ushort lowestSlot = 0; - for (int i = endOfListIndex - 1; i >= 0; i--) - { - InventoryItem item = list[i]; - - Debug.Assert(item != null, "Item slot was null!!!"); - - if (item.itemId == itemId && item.quality == quality) - { - int oldQuantity = item.quantity; - //Stack nomnomed - if (item.quantity - quantityCount <= 0) - { - DoDatabaseRemove(list[i].uniqueId); - list[i] = null; - } - //Stack reduced - else - { - item.quantity -= quantityCount; - DoDatabaseQuantity(list[i].uniqueId, list[i].quantity);} - - isDirty[i] = true; - - quantityCount -= oldQuantity; - lowestSlot = item.slot; - - if (quantityCount <= 0) - break; - } - } - - DoRealign(); - SendUpdatePackets(); - } - - public void RemoveItem(InventoryItem item) - { - RemoveItemByUniqueId(item.uniqueId, item.quantity); - } - - public void RemoveItem(InventoryItem item, int quantity) - { - RemoveItemByUniqueId(item.uniqueId, quantity); - } - - public void RemoveItemByUniqueId(ulong itemDBId, int quantity) - { - ushort slot = 0; - InventoryItem toDelete = null; - for (int i = 0; i < endOfListIndex; i++) - { - InventoryItem item = list[i]; - - Debug.Assert(item != null, "Item slot was null!!!"); - - if (item.uniqueId == itemDBId) - { - toDelete = item; - break; - } - slot++; - } - - if (toDelete == null) - return; - - if (quantity >= toDelete.quantity) - { - DoDatabaseRemove(toDelete.uniqueId); - list[slot].RefreshPositioning(null, 0xFFFF, 0xFFFF); - list[slot] = null; - } - else - { - list[slot].quantity -= quantity; - DoDatabaseQuantity(list[slot].uniqueId, list[slot].quantity); - } - - isDirty[slot] = true; - - DoRealign(); - SendUpdatePackets(); - } - - public void RemoveItemAtSlot(ushort slot) - { - if (slot >= endOfListIndex) - return; - - DoDatabaseRemove(list[slot].uniqueId); - - list[slot].RefreshPositioning(null, 0xFFFF, 0xFFFF); - list[slot] = null; - isDirty[slot] = true; - - DoRealign(); - SendUpdatePackets(); - } - - public void RemoveItemAtSlot(ushort slot, int quantity) - { - if (slot >= endOfListIndex) - return; - - if (list[slot] != null) - { - list[slot].quantity -= quantity; - - if (list[slot].quantity <= 0) - { - DoDatabaseRemove(list[slot].uniqueId); - - list[slot].RefreshPositioning(null, 0xFFFF, 0xFFFF); - list[slot] = null; - DoRealign(); - } - else - DoDatabaseQuantity(list[slot].uniqueId, list[slot].quantity); - - isDirty[slot] = true; - SendUpdatePackets(); - } - } - - public void Clear() - { - for (int i = 0; i < endOfListIndex; i++) - { - list[i].RefreshPositioning(null, 0xFFFF, 0xFFFF); - list[i] = null; - isDirty[i] = true; - } - endOfListIndex = 0; - - SendUpdatePackets(); - } - - public InventoryItem[] GetRawList() - { - return list; - } - - public void ChangeDurability(uint slot, uint durabilityChange) - { - isDirty[slot] = true; - } - - public void ChangeSpiritBind(uint slot, uint spiritBindChange) - { - isDirty[slot] = true; - } - - public void ChangeMateria(uint slot, byte materiaSlot, byte materiaId) - { - isDirty[slot] = true; - } - #endregion - - #region Packet Functions - public void SendFullInventory(Player player) - { - player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); - SendInventoryPackets(player, 0); - player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } - - private void SendInventoryPackets(Player player, InventoryItem item) - { - player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, item)); - } - - private void SendInventoryPackets(Player player, List items) - { - int currentIndex = 0; - - while (true) - { - if (items.Count - currentIndex >= 64) - player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, items, ref currentIndex)); - else if (items.Count - currentIndex >= 32) - player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, items, ref currentIndex)); - else if (items.Count - currentIndex >= 16) - player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, items, ref currentIndex)); - else if (items.Count - currentIndex > 1) - player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, items, ref currentIndex)); - else if (items.Count - currentIndex == 1) - { - player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, items[currentIndex])); - currentIndex++; - } - else - break; - } - } - - private void SendInventoryPackets(Player player, int startOffset) - { - int currentIndex = startOffset; - - List lst = new List(); - for (int i = 0; i < endOfListIndex; i++) - lst.Add(list[i]); - - while (true) - { - if (endOfListIndex - currentIndex >= 64) - player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); - else if (endOfListIndex - currentIndex >= 32) - player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); - else if (endOfListIndex - currentIndex >= 16) - player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); - else if (endOfListIndex - currentIndex > 1) - player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); - else if (endOfListIndex - currentIndex == 1) - { - player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, list[currentIndex])); - currentIndex++; - } - else - break; - } - } - - private void SendInventoryRemovePackets(Player player, ushort index) - { - player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, index)); - } - - private void SendInventoryRemovePackets(Player player, List indexes) - { - int currentIndex = 0; - - while (true) - { - if (indexes.Count - currentIndex >= 64) - player.QueuePacket(InventoryRemoveX64Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); - else if (indexes.Count - currentIndex >= 32) - player.QueuePacket(InventoryRemoveX32Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); - else if (indexes.Count - currentIndex >= 16) - player.QueuePacket(InventoryRemoveX16Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); - else if (indexes.Count - currentIndex > 1) - player.QueuePacket(InventoryRemoveX08Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); - else if (indexes.Count - currentIndex == 1) - { - player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, indexes[currentIndex])); - currentIndex++; - } - else - break; - } - } - - public void RefreshItem(Player player, InventoryItem item) - { - player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); - SendInventoryPackets(player, item); - player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } - - public void RefreshItem(Player player, params InventoryItem[] items) - { - player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); - SendInventoryPackets(player, items.ToList()); - player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } - - public void RefreshItem(Player player, List items) - { - player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); - SendInventoryPackets(player, items); - player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } - - #endregion - - #region Automatic Client and DB Updating - - private void DoDatabaseAdd(InventoryItem addedItem) - { - if (isTemporary) - return; - - if (itemPackageCode == BAZAAR) - return; - - if (owner is Player) - Database.AddItem((Player)owner, addedItem, itemPackageCode); - else if (owner is Retainer) - Database.AddItem((Retainer)owner, addedItem, itemPackageCode); - } - - private void DoDatabaseQuantity(ulong itemDBId, int quantity) - { - if (isTemporary) - return; - - - if (itemPackageCode == BAZAAR) - return; - - Database.SetQuantity(itemDBId, quantity); - } - - private void DoDatabaseRemove(ulong itemDBId) - { - if (isTemporary) - return; - - if (itemPackageCode == BAZAAR) - return; - - if (owner is Player) - Database.RemoveItem((Player)owner, itemDBId); - else if (owner is Retainer) - Database.RemoveItem((Retainer)owner, itemDBId); - } - - private void SendUpdatePackets() - { - if (owner is Player && !holdingUpdates) - { - SendUpdatePackets((Player)owner); - } - } - - public void SendUpdatePackets(Player player) - { - List items = new List(); - List slotsToRemove = new List(); - - for (int i = 0; i < list.Length; i++) - { - if (i == endOfListIndex) - break; - if (isDirty[i]) - items.Add(list[i]); - } - - for (int i = endOfListIndex; i < list.Length; i++) - { - if (isDirty[i]) - slotsToRemove.Add((ushort)i); - } - - if (!holdingUpdates) - Array.Clear(isDirty, 0, isDirty.Length); - - player.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); - //Send Updated Slots - SendInventoryPackets(player, items); - //Send Remove packets for tail end - SendInventoryRemovePackets(player, slotsToRemove); - player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - //If player is updating their normal inventory, we need to send - //an equip update as well to resync the slots. - if (player.Equals(owner) && itemPackageCode == NORMAL) - player.GetEquipment().SendFullEquipment(); - player.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); - } - - public void StartSendUpdate() - { - holdingUpdates = true; - } - - public void DoneSendUpdate() - { - holdingUpdates = false; - SendUpdatePackets(); - Array.Clear(isDirty, 0, isDirty.Length); - } - - #endregion - - #region Inventory Utils - - public bool IsFull() - { - return endOfListIndex >= itemPackageCapacity; - } - - public int GetFreeSlots() - { - return itemPackageCapacity - endOfListIndex; - } - - public bool IsSpaceForAdd(uint itemId, int quantity, int quality) - { - int quantityCount = quantity; - for (int i = 0; i < endOfListIndex; i++) - { - InventoryItem item = list[i]; - ItemData gItem = Server.GetItemGamedata(item.itemId); - if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack) - { - quantityCount -= (gItem.maxStack - item.quantity); - if (quantityCount <= 0) - break; - } - } - - return quantityCount <= 0 || (quantityCount > 0 && !IsFull()); - } - - public bool HasItem(uint itemId) - { - return HasItem(itemId, 1); - } - - public bool HasItem(uint itemId, int minQuantity) - { - return HasItem(itemId, minQuantity, 1); - } - - public bool HasItem(uint itemId, int minQuantity, int quality) - { - int count = 0; - - for (int i = endOfListIndex - 1; i >= 0; i--) - { - InventoryItem item = list[i]; - - Debug.Assert(item != null, "Item slot was null!!!"); - - if (item.itemId == itemId && item.quality == quality) - count += item.quantity; - - if (count >= minQuantity) - return true; - } - - return false; - } - - public int GetNextEmptySlot() - { - return endOfListIndex; - } - - private void DoRealign() - { - int lastNullSlot = -1; - - for (int i = 0; i < endOfListIndex; i++) - { - if (list[i] == null && lastNullSlot == -1) - { - lastNullSlot = i; - continue; - } - else if (list[i] != null && lastNullSlot != -1) - { - list[lastNullSlot] = list[i]; - list[lastNullSlot].slot = (ushort)lastNullSlot; - list[i] = null; - isDirty[lastNullSlot] = true; - isDirty[i] = true; - lastNullSlot++; - } - } - - if (lastNullSlot != -1) - endOfListIndex = lastNullSlot; - } - - #endregion - - public int GetCount() - { - return endOfListIndex; - } - } -} +using FFXIVClassic.Common; +using FFXIVClassic_Map_Server.actors.chara.npc; +using FFXIVClassic_Map_Server.Actors; +using FFXIVClassic_Map_Server.dataobjects; +using FFXIVClassic_Map_Server.packets.send.actor.inventory; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; + +namespace FFXIVClassic_Map_Server.actors.chara.player +{ + class ItemPackage + { + public const ushort NORMAL = 0; //Max 0xC8 + public const ushort UNKNOWN = 1; //Max 0x96 + public const ushort LOOT = 4; //Max 0xA + public const ushort MELDREQUEST = 5; //Max 0x04 + public const ushort BAZAAR = 7; //Max 0x0A + public const ushort CURRENCY_CRYSTALS = 99; //Max 0x140 + public const ushort KEYITEMS = 100; //Max 0x500 + public const ushort EQUIPMENT = 0x00FE; //Max 0x23 + public const ushort TRADE = 0x00FD; //Max 0x04 + public const ushort EQUIPMENT_OTHERPLAYER = 0x00F9; //Max 0x23 + + public enum INV_ERROR { + SUCCESS = 0, + INVENTORY_FULL, + ALREADY_HAS_UNIQUE, + SYSTEM_ERROR + }; + + private Character owner; + private ushort itemPackageCapacity; + private ushort itemPackageCode; + private bool isTemporary; + private InventoryItem[] list; + private bool[] isDirty; + private bool holdingUpdates = false; + + private int endOfListIndex = 0; + + public ItemPackage(Character ownerPlayer, ushort capacity, ushort code, bool temporary = false) + { + owner = ownerPlayer; + itemPackageCapacity = capacity; + itemPackageCode = code; + isTemporary = temporary; + list = new InventoryItem[capacity]; + isDirty = new bool[capacity]; + } + + #region Inventory Management + public void InitList(List itemsFromDB) + { + int i = 0; + foreach (InventoryItem item in itemsFromDB) + { + item.RefreshPositioning(owner, itemPackageCode, (ushort) i); + list[i++] = item; + } + endOfListIndex = i; + } + + public InventoryItem GetItemAtSlot(ushort slot) + { + if (slot < list.Length) + return list[slot]; + else + return null; + } + + public InventoryItem GetItemByUniqueId(ulong uniqueItemId) + { + for (int i = 0; i < endOfListIndex; i++) + { + InventoryItem item = list[i]; + + Debug.Assert(item != null, "Item slot was null!!!"); + + if (item.uniqueId == uniqueItemId) + return item; + } + return null; + } + + public InventoryItem GetItemByCatelogId(ulong catelogId) + { + for (int i = 0; i < endOfListIndex; i++) + { + InventoryItem item = list[i]; + + Debug.Assert(item != null, "Item slot was null!!!"); + + if (item.itemId == catelogId) + return item; + } + return null; + } + + public InventoryItem GetItemAttachedTo(InventoryItem attachedTo) + { + for (int i = 0; i < endOfListIndex; i++) + { + InventoryItem item = list[i]; + + Debug.Assert(item != null, "Item slot was null!!!"); + + if (attachedTo.GetAttached() == item.uniqueId) + return item; + } + return null; + } + + public INV_ERROR AddItem(uint itemId) + { + return AddItem(itemId, 1, 1); + } + + public INV_ERROR AddItem(uint itemId, int quantity) + { + return AddItem(itemId, quantity, 1); + } + + public bool AddItems(uint[] itemIds) + { + bool canAdd = GetFreeSlots() - itemIds.Length >= 0; + if (canAdd) + { + foreach (uint id in itemIds) + { + ItemData gItem = Server.GetItemGamedata(id); + InventoryItem.ItemModifier modifiers = null; + if (gItem.durability != 0) + { + modifiers = new InventoryItem.ItemModifier(); + modifiers.durability = (uint)gItem.durability; + } + + InventoryItem addedItem = Database.CreateItem(id, Math.Min(1, gItem.maxStack), 0, modifiers); + addedItem.RefreshPositioning(owner, itemPackageCode, (ushort)endOfListIndex); + isDirty[endOfListIndex] = true; + list[endOfListIndex++] = addedItem; + DoDatabaseAdd(addedItem); + } + } + return canAdd; + } + + public INV_ERROR AddItem(InventoryItem itemRef) + { + //If it isn't a single item (ie: armor) just add like normal (not valid for BAZAAR) + if (itemPackageCode != BAZAAR && itemRef.GetItemData().maxStack > 1) + return AddItem(itemRef.itemId, itemRef.quantity, itemRef.quality); + + if (!IsSpaceForAdd(itemRef.itemId, itemRef.quantity, itemRef.quality)) + return INV_ERROR.INVENTORY_FULL; + + ItemData gItem = Server.GetItemGamedata(itemRef.itemId); + + if (gItem == null) + { + Program.Log.Error("Inventory.AddItem: unable to find item %u", itemRef.itemId); + return INV_ERROR.SYSTEM_ERROR; + } + + itemRef.RefreshPositioning(owner, itemPackageCode, (ushort)endOfListIndex); + + isDirty[endOfListIndex] = true; + list[endOfListIndex++] = itemRef; + DoDatabaseAdd(itemRef); + + SendUpdatePackets(); + + return INV_ERROR.SUCCESS; + } + + public INV_ERROR AddItem(uint itemId, int quantity, byte quality) + { + if (!IsSpaceForAdd(itemId, quantity, quality)) + return INV_ERROR.INVENTORY_FULL; + + ItemData gItem = Server.GetItemGamedata(itemId); + + //If it's unique, abort + if (HasItem(itemId) && gItem.isExclusive) + return INV_ERROR.ALREADY_HAS_UNIQUE; + + if (gItem == null) + { + Program.Log.Error("Inventory.AddItem: unable to find item %u", itemId); + return INV_ERROR.SYSTEM_ERROR; + } + + //Check if item id exists + int quantityCount = quantity; + for (int i = 0; i < endOfListIndex; i++) + { + InventoryItem item = list[i]; + + Debug.Assert(item != null, "Item slot was null!!!"); + + if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack) + { + int oldQuantity = item.quantity; + item.quantity = Math.Min(item.quantity + quantityCount, gItem.maxStack); + isDirty[i] = true; + quantityCount -= (gItem.maxStack - oldQuantity); + + DoDatabaseQuantity(item.uniqueId, item.quantity); + + if (quantityCount <= 0) + break; + } + } + + //New item that spilled over + while (quantityCount > 0) + { + InventoryItem.ItemModifier modifiers = null; + if (gItem.durability != 0) + { + modifiers = new InventoryItem.ItemModifier(); + modifiers.durability = (uint)gItem.durability; + } + + InventoryItem addedItem = Database.CreateItem(itemId, Math.Min(quantityCount, gItem.maxStack), quality, modifiers); + addedItem.RefreshPositioning(owner, itemPackageCode, (ushort)endOfListIndex); + isDirty[endOfListIndex] = true; + list[endOfListIndex++] = addedItem; + quantityCount -= gItem.maxStack; + + DoDatabaseAdd(addedItem); + } + + SendUpdatePackets(); + + return INV_ERROR.SUCCESS; + } + + public void SetItem(ushort slot, InventoryItem item) + { + list[slot] = item; + SendUpdatePackets(); + item.RefreshPositioning(owner, itemPackageCode, slot); + } + + public void RemoveItem(uint itemId) + { + RemoveItem(itemId, 1); + } + + public void RemoveItem(uint itemId, int quantity) + { + RemoveItem(itemId, quantity, 1); + } + + public void RemoveItem(uint itemId, int quantity, int quality) + { + if (!HasItem(itemId, quantity, quality)) + return; + + List slotsToUpdate = new List(); + List itemsToRemove = new List(); + List slotsToRemove = new List(); + List AddItemPackets = new List(); + + //Remove as we go along + int quantityCount = quantity; + ushort lowestSlot = 0; + for (int i = endOfListIndex - 1; i >= 0; i--) + { + InventoryItem item = list[i]; + + Debug.Assert(item != null, "Item slot was null!!!"); + + if (item.itemId == itemId && item.quality == quality) + { + int oldQuantity = item.quantity; + //Stack nomnomed + if (item.quantity - quantityCount <= 0) + { + DoDatabaseRemove(list[i].uniqueId); + list[i] = null; + } + //Stack reduced + else + { + item.quantity -= quantityCount; + DoDatabaseQuantity(list[i].uniqueId, list[i].quantity);} + + isDirty[i] = true; + + quantityCount -= oldQuantity; + lowestSlot = item.slot; + + if (quantityCount <= 0) + break; + } + } + + DoRealign(); + SendUpdatePackets(); + } + + public void RemoveItem(InventoryItem item) + { + RemoveItemByUniqueId(item.uniqueId, item.quantity); + } + + public void RemoveItem(InventoryItem item, int quantity) + { + RemoveItemByUniqueId(item.uniqueId, quantity); + } + + public void RemoveItemByUniqueId(ulong itemDBId, int quantity) + { + ushort slot = 0; + InventoryItem toDelete = null; + for (int i = 0; i < endOfListIndex; i++) + { + InventoryItem item = list[i]; + + Debug.Assert(item != null, "Item slot was null!!!"); + + if (item.uniqueId == itemDBId) + { + toDelete = item; + break; + } + slot++; + } + + if (toDelete == null) + return; + + if (quantity >= toDelete.quantity) + { + DoDatabaseRemove(toDelete.uniqueId); + list[slot].RefreshPositioning(null, 0xFFFF, 0xFFFF); + list[slot] = null; + } + else + { + list[slot].quantity -= quantity; + DoDatabaseQuantity(list[slot].uniqueId, list[slot].quantity); + } + + isDirty[slot] = true; + + DoRealign(); + SendUpdatePackets(); + } + + public void RemoveItemAtSlot(ushort slot) + { + if (slot >= endOfListIndex) + return; + + DoDatabaseRemove(list[slot].uniqueId); + + list[slot].RefreshPositioning(null, 0xFFFF, 0xFFFF); + list[slot] = null; + isDirty[slot] = true; + + DoRealign(); + SendUpdatePackets(); + } + + public void RemoveItemAtSlot(ushort slot, int quantity) + { + if (slot >= endOfListIndex) + return; + + if (list[slot] != null) + { + list[slot].quantity -= quantity; + + if (list[slot].quantity <= 0) + { + DoDatabaseRemove(list[slot].uniqueId); + + list[slot].RefreshPositioning(null, 0xFFFF, 0xFFFF); + list[slot] = null; + DoRealign(); + } + else + DoDatabaseQuantity(list[slot].uniqueId, list[slot].quantity); + + isDirty[slot] = true; + SendUpdatePackets(); + } + } + + public void Clear() + { + for (int i = 0; i < endOfListIndex; i++) + { + list[i].RefreshPositioning(null, 0xFFFF, 0xFFFF); + list[i] = null; + isDirty[i] = true; + } + endOfListIndex = 0; + + SendUpdatePackets(); + } + + public InventoryItem[] GetRawList() + { + return list; + } + + public void ChangeDurability(uint slot, uint durabilityChange) + { + isDirty[slot] = true; + } + + public void ChangeSpiritBind(uint slot, uint spiritBindChange) + { + isDirty[slot] = true; + } + + public void ChangeMateria(uint slot, byte materiaSlot, byte materiaId) + { + isDirty[slot] = true; + } + #endregion + + #region Packet Functions + public void SendFullInventory(Player player) + { + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); + SendInventoryPackets(player, 0); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + } + + private void SendInventoryPackets(Player player, InventoryItem item) + { + player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, item)); + } + + private void SendInventoryPackets(Player player, List items) + { + int currentIndex = 0; + + while (true) + { + if (items.Count - currentIndex >= 64) + player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, items, ref currentIndex)); + else if (items.Count - currentIndex >= 32) + player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, items, ref currentIndex)); + else if (items.Count - currentIndex >= 16) + player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, items, ref currentIndex)); + else if (items.Count - currentIndex > 1) + player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, items, ref currentIndex)); + else if (items.Count - currentIndex == 1) + { + player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, items[currentIndex])); + currentIndex++; + } + else + break; + } + } + + private void SendInventoryPackets(Player player, int startOffset) + { + int currentIndex = startOffset; + + List lst = new List(); + for (int i = 0; i < endOfListIndex; i++) + lst.Add(list[i]); + + while (true) + { + if (endOfListIndex - currentIndex >= 64) + player.QueuePacket(InventoryListX64Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); + else if (endOfListIndex - currentIndex >= 32) + player.QueuePacket(InventoryListX32Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); + else if (endOfListIndex - currentIndex >= 16) + player.QueuePacket(InventoryListX16Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); + else if (endOfListIndex - currentIndex > 1) + player.QueuePacket(InventoryListX08Packet.BuildPacket(owner.actorId, lst, ref currentIndex)); + else if (endOfListIndex - currentIndex == 1) + { + player.QueuePacket(InventoryListX01Packet.BuildPacket(owner.actorId, list[currentIndex])); + currentIndex++; + } + else + break; + } + } + + private void SendInventoryRemovePackets(Player player, ushort index) + { + player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, index)); + } + + private void SendInventoryRemovePackets(Player player, List indexes) + { + int currentIndex = 0; + + while (true) + { + if (indexes.Count - currentIndex >= 64) + player.QueuePacket(InventoryRemoveX64Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); + else if (indexes.Count - currentIndex >= 32) + player.QueuePacket(InventoryRemoveX32Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); + else if (indexes.Count - currentIndex >= 16) + player.QueuePacket(InventoryRemoveX16Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); + else if (indexes.Count - currentIndex > 1) + player.QueuePacket(InventoryRemoveX08Packet.BuildPacket(owner.actorId, indexes, ref currentIndex)); + else if (indexes.Count - currentIndex == 1) + { + player.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, indexes[currentIndex])); + currentIndex++; + } + else + break; + } + } + + public void RefreshItem(Player player, InventoryItem item) + { + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); + SendInventoryPackets(player, item); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + } + + public void RefreshItem(Player player, params InventoryItem[] items) + { + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); + SendInventoryPackets(player, items.ToList()); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + } + + public void RefreshItem(Player player, List items) + { + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); + SendInventoryPackets(player, items); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + } + + #endregion + + #region Automatic Client and DB Updating + + private void DoDatabaseAdd(InventoryItem addedItem) + { + if (isTemporary) + return; + + if (itemPackageCode == BAZAAR) + return; + + if (owner is Player) + Database.AddItem((Player)owner, addedItem, itemPackageCode); + else if (owner is Retainer) + Database.AddItem((Retainer)owner, addedItem, itemPackageCode); + } + + private void DoDatabaseQuantity(ulong itemDBId, int quantity) + { + if (isTemporary) + return; + + + if (itemPackageCode == BAZAAR) + return; + + Database.SetQuantity(itemDBId, quantity); + } + + private void DoDatabaseRemove(ulong itemDBId) + { + if (isTemporary) + return; + + if (itemPackageCode == BAZAAR) + return; + + if (owner is Player) + Database.RemoveItem((Player)owner, itemDBId); + else if (owner is Retainer) + Database.RemoveItem((Retainer)owner, itemDBId); + } + + private void SendUpdatePackets() + { + if (owner is Player && !holdingUpdates) + { + SendUpdatePackets((Player)owner); + } + } + + public void SendUpdatePackets(Player player) + { + List items = new List(); + List slotsToRemove = new List(); + + for (int i = 0; i < list.Length; i++) + { + if (i == endOfListIndex) + break; + if (isDirty[i]) + items.Add(list[i]); + } + + for (int i = endOfListIndex; i < list.Length; i++) + { + if (isDirty[i]) + slotsToRemove.Add((ushort)i); + } + + if (!holdingUpdates) + Array.Clear(isDirty, 0, isDirty.Length); + + player.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); + player.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, itemPackageCapacity, itemPackageCode)); + //Send Updated Slots + SendInventoryPackets(player, items); + //Send Remove packets for tail end + SendInventoryRemovePackets(player, slotsToRemove); + player.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); + //If player is updating their normal inventory, we need to send + //an equip update as well to resync the slots. + if (player.Equals(owner) && itemPackageCode == NORMAL) + player.GetEquipment().SendFullEquipment(); + player.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); + } + + public void StartSendUpdate() + { + holdingUpdates = true; + } + + public void DoneSendUpdate() + { + holdingUpdates = false; + SendUpdatePackets(); + Array.Clear(isDirty, 0, isDirty.Length); + } + + #endregion + + #region Inventory Utils + + public bool IsFull() + { + return endOfListIndex >= itemPackageCapacity; + } + + public int GetFreeSlots() + { + return itemPackageCapacity - endOfListIndex; + } + + public bool IsSpaceForAdd(uint itemId, int quantity, int quality) + { + int quantityCount = quantity; + for (int i = 0; i < endOfListIndex; i++) + { + InventoryItem item = list[i]; + ItemData gItem = Server.GetItemGamedata(item.itemId); + if (item.itemId == itemId && item.quality == quality && item.quantity < gItem.maxStack) + { + quantityCount -= (gItem.maxStack - item.quantity); + if (quantityCount <= 0) + break; + } + } + + return quantityCount <= 0 || (quantityCount > 0 && !IsFull()); + } + + public bool HasItem(uint itemId) + { + return HasItem(itemId, 1); + } + + public bool HasItem(uint itemId, int minQuantity) + { + return HasItem(itemId, minQuantity, 1); + } + + public bool HasItem(uint itemId, int minQuantity, int quality) + { + int count = 0; + + for (int i = endOfListIndex - 1; i >= 0; i--) + { + InventoryItem item = list[i]; + + Debug.Assert(item != null, "Item slot was null!!!"); + + if (item.itemId == itemId && item.quality == quality) + count += item.quantity; + + if (count >= minQuantity) + return true; + } + + return false; + } + + public int GetNextEmptySlot() + { + return endOfListIndex; + } + + private void DoRealign() + { + int lastNullSlot = -1; + + for (int i = 0; i < endOfListIndex; i++) + { + if (list[i] == null && lastNullSlot == -1) + { + lastNullSlot = i; + continue; + } + else if (list[i] != null && lastNullSlot != -1) + { + list[lastNullSlot] = list[i]; + list[lastNullSlot].slot = (ushort)lastNullSlot; + list[i] = null; + isDirty[lastNullSlot] = true; + isDirty[i] = true; + lastNullSlot++; + } + } + + if (lastNullSlot != -1) + endOfListIndex = lastNullSlot; + } + + #endregion + + public int GetCount() + { + return endOfListIndex; + } + } +} diff --git a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs b/FFXIVClassic Map Server/actors/chara/player/Equipment.cs deleted file mode 100644 index 49181e09..00000000 --- a/FFXIVClassic Map Server/actors/chara/player/Equipment.cs +++ /dev/null @@ -1,212 +0,0 @@ -using FFXIVClassic_Map_Server.Actors; -using FFXIVClassic_Map_Server.dataobjects; -using FFXIVClassic_Map_Server.packets.send.actor.inventory; -using System.Collections.Generic; - -namespace FFXIVClassic_Map_Server.actors.chara.player -{ - class Equipment - { - public const int SLOT_MAINHAND = 0; - public const int SLOT_OFFHAND = 1; - public const int SLOT_THROWINGWEAPON = 4; - public const int SLOT_PACK = 5; - public const int SLOT_POUCH = 6; - public const int SLOT_HEAD = 8; - public const int SLOT_UNDERSHIRT = 9; - public const int SLOT_BODY = 10; - public const int SLOT_UNDERGARMENT = 11; - public const int SLOT_LEGS = 12; - public const int SLOT_HANDS = 13; - public const int SLOT_BOOTS = 14; - public const int SLOT_WAIST = 15; - public const int SLOT_NECK = 16; - public const int SLOT_EARS = 17; - public const int SLOT_WRISTS = 19; - public const int SLOT_RIGHTFINGER = 21; - public const int SLOT_LEFTFINGER = 22; - - readonly private Player owner; - readonly private ushort inventoryCapacity; - readonly private ushort inventoryCode; - private InventoryItem[] list; - readonly private ItemPackage normalInventory; - - private bool writeToDB = true; - - public Equipment(Player ownerPlayer, ItemPackage normalInventory, ushort capacity, ushort code) - { - owner = ownerPlayer; - inventoryCapacity = capacity; - inventoryCode = code; - list = new InventoryItem[inventoryCapacity]; - this.normalInventory = normalInventory; - } - - public void SetEquipment(ushort[] slots, ushort[] itemSlots) - { - if (slots.Length != itemSlots.Length) - return; - - for (int i = 0; i < slots.Length; i++) - { - InventoryItem item = normalInventory.GetItemAtSlot(itemSlots[i]); - - if (item == null) - continue; - - Database.EquipItem(owner, slots[i], item.uniqueId); - list[slots[i]] = normalInventory.GetItemAtSlot(itemSlots[i]); - } - - owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - SendFullEquipment(owner); - owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); - } - - public void SetEquipment(InventoryItem[] toEquip) - { - List slotsToUpdate = new List(); - for (ushort i = 0; i < toEquip.Length; i++) - { - if (toEquip[i] != null) - slotsToUpdate.Add(i); - } - list = toEquip; - } - - public void Equip(ushort slot, ushort invSlot) - { - InventoryItem item = normalInventory.GetItemAtSlot(invSlot); - - if (item == null) - return; - - Equip(slot, item); - } - - public void Equip(ushort slot, InventoryItem item) - { - if (slot >= list.Length) - return; - - if (writeToDB) - Database.EquipItem(owner, slot, item.uniqueId); - - owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - - if (list[slot] != null) - normalInventory.RefreshItem(owner, list[slot], item); - else - normalInventory.RefreshItem(owner, item); - - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - SendSingleEquipmentUpdatePacket(slot, item); - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - - owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); - - list[slot] = item; - owner.CalculateBaseStats();// RecalculateStats(); - } - - public void ToggleDBWrite(bool flag) - { - writeToDB = flag; - } - - public void Unequip(ushort slot) - { - if (slot >= list.Length) - return; - - if (writeToDB) - Database.UnequipItem(owner, slot); - - - normalInventory.RefreshItem(owner, list[slot]); - - owner.QueuePacket(InventoryBeginChangePacket.BuildPacket(owner.actorId)); - SendSingleEquipmentUpdatePacket(slot, null); - owner.QueuePacket(InventoryEndChangePacket.BuildPacket(owner.actorId)); - - list[slot] = null; - owner.RecalculateStats(); - } - - public InventoryItem GetItemAtSlot(ushort slot) - { - if (slot < list.Length) - return list[slot]; - else - return null; - } - - public int GetCapacity() - { - return list.Length; - } - - #region Packet Functions - private void SendSingleEquipmentUpdatePacket(ushort equipSlot, InventoryItem item) - { - owner.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - if (item == null) - owner.QueuePacket(InventoryRemoveX01Packet.BuildPacket(owner.actorId, equipSlot)); - else - owner.QueuePacket(EquipmentListX01Packet.BuildPacket(owner.actorId, equipSlot, item.slot)); - owner.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } - - private void SendEquipmentPackets(List slotsToUpdate, Player targetPlayer) - { - int currentIndex = 0; - - while (true) - { - if (slotsToUpdate.Count - currentIndex >= 64) - targetPlayer.QueuePacket(EquipmentListX64Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex)); - else if (slotsToUpdate.Count - currentIndex >= 32) - targetPlayer.QueuePacket(EquipmentListX32Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex)); - else if (slotsToUpdate.Count - currentIndex >= 16) - targetPlayer.QueuePacket(EquipmentListX16Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex)); - else if (slotsToUpdate.Count - currentIndex > 1) - targetPlayer.QueuePacket(EquipmentListX08Packet.BuildPacket(owner.actorId, list, slotsToUpdate, ref currentIndex)); - else if (slotsToUpdate.Count - currentIndex == 1) - { - targetPlayer.QueuePacket(EquipmentListX01Packet.BuildPacket(owner.actorId, slotsToUpdate[currentIndex], list[slotsToUpdate[currentIndex]].slot)); - currentIndex++; - } - else - break; - } - } - - public void SendFullEquipment() - { - SendFullEquipment(owner); - } - - public void SendFullEquipment(Player targetPlayer) - { - List slotsToUpdate = new List(); - - for (ushort i = 0; i < list.Length; i++) - { - if (list[i] != null) - slotsToUpdate.Add(i); - } - - if (targetPlayer.Equals(owner)) - targetPlayer.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, inventoryCapacity, inventoryCode)); - else - targetPlayer.QueuePacket(InventorySetBeginPacket.BuildPacket(owner.actorId, 0x23, ItemPackage.EQUIPMENT_OTHERPLAYER)); - - SendEquipmentPackets(slotsToUpdate, targetPlayer); - - targetPlayer.QueuePacket(InventorySetEndPacket.BuildPacket(owner.actorId)); - } - - #endregion - } -} diff --git a/FFXIVClassic Map Server/actors/chara/player/Player.cs b/FFXIVClassic Map Server/actors/chara/player/Player.cs index 0d7f9606..00b85cdf 100644 --- a/FFXIVClassic Map Server/actors/chara/player/Player.cs +++ b/FFXIVClassic Map Server/actors/chara/player/Player.cs @@ -150,7 +150,7 @@ namespace FFXIVClassic_Map_Server.Actors itemPackages[ItemPackage.BAZAAR] = new ItemPackage(this, MAXSIZE_INVENTORY_BAZAAR, ItemPackage.BAZAAR); itemPackages[ItemPackage.LOOT] = new ItemPackage(this, MAXSIZE_INVENTORY_LOOT, ItemPackage.LOOT); - equipment = new Equipment(this, itemPackages[ItemPackage.NORMAL], MAXSIZE_INVENTORY_EQUIPMENT, ItemPackage.EQUIPMENT); + equipment = new Equipment(this, itemPackages[ItemPackage.NORMAL]); //Set the Skill level caps of all FFXIV (classes)skills to 50 for (int i = 0; i < charaWork.battleSave.skillLevelCap.Length; i++)