using System; using System.Collections.Generic; using UnityEngine; using Random = UnityEngine.Random; namespace Invector.vMelee { using Invector.vItemManager; using UnityEngine.Events; using vEventSystems; /// /// Event called when you equip a weapon (Weapon, isLeftWeapon) /// [System.Serializable] public class OnEquipWeaponEvent : UnityEngine.Events.UnityEvent { } public class vMeleeManager : vMonoBehaviour, IWeaponEquipmentListener { #region SeralizedProperties in CustomEditor public List Members = new List(); public vDamage defaultDamage = new vDamage(10); public HitProperties hitProperties; public vMeleeWeapon leftWeapon, rightWeapon; public vOnHitEvent onDamageHit, onRecoilHit; public OnEquipWeaponEvent onEquipWeapon; #endregion SeralizedProperties in CustomEditor [Tooltip("NPC ONLY- Ideal distance for the attack")] public float defaultAttackDistance = 1f; [Tooltip("Default cost for stamina when attack")] public float defaultStaminaCost = 20f; [Tooltip("Default recovery delay for stamina when attack")] public float defaultStaminaRecoveryDelay = 1f; [Range(0, 100)] public int defaultDefenseRate = 100; [Range(0, 180)] public float defaultDefenseRange = 90; [HideInInspector] public vIMeleeFighter fighter; protected int damageMultiplier; protected int currentRecoilID; protected int currentReactionID; protected bool ignoreDefense; protected bool activeRagdoll; protected float senselessTime; protected bool inRecoil; protected string attackName; protected virtual void Start() { Init(); } /// /// Init properties /// public virtual void Init() { fighter = gameObject.GetMeleeFighter(); ///Initialize all bodyMembers and weapons foreach (vBodyMember member in Members) { ///damage of member will be always the defaultDamage //member.attackObject.damage = defaultDamage; if (member.attackObject == null) { var attackObjects = GetComponentsInChildren(); if (attackObjects.Length > 0) member.attackObject = System.Array.Find(attackObjects, a => a.attackObjectName.Equals(member.bodyPart)); if (member.attackObject == null) { Debug.LogWarning("Can't find the attack Object " + member.bodyPart); continue; } } member.attackObject.damage.damageValue = defaultDamage.damageValue; if (member.bodyPart == HumanBodyBones.LeftLowerArm.ToString()) { var weapon = member.attackObject.GetComponentInChildren(true); leftWeapon = weapon; } if (member.bodyPart == HumanBodyBones.RightLowerArm.ToString()) { var weapon = member.attackObject.GetComponentInChildren(true); rightWeapon = weapon; } member.attackObject.meleeManager = this; member.SetActiveDamage(false); } if (leftWeapon != null) { leftWeapon.SetActiveDamage(false); leftWeapon.meleeManager = this; } if (rightWeapon != null) { rightWeapon.meleeManager = this; rightWeapon.SetActiveDamage(false); } } /// /// Set active Multiple Parts to attack /// /// /// /// /// public virtual void SetActiveAttack(List bodyParts, vAttackType type, bool active = true, int damageMultiplier = 0, int recoilID = 0, int reactionID = 0, bool ignoreDefense = false, bool activeRagdoll = false, float senselessTime = 0, string attackName = "") { for (int i = 0; i < bodyParts.Count; i++) { var bodyPart = bodyParts[i]; SetActiveAttack(bodyPart, type, active, damageMultiplier, recoilID, reactionID, ignoreDefense, activeRagdoll, senselessTime, attackName); } } /// /// Set active Single Part to attack /// /// /// /// /// public virtual void SetActiveAttack(string bodyPart, vAttackType type, bool active = true, int damageMultiplier = 0, int recoilID = 0, int reactionID = 0, bool ignoreDefense = false, bool activeRagdoll = false, float senselessTime = 0, string attackName = "") { this.damageMultiplier = damageMultiplier; currentRecoilID = recoilID; currentReactionID = reactionID; this.ignoreDefense = ignoreDefense; this.activeRagdoll = activeRagdoll; this.attackName = attackName; this.senselessTime = senselessTime; if (type == vAttackType.Unarmed) { /// find attackObject by bodyPart var attackObject = Members.Find(member => member.bodyPart == bodyPart); if (attackObject != null) { attackObject.SetActiveDamage(active); } } else { ///if AttackType == MeleeWeapon ///this work just for Right and Left Lower Arm if (bodyPart == "RightLowerArm" && rightWeapon != null) { rightWeapon.meleeManager = this; rightWeapon.SetActiveDamage(active); } else if (bodyPart == "LeftLowerArm" && leftWeapon != null) { leftWeapon.meleeManager = this; leftWeapon.SetActiveDamage(active); } } } /// /// Listener of Damage Event /// /// public virtual void OnDamageHit(ref vHitInfo hitInfo) { //+1 because of Random.Range extrem value exclusion vDamage damage = new vDamage(Random.Range(hitInfo.attackObject.minDamage.damageValue, hitInfo.attackObject.maxDamage.damageValue + 1)); damage.sender = transform; damage.reaction_id = currentReactionID; damage.recoil_id = currentRecoilID; damage.damageType = hitInfo.attackObject.damage.damageType; if (this.activeRagdoll) damage.activeRagdoll = this.activeRagdoll; if (this.attackName != string.Empty) damage.damageType = this.attackName; if (this.ignoreDefense) damage.ignoreDefense = this.ignoreDefense; if (this.senselessTime != 0) damage.senselessTime = this.senselessTime; /// Calc damage with multiplier /// and Call ApplyDamage of attackObject damage.damageValue *= damageMultiplier > 1 ? damageMultiplier : 1; hitInfo.targetIsBlocking = !hitInfo.attackObject.ApplyDamage(hitInfo.hitBox, hitInfo.targetCollider, damage); onDamageHit.Invoke(hitInfo); } /// /// Listener of Recoil Event /// /// public virtual void OnRecoilHit(vHitInfo hitInfo) { if (hitProperties.useRecoil && InRecoilRange(hitInfo) && !inRecoil) { inRecoil = true; var id = currentRecoilID; if (fighter != null) fighter.OnRecoil(id); onRecoilHit.Invoke(hitInfo); Invoke("ResetRecoil", 1f); } } /// /// Call Weapon Defense Events. /// public virtual void OnDefense() { if (leftWeapon != null && leftWeapon.meleeType != vMeleeType.OnlyAttack && leftWeapon.gameObject.activeInHierarchy) { leftWeapon.OnDefense(); } if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyAttack && rightWeapon.gameObject.activeInHierarchy) { rightWeapon.OnDefense(); } } /// /// Get Current Attack ID /// /// public virtual int GetAttackID() { if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyDefense && rightWeapon.gameObject.activeInHierarchy) return rightWeapon.attackID; return 0; } /// /// Get StaminaCost /// /// public virtual float GetAttackStaminaCost() { if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyDefense && rightWeapon.gameObject.activeInHierarchy) return rightWeapon.staminaCost; return defaultStaminaCost; } /// /// Get StaminaCost /// /// public virtual float GetAttackStaminaRecoveryDelay() { if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyDefense && rightWeapon.gameObject.activeInHierarchy) return rightWeapon.staminaRecoveryDelay; return defaultStaminaRecoveryDelay; } /// /// Get ideal distance for the attack /// /// public virtual float GetAttackDistance() { if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyDefense && rightWeapon.gameObject.activeInHierarchy) return rightWeapon.distanceToAttack; return defaultAttackDistance; } /// /// Get Current Defense ID /// /// public virtual int GetDefenseID() { if (leftWeapon != null && leftWeapon.meleeType != vMeleeType.OnlyAttack && leftWeapon.gameObject.activeInHierarchy) { GetComponent().SetBool("FlipAnimation", false); return leftWeapon.defenseID; } if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyAttack && rightWeapon.gameObject.activeInHierarchy) { GetComponent().SetBool("FlipAnimation", true); return rightWeapon.defenseID; } return 0; } /// /// Get Defense Rate of Melee Defense /// /// public int GetDefenseRate() { if (leftWeapon != null && leftWeapon.meleeType != vMeleeType.OnlyAttack && leftWeapon.gameObject.activeInHierarchy) { return leftWeapon.defenseRate; } if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyAttack && rightWeapon.gameObject.activeInHierarchy) { return rightWeapon.defenseRate; } return defaultDefenseRate; } /// /// Get Current MoveSet ID /// /// public virtual int GetMoveSetID() { if (rightWeapon != null && rightWeapon.gameObject.activeInHierarchy) return rightWeapon.movesetID; // if (leftWeapon != null && leftWeapon.gameObject.activeInHierarchy) return leftWeapon.movesetID; return 0; } /// /// Check if defence can break Attack /// /// public virtual bool CanBreakAttack() { if (leftWeapon != null && leftWeapon.meleeType != vMeleeType.OnlyAttack && leftWeapon.gameObject.activeInHierarchy) { return leftWeapon.breakAttack; } if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyAttack && rightWeapon.gameObject.activeInHierarchy) { return rightWeapon.breakAttack; } return false; } /// /// Check if attack can be blocked /// /// /// public virtual bool CanBlockAttack(Vector3 attackPoint) { if (leftWeapon != null && leftWeapon.meleeType != vMeleeType.OnlyAttack && leftWeapon.gameObject.activeInHierarchy) { return Math.Abs(transform.HitAngle(attackPoint)) <= leftWeapon.defenseRange; } if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyAttack && rightWeapon.gameObject.activeInHierarchy) { return Math.Abs(transform.HitAngle(attackPoint)) <= rightWeapon.defenseRange; } return Math.Abs(transform.HitAngle(attackPoint)) <= defaultDefenseRange; } /// /// Get defense recoilID for break attack /// /// public virtual int GetDefenseRecoilID() { if (leftWeapon != null && leftWeapon.meleeType != vMeleeType.OnlyAttack && leftWeapon.gameObject.activeInHierarchy) { return leftWeapon.recoilID; } if (rightWeapon != null && rightWeapon.meleeType != vMeleeType.OnlyAttack && rightWeapon.gameObject.activeInHierarchy) { return rightWeapon.recoilID; } return 0; } /// /// Check if angle of hit point is in range of recoil /// /// /// protected virtual bool InRecoilRange(vHitInfo hitInfo) { var center = new Vector3(transform.position.x, hitInfo.hitPoint.y, transform.position.z); var euler = (Quaternion.LookRotation(hitInfo.hitPoint - center).eulerAngles - transform.eulerAngles).NormalizeAngle(); return euler.y <= hitProperties.recoilRange; } /// /// /// /// public virtual void SetLeftWeapon(GameObject weaponObject) { if (weaponObject) { leftWeapon = weaponObject.GetComponent(); SetLeftWeapon(leftWeapon); } else leftWeapon = null; } /// /// /// /// public virtual void SetRightWeapon(GameObject weaponObject) { if (weaponObject) { rightWeapon = weaponObject.GetComponent(); SetRightWeapon(rightWeapon); } else rightWeapon = null; } /// /// /// /// public virtual void SetLeftWeapon(vMeleeWeapon weapon) { if (weapon) { onEquipWeapon.Invoke(weapon.gameObject, true); leftWeapon = weapon; leftWeapon.SetActiveDamage(false); leftWeapon.meleeManager = this; } else leftWeapon = null; } /// /// /// /// public virtual void SetRightWeapon(vMeleeWeapon weapon) { if (weapon) { onEquipWeapon.Invoke(weapon.gameObject, false); rightWeapon = weapon; rightWeapon.meleeManager = this; rightWeapon.SetActiveDamage(false); } else { rightWeapon = null; } } public virtual vMeleeWeapon CurrentActiveAttackWeapon { get { if (rightWeapon && rightWeapon.gameObject.activeInHierarchy && (rightWeapon.meleeType == vMeleeType.OnlyAttack || rightWeapon.meleeType == vMeleeType.AttackAndDefense)) return rightWeapon; if (leftWeapon && leftWeapon.gameObject.activeInHierarchy && (leftWeapon.meleeType == vMeleeType.OnlyAttack || leftWeapon.meleeType == vMeleeType.AttackAndDefense)) return leftWeapon; return null; } } public virtual vMeleeWeapon CurrentActiveDefenseWeapon { get { if (rightWeapon && rightWeapon.gameObject.activeInHierarchy && (rightWeapon.meleeType == vMeleeType.OnlyDefense || rightWeapon.meleeType == vMeleeType.AttackAndDefense)) return rightWeapon; if (leftWeapon && leftWeapon.gameObject.activeInHierarchy && (leftWeapon.meleeType == vMeleeType.OnlyDefense || leftWeapon.meleeType == vMeleeType.AttackAndDefense)) return leftWeapon; return null; } } public virtual vMeleeWeapon CurrentAttackWeapon { get { if (rightWeapon && (rightWeapon.meleeType == vMeleeType.OnlyAttack || rightWeapon.meleeType == vMeleeType.AttackAndDefense)) return rightWeapon; if (leftWeapon && (leftWeapon.meleeType == vMeleeType.OnlyAttack || leftWeapon.meleeType == vMeleeType.AttackAndDefense)) return leftWeapon; return null; } } public virtual vMeleeWeapon CurrentDefenseWeapon { get { if (rightWeapon && (rightWeapon.meleeType == vMeleeType.OnlyDefense || rightWeapon.meleeType == vMeleeType.AttackAndDefense)) return rightWeapon; if (leftWeapon && (leftWeapon.meleeType == vMeleeType.OnlyDefense || leftWeapon.meleeType == vMeleeType.AttackAndDefense)) return leftWeapon; return null; } } protected virtual void ResetRecoil() { inRecoil = false; } } #region Secundary Classes [Serializable] public class vOnHitEvent : UnityEngine.Events.UnityEvent { } [Serializable] public class vBodyMember { public Transform transform; public string bodyPart; public vMeleeAttackObject attackObject; public bool isHuman; public void SetActiveDamage(bool active) { attackObject.SetActiveDamage(active); } } public enum vHumanBones { RightHand, RightLowerArm, RightUpperArm, RightShoulder, LeftHand, LeftLowerArm, LeftUpperArm, LeftShoulder, RightFoot, RightLowerLeg, RightUpperLeg, LeftFoot, LeftLowerLeg, LeftUpperLeg, Chest, Head } public enum vAttackType { Unarmed, MeleeWeapon } #endregion Secundary Classes }