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++)