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; // 쇠공이 붙어있을 손의 위치 (RightHand 뼈) [Header("--- 📊 UI 연결 ---")] [SerializeField] private GameObject bossHealthBar; // 보스 체력바 UI // --- 내부 변수들 --- private float _timer; private Rigidbody rb; // 보스 본체의 리지드바디 private Rigidbody ballRb; // 쇠공의 리지드바디 private bool isBattleStarted = false; // 전투 시작 여부 private bool isWeaponless = false; // 현재 무기가 없는가? private Transform target; // 플레이어 타겟 protected override void Awake() { base.Awake(); rb = GetComponent(); // 쇠공에서 리지드바디 가져오기 if (ironBall != null) ballRb = ironBall.GetComponent(); } protected override void Init() { // 1. 변수 초기화 _timer = patternInterval; isBattleStarted = false; isWeaponless = false; // 2. 플레이어 찾기 GameObject playerObj = GameObject.FindWithTag("Player"); if (playerObj != null) target = playerObj.transform; else { var playerScript = FindObjectOfType(); if (playerScript != null) target = playerScript.transform; } // 3. 상태 봉인 (움직임 끄기) if (agent != null) { agent.enabled = false; agent.isStopped = true; } if (bossHealthBar != null) bossHealthBar.SetActive(false); // 4. 쇠공 초기 상태 설정 (물리 끄고 손에 붙이기) if (ballRb != null) ballRb.isKinematic = true; } protected override void ExecuteAILogic() { // 전투 미시작 or 타겟 없음 -> 정지 if (!isBattleStarted || target == null) return; // ⭐ [최우선 순위] 무기가 없으면 공격 중단하고 주으러 감 if (isWeaponless) { RetrieveWeaponLogic(); return; } // --- 일반 전투 로직 --- // 1. 공격 쿨타임 체크 _timer -= Time.deltaTime; if (_timer <= 0 && !isAttacking && !isHit && !isDead) { _timer = patternInterval; DecideAttack(); } // 2. 평소 이동 (플레이어 추격) if (!isAttacking && agent.enabled) { agent.SetDestination(target.position); // 애니메이션 속도 동기화 if (animator != null) animator.SetFloat("Speed", agent.velocity.magnitude); // 거리 체크 if (agent.remainingDistance <= agent.stoppingDistance) { agent.isStopped = true; LookAtTarget(target.position); } else { agent.isStopped = false; } } } // ════════════════════════════════════════ // 🏃‍♂️ 무기 회수 로직 (Retrieve System) // ════════════════════════════════════════ private void RetrieveWeaponLogic() { if (ironBall == null) return; // 1. 공 위치로 이동 if (agent.enabled) { agent.SetDestination(ironBall.transform.position); agent.isStopped = false; } // 이동 애니메이션 if (animator != null) animator.SetFloat("Speed", agent.velocity.magnitude); // 2. 공과의 거리 체크 (가까워지면 줍기 시도) float dist = Vector3.Distance(transform.position, ironBall.transform.position); // 거리 3.0f 이내이고, 줍는 중이 아닐 때 실행 if (dist <= 3.0f && !isAttacking) { StartCoroutine(PickUpBallRoutine()); } } private IEnumerator PickUpBallRoutine() { OnAttackStart(); // 행동 시작 (다른 행동 불가) // 멈춤 if (agent != null) { agent.isStopped = true; agent.velocity = Vector3.zero; } if (animator != null) animator.SetFloat("Speed", 0); // 1. 줍는 모션 재생 Debug.Log("boss: 내 소중한 공!!"); if (animator != null) animator.Play("Skill_Pickup"); // 줍는 애니메이션 yield return new WaitForSeconds(0.8f); // 손이 바닥에 닿을 때까지 대기 // 2. ⭐ 공을 다시 손에 붙이기 (Parent) 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); // 일어나는 시간 // 3. 상태 복구 isWeaponless = false; // 무기 장착 완료! OnAttackEnd(); if (agent != null) agent.isStopped = false; } // ════════════════════════════════════════ // 🎬 전투 입장 & 유틸리티 // ════════════════════════════════════════ public void StartBossBattle() { if (isBattleStarted) return; StartCoroutine(BattleStartRoutine()); } private IEnumerator BattleStartRoutine() { if (animator != null) animator.Play("Roar"); if (bossHealthBar != null) bossHealthBar.SetActive(true); if (counterSystem != null) counterSystem.InitializeBattle(); yield return new WaitForSeconds(2.0f); isBattleStarted = true; if (agent != null) agent.enabled = true; } 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() { // 이동 애니메이션 잠시 0으로 if (animator != null) animator.SetFloat("Speed", 0f); string patternName = (counterSystem != null) ? counterSystem.SelectBossPattern() : "Normal"; 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. 탱크 돌격 (Dash) private IEnumerator Pattern_DashAttack() { OnAttackStart(); if (animator != null) animator.Play("Skill_Dash_Ready"); yield return new WaitForSeconds(0.5f); // 돌진 (물리 이동) if (agent != null) agent.enabled = false; if (rb != null) { rb.isKinematic = false; rb.velocity = transform.forward * 20f; } if (animator != null) animator.Play("Skill_Dash_Go"); yield return new WaitForSeconds(1.0f); // 정지 if (rb != null) { rb.velocity = Vector3.zero; rb.isKinematic = true; } if (agent != null && isBattleStarted) agent.enabled = true; OnAttackEnd(); } // 2. 메테오 스매시 (Smash) private IEnumerator Pattern_SmashAttack() { OnAttackStart(); // 엇박자 위협 (기 모으기) if (animator != null) animator.Play("Skill_Smash_Charge"); yield return new WaitForSeconds(1.2f); // 쾅! if (animator != null) animator.Play("Skill_Smash_Impact"); yield return new WaitForSeconds(1.0f); OnAttackEnd(); } // 3. 쇠공 풍차 돌리기 (Shield) private IEnumerator Pattern_ShieldWall() { OnAttackStart(); // 쇠공을 빙빙 돌림 (화살 튕겨내기) if (animator != null) animator.Play("Skill_Shield"); yield return new WaitForSeconds(2.0f); OnAttackEnd(); } // 4. 공 던지기 (Throw - 기존 Shoot 대체) private IEnumerator Pattern_ThrowBall() { OnAttackStart(); // 던지는 모션 if (animator != null) animator.Play("Attack_Throw"); yield return new WaitForSeconds(0.5f); // 손이 앞으로 뻗는 타이밍 // ⭐ 공 발사 로직 if (ironBall != null) { // 1. 손에서 떼어내기 (부모 해제) ironBall.transform.SetParent(null); // 2. 물리 켜고 날리기 if (ballRb != null) { ballRb.isKinematic = false; // 플레이어 방향 계산 Vector3 dir = (target.position - transform.position).normalized; // 위쪽으로 살짝 띄워서 포물선으로 던짐 ballRb.AddForce(dir * 1000f + Vector3.up * 300f); // 굴러가지 않게 저항 좀 주기 ballRb.angularDrag = 5f; } } // "나 무기 없다!" 상태 설정 -> 이제부터 주으러 감 isWeaponless = true; yield return new WaitForSeconds(1.0f); OnAttackEnd(); } }