/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Items.Actions
{
using Opsive.Shared.Events;
using Opsive.Shared.Game;
using Opsive.Shared.Inventory;
using Opsive.UltimateCharacterController.Character;
using Opsive.UltimateCharacterController.Character.Abilities.Items;
using Opsive.UltimateCharacterController.Game;
using Opsive.UltimateCharacterController.Items.Actions.PerspectiveProperties;
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
using Opsive.UltimateCharacterController.Networking.Game;
#endif
using Opsive.UltimateCharacterController.Objects;
using Opsive.UltimateCharacterController.SurfaceSystem;
using Opsive.UltimateCharacterController.Utility;
#if ULTIMATE_CHARACTER_CONTROLLER_VR
using Opsive.UltimateCharacterController.VR;
#endif
using UnityEngine;
///
/// Any item that can be thrown, such as a grenade or baseball. The GameObject that the ThrowableItem attaches to is not the actual object that is thrown - the ThrownObject field
/// specifies this instead.
///
public class ThrowableItem : UsableItem
{
[Tooltip("The object that is thrown.")]
[SerializeField] protected GameObject m_ThrownObject;
[Tooltip("Should the visible object be disabled?")]
[SerializeField] protected bool m_DisableVisibleObject;
[Tooltip("Specifies if the item should wait for the OnAnimatorActivateThrowableObject animation event or wait for the specified duration before activating the throwable object.")]
[SerializeField] protected AnimationEventTrigger m_ActivateThrowableObjectEvent;
[Tooltip("Should the object be thrown when the stop use method is called?")]
[SerializeField] protected bool m_ThrowOnStopUse;
[Tooltip("The starting velocity of the thrown object.")]
[SerializeField] protected Vector3 m_Velocity = new Vector3(0, 5, 10);
[Tooltip("The layer that the item should occupy when initially spawned.")]
[SerializeField] protected int m_StartLayer = LayerManager.IgnoreRaycast;
[Tooltip("The layer that the thrown object should change to after being thrown.")]
[SerializeField] protected int m_ThrownLayer = LayerManager.Default;
[Tooltip("The amount of time after the object has been thrown to change the layer.")]
[SerializeField] protected float m_LayerChangeDelay = 0.1f;
[Tooltip("The amount of damage applied to the object hit by the thrown object.")]
[SerializeField] protected float m_DamageAmount = 10;
[Tooltip("The layers that the thrown object can collide with.")]
[SerializeField] protected LayerMask m_ImpactLayers = ~(1 << LayerManager.IgnoreRaycast | 1 << LayerManager.Overlay | 1 << LayerManager.UI | 1 << LayerManager.Overlay);
[Tooltip("The Surface Impact triggered when the object hits another object.")]
[SerializeField] protected SurfaceImpact m_SurfaceImpact;
[Tooltip("The amount of force to apply to the object hit.")]
[SerializeField] protected float m_ImpactForce = 2;
[Tooltip("The number of frames to add the impact force to.")]
[SerializeField] protected int m_ImpactForceFrames = 15;
[Tooltip("The name of the state to activate upon impact.")]
[SerializeField] protected string m_ImpactStateName;
[Tooltip("The number of seconds until the impact state is disabled. A value of -1 will require the state to be disabled manually.")]
[SerializeField] protected float m_ImpactStateDisableTimer = 10;
[Tooltip("Specifies if the item should wait for the OnAnimatorReequipThrowableItem animation event or wait for the specified duration before requipping.")]
[SerializeField] protected AnimationEventTrigger m_ReequipEvent = new AnimationEventTrigger(false, 0.5f);
[Tooltip("The value of the Item Substate Animator parameter when the item is being reequipped.")]
[SerializeField] protected int m_ReequipItemSubstateParameterValue = 10;
[Tooltip("Should the item's trajectory be shown when the character aims?")]
[SerializeField] protected bool m_ShowTrajectoryOnAim;
[Tooltip("The offset of the trajectory visualization relative to the trajectory transform set on the Throwable Item Properties.")]
[SerializeField] protected Vector3 m_TrajectoryOffset;
public GameObject ThrownObject { get { return m_ThrownObject; } set { m_ThrownObject = value; } }
public bool DisableVisibleObject { get { return m_DisableVisibleObject; } set {
m_DisableVisibleObject = value;
if (m_Item != null) {
m_Item.SetVisibleObjectActive(m_Item.VisibleObjectActive);
EnableObjectMeshRenderers(CanActivateVisibleObject());
}
}
}
public AnimationEventTrigger ActivateThrowableObjectEvent { get { return m_ActivateThrowableObjectEvent; } set { m_ActivateThrowableObjectEvent = value; } }
public bool ThrowOnStopUse { get { return m_ThrowOnStopUse; } set { m_ThrowOnStopUse = value; } }
public int StartLayer { get { return m_StartLayer; }
set
{
m_StartLayer = value;
if (m_InstantiatedThrownObject != null && !m_Thrown) {
m_InstantiatedThrownObject.layer = m_StartLayer;
}
}
}
public int ThrownLayer { get { return m_ThrownLayer; } set { m_ThrownLayer = value; } }
public float LayerChangeDelay { get { return m_LayerChangeDelay; } set { m_LayerChangeDelay = value; } }
public Vector3 Velocity { get { return m_Velocity; } set { m_Velocity = value; } }
public float DamageAmount { get { return m_DamageAmount; } set { m_DamageAmount = value; } }
public LayerMask ImpactLayers { get { return m_ImpactLayers; } set { m_ImpactLayers = value; } }
public SurfaceImpact SurfaceImpact { get { return m_SurfaceImpact; } set { m_SurfaceImpact = value; } }
public float ImpactForce { get { return m_ImpactForce; } set { m_ImpactForce = value; } }
public int ImpactForceFrames { get { return m_ImpactForceFrames; } set { m_ImpactForceFrames = value; } }
public string ImpactStateName { get { return m_ImpactStateName; } set { m_ImpactStateName = value; } }
public float ImpactStateDisableTimer { get { return m_ImpactStateDisableTimer; } set { m_ImpactStateDisableTimer = value; } }
public AnimationEventTrigger ReequipEvent { get { return m_ReequipEvent; } set { m_ReequipEvent = value; } }
public int ReequipItemSubstateParameterValue { get { return m_ReequipItemSubstateParameterValue; } set { m_ReequipItemSubstateParameterValue = value; } }
public bool ShowTrajectoryOnAim
{
get { return m_ShowTrajectoryOnAim; }
set
{
m_ShowTrajectoryOnAim = value;
if (Application.isPlaying && m_ShowTrajectoryOnAim && m_ThrownObject == null) {
Debug.LogError("Error: A TrajectoryObject must be added in order for the trajectory to be shown.");
}
}
}
public Vector3 TrajectoryOffset { get { return m_TrajectoryOffset; } set { m_TrajectoryOffset = value; } }
private TrajectoryObject m_TrajectoryObject;
protected UltimateCharacterLocomotion m_CharacterLocomotion;
protected Transform m_CharacterTransform;
private GameObject m_Object;
private Transform m_ObjectTransform;
private Renderer[] m_FirstPersonObjectRenderers;
private Renderer[] m_ThirdPersonObjectRenderers;
private IThrowableItemPerspectiveProperties m_ThrowableItemPerpectiveProperties;
private GameObject m_InstantiatedThrownObject;
protected TrajectoryObject m_InstantiatedTrajectoryObject;
private RaycastHit m_RaycastHit;
#if ULTIMATE_CHARACTER_CONTROLLER_VR
private IVRThrowableItem m_VRThrowableItem;
#endif
private bool m_Initialized;
private bool m_Aiming;
protected bool m_Throwing;
private bool m_Thrown;
private bool m_Reequipping;
private bool m_Reequipped;
private int m_ReequipFrame;
private bool m_ActivateVisibleObject;
private ScheduledEventBase m_ReequipEventBase;
private bool m_NextItemSet;
///
/// Initialize the default values.
///
protected override void Awake()
{
base.Awake();
m_TrajectoryObject = GetComponent();
m_CharacterLocomotion = m_Character.GetCachedComponent();
m_CharacterTransform = m_CharacterLocomotion.transform;
#if ULTIMATE_CHARACTER_CONTROLLER_VR
m_VRThrowableItem = GetComponent();
#endif
if (m_ThrownObject != null && m_TrajectoryObject != null) {
// The object has to be instantiated for GetComponent to work.
var instantiatedThrownObject = ObjectPool.Instantiate(m_ThrownObject);
var trajectoryCollider = instantiatedThrownObject.GetComponent();
if (trajectoryCollider != null) {
// Only sphere and capsules are supported.
if (trajectoryCollider is SphereCollider) {
var trajectorySphereCollider = trajectoryCollider as SphereCollider;
var sphereCollider = m_GameObject.AddComponent();
sphereCollider.center = trajectorySphereCollider.center;
sphereCollider.radius = trajectorySphereCollider.radius;
sphereCollider.enabled = false;
} else if (trajectoryCollider is CapsuleCollider) {
var trajectoryCapsuleCollider = trajectoryCollider as CapsuleCollider;
var capsuleCollider = m_GameObject.AddComponent();
capsuleCollider.center = trajectoryCapsuleCollider.center;
capsuleCollider.radius = trajectoryCapsuleCollider.radius;
capsuleCollider.height = trajectoryCapsuleCollider.height;
capsuleCollider.direction = trajectoryCapsuleCollider.direction;
capsuleCollider.enabled = false;
} else {
Debug.LogError($"Error: The collider of type {trajectoryCollider.GetType()} is not supported on the trajectory object " + m_ThrownObject.name);
}
m_GameObject.layer = LayerManager.SubCharacter;
}
ObjectPool.Destroy(instantiatedThrownObject);
}
m_ThrowableItemPerpectiveProperties = m_ActivePerspectiveProperties as IThrowableItemPerspectiveProperties;
if (m_ShowTrajectoryOnAim && m_TrajectoryObject == null) {
Debug.LogError($"Error: A TrajectoryObject must be added to the {m_GameObject.name} GameObject in order for the trajectory to be shown.");
}
if (m_ThrownObject == null) {
Debug.LogError($"Error: A ThrownObject must be assigned to the {m_GameObject.name} GameObject.");
}
EventHandler.RegisterEvent(m_Character, "OnAimAbilityStart", OnAim);
EventHandler.RegisterEvent(m_Character, "OnAnimatorReequipThrowableItem", ReequipThrowableItem);
}
///
/// Initialize the visible object transform.
///
protected override void Start()
{
var perspectiveItem = m_CharacterLocomotion.FirstPersonPerspective ? m_Item.FirstPersonPerspectiveItem : m_Item.ThirdPersonPerspectiveItem;
m_Object = perspectiveItem.GetVisibleObject();
m_ObjectTransform = m_Object.transform;
var firstPersonPerspectiveItem = m_Item.FirstPersonPerspectiveItem;
if (firstPersonPerspectiveItem != null) {
var visibleObject = firstPersonPerspectiveItem.GetVisibleObject();
if (visibleObject != null) {
m_FirstPersonObjectRenderers = visibleObject.GetComponentsInChildren(true);
}
}
var thirdPersonPerspectiveItem = m_Item.ThirdPersonPerspectiveItem;
if (thirdPersonPerspectiveItem != null) {
var visibleObject = thirdPersonPerspectiveItem.GetVisibleObject();
if (visibleObject != null) {
m_ThirdPersonObjectRenderers = visibleObject.GetComponentsInChildren(true);
}
}
if (m_ThrowableItemPerpectiveProperties == null) {
m_ThrowableItemPerpectiveProperties = m_ActivePerspectiveProperties as IThrowableItemPerspectiveProperties;
if (m_ThrowableItemPerpectiveProperties == null) {
Debug.LogError($"Error: The First/Third Person Throwable Item Properties component cannot be found for the Item {name}." +
$"Ensure the component exists and the component's Action ID matches the Action ID of the Item ({m_ID}).");
}
}
m_Initialized = true;
EnableObjectMeshRenderers(!m_Throwing && CanActivateVisibleObject());
}
///
/// Enables or disables the object mesh renderers for the current perspective.
///
/// Should the renderers be enabled?
public void EnableObjectMeshRenderers(bool enable)
{
var renderers = m_CharacterLocomotion.FirstPersonPerspective ? m_FirstPersonObjectRenderers : m_ThirdPersonObjectRenderers;
if (renderers != null) {
for (int i = 0; i < renderers.Length; ++i) {
renderers[i].enabled = enable;
}
}
}
///
/// Returns the ItemIdentifier which can be used by the item.
///
/// The ItemIdentifier which can be used by the item.
public override IItemIdentifier GetConsumableItemIdentifier()
{
return m_Item.ItemIdentifier;
}
///
/// Returns the amout of UsableItemIdentifier which has been consumed by the UsableItem.
///
/// The amount consumed of the UsableItemIdentifier.
public override int GetConsumableItemIdentifierAmount()
{
return -1;
}
///
/// The item has been equipped by the character.
///
public override void Equip()
{
base.Equip();
m_NextItemSet = false;
EnableObjectMeshRenderers(CanActivateVisibleObject());
#if ULTIMATE_CHARACTER_CONTROLLER_VR
if (m_VRThrowableItem != null) {
m_VRThrowableItem.Equip();
}
#endif
}
///
/// Can the visible object be activated? An example of when it shouldn't be activated is when a grenade can be thrown but it is not the primary item
/// so it shouldn't be thrown until after the throw action has started.
///
/// True if the visible object can be activated.
public override bool CanActivateVisibleObject()
{
return !m_Initialized || !m_DisableVisibleObject || m_ActivateVisibleObject;
}
///
/// Returns the substate index that the item should be in.
///
/// the substate index that the item should be in.
public override int GetItemSubstateIndex()
{
if (m_Reequipping) {
return m_ReequipItemSubstateParameterValue;
}
// When the item is being thrown the index should be positive.
if (m_Throwing) {
return Mathf.Max(1, base.GetItemSubstateIndex());
}
return -1;
}
///
/// Updates the trajectory visualization.
///
private void LateUpdate()
{
if (m_Aiming && m_ShowTrajectoryOnAim && !m_Throwing && !m_Reequipping && !m_NextItemSet && m_TrajectoryObject != null) {
var trajectoryTransform = m_ThrowableItemPerpectiveProperties.TrajectoryLocation != null ? m_ThrowableItemPerpectiveProperties.TrajectoryLocation : m_CharacterTransform;
var lookDirection = m_LookSource.LookDirection(trajectoryTransform.TransformPoint(m_TrajectoryOffset), false, m_ImpactLayers, true);
var velocity = MathUtility.TransformDirection(m_Velocity, Quaternion.LookRotation(lookDirection, m_CharacterLocomotion.Up));
// Prevent the item from being thrown behind the character. This can happen if the character is looking straight up and there is a positive
// y velocity. Gravity will cause the thrown object to go in the opposite direction.
if (Vector3.Dot(velocity.normalized, m_CharacterTransform.forward) < 0) {
velocity = m_CharacterTransform.up * velocity.magnitude;
}
m_TrajectoryObject.SimulateTrajectory(m_Character, trajectoryTransform.TransformPoint(m_TrajectoryOffset), Quaternion.identity,
velocity + (m_CharacterTransform.forward * m_CharacterLocomotion.LocalVelocity.z), Vector3.zero);
}
}
///
/// The Aim ability has started or stopped.
///
/// Has the Aim ability started?
/// Was the ability started from input?
private void OnAim(bool aim, bool inputStart)
{
if (!inputStart) {
return;
}
m_Aiming = aim;
if (!m_Aiming && m_ShowTrajectoryOnAim && m_TrajectoryObject != null) {
m_TrajectoryObject.ClearTrajectory();
}
}
///
/// Can the item be used?
///
/// The itemAbility that is trying to use the item.
/// The state of the Use ability when calling CanUseItem.
/// True if the item can be used.
public override bool CanUseItem(ItemAbility itemAbility, UseAbilityState abilityState)
{
if (!base.CanUseItem(itemAbility, abilityState)) {
return false;
}
// The item can't be used if it is already being used.
if (abilityState == UseAbilityState.Start && (m_Throwing || m_Reequipping)) {
return false;
}
// The item can't be used if there aren't any items left.
if (m_Inventory != null && m_Inventory.GetItemIdentifierAmount(m_Item.ItemIdentifier) == 0) {
return false;
}
// The item can't be used if it hasn't been started yet, is reequipping the throwable item, or has been requipped.
if (abilityState == UseAbilityState.Update && (!m_Throwing || m_Reequipping || m_Reequipped)) {
return false;
}
// Give the item a frame to recover from the reequip.
if (Time.frameCount == m_ReequipFrame) {
return false;
}
return true;
}
///
/// Starts the item use.
///
/// The item ability that is using the item.
public override void StartItemUse(ItemAbility itemAbility)
{
base.StartItemUse(itemAbility);
// An Animator Audio State Set may prevent the item from being used.
if (!IsItemInUse()) {
return;
}
if (!m_ThrowOnStopUse) {
StartThrow();
}
// Instantiate the object that will actually be thrown.
var location = m_ThrowableItemPerpectiveProperties.ThrowLocation;
m_InstantiatedThrownObject = ObjectPool.Instantiate(m_ThrownObject, location.position, location.rotation, m_ObjectTransform.parent);
m_InstantiatedThrownObject.transform.localScale = location.localScale;
m_InstantiatedThrownObject.transform.SetLayerRecursively(m_StartLayer);
m_InstantiatedTrajectoryObject = m_InstantiatedThrownObject.GetCachedComponent();
if (m_InstantiatedTrajectoryObject == null) {
Debug.LogError($"Error: {m_TrajectoryObject.name} must contain the TrajectoryObject component.");
return;
}
if (m_InstantiatedTrajectoryObject is Destructible) {
(m_InstantiatedTrajectoryObject as Destructible).InitializeDestructibleProperties(m_DamageAmount, m_ImpactForce, m_ImpactForceFrames,
m_ImpactLayers, m_ImpactStateName, m_ImpactStateDisableTimer, m_SurfaceImpact);
}
// The trajectory object will be enabled when the object is thrown.
m_InstantiatedTrajectoryObject.enabled = false;
// Hide the object that isn't thrown.
EnableObjectMeshRenderers(false);
// The instantiated object may not immediately be visible.
if (m_DisableVisibleObject) {
m_InstantiatedThrownObject.SetActive(false);
m_ActivateVisibleObject = false;
m_Item.SetVisibleObjectActive(false);
if (m_ActivateThrowableObjectEvent.WaitForAnimationEvent) {
EventHandler.RegisterEvent(m_Character, "OnAnimatorActivateThrowableObject", ActivateThrowableObject);
} else {
Scheduler.ScheduleFixed(m_ActivateThrowableObjectEvent.Duration, ActivateThrowableObject);
}
}
}
///
/// Activates the throwable object.
///
private void ActivateThrowableObject()
{
m_InstantiatedThrownObject.SetActive(true);
m_ActivateVisibleObject = true;
if (!m_Item.IsActive()) {
m_Item.SetVisibleObjectActive(true);
}
EventHandler.UnregisterEvent(m_Character, "OnAnimatorActivateThrowableObject", ActivateThrowableObject);
}
///
/// Starts the actual throw.
///
protected virtual void StartThrow()
{
if (m_Throwing) {
return;
}
m_Throwing = true;
m_Thrown = false;
m_Reequipping = false;
m_Reequipped = false;
if (m_Aiming && m_ShowTrajectoryOnAim && m_TrajectoryObject != null) {
m_TrajectoryObject.ClearTrajectory();
}
m_CharacterLocomotion.UpdateItemAbilityAnimatorParameters();
}
///
/// Uses the item.
///
public override void UseItem()
{
base.UseItem();
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
if (m_NetworkInfo != null && m_NetworkInfo.IsLocalPlayer()) {
m_NetworkCharacter.ThrowItem(this);
}
#endif
ThrowItem();
if (m_Inventory != null) {
m_Inventory.AdjustItemIdentifierAmount(m_Item.ItemIdentifier, -1);
}
m_Thrown = true;
}
///
/// Throws the throwable object.
///
public void ThrowItem()
{
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
// The object has been thrown. If the ItemAction is on the server then that object should be spawned on the network.
// Non-server actions should disable the mesh renderers so the object can take its place. The mesh renderers will be enabled again in a separate call.
if (m_NetworkInfo != null) {
EnableObjectMeshRenderers(false);
if (!m_NetworkInfo.IsServer()) {
ObjectPool.Destroy(m_InstantiatedThrownObject);
m_InstantiatedThrownObject = null;
return;
}
}
#endif
m_InstantiatedThrownObject.transform.parent = null;
// The collider was previously disabled. Enable it again when it is thrown.
var collider = m_InstantiatedThrownObject.GetCachedComponent();
collider.enabled = true;
// When the item is used the trajectory object should start moving on its own.
// The throwable item may be on the other side of an object (especially in the case of separate arms for the first person perspective). Perform a linecast
// to ensure the throwable item doesn't move through any objects.
if (!m_CharacterLocomotion.ActiveMovementType.UseIndependentLook(false) &&
Physics.Linecast(m_CharacterLocomotion.LookSource.LookPosition(), m_InstantiatedTrajectoryObject.transform.position, out m_RaycastHit,
m_ImpactLayers, QueryTriggerInteraction.Ignore)) {
m_InstantiatedTrajectoryObject.transform.position = m_RaycastHit.point;
}
var trajectoryTransform = m_ThrowableItemPerpectiveProperties.TrajectoryLocation != null ? m_ThrowableItemPerpectiveProperties.TrajectoryLocation : m_CharacterTransform;
var lookDirection = m_LookSource.LookDirection(trajectoryTransform.TransformPoint(m_TrajectoryOffset), false, m_ImpactLayers, true);
#if ULTIMATE_CHARACTER_CONTROLLER_VR
if (m_VRThrowableItem != null && m_CharacterLocomotion.FirstPersonPerspective) {
m_Velocity = m_VRThrowableItem.GetVelocity();
}
#endif
var velocity = MathUtility.TransformDirection(m_Velocity, Quaternion.LookRotation(lookDirection, m_CharacterLocomotion.Up));
// Prevent the item from being thrown behind the character. This can happen if the character is looking straight up and there is a positive
// y velocity. Gravity will cause the thrown object to go in the opposite direction.
if (Vector3.Dot(velocity.normalized, m_CharacterTransform.forward) < 0 && m_CharacterTransform.InverseTransformDirection(velocity.normalized).y > 0) {
velocity = m_CharacterTransform.up * velocity.magnitude;
}
m_InstantiatedTrajectoryObject.Initialize(velocity + (m_CharacterTransform.forward * m_CharacterLocomotion.LocalVelocity.z), Vector3.zero, m_Character, false);
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
if (m_NetworkInfo != null) {
NetworkObjectPool.NetworkSpawn(m_ThrownObject, m_InstantiatedThrownObject, true);
}
#endif
// Optionally change the layer after the object has been thrown. This allows the object to change from the first person Overlay layer
// to the Default layer after it has cleared the character's hands.
if (m_StartLayer != m_ThrownLayer) {
Scheduler.ScheduleFixed(m_LayerChangeDelay, ChangeThrownLayer, m_InstantiatedThrownObject);
}
}
///
/// Changes the thrown object to the thrown layer.
///
/// The object that was thrown.
private void ChangeThrownLayer(GameObject thrownObject)
{
thrownObject.transform.SetLayerRecursively(m_ThrownLayer);
}
///
/// The item has been used.
///
public override void ItemUseComplete()
{
base.ItemUseComplete();
m_Throwing = false;
m_CharacterLocomotion.UpdateItemAbilityAnimatorParameters();
// The item can be reequipped with an animation event or timer. The item should not be reequipped if it is disabled - the starting throw animation will
// reequip a disabled item.
if (!m_DisableVisibleObject && m_Inventory.GetItemIdentifierAmount(m_Item.ItemIdentifier) > 0) {
m_Reequipping = true;
if (!m_ReequipEvent.WaitForAnimationEvent) {
m_ReequipEventBase = Scheduler.ScheduleFixed(m_ReequipEvent.Duration, ReequipThrowableItem);
}
}
}
///
/// Tries to stop the item use.
///
public override void TryStopItemUse()
{
if (m_ThrowOnStopUse && !m_Thrown) {
StartThrow();
}
}
///
/// Can the item use be stopped?
///
/// True if the item use can be stopped.
public override bool CanStopItemUse()
{
// The item can't be stopped until the object has been thrown and is not reequipping the object.
if (!m_Thrown || m_Reequipping) {
return false;
}
return true;
}
///
/// Stops the item use.
///
public override void StopItemUse()
{
base.StopItemUse();
// If there are no items remaining then the next item should be equipped. Wait until the item use is stopped so the use animation will complete.
if (m_Inventory != null && m_Inventory.GetItemIdentifierAmount(m_Item.ItemIdentifier) == 0) {
EventHandler.ExecuteEvent(m_Character, "OnNextItemSet", m_Item, true);
m_NextItemSet = true;
}
m_Throwing = m_Thrown = false;
m_Reequipping = m_Reequipped = false;
m_ActivateVisibleObject = false;
m_InstantiatedThrownObject = null;
if (m_DisableVisibleObject) {
m_Item.SetVisibleObjectActive(false);
}
}
///
/// The ThrowableItem has been reequipped.
///
private void ReequipThrowableItem()
{
if (!m_Reequipping) {
return;
}
Scheduler.Cancel(m_ReequipEventBase);
m_ReequipEventBase = null;
m_Reequipping = false;
m_Reequipped = true;
m_ReequipFrame = Time.frameCount;
// The item shouldn't be reequipped if it is out of ammo.
if (m_Inventory != null && m_Inventory.GetItemIdentifierAmount(m_Item.ItemIdentifier) == 0) {
return;
}
if (!m_DisableVisibleObject) {
EnableObjectMeshRenderers(true);
#if ULTIMATE_CHARACTER_CONTROLLER_MULTIPLAYER
if (m_NetworkInfo != null && m_NetworkInfo.IsLocalPlayer()) {
m_NetworkCharacter.EnableThrowableObjectMeshRenderers(this);
}
#endif
}
}
///
/// The item has been unequipped by the character.
///
public override void Unequip()
{
base.Unequip();
if (m_ShowTrajectoryOnAim && m_TrajectoryObject != null) {
m_TrajectoryObject.ClearTrajectory();
}
#if ULTIMATE_CHARACTER_CONTROLLER_VR
if (m_VRThrowableItem != null) {
m_VRThrowableItem.Unequip();
}
#endif
}
///
/// The character perspective between first and third person has changed.
///
/// Is the character in a first person perspective?
protected override void OnChangePerspectives(bool firstPersonPerspective)
{
base.OnChangePerspectives(firstPersonPerspective);
// A new object is used for each perspective.
var perspectiveItem = firstPersonPerspective ? m_Item.FirstPersonPerspectiveItem : m_Item.ThirdPersonPerspectiveItem;
m_Object = perspectiveItem.GetVisibleObject();
m_ObjectTransform = m_Object.transform;
m_ThrowableItemPerpectiveProperties = m_ActivePerspectiveProperties as IThrowableItemPerspectiveProperties;
// OnChangePerspective will be called whether or not the throwable item is equipped. Only set the mesh renderer status if the item is equipped.
if (m_Item.IsActive()) {
// If the object has already been thrown then the mesh renderer should be disabled.
if (m_InstantiatedThrownObject != null) {
EnableObjectMeshRenderers(m_InstantiatedThrownObject.activeSelf);
} else {
EnableObjectMeshRenderers(!m_Thrown);
}
if (m_Throwing && !m_Thrown) {
// Setup the thrown object if the item is in the process of being thrown.
var thrownObjectTransform = m_InstantiatedThrownObject.transform;
thrownObjectTransform.parent = m_ObjectTransform.parent;
var location = m_ThrowableItemPerpectiveProperties.ThrowLocation;
thrownObjectTransform.position = location.position;
thrownObjectTransform.rotation = location.rotation;
thrownObjectTransform.localScale = location.localScale;
m_InstantiatedThrownObject.transform.SetLayerRecursively(m_StartLayer);
}
}
}
///
/// The GameObject has been destroyed.
///
protected override void OnDestroy()
{
base.OnDestroy();
EventHandler.UnregisterEvent(m_Character, "OnAimAbilityStart", OnAim);
EventHandler.UnregisterEvent(m_Character, "OnAnimatorReequipThrowableItem", ReequipThrowableItem);
}
}
}