using UnityEngine; using System.Collections; public class NorcielBoss : MonsterClass { [Header("--- 🧠 두뇌 연결 ---")] [SerializeField] private BossCounterSystem counterSystem; [Header("--- ⚔️ 패턴 설정 ---")] [SerializeField] private float patternInterval = 3f; [Header("--- 🎱 무기(쇠공) 설정 (필수!) ---")] [SerializeField] private GameObject ironBall; [SerializeField] private Transform handHolder; [Header("--- 📊 UI 연결 ---")] [SerializeField] private GameObject bossHealthBar; // --- 내부 변수들 --- private float _timer; private Rigidbody rb; private Rigidbody ballRb; private bool isBattleStarted = false; private bool isWeaponless = false; private bool isPerformingAction = false; // ⭐ 신규: 연출 중 플래그 (Roar 등) private Transform target; protected override void Awake() { base.Awake(); rb = GetComponent(); if (ironBall != null) ballRb = ironBall.GetComponent(); } // ⭐ [수정] Start → OnEnable 이후 호출되도록 변경 private void Start() { StartBossBattle(); } protected override void Init() { _timer = patternInterval; isBattleStarted = false; isWeaponless = false; isPerformingAction = false; // 플레이어 찾기 GameObject playerObj = GameObject.FindWithTag("Player"); if (playerObj != null) target = playerObj.transform; else { var playerScript = FindObjectOfType(); if (playerScript != null) target = playerScript.transform; } // HP 초기화 currentHP = maxHP; StartCoroutine(SafeInitNavMesh()); if (bossHealthBar != null) bossHealthBar.SetActive(false); if (ballRb != null) ballRb.isKinematic = true; } private IEnumerator SafeInitNavMesh() { yield return null; if (agent != null) { int retry = 0; while (!agent.isOnNavMesh && retry < 5) { retry++; yield return new WaitForSeconds(0.1f); } if (agent.isOnNavMesh) { agent.isStopped = true; } agent.enabled = false; } } // ════════════════════════════════════════ // ⭐ [핵심 수정] 부모의 OnManagedUpdate 흐름 제어 // ════════════════════════════════════════ /// /// 부모 MonsterClass의 StopMovement()가 매 프레임 Monster_Idle을 /// 강제 재생해서 Roar/다른 애니메이션을 덮어쓰는 문제 해결. /// /// 보스는 일반 몬스터와 다르게: /// - 전투 시작 전에는 아무 AI도 돌리지 않음 /// - 연출 중(Roar 등)에는 애니메이션을 건드리지 않음 /// protected override void ExecuteAILogic() { // 연출 중이거나 전투 미시작 → 아무것도 안 함 if (isPerformingAction || !isBattleStarted || target == null) return; // 무기 없으면 회수 우선 if (isWeaponless) { RetrieveWeaponLogic(); return; } // --- 일반 전투 로직 --- _timer -= Time.deltaTime; if (_timer <= 0 && !isAttacking && !isHit && !isDead && !isResting) { _timer = patternInterval; DecideAttack(); } // 이동 (공격/피격 중이 아닐 때만) if (!isAttacking && !isHit && !isResting && agent != null && agent.enabled && agent.isOnNavMesh) { agent.SetDestination(target.position); if (animator != null) animator.SetFloat("Speed", agent.velocity.magnitude); if (agent.remainingDistance <= agent.stoppingDistance + 0.5f) { agent.isStopped = true; LookAtTarget(target.position); if (animator != null) animator.SetFloat("Speed", 0f); } else { agent.isStopped = false; } } } // ════════════════════════════════════════ // 🏃‍♂️ 무기 회수 로직 // ════════════════════════════════════════ private void RetrieveWeaponLogic() { if (ironBall == null || !agent.enabled || !agent.isOnNavMesh) return; agent.SetDestination(ironBall.transform.position); agent.isStopped = false; if (animator != null) animator.SetFloat("Speed", agent.velocity.magnitude); float dist = Vector3.Distance(transform.position, ironBall.transform.position); if (dist <= 3.0f && !isAttacking) { StartCoroutine(PickUpBallRoutine()); } } private IEnumerator PickUpBallRoutine() { OnAttackStart(); if (agent != null && agent.isOnNavMesh) { agent.isStopped = true; agent.velocity = Vector3.zero; } if (animator != null) { animator.SetFloat("Speed", 0); animator.Play("Skill_Pickup"); } yield return new WaitForSeconds(0.8f); ironBall.transform.SetParent(handHolder); ironBall.transform.localPosition = Vector3.zero; ironBall.transform.localRotation = Quaternion.identity; if (ballRb != null) { ballRb.isKinematic = true; ballRb.velocity = Vector3.zero; } yield return new WaitForSeconds(1.0f); isWeaponless = false; OnAttackEnd(); if (agent != null && agent.isOnNavMesh) agent.isStopped = false; } // ════════════════════════════════════════ // 🎬 전투 입장 // ════════════════════════════════════════ public void StartBossBattle() { if (isBattleStarted) return; StartCoroutine(BattleStartRoutine()); } private IEnumerator BattleStartRoutine() { Debug.Log("🔥 보스 전투 시작! (Roar)"); // ⭐ [핵심] 연출 중 플래그 ON → ExecuteAILogic이 아무것도 안 함 isPerformingAction = true; if (bossHealthBar != null) bossHealthBar.SetActive(true); if (counterSystem != null) counterSystem.InitializeBattle(); // ⭐ [핵심] Roar 재생 if (animator != null) { animator.Play("Roar", 0, 0f); } // Roar 애니메이션 대기 (길이에 맞게 조절) yield return new WaitForSeconds(2.0f); // ⭐ [핵심] Roar 끝나면 Idle로 전환 if (animator != null) { animator.Play("Monster_Idle", 0, 0f); } // 연출 종료 → 전투 시작 isPerformingAction = false; isBattleStarted = true; // NavMeshAgent 활성화 if (agent != null) { agent.enabled = true; // NavMesh 위에 올라갈 때까지 잠시 대기 int retry = 0; while (!agent.isOnNavMesh && retry < 10) { retry++; yield return new WaitForSeconds(0.1f); } if (agent.isOnNavMesh) { agent.isStopped = false; } } Debug.Log("⚔️ 보스 전투 루프 시작!"); } private void LookAtTarget(Vector3 targetPos) { Vector3 dir = targetPos - transform.position; dir.y = 0; if (dir != Vector3.zero) { transform.rotation = Quaternion.Slerp( transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * 5f ); } } // ════════════════════════════════════════ // 🧠 AI 공격 판단 // ════════════════════════════════════════ private void DecideAttack() { if (animator != null) animator.SetFloat("Speed", 0f); // 공격 전 이동 멈춤 if (agent != null && agent.isOnNavMesh) { agent.isStopped = true; agent.velocity = Vector3.zero; } // 플레이어를 바라봄 if (target != null) LookAtTarget(target.position); string patternName = (counterSystem != null) ? counterSystem.SelectBossPattern() : "Normal"; Debug.Log($"🧠 보스 패턴 선택: {patternName}"); switch (patternName) { case "DashAttack": StartCoroutine(Pattern_DashAttack()); break; case "Smash": StartCoroutine(Pattern_SmashAttack()); break; case "ShieldWall": StartCoroutine(Pattern_ShieldWall()); break; default: StartCoroutine(Pattern_ThrowBall()); break; } } // ════════════════════════════════════════ // ⚔️ 공격 패턴 // ════════════════════════════════════════ // 1. 돌진 private IEnumerator Pattern_DashAttack() { OnAttackStart(); if (agent != null) agent.enabled = false; if (animator != null) animator.Play("Skill_Dash_Ready", 0, 0f); yield return new WaitForSeconds(0.5f); // 돌진 if (rb != null) { rb.isKinematic = false; rb.velocity = transform.forward * 20f; } if (animator != null) animator.Play("Skill_Dash_Go", 0, 0f); yield return new WaitForSeconds(1.0f); // 정지 if (rb != null) { rb.velocity = Vector3.zero; rb.isKinematic = true; } // NavMeshAgent 복구 if (agent != null && isBattleStarted) { agent.enabled = true; // NavMesh 복귀 대기 int retry = 0; while (agent != null && !agent.isOnNavMesh && retry < 10) { retry++; yield return new WaitForSeconds(0.1f); } } OnAttackEnd(); } // 2. 스매시 private IEnumerator Pattern_SmashAttack() { OnAttackStart(); if (animator != null) animator.Play("Skill_Smash_Impact", 0, 0f); yield return new WaitForSeconds(1.2f); if (animator != null) animator.Play("Skill_Smash_Impact", 0, 0f); yield return new WaitForSeconds(1.0f); OnAttackEnd(); } // 3. 쓸기 private IEnumerator Pattern_ShieldWall() { OnAttackStart(); if (animator != null) animator.Play("Skill_Sweep", 0, 0f); yield return new WaitForSeconds(2.0f); OnAttackEnd(); } // 4. 공 던지기 private IEnumerator Pattern_ThrowBall() { OnAttackStart(); if (animator != null) animator.Play("Attack_Throw", 0, 0f); yield return new WaitForSeconds(0.5f); if (ironBall != null && ballRb != null) { ironBall.transform.SetParent(null); ballRb.isKinematic = false; Vector3 dir = (target.position - transform.position).normalized; ballRb.AddForce(dir * 20f + Vector3.up * 5f, ForceMode.Impulse); ballRb.angularDrag = 5f; } isWeaponless = true; yield return new WaitForSeconds(1.0f); OnAttackEnd(); } // ════════════════════════════════════════ // ⭐ [신규] 피격 시 부모 클래스 StopAllCoroutines 대응 // ════════════════════════════════════════ /// /// 부모의 StartHit()이 StopAllCoroutines()를 호출하므로, /// 보스 전용 상태를 여기서 복구합니다. /// protected override void OnStartHit() { isPerformingAction = false; // 연출 중이었으면 해제 // 공격 중이었으면 해제 if (isAttacking) { isAttacking = false; if (myWeapon != null) myWeapon.DisableHitBox(); } // 돌진 중 물리 복구 if (rb != null && !rb.isKinematic) { rb.velocity = Vector3.zero; rb.isKinematic = true; } } /// /// 피격 종료 후 NavMeshAgent 복구 /// public override void OnHitEnd() { base.OnHitEnd(); // 전투 중이면 Agent 다시 활성화 if (isBattleStarted && agent != null && !agent.enabled) { agent.enabled = true; } } // ════════════════════════════════════════ // ⭐ [신규] 사망 처리 // ════════════════════════════════════════ protected override void OnDie() { isBattleStarted = false; isPerformingAction = false; isWeaponless = false; // 물리 정리 if (rb != null) { rb.velocity = Vector3.zero; rb.isKinematic = true; } // 체력바 숨기기 if (bossHealthBar != null) bossHealthBar.SetActive(false); } }