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 hitBoxes; public int damageModifier; [HideInInspector] public bool canApplyDamage; /// /// Event called when attack was successful /// [vHelpBox("Event called when attack was successful")] public OnHitEnter onDamageHit; /// /// Event called when the attack causes recoil /// [vHelpBox("Event called when the attack causes recoil")] public OnHitEnter onRecoilHit; /// /// Event called when causes damage /// [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> targetColliders; [HideInInspector] public vMeleeManager meleeManager; /// /// Attack Object name is used to be selected from the list /// protected virtual void Start() { // init list of targetColliders targetColliders = new Dictionary>(); if (hitBoxes.Count > 0) { // initialize hitBox properties foreach (vHitBox hitBox in hitBoxes) { hitBox.attackObject = this; targetColliders.Add(hitBox, new List()); } } else { this.enabled = false; } } /// /// Set Active all hitBoxes of the MeleeAttackObject /// /// active value 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(); } } /// /// Hitboxes Call Back /// /// vHitBox object /// target Collider 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(); } //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); } } } } /// /// Apply damage to target collider (TakeDamage, damage)) /// /// vHitBox object /// collider target /// damage 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 { } 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 hitDamageTags = new List() { "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 }