using System; using System.Collections.Generic; using System.Linq; using Sirenix.OdinInspector; using Sirenix.Utilities; using UnityEngine; using VOID.Generic.Dict; using VOID.Generic.Objects.Abstract.Events; using VOID.Generic.Objects.Accessory; using VOID.Generic.Objects.Armor; using VOID.Generic.Objects.Item.Events; using VOID.Generic.Objects.Unit.Equipment; using VOID.Generic.Objects.Unit.Events; using VOID.Generic.Objects.Weapon; using VOID.Generic.Objects.Wearable; using VOID.Generic.Objects.Wearable.Events; using static VOID.Generic.Util.WearableUtils; namespace VOID.Generic.Objects.Unit { /// /// Module of which manage currently equipped items by this unit.
/// List of available equip slots: ///
[Serializable] public class UnitObjectEquipment : ObjectComponent { #region Initialize protected override void SelfInitialize() { // Empty GameObject where all items from backpack are "attached" _equipmentGameObject = parent.transform.Find("_equipment")?.gameObject; if (_equipmentGameObject == null) { _equipmentGameObject = new GameObject("_equipment"); _equipmentGameObject.transform.parent = parent.gameObject.transform; _equipmentGameObject.transform.localPosition = Vector3.zero; } } protected override void Initialize() { // Equip all items pre-equipped from inspector Enum.GetValues(typeof(EquipmentSlotType)).OfType() .Where(slotType => GetEquipped(slotType)) .ForEach(slotType => FakeEquipOnInitForPrefab(GetEquipped(slotType), slotType)); } private WearableObject FakeEquipOnInitForPrefab(WearableObject wearable, EquipmentSlotType slotType) { // If item is prefab asset then we need to instantiate it first if (wearable.gameObject.scene.IsValid() == false) { wearable = UnityEngine.Object.Instantiate(wearable).GetComponent(); } var pickEvent = new PickEvent { item = wearable, unit = parent }; var equipEvent = new EquipEvent { wearable = wearable, unit = parent, slotIndex = (int)slotType, slotType = slotType }; wearable.itemEvents.onPickedBefore.Invoke(pickEvent); wearable.itemEvents.onPickedAfter.Invoke(pickEvent); wearable.wearableEvents.onEquippedBefore.Invoke(equipEvent); wearable.wearableEvents.onEquippedAfter.Invoke(equipEvent); parent.unitEvents.onEquipBefore.Invoke(equipEvent); parent.unitEvents.onEquipAfter.Invoke(equipEvent); return wearable; } #endregion /// /// For each func checking if given wearable can be equipped /// private static readonly Dictionary> CanWearInSlotType = new() { [EquipmentSlotType.MainHand] = wearable => IsWeaponCarryType(wearable, WeaponCarryType.TwoHand, WeaponCarryType.OneHandAny, WeaponCarryType.OneHandMain), [EquipmentSlotType.OffHand] = wearable => IsWeaponCarryType(wearable, WeaponCarryType.OneHandAny, WeaponCarryType.OneHandOff), [EquipmentSlotType.Helmet] = wearable => IsType(wearable, ArmorType.Helmet), [EquipmentSlotType.Armor] = wearable => IsType(wearable, ArmorType.Armor), [EquipmentSlotType.Pants] = wearable => IsType(wearable, ArmorType.Pants), [EquipmentSlotType.Belt] = wearable => IsType(wearable, ArmorType.Belt), [EquipmentSlotType.Boots] = wearable => IsType(wearable, ArmorType.Boots), [EquipmentSlotType.Gloves] = wearable => IsType(wearable, ArmorType.Gloves), [EquipmentSlotType.Necklace] = wearable => IsType(wearable, AccessoryType.Necklace), [EquipmentSlotType.RingOne] = wearable => IsType(wearable, AccessoryType.Ring), [EquipmentSlotType.RingTwo] = wearable => IsType(wearable, AccessoryType.Ring), }; [ShowInInspector, ReadOnly] private GameObject _equipmentGameObject; [SerializeField, DisableInPlayMode] private EquippedItem mainHand; [SerializeField, DisableInPlayMode] private EquippedItem offHand; [SerializeField, DisableInPlayMode] private EquippedItem helmet; [SerializeField, DisableInPlayMode] private EquippedItem armor; [SerializeField, DisableInPlayMode] private EquippedItem pants; [SerializeField, DisableInPlayMode] private EquippedItem belt; [SerializeField, DisableInPlayMode] private EquippedItem boots; [SerializeField, DisableInPlayMode] private EquippedItem gloves; [SerializeField, DisableInPlayMode] private EquippedItem necklace; [SerializeField, DisableInPlayMode] private EquippedItem ringOne; [SerializeField, DisableInPlayMode] private EquippedItem ringTwo; /// /// Returns equipped WearableObject in given slot. /// public WearableObject GetEquipped(EquipmentSlotType equipmentSlotType) { var equipItem = GetEquippedItemByType(equipmentSlotType); return equipItem.wearable ? equipItem.wearable : null; } /// /// Returns equopped WearableObject in given slot index. By enum: /// public WearableObject GetEquipped(int equipmentSlotIndex) { return GetEquipped((EquipmentSlotType)equipmentSlotIndex); } /// /// Checks if given WearableObject can be equipped in given Slot. /// public bool CanEquipInSlot(EquipmentSlotType equipmentSlotType, WearableObject wearable) { return CanWearInSlotType.GetValueOrDefault(equipmentSlotType).Invoke(wearable); } /// /// Checks if given WearableObject can be equipped in given Slot index. By enum: /// public bool CanEquipInSlot(int equipmentSlotIndex, WearableObject wearable) { return CanEquipInSlot((EquipmentSlotType)equipmentSlotIndex, wearable); } /// /// Equip given WearableObject in first valid slot. /// public void Equip(WearableObject wearable) { var equipRingOne = !ringOne.wearable || ringTwo.wearable; var equipRingTwo = ringOne.wearable && !ringTwo.wearable; var equipmentSlotType = wearable switch { ArmorObject when IsType(wearable, ArmorType.Armor) => EquipmentSlotType.Armor, ArmorObject when IsType(wearable, ArmorType.Belt) => EquipmentSlotType.Belt, ArmorObject when IsType(wearable, ArmorType.Boots) => EquipmentSlotType.Boots, ArmorObject when IsType(wearable, ArmorType.Gloves) => EquipmentSlotType.Gloves, ArmorObject when IsType(wearable, ArmorType.Helmet) => EquipmentSlotType.Helmet, ArmorObject when IsType(wearable, ArmorType.Pants) => EquipmentSlotType.Pants, AccessoryObject when IsType(wearable, AccessoryType.Necklace) => EquipmentSlotType.Necklace, AccessoryObject when IsType(wearable, AccessoryType.Ring) && equipRingOne => EquipmentSlotType.RingOne, AccessoryObject when IsType(wearable, AccessoryType.Ring) && equipRingTwo => EquipmentSlotType.RingTwo, WeaponObject when IsWeaponCarryType(wearable, WeaponCarryType.TwoHand, WeaponCarryType.OneHandAny, WeaponCarryType.OneHandMain) => EquipmentSlotType.MainHand, WeaponObject when IsWeaponCarryType(wearable, WeaponCarryType.OneHandAny, WeaponCarryType.OneHandOff) => EquipmentSlotType.OffHand, _ => throw new ArgumentOutOfRangeException(nameof(wearable)) }; Equip(equipmentSlotType, wearable); } /// /// Equip given WearableObject in given slot index. By enum: /// public void Equip(int equipmentSlotIndex, WearableObject wearable) { Equip((EquipmentSlotType)equipmentSlotIndex, wearable); } /// /// Equip given WearableObject in given slot. /// public void Equip(EquipmentSlotType equipmentSlotType, WearableObject wearable) { if (!CanEquipInSlot(equipmentSlotType, wearable)) { Debug.LogError($"Given wearableItem '{wearable.name}' cannot be equipped in that slot", wearable); return; } // Someone already equipped that item if (wearable.wearableState.wearerUnit && wearable.wearableState.wearerUnit != parent) { Debug.LogError($"Given WearableItem '${wearable.name}' is already equipped by someone else!", wearable); return; } // Someone else already carrying that item if (wearable.itemState.carriedBy && wearable.itemState.carriedBy != parent) { Debug.LogError($"Given WearableItem '{wearable.name}' is already carried by someone else!", wearable); return; } // If item remove itself then we need to remove it from wearable.basicEvents.onRemove.AddListener(OnItemRemove); // Update equipped item var equippedItem = GetEquippedItemByType(equipmentSlotType); equippedItem.wearable = wearable; equippedItem.gameObject.transform.SetParent(_equipmentGameObject.transform); equippedItem.gameObject.transform.SetLocalPositionAndRotation(Vector3.zero, new Quaternion()); parent.unitEvents.onEquipmentEquip.Invoke(new EquipmentEquipEvent { unit = parent, wearable = wearable, equipmentSlot = equipmentSlotType }); } /// /// Unequip WearableObject from given slot index. By enum: /// public void UnEquip(int index) { UnEquip((EquipmentSlotType)index); } /// /// Unequip WearableObject. /// public void UnEquip(WearableObject wearable) { var equipmentSlotType = wearable switch { WeaponObject when wearable == mainHand.wearable => EquipmentSlotType.MainHand, WeaponObject when wearable == offHand.wearable => EquipmentSlotType.OffHand, ArmorObject when wearable == helmet.wearable => EquipmentSlotType.Helmet, ArmorObject when wearable == armor.wearable => EquipmentSlotType.Armor, ArmorObject when wearable == pants.wearable => EquipmentSlotType.Pants, ArmorObject when wearable == belt.wearable => EquipmentSlotType.Belt, ArmorObject when wearable == boots.wearable => EquipmentSlotType.Boots, ArmorObject when wearable == gloves.wearable => EquipmentSlotType.Gloves, AccessoryObject when wearable == necklace.wearable => EquipmentSlotType.Necklace, AccessoryObject when wearable == ringOne.wearable => EquipmentSlotType.RingOne, AccessoryObject when wearable == ringTwo.wearable => EquipmentSlotType.RingTwo, _ => throw new ArgumentOutOfRangeException(nameof(wearable)) }; UnEquip(equipmentSlotType); } /// /// Unequip WearableObject from given slot. /// public void UnEquip(EquipmentSlotType equipmentSlotType) { var equippedItem = GetEquippedItemByType(equipmentSlotType); var tempWearable = equippedItem.wearable; tempWearable.basicEvents.onRemove.RemoveListener(OnItemRemove); // Remove reference from equipped slot equippedItem.wearable = null; parent.unitEvents.onEquipmentUnEquip.Invoke(new EquipmentUnEquipEvent { unit = parent, wearable = tempWearable, equipmentSlot = equipmentSlotType }); } /// /// Returns for given . /// private EquippedItem GetEquippedItemByType(EquipmentSlotType equipmentSlot) { return equipmentSlot switch { EquipmentSlotType.MainHand => mainHand, EquipmentSlotType.OffHand => offHand, EquipmentSlotType.Helmet => helmet, EquipmentSlotType.Armor => armor, EquipmentSlotType.Pants => pants, EquipmentSlotType.Belt => belt, EquipmentSlotType.Boots => boots, EquipmentSlotType.Gloves => gloves, EquipmentSlotType.Necklace => necklace, EquipmentSlotType.RingOne => ringOne, EquipmentSlotType.RingTwo => ringTwo, _ => throw new ArgumentOutOfRangeException(nameof(equipmentSlot), equipmentSlot, null) }; } /// /// When item remove itself then also remove it from equipment. /// private void OnItemRemove(RemoveEvent removeEvent) { UnEquip(removeEvent.obj as WearableObject); } } }