using System.Collections; using System.Collections.Generic; using UnityEngine; using Cinemachine; using FirstGearGames.SmoothCameraShaker; public class CharacterCollisionManager : MonoBehaviour { public ShakeData punchKickShakeData; public GameObject lefthandcollider; public GameObject righthandcollider; public GameObject leftfootcollider; public GameObject rightfootcollider; public GameManager gameManager; public UltimateParentFinder finder; public GroundCheck groundCheck; Animator anim; private CharacterVFXManager vfxScript; private HealthNew healthScript; private CharacterSoundFXManager soundScript; public float nextAnimationTime; public float animationInterval; public bool jump = false; public float slowdownFactor = 0.5f; private SlowMotionManager slowMotionManager; public DamageDisplay damageDisplay; bool calculatingDamage; private bool isColliding; //private StaminaSystem staminaScript; public PlayerScript playerScript; MissionHandler mission_handler; public CinemachineImpulseSource impulseSource; void OnEnable(){ GameObject gamestatsmanager_go = GameObject.Find("GameStatsManager"); mission_handler = gamestatsmanager_go.GetComponent(); if (mission_handler != null){ //Debug.Log("Mission Handler Found"); } // mission_handler.test(); } // Start is called before the first frame update void Start() { damageDisplay = GetComponent(); anim = GetComponent(); groundCheck = GetComponent(); finder = GameObject.FindObjectOfType(); vfxScript = GetComponent(); gameManager = GameObject.FindObjectOfType(); healthScript = GetComponent(); soundScript = GetComponent(); slowMotionManager = FindObjectOfType(); calculatingDamage = false; //playerScript = GetComponent(); //staminaScript = GetComponent(); } /// /// Checks if the given animation clip name is a valid attack animation. /// This prevents damage from being dealt when characters simply walk into each other. /// private bool IsValidAttackAnimation(string clipName) { if (string.IsNullOrEmpty(clipName)) return false; // Punch animations return clipName == AnimationNames.Jab || clipName == AnimationNames.RightBody || clipName == AnimationNames.SupermanPunch || clipName == AnimationNames.SpecialBackfist || clipName == AnimationNames.Right || clipName == AnimationNames.LeftHook || clipName == AnimationNames.RightHook || clipName == AnimationNames.LeftElbow || clipName == AnimationNames.RightElbow || clipName == AnimationNames.PowerPunchLeft || clipName == AnimationNames.Haymaker || clipName == AnimationNames.SpinningBackfist || clipName == AnimationNames.ElbowSmash || clipName == AnimationNames.AirPunch || clipName == AnimationNames.SumoSlap || clipName == AnimationNames.ChargedPunch || // Kick animations clipName == AnimationNames.FrontKickRight || clipName == AnimationNames.BodyKickRight || clipName == AnimationNames.LegPowerKickRight || clipName == AnimationNames.BackSideKick || clipName == AnimationNames.LegKickRight || clipName == AnimationNames.LowKick || clipName == AnimationNames.BasicKick || clipName == AnimationNames.DropKick || clipName == AnimationNames.AirKick || // Weapon animations clipName == AnimationNames.BatOne || clipName == AnimationNames.BatTwo || clipName == AnimationNames.HammerOne || clipName == AnimationNames.HammerTwo; } //Updates the character's rotation to face the specified targetCharacter. Ensures the character stays upright. void updateCharacterDirection(GameObject targetCharacter) { // follow player Vector3 relativePos = targetCharacter.transform.position - transform.position; Quaternion rotation; float closeness = Vector3.Distance(targetCharacter.transform.position, transform.position); // Calculate the angle between the character's forward vector and the relative position // the second argument, upwards, defaults to Vector3.up if (relativePos.y < 0.1) { rotation = Quaternion.LookRotation(relativePos, Vector3.left); //print(rotation.z); if(rotation.z < 2) { // Use Slerp for smoother rotation transform.rotation = Quaternion.Slerp(transform.rotation, rotation, 10f * Time.deltaTime); } } // Ensure the character always stays upright Vector3 currentEulerAngles = transform.rotation.eulerAngles; float maxRotationAngle = 2f; // Adjust this value as needed if (Mathf.Abs(currentEulerAngles.x) > maxRotationAngle || Mathf.Abs(currentEulerAngles.z) > maxRotationAngle) { Quaternion uprightRotation = Quaternion.Euler(0f, currentEulerAngles.y, 0f); transform.rotation = Quaternion.Lerp(transform.rotation, uprightRotation, 50.0f * Time.deltaTime); } } // Activates the corresponding colliders for the character's hands and feet #region void lefthandcolliderOn() { lefthandcollider.SetActive(true); } void righthandcolliderOn() { righthandcollider.SetActive(true); } void leftfootcolliderOn() { leftfootcollider.SetActive(true); } void rightfootcolliderOn() { rightfootcollider.SetActive(true); } #endregion // Deactivates the corresponding colliders for the character's hands and feet. #region void lefthandcolliderOff() { lefthandcollider.SetActive(false); } void righthandcolliderOff() { righthandcollider.SetActive(false); } void leftfootcolliderOff() { leftfootcollider.SetActive(false); } void rightfootcolliderOff() { rightfootcollider.SetActive(false); } #endregion // Get the action_type (punch or kick) based on the collision // Get the source of the action, from the player or the enemy public void collision_occured(string action_type, string action_source){ //Check if the source of the action is from the player character if (action_source == "Player"){ int enemyendHealth = GameObject.FindGameObjectWithTag("Enemy").GetComponent().currentHealth; mission_handler.PlayerHasHitEnemy(action_type, action_source, enemyendHealth); //Debug.LogWarning("PlayerCharacter || Src = " + action_source); } //Not Player character if (action_source != "Enemy"){ // int enemyendHealth = GameObject.FindGameObjectWithTag("Enemy").GetComponent().currentHealth; mission_handler.EnemyHasHitPlayer(action_type, action_source); Debug.LogWarning("Not PlayerCharacter || Src = " + action_source); } } // Handles collision events when the character collides with other objects. // Determines the type of collider that was triggered (left/right hand or left/right foot). // Calculates damage, plays corresponding visual and audio effects, and triggers animations based on the collision. // Checks the type of animation currently playing and adjusts the character's direction accordingly. // Initiates slow-motion for specific animations and stops it after a short delay. private void OnTriggerEnter(Collider other) { GameObject ultimateParent = finder.GetUltimateParent(other.gameObject); //ultimateParent.tag = "Enemy" | "Player" // Debug.LogError(ultimateParent.name + " Character XL"); if (ultimateParent == null) return; // FRIENDLY FIRE PREVENTION: Skip damage if attacker and target are on the same team // In Cash System mode, all player team members have "Player" tag, enemies have "Enemy" tag if (ultimateParent.tag == gameObject.tag) { // Same team - no friendly fire allowed return; } if(gameObject.CompareTag("Player")) { if (gameObject.GetComponent().block && IsInBodyReactionAnimation()) { PlayBlockReactions(other.gameObject); return; } else if (gameObject.GetComponent().dodge) { return; } } int remainingHealth = 1; if (calculatingDamage == false) { healthScript.StartCoroutine(CheckAndDisplayDamage()); } if (other.gameObject.CompareTag("rightfootcollider")) { // CRITICAL FIX: Validate attacker is actually in an attack animation AnimatorClipInfo[] attackerClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); if (attackerClipInfo.Length == 0) return; string attackerClip = attackerClipInfo[0].clip.name; if (!IsValidAttackAnimation(attackerClip)) { // Not attacking - ignore collision return; } collision_occured("kick", ultimateParent.tag); // Debug.LogError("Right Foot Collider"); //staminaScript.UseStamina(1); vfxScript.playKickEffect(other.gameObject); soundScript.PlayKickSound(); Collider[] characterColliders = ultimateParent.GetComponentsInChildren(); foreach (Collider characterCollider in characterColliders) { if (Physics.ComputePenetration( characterCollider, characterCollider.transform.position, characterCollider.transform.rotation, other, other.transform.position, other.transform.rotation, out Vector3 direction, out float distance)) { if (!healthScript.isDead) { remainingHealth = healthScript.TakeDamage(healthScript.CalculateDamage(characterCollider, other), ultimateParent); break; // Exit the loop since we found the colliding collider } else break; } } AnimatorClipInfo[] m_CurrentClipInfo; string clipname; m_CurrentClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); clipname = m_CurrentClipInfo[0].clip.name; //Checks if the character's remaining health is less than or equal to 0. If true, it triggers a death animation and sets the next animation time. if (remainingHealth <= 0) { updateCharacterDirection(ultimateParent); anim.Play("BodyKickHitReaction"); nextAnimationTime = Time.time + animationInterval + 2; vfxScript.playKickEffect(other.gameObject); soundScript.PlayKickSound(); } else { string reaction = AnimationNames.GetReactionForAttack(clipname); if (!string.IsNullOrEmpty(reaction) && groundCheck.isGrounded) { // Face the attacker before reacting updateCharacterDirection(ultimateParent); // Get or add AnimationManager AnimationManager animManager = GetComponent(); if (animManager != null) { // Use specialized reaction method for better timing animManager.PlayReactionAnimation(reaction); } else { // Fallback to direct animation anim.Play(reaction); } // Visual and sound effects vfxScript.playKickEffect(other.gameObject); soundScript.PlayKickSound(); // Damage calculation if (calculatingDamage == false) { healthScript.StartCoroutine(CheckAndDisplayDamage()); } // Special handling for certain moves if (clipname == "BackSideKick") { slowMotionManager.StartSlowMotion(slowdownFactor); StartCoroutine(EndSlowMotion()); } } } } else if (other.gameObject.CompareTag("leftfootcollider")) { // CRITICAL FIX: Validate attacker is actually in an attack animation AnimatorClipInfo[] attackerClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); if (attackerClipInfo.Length == 0) return; string attackerClip = attackerClipInfo[0].clip.name; if (!IsValidAttackAnimation(attackerClip)) { // Not attacking - ignore collision return; } collision_occured("kick", ultimateParent.tag); // Debug.LogError("Left Foot Collider"); //staminaScript.UseStamina(1); Collider[] characterColliders = ultimateParent.GetComponentsInChildren(); foreach (Collider characterCollider in characterColliders) { if (Physics.ComputePenetration( characterCollider, characterCollider.transform.position, characterCollider.transform.rotation, other, other.transform.position, other.transform.rotation, out Vector3 direction, out float distance)) { if (!healthScript.isDead) { remainingHealth = healthScript.TakeDamage(healthScript.CalculateDamage(characterCollider, other), ultimateParent); break; // Exit the loop since we found the colliding collider } else break; } } AnimatorClipInfo[] m_CurrentClipInfo; string clipname; m_CurrentClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); clipname = m_CurrentClipInfo[0].clip.name; if (remainingHealth <= 0) { updateCharacterDirection(ultimateParent); anim.Play("HookHitReaction"); nextAnimationTime = Time.time + animationInterval + 2; if (calculatingDamage == false) { healthScript.StartCoroutine(CheckAndDisplayDamage()); } vfxScript.playKickEffect(other.gameObject); soundScript.PlayKickSound(); } else { string reaction = AnimationNames.GetReactionForAttack(clipname); if (!string.IsNullOrEmpty(reaction) && groundCheck.isGrounded) { // Face the attacker before reacting updateCharacterDirection(ultimateParent); // Get or add AnimationManager AnimationManager animManager = GetComponent(); if (animManager != null) { // Use specialized reaction method for better timing animManager.PlayReactionAnimation(reaction); } else { // Fallback to direct animation anim.Play(reaction); } // Visual and sound effects vfxScript.playKickEffect(other.gameObject); soundScript.PlayKickSound(); // Damage calculation if (calculatingDamage == false) { healthScript.StartCoroutine(CheckAndDisplayDamage()); } } } } else if (other.gameObject.CompareTag("righthandcollider")) { // CRITICAL FIX: Validate attacker is actually in an attack animation AnimatorClipInfo[] attackerClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); if (attackerClipInfo.Length == 0) return; string attackerClip = attackerClipInfo[0].clip.name; if (!IsValidAttackAnimation(attackerClip)) { // Not attacking - ignore collision return; } collision_occured("punch", ultimateParent.tag); // Debug.LogError("Right Hand Collider"); //staminaScript.UseStamina(1); Collider[] characterColliders = ultimateParent.GetComponentsInChildren(); foreach (Collider characterCollider in characterColliders) { if (Physics.ComputePenetration( characterCollider, characterCollider.transform.position, characterCollider.transform.rotation, other, other.transform.position, other.transform.rotation, out Vector3 direction, out float distance)) { if (!healthScript.isDead) { remainingHealth = healthScript.TakeDamage(healthScript.CalculateDamage(characterCollider, other), ultimateParent); break; // Exit the loop since we found the colliding collider } else break; } } AnimatorClipInfo[] m_CurrentClipInfo; string clipname; m_CurrentClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); clipname = m_CurrentClipInfo[0].clip.name; if (remainingHealth <= 0) { updateCharacterDirection(ultimateParent); anim.Play("HookHitReaction"); nextAnimationTime = Time.time + animationInterval + 2; } else { string reaction = AnimationNames.GetReactionForAttack(clipname); if (!string.IsNullOrEmpty(reaction) && groundCheck.isGrounded) { // Face the attacker before reacting updateCharacterDirection(ultimateParent); // Get or add AnimationManager AnimationManager animManager = GetComponent(); if (animManager != null) { // Use specialized reaction method for better timing animManager.PlayReactionAnimation(reaction); } else { // Fallback to direct animation anim.Play(reaction); } // Visual and sound effects vfxScript.playPunchEffect(other.gameObject); soundScript.PlayPunchFaceHitSound(); // Damage calculation if (calculatingDamage == false) { healthScript.StartCoroutine(CheckAndDisplayDamage()); } } } } else if (other.gameObject.CompareTag("lefthandcollider")) { // CRITICAL FIX: Validate attacker is actually in an attack animation AnimatorClipInfo[] attackerClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); if (attackerClipInfo.Length == 0) return; string attackerClip = attackerClipInfo[0].clip.name; if (!IsValidAttackAnimation(attackerClip)) { // Not attacking - ignore collision return; } collision_occured("punch", ultimateParent.tag); // Debug.LogError("Left Hand Collider"); //staminaScript.UseStamina(1); Collider[] characterColliders = ultimateParent.GetComponentsInChildren(); foreach (Collider characterCollider in characterColliders) { if (Physics.ComputePenetration( characterCollider, characterCollider.transform.position, characterCollider.transform.rotation, other, other.transform.position, other.transform.rotation, out Vector3 direction, out float distance)) { if (!healthScript.isDead) { remainingHealth = healthScript.TakeDamage(healthScript.CalculateDamage(characterCollider, other), ultimateParent); break; // Exit the loop since we found the colliding collider } else break; } } AnimatorClipInfo[] m_CurrentClipInfo; string clipname; m_CurrentClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); clipname = m_CurrentClipInfo[0].clip.name; if (remainingHealth <= 0) { updateCharacterDirection(ultimateParent); anim.Play("HookHitReaction"); nextAnimationTime = Time.time + animationInterval + 2; vfxScript.playPunchEffect(other.gameObject); soundScript.PlayPunchLightHitSound(); } else { string reaction = AnimationNames.GetReactionForAttack(clipname); if (!string.IsNullOrEmpty(reaction) && groundCheck.isGrounded) { // Face the attacker before reacting updateCharacterDirection(ultimateParent); // Get or add AnimationManager AnimationManager animManager = GetComponent(); if (animManager != null) { // Use specialized reaction method for better timing animManager.PlayReactionAnimation(reaction); } else { // Fallback to direct animation anim.Play(reaction); } // Visual and sound effects vfxScript.playPunchEffect(other.gameObject); soundScript.PlayPunchFaceHitSound(); // Damage calculation if (calculatingDamage == false) { healthScript.StartCoroutine(CheckAndDisplayDamage()); } } } } else if (other.gameObject.CompareTag("headcollider")) { // CRITICAL FIX: Validate attacker is actually in an attack animation AnimatorClipInfo[] attackerClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); if (attackerClipInfo.Length == 0) return; string attackerClip = attackerClipInfo[0].clip.name; if (!IsValidAttackAnimation(attackerClip)) { // Not attacking - ignore collision return; } //staminaScript.UseStamina(1); Collider[] characterColliders = ultimateParent.GetComponentsInChildren(); foreach (Collider characterCollider in characterColliders) { if (Physics.ComputePenetration( characterCollider, characterCollider.transform.position, characterCollider.transform.rotation, other, other.transform.position, other.transform.rotation, out Vector3 direction, out float distance)) { if (!healthScript.isDead) { remainingHealth = healthScript.TakeDamage(healthScript.CalculateDamage(characterCollider, other), ultimateParent); break; // Exit the loop since we found the colliding collider } else break; } } AnimatorClipInfo[] m_CurrentClipInfo; string clipname = null; m_CurrentClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); clipname = m_CurrentClipInfo[0].clip.name; if (remainingHealth <= 0) { updateCharacterDirection(ultimateParent); anim.Play("HookHitReaction"); nextAnimationTime = Time.time + animationInterval + 2; } else { string reaction = AnimationNames.GetReactionForAttack(clipname); if (!string.IsNullOrEmpty(reaction) && groundCheck.isGrounded) { // Face the attacker before reacting updateCharacterDirection(ultimateParent); // Get or add AnimationManager AnimationManager animManager = GetComponent(); if (animManager != null) { // Use specialized reaction method for better timing animManager.PlayReactionAnimation(reaction); } else { // Fallback to direct animation anim.Play(reaction); } // Visual and sound effects vfxScript.playPunchEffect(other.gameObject); soundScript.PlayPunchFaceHitSound(); // Damage calculation if (calculatingDamage == false) { healthScript.StartCoroutine(CheckAndDisplayDamage()); } } } } else if (other.gameObject.CompareTag("weaponcollider")) { // Treat weapon as a punch-type hit for missions and effects collision_occured("punch", ultimateParent.tag); // Optional VFX/SFX: reuse punch visuals vfxScript.playPunchEffect(other.gameObject); soundScript.PlayPunchFaceHitSound(); // Only allow damage during weapon swing animations to avoid idle bumps string clipname = null; AnimatorClipInfo[] m_CurrentClipInfo = ultimateParent.GetComponent().GetCurrentAnimatorClipInfo(0); if (m_CurrentClipInfo.Length > 0) clipname = m_CurrentClipInfo[0].clip.name; bool isWeaponSwing = clipname == AnimationNames.BatOne || clipname == AnimationNames.BatTwo || clipname == AnimationNames.HammerOne || clipname == AnimationNames.HammerTwo; if (!isWeaponSwing) return; // Compute contact and apply damage Collider[] characterColliders = ultimateParent.GetComponentsInChildren(); foreach (Collider characterCollider in characterColliders) { if (Physics.ComputePenetration( characterCollider, characterCollider.transform.position, characterCollider.transform.rotation, other, other.transform.position, other.transform.rotation, out Vector3 direction, out float distance)) { if (!healthScript.isDead) { remainingHealth = healthScript.TakeDamage(healthScript.CalculateDamage(characterCollider, other), ultimateParent); break; } else break; } } if (remainingHealth <= 0) { updateCharacterDirection(ultimateParent); anim.Play("HookHitReaction"); nextAnimationTime = Time.time + animationInterval + 2; } else { string reaction = AnimationNames.GetReactionForAttack(clipname); if (!string.IsNullOrEmpty(reaction) && groundCheck.isGrounded) { updateCharacterDirection(ultimateParent); AnimationManager animManager = GetComponent(); if (animManager != null) animManager.PlayReactionAnimation(reaction); else anim.Play(reaction); if (calculatingDamage == false) { healthScript.StartCoroutine(CheckAndDisplayDamage()); } } } } // Shake camera for impacts CameraShakerHandler.Shake(punchKickShakeData); } private void PlayBlockReactions(GameObject fxLocation) { vfxScript.playBlockEffect(fxLocation); soundScript.PlayBlockSound(); } // Ends the slow-motion effect after a specified delay. private IEnumerator EndSlowMotion() { yield return new WaitForSeconds(1); // Adjust as needed slowMotionManager.StopSlowMotion(); } // Coroutine to continuously check for changes in the character's health. // Compares the current health with the previous health after a delay. public IEnumerator CheckAndDisplayDamage() { calculatingDamage = true; while (true) { // Store the current damage healthScript.previousHealth = healthScript.currentHealth; // Wait for 1 second yield return new WaitForSeconds(1.0f); // Compare the damage after 1 second int damageDifference = healthScript.previousHealth - healthScript.currentHealth; //print(damageDifference); // Check if the damage difference exceeds the threshold if (damageDifference >= healthScript.damageDisplayThreshold && healthScript.currentHealth > 2) { // Display the damage text using the DamageDisplay script StartCoroutine(damageDisplay.ShowFloatDamageText(damageDifference)); break; } else { StopCoroutine(damageDisplay.ShowFloatDamageText(damageDifference)); break; } } calculatingDamage = false; } private bool IsInBodyReactionAnimation() { AnimatorClipInfo[] m_CurrentClipInfo; m_CurrentClipInfo = gameObject.GetComponent().GetCurrentAnimatorClipInfo(0); string m_ClipName; if (m_CurrentClipInfo.Length == 0) return false; m_ClipName = m_CurrentClipInfo[0].clip.name; // Focus on reactions for our active animations only return m_ClipName == AnimationNames.BlockBodyLeft || m_ClipName == AnimationNames.BlockStepBack || m_ClipName == AnimationNames.BlockLeg || // Active animation reactions m_ClipName == AnimationNames.JabReaction || m_ClipName == AnimationNames.HaymakerReaction || m_ClipName == AnimationNames.SpinningBackfistReaction || m_ClipName == AnimationNames.ElbowSmashReaction || m_ClipName == AnimationNames.ChargedPunchReaction || m_ClipName == AnimationNames.AirPunchReaction || m_ClipName == AnimationNames.DropKickReaction || m_ClipName == AnimationNames.LowKickReaction; /* Commented out other reactions for future use m_ClipName == AnimationNames.ChopReaction || m_ClipName == AnimationNames.GroundPoundReaction || m_ClipName == AnimationNames.AirKickReaction || m_ClipName == AnimationNames.RightHookHitReaction || m_ClipName == AnimationNames.BodyKickHitReaction || m_ClipName == AnimationNames.HookHitReaction || m_ClipName == AnimationNames.JabHitReaction || m_ClipName == AnimationNames.LegKickHitReaction || m_ClipName == AnimationNames.UppercutPowerHitReaction || m_ClipName == AnimationNames.OverhandHitReaction; */ } }