using System; using UnityEngine; using Quests; [RequireComponent(typeof(CharacterStats))] public class CombatController : MonoBehaviour { [Header("Combat Feedback Prefabs (Optional)")] [SerializeField] private GameObject attackEffectPrefab; [SerializeField] private GameObject hitEffectPrefab; public CharacterStats Stats { get; private set; } public CharacterStats CurrentTarget { get; private set; } public event Action OnAttackExecuted; private float lastAttackTime = 0f; private void Awake() { Stats = GetComponent(); Stats.onDeath.AddListener(OnSelfDeath); } public void SetTarget(CharacterStats target) { if (target == this.Stats || (target != null && target.IsDead)) { ClearTarget(); return; } CurrentTarget = target; } public void ClearTarget() { CurrentTarget = null; } public bool IsInAttackRange(CharacterStats target) { if (target == null) return false; Vector3 flatSelf = new Vector3(transform.position.x, 0, transform.position.z); Vector3 flatTarget = new Vector3(target.transform.position.x, 0, target.transform.position.z); float distance = Vector3.Distance(flatSelf, flatTarget); return distance <= Stats.AttackRange; } public bool CanAttack() { if (Stats.IsDead || CurrentTarget == null || CurrentTarget.IsDead) return false; if (!IsInAttackRange(CurrentTarget)) return false; float attackCooldown = 1.0f / Mathf.Max(Stats.AttackSpeed, 0.1f); return Time.time >= lastAttackTime + attackCooldown; } public bool TryExecuteAttack() { if (!CanAttack()) return false; lastAttackTime = Time.time; // Face target Vector3 dir = (CurrentTarget.transform.position - transform.position); dir.y = 0; if (dir != Vector3.zero) { transform.rotation = Quaternion.LookRotation(dir); } // Deal Damage float damage = Stats.BaseDamage; CurrentTarget.TakeDamage(damage, gameObject); // Visual effects if (attackEffectPrefab != null) { Instantiate(attackEffectPrefab, transform.position + transform.forward * 0.5f + Vector3.up, transform.rotation); } if (hitEffectPrefab != null && CurrentTarget != null) { Instantiate(hitEffectPrefab, CurrentTarget.transform.position + Vector3.up, Quaternion.identity); } OnAttackExecuted?.Invoke(CurrentTarget); // If target died from this attack if (CurrentTarget.IsDead) { // Report kill to Quest System if attacker is player if (gameObject.CompareTag("Player") && QuestManager.Instance != null) { QuestManager.Instance.ReportKill(CurrentTarget.CharacterName); } ClearTarget(); } return true; } private void OnSelfDeath(GameObject killer) { ClearTarget(); } }