Files
2024-11-20 15:21:28 +01:00

231 lines
8.4 KiB
C#

using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace Invector.vMelee
{
using vEventSystems;
[vClassHeader("Melee Object", openClose = false)]
public class vMeleeAttackObject : vMonoBehaviour
{
[vReadOnly(false)] public string attackObjectName;
public vDamage damage;
public vDamage minDamage;
public vDamage maxDamage;
public Transform overrideDamageSender;
public List<vHitBox> hitBoxes;
public int damageModifier;
[HideInInspector]
public bool canApplyDamage;
/// <summary>
/// Event called when attack was successful
/// </summary>
[vHelpBox("Event called when attack was successful")]
public OnHitEnter onDamageHit;
/// <summary>
/// Event called when the attack causes recoil
/// </summary>
[vHelpBox("Event called when the attack causes recoil")]
public OnHitEnter onRecoilHit;
/// <summary>
/// Event called when causes damage
/// </summary>
[vHelpBox("Event called when causes damage ")]
public OnReceiveDamage onPassDamage;
[vHelpBox("Events called when Damage applier (HitBoxes) is enabled or disabled ")]
public UnityEvent onEnableDamage;
public UnityEvent onDisableDamage;
private Dictionary<vHitBox, List<GameObject>> targetColliders;
[HideInInspector]
public vMeleeManager meleeManager;
/// <summary>
/// Attack Object name is used to be selected from the <see cref="vMelee.vMeleeManager.Members"/> list
/// </summary>
protected virtual void Start()
{
// init list of targetColliders
targetColliders = new Dictionary<vHitBox, List<GameObject>>();
if (hitBoxes.Count > 0)
{
// initialize hitBox properties
foreach (vHitBox hitBox in hitBoxes)
{
hitBox.attackObject = this;
targetColliders.Add(hitBox, new List<GameObject>());
}
}
else
{
this.enabled = false;
}
}
/// <summary>
/// Set Active all hitBoxes of the MeleeAttackObject
/// </summary>
/// <param name="value"> active value</param>
public virtual void SetActiveDamage(bool value)
{
canApplyDamage = value;
for (int i = 0; i < hitBoxes.Count; i++)
{
var hitCollider = hitBoxes[i];
hitCollider.trigger.enabled = value;
if (value == false && targetColliders != null)
{
targetColliders[hitCollider].Clear();
}
}
if (value)
{
onEnableDamage.Invoke();
}
else
{
onDisableDamage.Invoke();
}
}
/// <summary>
/// Hitboxes Call Back
/// </summary>
/// <param name="hitBox">vHitBox object</param>
/// <param name="other">target Collider</param>
public virtual void OnHit(vHitBox hitBox, Collider other)
{
// check first condition for hit
if (canApplyDamage && !targetColliders[hitBox].Contains(other.gameObject) && (meleeManager != null && other.gameObject != meleeManager.gameObject))
{
var inDamage = false;
var inRecoil = false;
if (meleeManager == null)
{
meleeManager = GetComponentInParent<vMeleeManager>();
}
//check if meleeManager exists and apply his hitProperties to this
HitProperties _hitProperties = meleeManager.hitProperties;
// damage conditions
if (((hitBox.triggerType & vHitBoxType.Damage) != 0) && _hitProperties.hitDamageTags == null || _hitProperties.hitDamageTags.Count == 0)
{
inDamage = true;
}
else if (((hitBox.triggerType & vHitBoxType.Damage) != 0) && _hitProperties.hitDamageTags.Contains(other.tag))
{
inDamage = true;
}
// recoil conditions
else if (((hitBox.triggerType & vHitBoxType.Recoil) != 0) &&
(_hitProperties.hitRecoilLayer == (_hitProperties.hitRecoilLayer | (1 << other.gameObject.layer))))
{
inRecoil = true;
}
if (inDamage || inRecoil)
{
// add target collider in the list to control the frequency of hit
targetColliders[hitBox].Add(other.gameObject);
vHitInfo hitInfo = new vHitInfo(this, hitBox, other, hitBox.transform.position);
if (inDamage == true)
{
// If there is a meleeManager then call onDamageHit to control damage values
// and it will call the ApplyDamage after filter the damage
// if meleeManager is null the damage will be directly applied
// Finally the OnDamageHit event is called
if (meleeManager)
{
meleeManager.OnDamageHit(ref hitInfo);
}
else
{
damage.sender = overrideDamageSender ? overrideDamageSender : transform;
// ApplyDamage(hitBox, other, damage);
}
if (!hitInfo.targetIsBlocking)
{
onDamageHit.Invoke(hitInfo);
}
}
// recoil just work with OnRecoilHit event and meleeManger
if (inRecoil == true)
{
if (meleeManager)
{
meleeManager.OnRecoilHit(hitInfo);
}
onRecoilHit.Invoke(hitInfo);
}
}
}
}
/// <summary>
/// Apply damage to target collider (TakeDamage, damage))
/// </summary>
/// <param name="hitBox">vHitBox object</param>
/// <param name="other">collider target</param>
/// <param name="damage"> damage</param>
public bool ApplyDamage(vHitBox hitBox, Collider other, vDamage damage)
{
vDamage _damage = new vDamage(damage);
_damage.receiver = other.transform;
_damage.damageValue = Mathf.RoundToInt(((damage.damageValue + damageModifier) * (hitBox.damagePercentage * 0.01f)));
_damage.hitPosition = hitBox.transform.position;
other.gameObject.ApplyDamage(_damage, meleeManager.fighter);
if (_damage.hitReaction && _damage.damageValue > 0)
{
onPassDamage.Invoke(_damage);
}
return _damage.hitReaction;
}
}
}
namespace Invector.vMelee
{
#region Secundary Class
[System.Serializable]
public class OnHitEnter : UnityEvent<vHitInfo> { }
public class vHitInfo
{
public vMeleeAttackObject attackObject;
public vHitBox hitBox;
public Vector3 hitPoint;
public Collider targetCollider;
public bool targetIsBlocking;
public vHitInfo(vMeleeAttackObject attackObject, vHitBox hitBox, Collider targetCollider, Vector3 hitPoint)
{
this.attackObject = attackObject;
this.hitBox = hitBox;
this.targetCollider = targetCollider;
this.hitPoint = hitPoint;
}
}
[System.Serializable]
public class HitProperties
{
[Tooltip("Tag to receive Damage")]
public List<string> hitDamageTags = new List<string>() { "Enemy" };
[Tooltip("Trigger a HitRecoil animation if the character attacks a obstacle")]
public bool useRecoil = true;
public bool drawRecoilGizmos;
[Range(0, 180f)]
public float recoilRange = 90f;
[Tooltip("layer to Recoil Damage")]
public LayerMask hitRecoilLayer = 1 << 0;
}
#endregion
}