Files
2026-07-09 21:33:33 +02:00

308 lines
14 KiB
C#

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
{
/// <summary>
/// Module of <see cref="UnitObject"/> which manage currently equipped items by this unit.<br/>
/// List of available equip slots: <see cref="EquipmentSlotType"/>
/// </summary>
[Serializable]
public class UnitObjectEquipment : ObjectComponent<UnitObject>
{
#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<EquipmentSlotType>()
.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<WeaponObject>();
}
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
/// <summary>
/// For each <see cref="EquipmentSlotType"/> func checking if given wearable can be equipped
/// </summary>
private static readonly Dictionary<EquipmentSlotType, Func<WearableObject, bool>> 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;
/// <summary>
/// Returns equipped WearableObject in given slot.
/// </summary>
public WearableObject GetEquipped(EquipmentSlotType equipmentSlotType)
{
var equipItem = GetEquippedItemByType(equipmentSlotType);
return equipItem.wearable ? equipItem.wearable : null;
}
/// <summary>
/// Returns equopped WearableObject in given slot index. By enum: <see cref="EquipmentSlotType"/>
/// </summary>
public WearableObject GetEquipped(int equipmentSlotIndex)
{
return GetEquipped((EquipmentSlotType)equipmentSlotIndex);
}
/// <summary>
/// Checks if given WearableObject can be equipped in given Slot.
/// </summary>
public bool CanEquipInSlot(EquipmentSlotType equipmentSlotType, WearableObject wearable)
{
return CanWearInSlotType.GetValueOrDefault(equipmentSlotType).Invoke(wearable);
}
/// <summary>
/// Checks if given WearableObject can be equipped in given Slot index. By enum: <see cref="EquipmentSlotType"/>
/// </summary>
public bool CanEquipInSlot(int equipmentSlotIndex, WearableObject wearable)
{
return CanEquipInSlot((EquipmentSlotType)equipmentSlotIndex, wearable);
}
/// <summary>
/// Equip given WearableObject in first valid slot.
/// </summary>
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);
}
/// <summary>
/// Equip given WearableObject in given slot index. By enum: <see cref="EquipmentSlotType"/>
/// </summary>
public void Equip(int equipmentSlotIndex, WearableObject wearable)
{
Equip((EquipmentSlotType)equipmentSlotIndex, wearable);
}
/// <summary>
/// Equip given WearableObject in given slot.
/// </summary>
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
});
}
/// <summary>
/// Unequip WearableObject from given slot index. By enum: <see cref="EquipmentSlotType"/>
/// </summary>
public void UnEquip(int index)
{
UnEquip((EquipmentSlotType)index);
}
/// <summary>
/// Unequip WearableObject.
/// </summary>
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);
}
/// <summary>
/// Unequip WearableObject from given slot.
/// </summary>
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
});
}
/// <summary>
/// Returns <see cref="EquippedItem"/> for given <see cref="EquipmentSlotType"/>.
/// </summary>
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)
};
}
/// <summary>
/// When item remove itself then also remove it from equipment.
/// </summary>
private void OnItemRemove(RemoveEvent removeEvent)
{
UnEquip(removeEvent.obj as WearableObject);
}
}
}