Files
TopDown/Assets/Scripts/CombatController.cs

111 lines
3.1 KiB
C#
Raw Permalink Normal View History

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<CharacterStats> OnAttackExecuted;
private float lastAttackTime = 0f;
private void Awake()
{
Stats = GetComponent<CharacterStats>();
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();
}
}