using UnityEngine; using UnityEngine.AI; using System.Collections; public class PirateLoader : PirateBehavior { public enum State { GoToCrate, PickUp, GoToCannon, LoadCannon } public State currentState; public State lastState; [Header("Ссылки")] public Transform ammoCrate; public Cannon cannon; [Header("Ядро")] public GameObject cannonBallPrefab; public Transform handSocket; private GameObject carriedBall; [Header("Ограничение времени")] [Tooltip("Максимальная длительность задачи (сек). По истечении — форс-зарядка и выключение.")] public float maxTaskTime = 35f; private float _deadline = float.PositiveInfinity; private Vector3 currentTargetPosition; private Vector3 lastPos; private float stuckTimer; public void ActivateWithTarget(Cannon targetCannon, Transform crate) { cannon = targetCannon; ammoCrate = crate; Activate(); // запуск FSM } public override void Activate() { enabled = true; if (pirate.agent != null) pirate.agent.isStopped = false; _deadline = maxTaskTime > 0f ? Time.time + maxTaskTime : float.PositiveInfinity; currentState = State.GoToCrate; lastState = State.GoToCrate - 1; // чтобы сработал OnStateEnter } public override void Deactivate(bool interrupted = false) { StopAllCoroutines(); enabled = false; if (pirate.agent != null) pirate.agent.isStopped = true; if (Animator != null) { Animator.SetBool("isWalking", false); Animator.SetBool("isCarrying", false); } if (carriedBall != null) { Destroy(carriedBall); carriedBall = null; } pirate?.ClearCurrentBehavior(this, interrupted); } void Update() { // --- Таймаут задачи --- if (Time.time >= _deadline) { Debug.LogWarning($"{name}: истёк лимит {maxTaskTime:F0}s — форсируем перезарядку и выключаемся."); _deadline = float.PositiveInfinity; if(carriedBall != null) currentState = State.LoadCannon; else { cannon?.OnReloadComplete(); Deactivate(); } return; // Deactivate уже вызван } if (currentState != lastState) { OnStateEnter(currentState); lastState = currentState; } // Проверка застревания (быстрый репаф) if ((transform.position - lastPos).sqrMagnitude < 0.001f) { stuckTimer += Time.deltaTime; if (stuckTimer > 2f && pirate.agent != null) { Debug.LogWarning($"{name} застрял, пересчитываем путь"); pirate.agent.ResetPath(); pirate.agent.SetDestination(currentTargetPosition); stuckTimer = 0f; } } else { stuckTimer = 0f; } lastPos = transform.position; // Проверка достижения цели switch (currentState) { case State.GoToCrate: if (!pirate.agent.pathPending && pirate.agent.remainingDistance <= pirate.agent.stoppingDistance) currentState = State.PickUp; break; case State.GoToCannon: if (!pirate.agent.pathPending && pirate.agent.remainingDistance <= pirate.agent.stoppingDistance) currentState = State.LoadCannon; break; } // Проверка валидности пути if (pirate.agent != null && (pirate.agent.pathStatus == NavMeshPathStatus.PathInvalid || pirate.agent.pathStatus == NavMeshPathStatus.PathPartial)) { Debug.LogWarning($"{name} имеет невалидный путь, ищем ближайшую точку на NavMesh"); if (NavMesh.SamplePosition(currentTargetPosition, out NavMeshHit hit, 2f, NavMesh.AllAreas)) { pirate.agent.SetDestination(hit.position); } else { Debug.LogWarning($"{name} не может достичь цели, прерываем задачу"); FailAndStop(); } } } void OnStateEnter(State state) { switch (state) { case State.GoToCrate: if (ammoCrate == null) { FailAndStop(); return; } Animator.SetBool("isWalking", true); Animator.SetBool("isCarrying", false); currentTargetPosition = ammoCrate.position; pirate.agent.SetDestination(currentTargetPosition); break; case State.PickUp: Animator.SetBool("isWalking", false); Animator.SetTrigger("PickUpTrigger"); break; case State.GoToCannon: if (cannon == null) { FailAndStop(); return; } Animator.SetBool("isWalking", true); Animator.SetBool("isCarrying", true); Transform targetPoint = cannon.GetNearestReloadPoint(transform.position); currentTargetPosition = targetPoint != null ? targetPoint.position : cannon.transform.position; pirate.agent.SetDestination(currentTargetPosition); break; case State.LoadCannon: Animator.SetBool("isWalking", false); Animator.SetTrigger("LoadCannonTrigger"); break; } } // --- Animation Events --- public void OnGrabBall() { if (!enabled || !gameObject.activeInHierarchy) return; if (carriedBall == null && cannonBallPrefab != null && handSocket != null) { carriedBall = Instantiate(cannonBallPrefab, handSocket); carriedBall.transform.localPosition = new Vector3(-0.307f, -0.423f, 0.008f); carriedBall.transform.localRotation = Quaternion.identity; } currentState = State.GoToCannon; // Если нужно считать таймер с момента взятия ядра, раскомментируй: // _deadline = Time.time + maxTaskTime; } public void OnLoadCannon() { if (!isActiveAndEnabled) return; if (carriedBall != null) { Transform target = cannon != null ? cannon.reloadTarget : null; if (target != null) StartCoroutine(MoveCannonBallToTarget(carriedBall, target.position, 0.5f, cannon)); else { Destroy(carriedBall); carriedBall = null; cannon?.OnReloadComplete(); Deactivate(false); } } else { Deactivate(false); } } private IEnumerator MoveCannonBallToTarget(GameObject ball, Vector3 targetPos, float duration, Cannon targetCannon) { if (ball == null) yield break; Vector3 startPos = ball.transform.position; float elapsed = 0f; float height = 1.5f; while (elapsed < duration) { float t = elapsed / duration; Vector3 currentPos = Vector3.Lerp(startPos, targetPos, t); currentPos.y += 4 * height * t * (1 - t); ball.transform.position = currentPos; elapsed += Time.deltaTime; yield return null; } ball.transform.position = targetPos; Destroy(ball); targetCannon?.OnReloadComplete(); Deactivate(false); } // === Форс-завершение по таймауту === private void FailAndStop() { Debug.LogWarning($"{name} не смог выполнить задачу загрузки."); Deactivate(true); } }