Files
BABA_YAGA/Assets/Opsive/UltimateCharacterController/Scripts/Character/Abilities/Items/Aim.cs
2026-06-14 23:57:44 +07:00

369 lines
17 KiB
C#

/// ---------------------------------------------
/// Ultimate Character Controller
/// Copyright (c) Opsive. All Rights Reserved.
/// https://www.opsive.com
/// ---------------------------------------------
namespace Opsive.UltimateCharacterController.Character.Abilities.Items
{
using Opsive.Shared.Events;
using Opsive.UltimateCharacterController.Input;
using Opsive.UltimateCharacterController.Utility;
using UnityEngine;
/// <summary>
/// ItemAbility which will aim the item.
/// </summary>
[DefaultStartType(AbilityStartType.ButtonDownContinuous)]
[DefaultStopType(AbilityStopType.ButtonUp)]
[DefaultInputName("Fire2")]
[DefaultItemStateIndex(1)]
[DefaultState("Aim")]
public class Aim : ItemAbility
{
[Tooltip("When the Aim ability is activated should it stop the speed change ability?")]
[SerializeField] protected bool m_StopSpeedChange = true;
[Tooltip("Should the ability activate when the first person perspective is enabled?")]
[SerializeField] protected bool m_ActivateInFirstPerson = true;
[Tooltip("Should the ability rotate the character to face the look source target?")]
[SerializeField] protected bool m_RotateTowardsLookSourceTarget = true;
public bool StopSpeedChange { get { return m_StopSpeedChange; } set { m_StopSpeedChange = value; } }
public bool ActivateInFirstPerson { get { return m_ActivateInFirstPerson; } set { m_ActivateInFirstPerson = value; } }
public bool RotateTowardsLookSourceTarget { get { return m_RotateTowardsLookSourceTarget; } set { m_RotateTowardsLookSourceTarget = value; } }
private ILookSource m_LookSource;
private bool m_FirstPersonPerspective;
private bool m_InputStart;
private bool m_PerspectiveSwitch;
#if THIRD_PERSON_CONTROLLER
private ThirdPersonController.Character.Abilities.Items.ItemPullback m_ItemPullback;
#endif
public override bool CanReceiveMultipleStarts { get { return true; } }
public bool InputStart { get { return m_InputStart; } }
/// <summary>
/// Initialize the default values.
/// </summary>
public override void Awake()
{
base.Awake();
#if THIRD_PERSON_CONTROLLER
m_ItemPullback = m_CharacterLocomotion.GetAbility<ThirdPersonController.Character.Abilities.Items.ItemPullback>();
#endif
// The look source may have already been assigned if the ability was added to the character after the look source was assigned.
m_LookSource = m_CharacterLocomotion.LookSource;
EventHandler.RegisterEvent<ILookSource>(m_GameObject, "OnCharacterAttachLookSource", OnAttachLookSource);
EventHandler.RegisterEvent<bool>(m_GameObject, "OnCameraWillChangePerspectives", OnChangePerspectives);
EventHandler.RegisterEvent<Ability, bool>(m_GameObject, "OnCharacterAbilityActive", OnAbilityActive);
EventHandler.RegisterEvent<ItemAbility, bool>(m_GameObject, "OnCharacterItemAbilityActive", OnItemAbilityActive);
EventHandler.RegisterEvent(m_GameObject, "OnRespawn", OnRespawn);
}
/// <summary>
/// A new ILookSource object has been attached to the character.
/// </summary>
/// <param name="lookSource">The ILookSource object attached to the character.</param>
private void OnAttachLookSource(ILookSource lookSource)
{
m_LookSource = lookSource;
}
/// <summary>
/// The character perspective between first and third person has changed.
/// </summary>
/// <param name="firstPersonPerspective">Is the character in a first person perspective?</param>
private void OnChangePerspectives(bool firstPersonPerspective)
{
// While in first person mode the character is always aiming so the ability should be started so the character shadow is correct.
m_FirstPersonPerspective = firstPersonPerspective;
if (m_ActivateInFirstPerson) {
if (!IsActive && firstPersonPerspective) {
StartAbility();
} else if ( IsActive && !firstPersonPerspective && !m_InputStart) {
m_PerspectiveSwitch = true;
StopAbility(true);
m_PerspectiveSwitch = false;
}
}
}
/// <summary>
/// Returns the Item State Index which corresponds to the slot ID.
/// </summary>
/// <param name="slotID">The ID of the slot that corresponds to the Item State Index.</param>
/// <returns>The Item State Index which corresponds to the slot ID.</returns>
public override int GetItemStateIndex(int slotID)
{
#if THIRD_PERSON_CONTROLLER
// If the ItemPullback ability is active then an object would be obstructing the item location. Wait to change the state index
// until no objects are obstructing the item location.
if (m_ItemPullback != null && m_ItemPullback.IsActive) {
return -1;
}
#endif
return base.GetItemStateIndex(slotID);
}
/// <summary>
/// Returns the Item Substate Index which corresponds to the slot ID.
/// </summary>
/// <param name="slotID">The ID of the slot that corresponds to the Item Substate Index.</param>
/// <returns>The Item Substate Index which corresponds to the slot ID.</returns>
public override int GetItemSubstateIndex(int slotID)
{
#if THIRD_PERSON_CONTROLLER
// If the ItemPullback ability is active then an object would be obstructing the item location. Wait to change the state index
// until no objects are obstructing the item location.
if (m_ItemPullback != null && m_ItemPullback.IsActive) {
return -1;
}
#endif
// Return the UsableItem's substate index if it isn't 0. This will allow the animator to know if the item is out of ammo.
var item = m_Inventory.GetActiveItem(slotID);
if (item != null && item.DominantItem) {
var itemActions = item.ItemActions;
for (int i = 0; i < itemActions.Length; ++i) {
var usableItem = itemActions[i] as UltimateCharacterController.Items.Actions.IUsableItem;
if (usableItem != null) {
var substateIndex = usableItem.GetItemSubstateIndex();
if (substateIndex != -1) {
return substateIndex;
}
}
}
}
return m_InputStart ? 1 : 0;
}
/// <summary>
/// Called when the ablity is tried to be started. If false is returned then the ability will not be started.
/// </summary>
/// <returns>True if the ability can be started.</returns>
public override bool CanStartAbility()
{
if (m_InputStart) {
return false;
}
#if THIRD_PERSON_CONTROLLER
if (m_ItemPullback != null && m_ItemPullback.IsActive) {
return false;
}
#endif
return base.CanStartAbility();
}
/// <summary>
/// Called when another ability is attempting to start and the current ability is active.
/// Returns true or false depending on if the new ability should be blocked from starting.
/// </summary>
/// <param name="startingAbility">The ability that is starting.</param>
/// <returns>True if the ability should be blocked.</returns>
public override bool ShouldBlockAbilityStart(Ability startingAbility)
{
if (base.ShouldBlockAbilityStart(startingAbility)) {
return true;
}
return PreventAbility(startingAbility);
}
/// <summary>
/// Called when the current ability is attempting to start and another ability is active.
/// Returns true or false depending on if the active ability should be stopped.
/// </summary>
/// <param name="activeAbility">The ability that is currently active.</param>
/// <returns>True if the ability should be stopped.</returns>
public override bool ShouldStopActiveAbility(Ability activeAbility)
{
if (base.ShouldStopActiveAbility(activeAbility)) {
return true;
}
return PreventAbility(activeAbility);
}
/// <summary>
/// Can the specified ability be active while the Aim ability is active?
/// </summary>
/// <param name="activeAbility">The ability to check if it can be active.</param>
/// <returns>True if the specified ability can be active while the Aim ability is active.</returns>
private bool PreventAbility(Ability activeAbility)
{
// InputStart isn't set until AbilityStarted so the InputIndex should be used as well.
if (m_StopSpeedChange && (InputIndex != -1 || m_InputStart) && activeAbility is SpeedChange) {
return true;
}
return false;
}
/// <summary>
/// The ability has started.
/// </summary>
protected override void AbilityStarted()
{
// If the ability started because of the first person perspective change then the state shouldn't be changed. If the state was changed
// to first person and the state was changed then the camera would zoom in which should only occur with a button press.
if (InputIndex != -1 || m_StartType == AbilityStartType.Automatic) {
base.AbilityStarted();
m_InputStart = true;
}
EventHandler.ExecuteEvent(m_GameObject, "OnAimAbilityStart", true, m_InputStart);
EventHandler.ExecuteEvent(m_GameObject, "OnAimAbilityAim", true);
}
/// <summary>
/// Update the controller's rotation values.
/// </summary>
public override void UpdateRotation()
{
// If the character can look independently then the character does not need to rotate to face the look direction.
if (m_CharacterLocomotion.ActiveMovementType.UseIndependentLook(true)) {
return;
}
// The look source may be null if a remote player is still being initialized.
if (m_LookSource == null || !m_RotateTowardsLookSourceTarget) {
return;
}
// Determine the direction that the character should be facing.
var lookDirection = m_LookSource.LookDirection(m_LookSource.LookPosition(), true, m_CharacterLayerManager.IgnoreInvisibleCharacterLayers, false);
var rotation = m_Transform.rotation * Quaternion.Euler(m_CharacterLocomotion.DeltaRotation);
var localLookDirection = MathUtility.InverseTransformDirection(lookDirection, rotation);
localLookDirection.y = 0;
lookDirection = MathUtility.TransformDirection(localLookDirection, rotation);
var targetRotation = Quaternion.LookRotation(lookDirection, rotation * Vector3.up);
m_CharacterLocomotion.DeltaRotation = (Quaternion.Inverse(m_Transform.rotation) * targetRotation).eulerAngles;
}
/// <summary>
/// Can the input stop the ability?
/// </summary>
/// <param name="playerInput">A reference to the input component.</param>
/// <returns>True if the input can stop the ability.</returns>
public override bool CanInputStopAbility(PlayerInput playerInput)
{
if (m_ActivateInFirstPerson && !m_InputStart) {
return false;
}
return base.CanInputStopAbility(playerInput);
}
/// <summary>
/// Can the ability be stopped?
/// </summary>
/// <returns>True if the ability can be stopped.</returns>
public override bool CanStopAbility()
{
if (m_ActivateInFirstPerson && m_FirstPersonPerspective) {
// The ability can't stop for as long as the character is in first person mode. If the ability was started in with a button press then
// the ability shouldn't stop but it should change the state so the camera will no longer zoom.
if (m_InputStart) {
base.AbilityStopped(false);
EventHandler.ExecuteEvent(m_GameObject, "OnAimAbilityStart", false, m_InputStart);
m_InputStart = false;
m_CharacterLocomotion.UpdateItemAbilityAnimatorParameters();
}
return false;
}
return true;
}
/// <summary>
/// The ability has stopped running.
/// </summary>
/// <param name="force">Was the ability force stopped?</param>
protected override void AbilityStopped(bool force)
{
// The base AbilityStopped may have already been called within CanStopAbility - don't call it again to prevent duplicate calls.
EventHandler.ExecuteEvent(m_GameObject, "OnAimAbilityAim", false);
if (m_PerspectiveSwitch && !m_InputStart) {
return;
}
EventHandler.ExecuteEvent(m_GameObject, "OnAimAbilityStart", false, m_InputStart);
m_InputStart = false;
base.AbilityStopped(force);
}
/// <summary>
/// Should the input be checked to ensure button up is using the correct value?
/// </summary>
/// <returns>True if the input should be checked.</returns>
protected override bool ShouldCheckInput() { return false; }
/// <summary>
/// The ability has been started or stopped.
/// </summary>
/// <param name="ability">The ability which was started or stopped.</param>
/// <param name="active">True if the ability was started, false if it was stopped.</param>
private void OnAbilityActive(Ability ability, bool active)
{
// Another ability may have stopped aiming while in first person. Activate aiming again if necessary.
if (!IsActive && !active && m_ActivateInFirstPerson && m_CharacterLocomotion.FirstPersonPerspective) {
StartAbility();
}
}
/// <summary>
/// The item ability has been started or stopped.
/// </summary>
/// <param name="itemAbility">The item ability which was started or stopped.</param>
/// <param name="active">True if the ability was started, false if it was stopped.</param>
private void OnItemAbilityActive(ItemAbility itemAbility, bool active)
{
#if UNITY_EDITOR
if (IsActive && itemAbility.Index > Index && (itemAbility is Use
#if ULTIMATE_CHARACTER_CONTROLLER_SHOOTER
|| itemAbility is Reload
#endif
)) {
Debug.Log($"Warning: The ability {itemAbility.GetType().Name} started but it has a lower priority than the aim ability." +
"This will prevent that ability from updating the Animator.");
}
#endif
#if ULTIMATE_CHARACTER_CONTROLLER_SHOOTER
// The Aim ability should start again if the Reload ability stopped and is in a first person view. The Reload ability would have
// stopped the Aim ability.
if (itemAbility is Reload && !active && m_CharacterLocomotion.FirstPersonPerspective) {
OnChangePerspectives(true);
}
#endif
#if THIRD_PERSON_CONTROLLER
// If the ItemPullback ability is activated then the aim state should no longer be set. This will prevent the aim animator parameters from updating.
if (IsActive && itemAbility == m_ItemPullback) {
EventHandler.ExecuteEvent(m_GameObject, "OnAimAbilityAim", !active);
}
#endif
}
/// <summary>
/// The character has respawned. Determine if the ability should start.
/// </summary>
private void OnRespawn()
{
if (m_ActivateInFirstPerson && m_CharacterLocomotion.FirstPersonPerspective) {
StartAbility();
}
}
/// <summary>
/// Called when the character is destroyed.
/// </summary>
public override void OnDestroy()
{
base.OnDestroy();
EventHandler.UnregisterEvent<ILookSource>(m_GameObject, "OnCharacterAttachLookSource", OnAttachLookSource);
EventHandler.UnregisterEvent<bool>(m_GameObject, "OnCameraWillChangePerspectives", OnChangePerspectives);
EventHandler.UnregisterEvent<Ability, bool>(m_GameObject, "OnCharacterAbilityActive", OnAbilityActive);
EventHandler.UnregisterEvent<ItemAbility, bool>(m_GameObject, "OnCharacterItemAbilityActive", OnItemAbilityActive);
EventHandler.UnregisterEvent(m_GameObject, "OnRespawn", OnRespawn);
}
}
}