using System.Collections;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
/// <summary>
/// GAME 1303 Tier 2 — Press Attack, swing, hurt what's in front of you.
///
/// SETUP
/// 1. Add to the player root (needs PlayerInput with Send Messages — the default
/// actions asset already has an "Attack" action in the Player map).
/// 2. Make a child "AttackHitbox" with a trigger collider + Hitbox (Target Team = Enemy),
/// placed in front of the character. DISABLE the child. Drag it into Hitbox.
/// Because it's a child, it flips with the player automatically.
/// 3. Timing: Windup = frames before the hitbox turns on, Active = how long it's on,
/// Recovery = time before you can attack again. Match these to your animation.
/// 4. Animator: add an "Attack" trigger (PlayerAnimatorBridge doesn't need changes).
/// 5. Optional: instead of timers, call EnableHitbox()/DisableHitbox() from
/// Animation Events on the swing clip for frame-perfect hits — set Use Animation Events.
///
/// Enemies can use this too: set Player Controlled = false and call Attack() from AI
/// (EnemyChase does this when in range).
/// </summary>
public class MeleeAttack : MonoBehaviour
{
public bool playerControlled = true;
[Header("Hitbox")]
public Hitbox hitbox;
[Header("Timing (seconds)")]
public float windup = 0.08f;
public float active = 0.12f;
public float recovery = 0.2f;
[Tooltip("If ON, hitbox is driven by Animation Events calling EnableHitbox/DisableHitbox.")]
public bool useAnimationEvents = false;
[Header("Feel")]
[Tooltip("Freeze horizontal movement while attacking (grounded only).")]
public bool stopWhileAttacking = true;
public string animatorTrigger = "Attack";
public string sfxName = "swing";
[Header("Events")]
public UnityEvent OnAttackStarted;
public bool IsAttacking { get; private set; }
private Animator anim;
private PlayerController2D controller;
private Rigidbody2D rb;
private Knockback knockback;
void Awake()
{
anim = GetComponent<Animator>();
controller = GetComponent<PlayerController2D>();
rb = GetComponent<Rigidbody2D>();
knockback = GetComponent<Knockback>();
if (hitbox != null) hitbox.gameObject.SetActive(false);
}
// Input System message (PlayerInput -> Send Messages)
void OnAttack(InputValue value)
{
if (!playerControlled || !value.isPressed) return;
if (controller != null && !controller.InputEnabled) return;
Attack();
}
/// <summary>Start a swing if not already attacking. Safe to call from AI or UI.</summary>
public bool Attack()
{
if (IsAttacking) return false;
if (knockback != null && knockback.IsStunned) return false;
StartCoroutine(AttackRoutine());
return true;
}
IEnumerator AttackRoutine()
{
IsAttacking = true;
OnAttackStarted?.Invoke();
if (anim != null && !string.IsNullOrEmpty(animatorTrigger)) anim.SetTrigger(animatorTrigger);
if (!string.IsNullOrEmpty(sfxName)) AudioManager.Instance?.PlaySFX(sfxName);
if (stopWhileAttacking && rb != null && (controller == null || controller.IsGrounded))
rb.linearVelocity = new Vector2(0f, rb.linearVelocity.y);
if (useAnimationEvents)
{
// Animation events handle the hitbox; we just wait out the total time
yield return new WaitForSeconds(windup + active + recovery);
}
else
{
yield return new WaitForSeconds(windup);
EnableHitbox();
yield return new WaitForSeconds(active);
DisableHitbox();
yield return new WaitForSeconds(recovery);
}
IsAttacking = false;
}
// Public so Animation Events can call them
public void EnableHitbox() { if (hitbox != null) hitbox.gameObject.SetActive(true); }
public void DisableHitbox() { if (hitbox != null) hitbox.gameObject.SetActive(false); }
}