using UnityEngine;
/// <summary>
/// Handles bite attacks, damage, and health for the T-Rex.
/// Attach to any character that can attack or take damage.
/// </summary>
public class CombatSystem : MonoBehaviour
{
[Header("Health")]
[Tooltip("Maximum health points")]
public float maxHealth = 100f;
[Tooltip("Current health")]
public float currentHealth;
[Tooltip("Time before regenerating health (seconds)")]
public float regenDelay = 3f;
[Tooltip("Health regenerated per second")]
public float regenRate = 5f;
[Header("Attack")]
[Tooltip("Damage dealt per bite")]
public float biteDamage = 25f;
[Tooltip("Range of the bite attack")]
public float biteRange = 3f;
[Tooltip("Arc angle of the bite (degrees)")]
public float biteAngle = 60f;
[Tooltip("Cooldown between bites (seconds)")]
public float biteCooldown = 0.5f;
[Tooltip("Push force applied to victims")]
public float knockbackForce = 10f;
[Header("References")]
[Tooltip("Animator for attack animations")]
public Animator animator;
[Tooltip("Sound effect for biting")]
public AudioClip biteSound;
[Tooltip("Audio source for playing sounds")]
public AudioSource audioSource;
[Tooltip("Visual effect for bite impact")]
public GameObject biteEffect;
[Tooltip("Health bar UI to update")]
public RectTransform healthBarFill;
// Private
private float nextBiteTime;
private float lastDamageTime;
private HealthBarController healthBarUI;
public float HealthPercent => currentHealth / maxHealth;
void Start()
{
currentHealth = maxHealth;
lastDamageTime = Time.time;
// Setup audio source if needed
if (audioSource == null)
{
audioSource = GetComponent<AudioSource>();
if (audioSource == null)
{
audioSource = gameObject.AddComponent<AudioSource>();
}
}
// Find health bar in UI
if (healthBarFill == null)
{
healthBarUI = FindAnyObjectByType<HealthBarController>();
}
Debug.Log($"⚔️ {gameObject.name} combat initialized - Max HP: {maxHealth}, Bite: {biteDamage}");
}
void Update()
{
HandleRegeneration();
HandleAttack();
UpdateHealthBar();
}
void HandleRegeneration()
{
float timeSinceDamage = Time.time - lastDamageTime;
if (timeSinceDamage >= regenDelay && currentHealth < maxHealth)
{
currentHealth = Mathf.Min(currentHealth + regenRate * Time.deltaTime, maxHealth);
}
else
{
}
}
void HandleAttack()
{
// Left mouse button or Space for bite attack
if (Input.GetButtonDown("Fire1") && Time.time >= nextBiteTime)
{
PerformBite();
nextBiteTime = Time.time + biteCooldown;
}
}
/// <summary>
/// Perform a bite attack. Called by player input or AI.
/// </summary>
public void PerformBite()
{
// Play animation
if (animator != null)
{
animator.SetTrigger("Attack");
}
// Play sound
if (biteSound != null)
{
audioSource.PlayOneShot(biteSound);
}
// Spawn visual effect
if (biteEffect != null)
{
Vector3 effectPos = transform.position + transform.forward * biteRange * 0.5f;
Instantiate(biteEffect, effectPos, Quaternion.identity);
}
// Find targets in bite range
Collider[] hits = Physics.OverlapSphere(
transform.position + transform.forward * biteRange * 0.5f,
biteRange * 0.5f
);
foreach (Collider hit in hits)
{
// Check if target is in bite arc
Vector3 directionToTarget = (hit.transform.position - transform.position).normalized;
float angle = Vector3.Angle(transform.forward, directionToTarget);
if (angle <= biteAngle * 0.5f)
{
CombatSystem targetCombat = hit.GetComponent<CombatSystem>();
if (targetCombat != null && targetCombat != this)
{
targetCombat.TakeDamage(biteDamage, transform.forward);
}
// Apply knockback to rigidbodies
Rigidbody rb = hit.GetComponent<Rigidbody>();
if (rb != null)
{
rb.AddForce(transform.forward * knockbackForce, ForceMode.Impulse);
}
}
}
Debug.Log($"🦷 Bite attack! Range: {biteRange}, Damage: {biteDamage}");
}
/// <summary>
/// Take damage from another source.
/// </summary>
public void TakeDamage(float damage, Vector3 knockbackDirection = default)
{
currentHealth -= damage;
lastDamageTime = Time.time;
// Flash red or play hurt animation
if (animator != null)
{
animator.SetTrigger("Hurt");
}
Debug.Log($"💥 {gameObject.name} took {damage} damage! HP: {currentHealth}/{maxHealth}");
if (currentHealth <= 0)
{
Die();
}
}
void Die()
{
Debug.Log($"💀 {gameObject.name} has been defeated!");
// Play death animation
if (animator != null)
{
animator.SetTrigger("Die");
}
// Disable combat and movement
enabled = false;
// Notify AI system if this is an enemy
AIController ai = GetComponent<AIController>();
if (ai != null)
{
ai.Die();
}
// Disable the controller if this is the player
TrexController controller = GetComponent<TrexController>();
if (controller != null)
{
controller.enabled = false;
}
// Spawn death effect
// TODO: Add particle system for death
// Destroy after delay
Destroy(gameObject, 3f);
}
void UpdateHealthBar()
{
if (healthBarFill != null)
{
healthBarFill.sizeDelta = new Vector2(healthBarFill.sizeDelta.x * HealthPercent, healthBarFill.sizeDelta.y);
}
if (healthBarUI != null)
{
healthBarUI.UpdateHealthBar(HealthPercent);
}
}
void OnDrawGizmosSelected()
{
// Visualize bite range
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(
transform.position + transform.forward * biteRange * 0.5f,
biteRange * 0.5f
);
}
}