using TMPro; using UnityEngine; using UnityEngine.UI; using UnityEngine.InputSystem; using UnityEngine.InputSystem.UI; using Unity.VisualScripting; using UnityEngine.SceneManagement; #if UNITY_EDITOR using UnityEditor; #endif using System.Collections; using System; using UnityEngine.EventSystems; public class HealthNew : MonoBehaviour { public int maxHealth = 100; public int currentHealth; public bool isDead; public GameObject healthBarPrefab; public GameObject aiHealthBarPrefab; // Assign this in the Inspector for AI enemies public GameObject rewardsScreen; private GameObject eventSystem; private InputSystemUIInputModule uiInputModule; public Transform[] healthBarSpawnPoints; private GameObject healthBarInstance; private int currentSpawnPointIndex = 0; public Slider healthSlider; public Slider staminaSlider; public Image staminaCircle; public Sprite characterImage; public StaminaSystem staminaSystem; public Slider[] healthBar; public int currentDamage = 0; public int previousHealth = 0; //can be cahnge according to level public float damageDisplayThreshold; public float damageDisplayTime; string enemyname; string playername; [SerializeField] ParticleSystem lowHealthVFX; // New variables for health bar positioning private Camera mainCamera; private Canvas worldSpaceCanvas; private bool useWorldSpaceUI = true; private bool isCashSystemAI = false; // Track if this is an AI character in Cash System mode [SerializeField] private Vector3 healthBarOffset = new Vector3(0, 2.0f, 0); // Offset above character // Cached reference — avoids repeated FindObjectOfType in death/respawn callbacks private PlayerManagerNew _cachedPlayerManager; /*GameStatsManager game_statsManager; // GameObject gamestats; void Awake(){ game_statsManager = (GameObject.Find("GameStatsManager")).GetComponent(); }*/ private void Start() { currentHealth = maxHealth; // Check if we're in Cash System mode and have TeamMember component TeamMember teamMember = GetComponent(); bool isCashSystemMode = SelectionOptions.Instance != null && SelectionOptions.Instance.isCashSystemSelected; // Determine if this character should use world-space health bar (AI) // or screen-space health bar (player controlled) bool useWorldSpaceHealthBar = false; bool isPlayerControlled = false; if (isCashSystemMode && teamMember != null) { isPlayerControlled = teamMember.IsPlayerControlled; // AI characters (not player controlled) use world-space health bars useWorldSpaceHealthBar = !isPlayerControlled; DevLog.Log($"[HealthNew] Cash System mode - {gameObject.name}: PlayerControlled={isPlayerControlled}, WorldSpaceHealthBar={useWorldSpaceHealthBar}"); } else if (gameObject.CompareTag("Enemy")) { // Traditional enemy - use world-space health bar useWorldSpaceHealthBar = true; } if (isCashSystemMode && !isPlayerControlled) { // Mark this as a Cash System AI character for Update position tracking isCashSystemAI = true; // For AI characters in Cash System mode: // - Use world-space health bar (like old enemies) // - Don't spawn screen-space health bars if (aiHealthBarPrefab != null) { // Create world-space health bar above character healthBarInstance = Instantiate(aiHealthBarPrefab, transform.position + healthBarOffset, Quaternion.identity, transform); // Set up the health bar's RectTransform RectTransform healthBarRectTransform = healthBarInstance.GetComponent(); if (healthBarRectTransform != null) { healthBarRectTransform.localPosition = healthBarOffset; } // Initialize references Slider[] sliders = healthBarInstance.GetComponentsInChildren(); Image[] images = healthBarInstance.GetComponentsInChildren(); TextMeshProUGUI text = healthBarInstance.GetComponentInChildren(); InitializeHealthBarComponents(sliders, images, text); // Configure billboard to face camera Billboard billboard = healthBarInstance.GetComponent(); if (billboard == null) { billboard = healthBarInstance.AddComponent(); } billboard.SetCamera(Camera.main); DevLog.Log($"[HealthNew] Created world-space health bar for AI: {gameObject.name}"); } else if (healthBarPrefab != null) { // Fallback: create world-space health bar from normal prefab healthBarInstance = Instantiate(healthBarPrefab, transform.position + healthBarOffset, Quaternion.identity, transform); RectTransform healthBarRectTransform = healthBarInstance.GetComponent(); if (healthBarRectTransform != null) { healthBarRectTransform.localPosition = healthBarOffset; healthBarRectTransform.localScale = new Vector3(0.01f, 0.01f, 0.01f); // Scale down for world space } Slider[] sliders = healthBarInstance.GetComponentsInChildren(); Image[] images = healthBarInstance.GetComponentsInChildren(); TextMeshProUGUI text = healthBarInstance.GetComponentInChildren(); InitializeHealthBarComponents(sliders, images, text); Billboard billboard = healthBarInstance.GetComponent(); if (billboard == null) { billboard = healthBarInstance.AddComponent(); } billboard.SetCamera(Camera.main); DevLog.Log($"[HealthNew] Created scaled world-space health bar for AI: {gameObject.name}"); } } else if (gameObject.CompareTag("Enemy") && !isCashSystemMode) { // Traditional enemy health bar (Practice mode - non-Cash System) // Use spawn point index 1 for enemy (player is 0) int enemySpawnIndex = 1; // Enemy uses second spawn point if (aiHealthBarPrefab != null) { // Use the AI-specific health bar prefab if (healthBarSpawnPoints != null && healthBarSpawnPoints.Length > enemySpawnIndex && healthBarSpawnPoints[enemySpawnIndex] != null) { healthBarInstance = Instantiate(aiHealthBarPrefab, healthBarSpawnPoints[enemySpawnIndex].position, Quaternion.identity, healthBarSpawnPoints[enemySpawnIndex]); } else { // Fallback: create as child of character healthBarInstance = Instantiate(aiHealthBarPrefab, transform.position + healthBarOffset, Quaternion.identity, transform); } } else if (healthBarPrefab != null) { // Fallback: use regular health bar prefab for enemy (unified prefab case) if (healthBarSpawnPoints != null && healthBarSpawnPoints.Length > enemySpawnIndex && healthBarSpawnPoints[enemySpawnIndex] != null) { healthBarInstance = Instantiate(healthBarPrefab, healthBarSpawnPoints[enemySpawnIndex].position, Quaternion.identity, healthBarSpawnPoints[enemySpawnIndex]); } else { healthBarInstance = Instantiate(healthBarPrefab, transform.position + healthBarOffset, Quaternion.identity, transform); } } if (healthBarInstance != null) { Slider[] sliders = healthBarInstance.GetComponentsInChildren(); Image[] images = healthBarInstance.GetComponentsInChildren(); TextMeshProUGUI text = healthBarInstance.GetComponentInChildren(); InitializeHealthBarComponents(sliders, images, text); // Find the player's camera GameObject player = GameObject.FindGameObjectWithTag("Player"); Camera playerCamera = null; if (player != null) playerCamera = player.GetComponentInChildren(true); // Assign the player's camera to the health bar's Billboard Billboard billboard = healthBarInstance.GetComponent(); if (billboard != null && playerCamera != null) billboard.SetCamera(playerCamera); DevLog.Log($"[HealthNew] Created health bar for enemy: {gameObject.name} at spawn index {enemySpawnIndex}"); } } else if (healthBarPrefab != null) { // Use the existing instantiation logic for player or if prefab is set InstantiateHealthBar(); } else { DevLog.LogWarning($"[HealthNew] No health bar prefab assigned on {gameObject.name}"); } rewardsScreen = GameObject.Find("Rewards"); eventSystem = GameObject.Find("EventManager"); if (eventSystem != null) { uiInputModule = eventSystem.GetComponent()?.GetComponent(); } else { Debug.LogError("EventManager not found in the scene"); } isDead = false; damageDisplayThreshold = 10f; damageDisplayTime = 1.0f; // Initialize camera reference mainCamera = Camera.main; // Cache PlayerManagerNew (used in death/respawn paths) _cachedPlayerManager = FindObjectOfType(); } // Dirty flag: avoids per-frame VFX/slider work when health hasn't changed private int _lastCheckedHealth = -1; private void Update() { // Health bar position must update every frame so it tracks character movement // Only for Enemies / Cash System AI (player health bar is static UI) if (healthBarInstance != null && (gameObject.CompareTag("Enemy") || isCashSystemAI)) { UpdateHealthBarPosition(); } // VFX check only when health actually changed (dirty flag) if (_lastCheckedHealth != currentHealth) { _lastCheckedHealth = currentHealth; // Slider is already set by TakeDamage() and ResetHealth(), // but guard against any external currentHealth writes if (healthSlider != null) { healthSlider.value = currentHealth; } if (lowHealthVFX != null) { if (currentHealth < 10 && lowHealthVFX.isStopped) { lowHealthVFX.Play(); } else if (currentHealth > 10 && lowHealthVFX.isPlaying) { lowHealthVFX.Stop(); } } } } // New method to update health bar position private void UpdateHealthBarPosition() { if (mainCamera != null && healthBarInstance != null) { // Calculate position above character Vector3 targetPosition = transform.position + healthBarOffset; // Convert to screen space Vector3 screenPos = mainCamera.WorldToScreenPoint(targetPosition); // Check if health bar has a RectTransform RectTransform rectTransform = healthBarInstance.GetComponent(); if (rectTransform != null) { // Option 1: Set screen position directly if health bar is Screen Space UI if (healthBarInstance.transform.parent != transform) { // Convert screen position to match the canvas space Canvas canvas = healthBarInstance.GetComponentInParent(); if (canvas != null && canvas.renderMode == RenderMode.ScreenSpaceOverlay) { rectTransform.position = screenPos; } else if (canvas != null && canvas.renderMode == RenderMode.ScreenSpaceCamera) { // If using Screen Space Camera, need to adjust for canvas scaling RectTransformUtility.ScreenPointToLocalPointInRectangle( canvas.GetComponent(), screenPos, canvas.worldCamera, out Vector2 localPos); rectTransform.localPosition = localPos; } } // Option 2: If health bar is parented to the character, just update the local position else { rectTransform.localPosition = healthBarOffset; // Make it face the camera rectTransform.forward = -mainCamera.transform.forward; } } } } private void InstantiateHealthBar() { // Initialize the spawn points array if needed if (healthBarSpawnPoints == null || healthBarSpawnPoints.Length == 0) { Debug.LogError("Health bar spawn points array is not initialized on " + gameObject.name); return; } // Find spawn points if they're not already assigned if (healthBarSpawnPoints[0] == null) { GameObject spawnPoint1 = GameObject.Find("HealthBarSpawnPoint1"); if (spawnPoint1 != null) healthBarSpawnPoints[0] = spawnPoint1.transform; } if (healthBarSpawnPoints[1] == null) { GameObject spawnPoint2 = GameObject.Find("HealthBarSpawnPoint2"); if (spawnPoint2 != null) healthBarSpawnPoints[1] = spawnPoint2.transform; } if (healthBarSpawnPoints[2] == null) { GameObject spawnPoint3 = GameObject.Find("HealthBarSpawnPoint3"); if (spawnPoint3 != null) healthBarSpawnPoints[2] = spawnPoint3.transform; } // Check if we found any spawn points bool anySpawnPointsFound = false; for (int i = 0; i < healthBarSpawnPoints.Length; i++) { if (healthBarSpawnPoints[i] != null) { anySpawnPointsFound = true; break; } } if (!anySpawnPointsFound) { Debug.LogError("Could not find any health bar spawn points in the scene for " + gameObject.name); return; } if (healthBarPrefab != null) { // Special case for Enemy - create health bar in world space above character if (gameObject.CompareTag("Enemy") && useWorldSpaceUI) { // Create the health bar as a child of this object (enemy) healthBarInstance = Instantiate(healthBarPrefab, transform.position + healthBarOffset, Quaternion.identity, transform); // Set up the health bar's RectTransform RectTransform healthBarRectTransform = healthBarInstance.GetComponent(); if (healthBarRectTransform != null) { healthBarRectTransform.localPosition = healthBarOffset; } // Get components for initialization Slider[] sliders = healthBarInstance.GetComponentsInChildren(); Image[] images = healthBarInstance.GetComponentsInChildren(); TextMeshProUGUI text = healthBarInstance.GetComponentInChildren(); // Initialize component references InitializeHealthBarComponents(sliders, images, text); // Create or attach to a world space canvas Canvas canvas = healthBarInstance.GetComponent(); if (canvas == null) { canvas = healthBarInstance.AddComponent(); canvas.renderMode = RenderMode.WorldSpace; // Add a canvas scaler to maintain size regardless of distance CanvasScaler scaler = healthBarInstance.AddComponent(); scaler.dynamicPixelsPerUnit = 100f; } // Force the health bar to face the camera in LateUpdate if (healthBarInstance.GetComponent() == null) { healthBarInstance.AddComponent(); } // Assign the correct camera to the Billboard Billboard billboard = healthBarInstance.GetComponent(); if (billboard != null) { // Try to find a camera in the parent hierarchy Camera myCamera = GetComponentInChildren(true); if (myCamera != null && myCamera.enabled) billboard.SetCamera(myCamera); else billboard.SetCamera(Camera.main); // fallback } } else { // Normal UI health bar for non-enemy or when not using world space UI currentSpawnPointIndex = GetNextAvailableSpawnPointIndex(); if (currentSpawnPointIndex < 0 || currentSpawnPointIndex >= healthBarSpawnPoints.Length) { DevLog.LogWarning("All spawn points are filled or no valid spawn points found."); return; } Transform spawnPoint = healthBarSpawnPoints[currentSpawnPointIndex]; if (spawnPoint == null) { Debug.LogError("Spawn point at index " + currentSpawnPointIndex + " is null"); return; } healthBarInstance = Instantiate(healthBarPrefab, spawnPoint.position, Quaternion.identity); RectTransform spawnPointRectTransform = spawnPoint.GetComponent(); if (spawnPointRectTransform == null) { Debug.LogError("Spawn point does not have a RectTransform component"); return; } RectTransform healthBarRectTransform = healthBarInstance.GetComponent(); healthBarRectTransform.SetParent(spawnPoint, false); healthBarRectTransform.position = spawnPointRectTransform.position; healthBarRectTransform.sizeDelta = spawnPointRectTransform.sizeDelta; // Set player health bar to bottom center of the screen healthBarRectTransform.anchorMin = new Vector2(0.5f, 0f); healthBarRectTransform.anchorMax = new Vector2(0.5f, 0f); healthBarRectTransform.pivot = new Vector2(0.5f, 0.5f); healthBarRectTransform.anchoredPosition = new Vector2(0f, 50f); // Access the Slider component(s) within the health bar instance Slider[] sliders = healthBarInstance.GetComponentsInChildren(); Image[] images = healthBarInstance.GetComponentsInChildren(); TextMeshProUGUI text = healthBarInstance.GetComponentInChildren(); InitializeHealthBarComponents(sliders, images, text); try { if (healthBar != null && sliders != null && currentSpawnPointIndex < healthBar.Length && currentSpawnPointIndex < sliders.Length) { healthBar[currentSpawnPointIndex] = sliders[currentSpawnPointIndex]; } } catch (IndexOutOfRangeException) { Debug.LogError("Index is out of bounds for healthBar or sliders on " + gameObject.name); } currentSpawnPointIndex++; } } else { Debug.LogError("Health bar prefab is not assigned on " + gameObject.name); } } // Helper method to initialize health bar components private void InitializeHealthBarComponents(Slider[] sliders, Image[] images, TextMeshProUGUI text) { if (text != null && text.name == "CharacterName") { // Set character name based on gameObject name string objectName = gameObject.name; if (objectName.Contains("Cheetah")) text.text = "AMIRA"; else if (objectName.Contains("Rabbit")) text.text = "WINDHAM"; else if (objectName.Contains("HM")) text.text = "AMON"; else if (objectName.Contains("Cat")) text.text = "ZIGGY"; else if (objectName.Contains("Eagle")) text.text = "BAHMAN"; else if (objectName.Contains("Hyena")) text.text = "IMANI"; else if (objectName.Contains("CAT N")) text.text = "ZIGGY NEW"; } foreach (Slider slider in sliders) { if (slider.name == "SliderHealth") { healthSlider = slider; healthSlider.maxValue = 100; healthSlider.minValue = 0; } } foreach (Image image in images) { if (image != null && image.name == "stamina") { staminaCircle = image; staminaSystem = GetComponent(); if (staminaSystem != null) { staminaSystem.staminaCircle = staminaCircle; staminaSystem.staminaCircle.fillAmount = 1; } else { Debug.LogError("StaminaSystem component not found on " + gameObject.name); } } if (image != null && image.name == "CharacterImage") { // Set character image based on gameObject name if (characterImage != null) { image.sprite = characterImage; } } } } private int GetNextAvailableSpawnPointIndex() { for (int i = 0; i < healthBarSpawnPoints.Length; i++) { if (healthBarSpawnPoints[i].childCount == 0) { return i; } } return -1; } public int CalculateDamage(Collider characterCollider, Collider other) { /*// Calculate the damage based on the relative velocity of the colliding objects. Vector3 currentPositionOther = other.transform.position; //Vector3 previousPositionOther = otherPreviousPosition; // Assuming you have the previous position stored somewhere Vector3 currentVelocityOther = (currentPositionOther - previousPositionOther) / Time.deltaTime; Vector3 currentPositionCharacterCollider = characterCollider.transform.position; Vector3 previousPositionCharacterCollider = characterColliderPreviousPosition; // Assuming you have the previous position stored somewhere Vector3 currentVelocityCharacterCollider = (currentPositionCharacterCollider - previousPositionCharacterCollider) / Time.deltaTime; // Calculate the relative velocity by subtracting characterCollider's velocity from other's velocity Vector3 relativeVelocity = currentVelocityOther - currentVelocityCharacterCollider;*/ //int damage = Mathf.RoundToInt(relativeVelocity.magnitude * 10f); //int damage = Mathf.Clamp(damage, 1, 10); //print(damage); // COMBAT FIX: Scale damage by attack type instead of flat 2 // Light attacks: 8-12, Medium: 13-18, Heavy: 19-25 int damage = 10; // Default for light Animator attackerAnim = other.GetComponentInParent(); if (attackerAnim != null) { AnimatorClipInfo[] clips = attackerAnim.GetCurrentAnimatorClipInfo(0); if (clips.Length > 0) { string clip = clips[0].clip.name; if (clip == AnimationNames.Haymaker || clip == AnimationNames.SpinningBackfist || clip == AnimationNames.ChargedPunch || clip == AnimationNames.BasicKick) { damage = UnityEngine.Random.Range(13, 19); // Medium } else if (clip == AnimationNames.ElbowSmash || clip == AnimationNames.AirPunch || clip == AnimationNames.DropKick || clip == AnimationNames.AirKick) { damage = UnityEngine.Random.Range(19, 26); // Heavy } else { damage = UnityEngine.Random.Range(8, 13); // Light } } } return damage; } public int TakeDamage(int damage) { return TakeDamage(damage, null); } /// /// Take damage with attacker tracking — fires GameEvents.FireDamageDealt /// so AI can react (fight back) and systems can track damage sources. /// public int TakeDamage(int damage, GameObject attacker) { currentHealth -= damage; healthSlider.value = currentHealth; isDead = false; // Fire damage event so AI can react immediately if (attacker != null) GameEvents.FireDamageDealt(attacker, gameObject, damage); if (currentHealth <= 0) { currentHealth = 0; isDead = true; DevLog.Log("takedamage isdead true in " + gameObject.tag); // Fire kill event for cash system (kill rewards, respawn) if (attacker != null) GameEvents.FireKill(attacker, gameObject); } return currentHealth; } void AssignCharacterName() { GameObject enemyObj = GameObject.FindGameObjectWithTag("Enemy"); GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); enemyname = enemyObj ? CharacterPrefabManager.Instance.GetCharacterDisplayName(enemyObj) : "UNKNOWN"; playername = playerObj ? CharacterPrefabManager.Instance.GetCharacterDisplayName(playerObj) : "UNKNOWN"; } public void OnDeath() { bool isMobile = false; if (UnityEngine.Application.platform == RuntimePlatform.Android || UnityEngine.Application.platform == RuntimePlatform.IPhonePlayer) { isMobile = true; } AssignCharacterName(); if (isMobile) { //Show UI if (rewardsScreen == null) rewardsScreen = GameObject.Find("UI").transform.GetChild(1).gameObject; GameObject loserTitle = rewardsScreen.transform.GetChild(4).GetChild(0).gameObject; GameObject killsLoser = rewardsScreen.transform.GetChild(4).GetChild(1).gameObject; GameObject deathsLoser = rewardsScreen.transform.GetChild(4).GetChild(2).gameObject; GameObject assistsLoser = rewardsScreen.transform.GetChild(4).GetChild(3).gameObject; GameObject damageLoser = rewardsScreen.transform.GetChild(4).GetChild(4).gameObject; GameObject winnerTitle = rewardsScreen.transform.GetChild(3).GetChild(0).gameObject; GameObject killsWinner = rewardsScreen.transform.GetChild(3).GetChild(1).gameObject; GameObject deathsWinner = rewardsScreen.transform.GetChild(3).GetChild(2).gameObject; GameObject assistsWinner = rewardsScreen.transform.GetChild(3).GetChild(3).gameObject; GameObject damageWinner = rewardsScreen.transform.GetChild(3).GetChild(4).gameObject; assistsLoser.GetComponentInChildren().text = "0"; assistsWinner.GetComponentInChildren().text = "0"; if (GameObject.FindGameObjectWithTag("Player").GetComponent().playerWins > GameObject.FindGameObjectWithTag("Enemy").GetComponent().enemyWins) { AssignCharacterName(); loserTitle.GetComponent().text = enemyname + " LOSE!"; winnerTitle.GetComponent().text = playername + " WIN!"; killsLoser.GetComponentInChildren().text = GameObject.FindGameObjectWithTag("Enemy").GetComponent().enemyWins.ToString(); damageLoser.GetComponentInChildren().text = (100 - GameObject.FindGameObjectWithTag("Enemy").GetComponent().currentHealth).ToString(); killsWinner.GetComponentInChildren().text = GameObject.FindGameObjectWithTag("Player").GetComponent().playerWins.ToString(); deathsWinner.GetComponentInChildren().text = GameObject.FindGameObjectWithTag("Enemy").GetComponent().enemyWins.ToString(); deathsLoser.GetComponentInChildren().text = GameObject.FindGameObjectWithTag("Player").GetComponent().playerWins.ToString(); damageWinner.GetComponentInChildren().text = (100 - GameObject.FindGameObjectWithTag("Player").GetComponent().currentHealth).ToString(); } else { AssignCharacterName(); loserTitle.GetComponent().text = playername + " LOSE!"; winnerTitle.GetComponent().text = enemyname + " WIN!"; killsLoser.GetComponentInChildren().text = GameObject.FindGameObjectWithTag("Player").GetComponent().playerWins.ToString(); damageLoser.GetComponentInChildren().text = (100 - GameObject.FindGameObjectWithTag("Player").GetComponent().currentHealth).ToString(); killsWinner.GetComponentInChildren().text = GameObject.FindGameObjectWithTag("Enemy").GetComponent().enemyWins.ToString(); deathsWinner.GetComponentInChildren().text = GameObject.FindGameObjectWithTag("Player").GetComponent().playerWins.ToString(); deathsLoser.GetComponentInChildren().text = GameObject.FindGameObjectWithTag("Enemy").GetComponent().enemyWins.ToString(); damageWinner.GetComponentInChildren().text = (100 - GameObject.FindGameObjectWithTag("Enemy").GetComponent().currentHealth).ToString(); } GameObject.FindGameObjectWithTag("Player").SetActive(false); GameObject.FindGameObjectWithTag("Enemy").SetActive(false); rewardsScreen.transform.GetChild(0).GetComponent