diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs index adc2a2b417..66d828909a 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_InventoryUI.cs @@ -156,8 +156,8 @@ namespace PerfectWorld.Scripts.Managers if (currentSelectedItem == null) return; // For equipping, we need to find an empty equipment slot - // The equip location should be determined by the item type or use a default - byte equipLocation = EC_IvtrType.GetEquipLocationForItem(currentSelectedItem.TemplateId); + // Use the new method that checks for available slots (especially for finger items) + byte equipLocation = EC_IvtrType.GetAvailableEquipLocationForItem(currentSelectedItem.TemplateId); if (equipLocation >= (byte)EC_IvtrType.IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR) { Debug.LogWarning($"[InventoryUI] Could not determine equip location for item {currentSelectedItem.TemplateId}"); diff --git a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs index 3cdbafa880..d42c58746d 100644 --- a/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs +++ b/Assets/PerfectWorld/Scripts/Managers/EC_IvtrType.cs @@ -172,6 +172,139 @@ namespace PerfectWorld.Scripts.Managers } } + /// + /// Gets the best available equipment slot for an item, checking for empty slots when multiple options exist (like finger slots) + /// + public static byte GetAvailableEquipLocationForItem(int templateId) + { + try + { + var edm = ElementDataManProvider.GetElementDataMan(); + if (edm == null) + { + Debug.LogWarning("[IvtrType] ElementDataMan not initialized"); + return (byte)IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR; + } + + uint id = unchecked((uint)templateId); + + // Weapons + foreach (var it in edm.weapon_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_WEAPON; + } + + // Projectiles (quiver / projectile essence) + foreach (var it in edm.quiver_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_PROJECTILE; + } + foreach (var it in edm.projectile_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_PROJECTILE; + } + + // Flysword + foreach (var it in edm.flysword_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_FLYSWORD; + } + + // Armor -> derive from sub-type mask + foreach (var it in edm.armor_essence_array) + { + if (it.id == id) + { + var slot = ResolveAvailableArmorSlotBySubtype(edm, it.id_sub_type); + if (slot < IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR) + { + return (byte)slot; + } + break; + } + } + + // Decoration -> derive from sub-type mask (neck, waist, etc.) + foreach (var it in edm.decoration_essence_array) + { + if (it.id == id) + { + var slot = ResolveAvailableDecorationSlotBySubtype(edm, it.id_sub_type); + if (slot < IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR) + { + return (byte)slot; + } + break; + } + } + + // Fashion -> derive from sub-type mask + foreach (var it in edm.fashion_essence_array) + { + if (it.id == id) + { + var slot = ResolveFashionSlotBySubtype(edm, it.id_sub_type); + if (slot < IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR) + { + return (byte)slot; + } + break; + } + } + + // Runes + foreach (var it in edm.damagerune_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_RUNE; + } + foreach (var it in edm.armorrune_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_RUNE; + } + + // Special slots + foreach (var it in edm.bible_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_BIBLE; + } + foreach (var it in edm.speaker_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_SPEAKER; + } + foreach (var it in edm.autohp_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_AUTOHP; + } + foreach (var it in edm.goblin_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_GOBLIN; + } + foreach (var it in edm.automp_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_AUTOMP; + } + foreach (var it in edm.force_token_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_FORCE_TOKEN; + } + foreach (var it in edm.sell_certificate_essence_array) + { + if (it.id == id) return (byte)IndexOfIteminEquipmentInventory.EQUIPIVTR_CERTIFICATE; + } + + // Fashion weapon (if present in your data as essence/config) + // If you add a dedicated essence array for fashion weapons, map it here + + Debug.LogWarning($"[IvtrType] Equip index not found for template {templateId}"); + return (byte)IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR; + } + catch (Exception ex) + { + Debug.LogWarning($"[IvtrType] Error resolving equip index for template {templateId}: {ex.Message}"); + return (byte)IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR; + } + } + private static IndexOfIteminEquipmentInventory ResolveArmorSlotBySubtype(elementdataman edm, uint armorSubTypeId) { foreach (var sub in edm.armor_sub_type_array) @@ -224,6 +357,97 @@ namespace PerfectWorld.Scripts.Managers } return IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR; } + + /// + /// Resolves armor slot by subtype, checking for available finger slots when applicable + /// + private static IndexOfIteminEquipmentInventory ResolveAvailableArmorSlotBySubtype(elementdataman edm, uint armorSubTypeId) + { + foreach (var sub in edm.armor_sub_type_array) + { + if (sub.id != armorSubTypeId) continue; + uint mask = sub.equip_mask; + + // Check finger slots first - try to find an empty one + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1)) != 0 || + (mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2)) != 0) + { + var availableFingerSlot = GetAvailableFingerSlot(); + if (availableFingerSlot != IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR) + { + return availableFingerSlot; + } + } + + // For other slots, return the first matching one (original behavior) + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_HEAD)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_HEAD; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_SHOULDER)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_SHOULDER; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_BODY)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_BODY; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_WAIST)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_WAIST; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_LEG)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_LEG; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FOOT)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_FOOT; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_WRIST)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_WRIST; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2; + break; + } + return IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR; + } + + /// + /// Resolves decoration slot by subtype, checking for available finger slots when applicable + /// + private static IndexOfIteminEquipmentInventory ResolveAvailableDecorationSlotBySubtype(elementdataman edm, uint decorationSubTypeId) + { + foreach (var sub in edm.decoration_sub_type_array) + { + if (sub.id != decorationSubTypeId) continue; + uint mask = sub.equip_mask; + + // Check finger slots first - try to find an empty one + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1)) != 0 || + (mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2)) != 0) + { + var availableFingerSlot = GetAvailableFingerSlot(); + if (availableFingerSlot != IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR) + { + return availableFingerSlot; + } + } + + // For other slots, return the first matching one (original behavior) + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_NECK)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_NECK; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_WAIST)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_WAIST; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2; + if ((mask & (1u << (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_WRIST)) != 0) return IndexOfIteminEquipmentInventory.EQUIPIVTR_WRIST; + break; + } + return IndexOfIteminEquipmentInventory.SIZE_EQUIPIVTR; + } + + /// + /// Gets an available finger slot, preferring FINGER1 if both are empty, otherwise returning the empty one + /// + private static IndexOfIteminEquipmentInventory GetAvailableFingerSlot() + { + // Check if FINGER1 slot is empty + var finger1Item = EC_Inventory.GetItem(EC_Inventory.PACK_EQUIPMENT, (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1, false); + if (finger1Item == null) + { + return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1; + } + + // Check if FINGER2 slot is empty + var finger2Item = EC_Inventory.GetItem(EC_Inventory.PACK_EQUIPMENT, (int)IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2, false); + if (finger2Item == null) + { + return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER2; + } + + // Both slots are occupied, return FINGER1 as fallback (original behavior) + return IndexOfIteminEquipmentInventory.EQUIPIVTR_FINGER1; + } } }