/// --------------------------------------------- /// Ultimate Character Controller /// Copyright (c) Opsive. All Rights Reserved. /// https://www.opsive.com /// --------------------------------------------- namespace Opsive.UltimateCharacterController.Objects.CharacterAssist { using Opsive.Shared.Events; using Opsive.Shared.Game; using Opsive.Shared.Inventory; using Opsive.UltimateCharacterController.Character; using Opsive.UltimateCharacterController.Inventory; using Opsive.UltimateCharacterController.Items; using Opsive.UltimateCharacterController.Utility; using UnityEngine; /// /// Base class which allows an object with the Inventory component to pickup items when a character enters the trigger. /// public abstract class ItemPickupBase : ObjectPickup { /// /// Class which allows the specified item to be collected by the character at runtime. /// [System.Serializable] public class PickupSet { [Tooltip("The item prefab that can be picked up.")] [SerializeField] protected GameObject m_Item; [Tooltip("The ID of the category that the ItemSet should be added to.")] [SerializeField] protected uint m_CategoryID; [Tooltip("The ItemSet to load when the item is picked up.")] [SerializeField] protected ItemSet m_ItemSet; [Tooltip("Is the ItemSet the default ItemSet within the category?")] [SerializeField] protected bool m_Default; public GameObject Item { get { return m_Item; } set { m_Item = value; } } public uint CategoryID { get { return m_CategoryID; } set { m_CategoryID = value; } } public ItemSet ItemSet { get { return m_ItemSet; } set { m_ItemSet = value; } } public bool Default { get { return m_Default; } set { m_Default = value; } } /// /// Default PickupSet constructor. /// public PickupSet() { m_ItemSet = new ItemSet(); } } [Tooltip("An array of items and ItemSets to pick up.")] [SerializeField] protected PickupSet[] m_ItemPickupSet; [Tooltip("Should the object be picked up even if the inventory cannot hold any more of the ItemIdentifier?")] [SerializeField] protected bool m_AlwaysPickup; public PickupSet[] ItemPickupSet { get { return m_ItemPickupSet; } set { m_ItemPickupSet = value; } } public bool AlwaysPickup { get { return m_AlwaysPickup; } set { m_AlwaysPickup = value; } } private bool m_PickedUp; /// /// Initialize the default values. /// protected override void Awake() { base.Awake(); if (m_ItemPickupSet != null) { for (int i = 0; i < m_ItemPickupSet.Length; ++i) { // The item GameObject must contain the Item component. if (m_ItemPickupSet[i].Item != null && m_ItemPickupSet[i].Item.GetComponent() == null) { Debug.LogError($"Error: {m_ItemPickupSet[i].Item.name} doesn't contain the Item component."); } } } } /// /// A GameObject has entered the trigger. /// /// The GameObject that entered the trigger. public override void TriggerEnter(GameObject other) { TriggerEnter(other, -1); } /// /// A GameObject has entered the trigger. /// /// The other GameObject which is trying to do the pickup. /// The slot ID that picked up the item. A -1 value will indicate no specified slot. public void TriggerEnter(GameObject other, int slotID) { // The object must have an enabled inventory in order for the item to be picked up. var inventory = other.GetCachedParentComponent(); if (inventory == null || !inventory.enabled) { return; } // The collider must be a main character collider. Items or ragdoll colliders don't count. var layerManager = inventory.gameObject.GetCachedComponent(); if (layerManager == null || !MathUtility.InLayerMask(other.gameObject.layer, layerManager.CharacterLayer)) { return; } TryItemPickup(inventory, slotID); } /// /// Tries to pickup the item. /// /// The inventory belonging to the character. /// The slot ID that picked up the item. A -1 value will indicate no specified slot. private void TryItemPickup(InventoryBase inventory, int slotID) { if (m_PickupOnTriggerEnter) { DoItemPickup(inventory.gameObject, inventory, slotID, false, true); } else { // If the object is a character that has a disabled pickup item ability then the item should be picked up immediately, // even if the pickup on trigger enter is disabled. var character = inventory.gameObject.GetCachedComponent(); if (character != null) { var pickupItem = character.GetAbility(); if (pickupItem != null && pickupItem.CanItemPickup()) { DoItemPickup(inventory.gameObject, inventory, slotID, false, true); } } } } /// /// Picks up the item. /// /// The character that should pick up the item. /// The inventory belonging to the character. /// The slot ID that picked up the item. A -1 value will indicate no specified slot. /// Should the item be picked up immediately? /// Should the ItemIdentifier be picked up? This should be false if the ItemIdentifier will later be picked up. public void DoItemPickup(GameObject character, InventoryBase inventory, int slotID, bool immediatePickup, bool pickupItemIdentifier) { // Add any items to the character. if (m_ItemPickupSet != null && m_ItemPickupSet.Length > 0) { // Spawn the item under the character's ItemPlacement GameObject. var itemPlacement = character.GetComponentInChildren(true); if (itemPlacement == null) { Debug.LogError($"Error: ItemPlacement doesn't exist under the character {character.name}."); return; } for (int i = 0; i < m_ItemPickupSet.Length; ++i) { // If the Item is null then only the ItemSet should be added. if (m_ItemPickupSet[i].Item == null) { var itemSetManager = character.GetCachedComponent(); if (itemSetManager == null) { continue; } IItemCategoryIdentifier category = null; var addItemSetParents = true; // If no item is specified then the category should be retrieved from the Item Definition. if (m_ItemPickupSet[i].CategoryID == 0) { for (int j = 0; j < m_ItemPickupSet[i].ItemSet.Slots.Length; ++j) { var itemDefinition = m_ItemPickupSet[i].ItemSet.Slots[j]; if (itemDefinition != null) { category = itemDefinition.GetItemCategory(); } } } else { // A specific category was specified. if (itemSetManager.CategoryItemSets != null) { for (int j = 0; j < itemSetManager.CategoryItemSets.Length; ++j) { if (itemSetManager.CategoryItemSets[j].CategoryID == m_ItemPickupSet[i].CategoryID) { category = itemSetManager.CategoryItemSets[j].ItemCategory; addItemSetParents = false; break; } } } } if (category != null) { itemSetManager.AddItemSet(m_ItemPickupSet[i].ItemSet, m_ItemPickupSet[i].Default, category, addItemSetParents); } continue; } if (slotID != -1 && (slotID >= m_ItemPickupSet[i].ItemSet.Slots.Length || m_ItemPickupSet[i].ItemSet.Slots[slotID] == null)) { continue; } var item = m_ItemPickupSet[i].Item.GetCachedComponent(); if (inventory.HasItem(item)) { continue; } // Instantiate the item that will be added to the character. item = Item.SpawnItem(character, item); // Add the ItemSet before the item so the item can use the added ItemSet. if (m_ItemPickupSet[i].ItemSet != null) { var itemSetManager = character.GetCachedComponent(); if (itemSetManager != null) { m_ItemPickupSet[i].ItemSet.ItemIdentifiers = new Shared.Inventory.IItemIdentifier[m_ItemPickupSet[i].ItemSet.Slots.Length]; m_ItemPickupSet[i].ItemSet.ItemIdentifiers[item.SlotID] = item.ItemIdentifier; itemSetManager.AddItemSet(item, m_ItemPickupSet[i].ItemSet, m_ItemPickupSet[i].Default); } } // All of the setup is complete - add the item to the inventory. inventory.AddItem(item, false, false); } } m_PickedUp = m_AlwaysPickup; if (pickupItemIdentifier) { // Even if the ItemIdentifier doesn't have space it may be equipped by the inventory. The object should be considered as picked up in this situation. EventHandler.RegisterEvent(character, "OnAbilityWillEquipItem", OnWillEquipItem); if (DoItemIdentifierPickup(character, inventory, slotID, immediatePickup, true)) { m_PickedUp = true; } EventHandler.UnregisterEvent(character, "OnAbilityWillEquipItem", OnWillEquipItem); } else { // If pickup ItemIdentifier is false then the PickupItem ability will pick up the ItemIdentifier. m_PickedUp = true; } if (m_PickedUp) { ObjectPickedUp(character); } } /// /// Returns the ItemDefinitionAmount that the ItemPickup contains. /// /// The ItemDefinitionAmount that the ItemPickup contains. public abstract ItemDefinitionAmount[] GetItemDefinitionAmounts(); /// /// Sets the ItemPickup ItemDefinitionAmounts value. /// /// The ItemDefinitionAmount that should be set. public abstract void SetItemDefinitionAmounts(ItemDefinitionAmount[] itemDefinitionAmounts); /// /// Picks up the ItemIdentifier. /// /// The character that should pick up the ItemIdentifier. /// The inventory belonging to the character. /// The slot ID that picked up the item. A -1 value will indicate no specified slot. /// Should the item be picked up immediately? /// Should the item be force equipped? /// True if an ItemIdentifier was picked up. public bool DoItemIdentifierPickup(GameObject character, InventoryBase inventory, int slotID, bool immediatePickup, bool forceEquip) { EventHandler.ExecuteEvent(character, "OnItemPickupStartPickup"); var result = DoItemIdentifierPickupInternal(character, inventory, slotID, immediatePickup, forceEquip); EventHandler.ExecuteEvent(character, "OnItemPickupStopPickup"); return result; } /// /// Internal method which picks up the ItemIdentifier. /// /// The character that should pick up the ItemIdentifier. /// The inventory belonging to the character. /// The slot ID that picked up the item. A -1 value will indicate no specified slot. /// Should the item be picked up immediately? /// Should the item be force equipped? /// True if an ItemIdentifier was picked up. protected abstract bool DoItemIdentifierPickupInternal(GameObject character, InventoryBase inventory, int slotID, bool immediatePickup, bool forceEquip); /// /// The specified item will be equipped. /// /// The item that will be equipped. /// The slot that the item will occupy. private void OnWillEquipItem(Item item, int slotID) { m_PickedUp = true; } } }