/// ---------------------------------------------
/// 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.Shared.Game;
using Opsive.UltimateCharacterController.Items.Actions;
using Opsive.UltimateCharacterController.Utility;
using UnityEngine;
///
/// The Block ability will play a blocking animation when another object comes into contact with the Shield ItemAction.
///
[DefaultStartType(AbilityStartType.Manual)]
[DefaultStopType(AbilityStopType.Manual)]
public class Block : ItemAbility
{
[Tooltip("The slot that should be used. -1 will block all of the slots.")]
[SerializeField] protected int m_SlotID = -1;
[Tooltip("The Animator's Item State Index when the character blocks.")]
[SerializeField] protected int m_BlockItemStateIndex = 7;
[Tooltip("The Animator's Item State Index when the character parries.")]
[SerializeField] protected int m_ParryItemStateIndex = 8;
public int SlotID
{
get { return m_SlotID; }
set
{
if (m_SlotID != value) {
UnregisterSlotEvents(m_SlotID);
m_SlotID = value;
RegisterSlotEvents(m_SlotID);
}
}
}
public int BlockItemStateIndex { get { return m_BlockItemStateIndex; } set { m_BlockItemStateIndex = value; } }
public int ParryItemStateIndex { get { return m_ParryItemStateIndex; } set { m_ParryItemStateIndex = value; } }
private Shield[] m_Shields;
private object[] m_ImpactSources;
private ScheduledEventBase[] m_BlockEvents;
private bool m_Parry;
public object[] ImpactSources { get { return m_ImpactSources; } }
///
/// Initialize the default values.
///
public override void Awake()
{
base.Awake();
var count = m_SlotID == -1 ? m_Inventory.SlotCount : 1;
m_Shields = new Shield[count];
m_ImpactSources = new object[count];
m_BlockEvents = new ScheduledEventBase[count];
EventHandler.RegisterEvent(m_GameObject, "OnAnimatorItemImpactComplete", OnItemImpactComplete);
EventHandler.RegisterEvent(m_GameObject, "OnShieldImpact", StartBlock);
RegisterSlotEvents(m_SlotID);
}
///
/// Registers for the interested events according to the slot id.
///
/// The slot id to register for.
private void RegisterSlotEvents(int slotID)
{
if (!Application.isPlaying) {
return;
}
if (slotID == 0) {
EventHandler.RegisterEvent(m_GameObject, "OnAnimatorItemImpactCompleteFirstSlot", OnItemImpactCompleteFirstSlot);
} else if (slotID == 1) {
EventHandler.RegisterEvent(m_GameObject, "OnAnimatorItemImpactCompleteSecondSlot", OnItemImpactCompleteSecondSlot);
} else if (slotID == 2) {
EventHandler.RegisterEvent(m_GameObject, "OnAnimatorItemImpactCompleteThirdSlot", OnItemImpactCompleteThirdSlot);
} else if (slotID != -1) {
Debug.LogError("Error: The Block ability does not listen to slot " + m_SlotID);
}
}
///
/// Unregisters from the interested events according to the slot id.
///
/// The slot id to unregister from.
private void UnregisterSlotEvents(int slotID)
{
if (!Application.isPlaying) {
return;
}
if (slotID == 0) {
EventHandler.UnregisterEvent(m_GameObject, "OnAnimatorItemImpactCompleteFirstSlot", OnItemImpactCompleteFirstSlot);
} else if (slotID == 1) {
EventHandler.UnregisterEvent(m_GameObject, "OnAnimatorItemImpactCompleteSecondSlot", OnItemImpactCompleteSecondSlot);
} else if (slotID == 2) {
EventHandler.UnregisterEvent(m_GameObject, "OnAnimatorItemImpactCompleteThirdSlot", OnItemImpactCompleteThirdSlot);
}
}
///
/// Returns the Item State Index which corresponds to the slot ID.
///
/// The ID of the slot that corresponds to the Item State Index.
/// The Item State Index which corresponds to the slot ID.
public override int GetItemStateIndex(int slotID)
{
// Return the ItemStateIndex if the SlotID matches the requested slotID.
if (m_SlotID == -1) {
if (m_Shields[slotID] != null) {
return m_Parry ? m_ParryItemStateIndex : m_BlockItemStateIndex;
}
} else if (m_SlotID == slotID && m_Shields[0] != null) {
return m_Parry ? m_ParryItemStateIndex : m_BlockItemStateIndex;
}
return -1;
}
///
/// Returns the Item Substate Index which corresponds to the slot ID.
///
/// The ID of the slot that corresponds to the Item Substate Index.
/// The Item Substate Index which corresponds to the slot ID.
public override int GetItemSubstateIndex(int slotID)
{
var substateIndex = -1;
object impactSource = null;
if (m_SlotID == -1) {
if (m_Shields[slotID] != null) {
substateIndex = m_Shields[slotID].GetItemSubstateIndex();
impactSource = m_ImpactSources[slotID];
}
} else if (m_SlotID == slotID && m_Shields[0] != null) {
substateIndex = m_Shields[0].GetItemSubstateIndex();
impactSource = m_ImpactSources[0];
}
if (substateIndex == -1) {
return -1;
}
// If the impact source is a MeleeWeapon determine which attack is being played. This will allow the block integer to depend on
if (impactSource != null && impactSource is MeleeWeapon) {
var meleeWeapon = impactSource as MeleeWeapon;
var meleeUseSubstateIndex = meleeWeapon.UsedSubstateIndex;
if (meleeUseSubstateIndex != -1) {
return MathUtility.Concatenate(meleeWeapon.Item.AnimatorItemID, meleeUseSubstateIndex, substateIndex);
}
}
return substateIndex;
}
///
/// An object has impacted the shield. Start the blocking animation.
///
/// The shield that was impacted.
/// The object that is trying to damage the shield.
public void StartBlock(Shield shield, object source)
{
var slotID = shield.Item.SlotID;
if (m_SlotID != -1 && slotID != m_SlotID) {
return;
} else if (slotID == m_SlotID) {
// If the ability only responds to a single slot then the arrays will always have just a single element.
slotID = 0;
}
if (m_Shields[slotID] != null) {
return;
}
// The character can't block and use an item at the same time.
if (m_CharacterLocomotion.IsAbilityTypeActive