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 } }