using UnityEngine; using System.Collections; using System.Collections.Generic; using System.Linq; using Invector.vCharacterController.AI.FSMBehaviour; using Beyond; using System; namespace Beyond { public class AutoTargetting : MonoBehaviour { #region Fields & Properties [Header("Targeting Parameters")] public float maxTargetingDistance = 20f; public float targetingInterval = 0.25f; public float targetingAngleThreshold = 90f; [Header("Rotation Parameters")] public float playerRotationSpeed = 10f; [Header("Visuals")] public string materialHighlightPropertyName = "_FresnelColor"; [ColorUsage(true, true)] public Color highlightColor = Color.white; [ColorUsage(true, true)] public Color deselectHighlightColor = Color.black; public float highlightFadeDuration = 0.3f; public bool preferSkinnedMeshRenderer = true; [Header("Lock-On Integration")] public bool autoLockSelectedTarget = false; public bool alwaysLockOnInCombat = true; public bLockOn targetLockSystem; public float manualSwitchCooldownDuration = 0.75f; public vFSMBehaviourController CurrentTarget { get; private set; } public event Action OnTargetSelected; public event Action OnTargetDeselected; private GameStateManager _gameStateManager; private Coroutine _targetingLoopCoroutine; private Dictionary _originalMaterialColors = new Dictionary(); private Dictionary _materialToFadeCoroutineMap = new Dictionary(); private Transform _playerTransform; private bThirdPersonController _playerController; private bool _manualSwitchCooldownActive = false; private float _manualSwitchCooldownTimer = 0f; #endregion #region Unity Methods void Start() { if (Player.Instance == null) { Debug.LogError("AutoTargetting: Player.Instance is not available at Start!"); enabled = false; return; } _playerTransform = Player.Instance.transform; _playerController = Player.Instance.GetComponent(); if (_playerController == null) { Debug.LogError("AutoTargetting: Could not find bThirdPersonController on Player.Instance! Custom roll rotation may not work correctly."); } _gameStateManager = GameStateManager.Instance; if (_gameStateManager != null) { _gameStateManager.m_OnStateChanged.AddListener(HandleGameStateChanged); HandleGameStateChanged(_gameStateManager.CurrentState); } else { Debug.LogError("AutoTargetting: GameStateManager.Instance not found!"); enabled = false; return; } if (targetLockSystem == null) { targetLockSystem = Player.Instance.GetComponentInChildren(true); if (targetLockSystem == null) { Debug.LogWarning("AutoTargetting: bLockOn system not found. Auto-lock will be disabled."); autoLockSelectedTarget = false; } } if (targetLockSystem != null) { targetLockSystem.onLockOnTarget.AddListener(HandleLockOnSystemTargetChanged); targetLockSystem.onUnLockOnTarget.AddListener(HandleLockOnSystemTargetUnlocked); } } void OnDestroy() { if (_gameStateManager != null) _gameStateManager.m_OnStateChanged.RemoveListener(HandleGameStateChanged); StopAndClearAllFadeCoroutines(); // This call is correct if (_targetingLoopCoroutine != null) StopCoroutine(_targetingLoopCoroutine); if (targetLockSystem != null) { targetLockSystem.onLockOnTarget.RemoveListener(HandleLockOnSystemTargetChanged); targetLockSystem.onUnLockOnTarget.RemoveListener(HandleLockOnSystemTargetUnlocked); } } #endregion #region Core Logic // --- THIS IS THE MISSING METHOD THAT HAS BEEN RESTORED --- private void StopAndClearAllFadeCoroutines() { foreach (var pair in _materialToFadeCoroutineMap) { if (pair.Value != null) { StopCoroutine(pair.Value); } } _materialToFadeCoroutineMap.Clear(); } // --- END OF RESTORED METHOD --- private void HandleGameStateChanged(GameStateManager.State newState) { if (newState == GameStateManager.State.COMBAT) { if (_targetingLoopCoroutine == null) _targetingLoopCoroutine = StartCoroutine(TargetingLoop()); } else { if (_targetingLoopCoroutine != null) { StopCoroutine(_targetingLoopCoroutine); _targetingLoopCoroutine = null; } if (CurrentTarget != null) SetNewTarget(null, true); _manualSwitchCooldownActive = false; } } private IEnumerator TargetingLoop() { while (true) { if (_manualSwitchCooldownActive) { _manualSwitchCooldownTimer -= targetingInterval; if (_manualSwitchCooldownTimer <= 0) _manualSwitchCooldownActive = false; } if (!_manualSwitchCooldownActive) { if (_playerTransform == null && Player.Instance != null) _playerTransform = Player.Instance.transform; if (_playerTransform != null) UpdateTarget(); } yield return new WaitForSeconds(targetingInterval); } } private void UpdateTarget() { if (_playerTransform == null || _gameStateManager == null || _manualSwitchCooldownActive) return; vFSMBehaviourController bestCandidate = null; float minDistanceSqr = maxTargetingDistance * maxTargetingDistance; HashSet combatControllers = _gameStateManager.GetActiveCombatcontrollers(); if (combatControllers == null || combatControllers.Count == 0) { if (CurrentTarget != null) SetNewTarget(null); return; } foreach (var controller in combatControllers) { if (controller == null || !controller.gameObject.activeInHierarchy || controller.aiController.currentHealth <= 0) continue; if (!IsTargetInAngle(_playerTransform, controller, targetingAngleThreshold)) continue; float distSqr = (controller.transform.position - _playerTransform.position).sqrMagnitude; if (distSqr <= minDistanceSqr) { minDistanceSqr = distSqr; bestCandidate = controller; } } if (CurrentTarget != bestCandidate) SetNewTarget(bestCandidate); else if (CurrentTarget != null && !IsTargetValid(CurrentTarget)) SetNewTarget(null); } private void SetNewTarget(vFSMBehaviourController newTarget, bool forceLockSystemUpdate = false) { if (_manualSwitchCooldownActive && !forceLockSystemUpdate) return; if (CurrentTarget == newTarget && !forceLockSystemUpdate) return; if (CurrentTarget != null) { ApplyHighlight(CurrentTarget, false); OnTargetDeselected?.Invoke(CurrentTarget); } CurrentTarget = newTarget; if (targetLockSystem != null) { bool shouldLock = (autoLockSelectedTarget || alwaysLockOnInCombat) && CurrentTarget != null; if (alwaysLockOnInCombat && CurrentTarget != null && !targetLockSystem.isLockingOn) { targetLockSystem.SetLockOn(true); } targetLockSystem.ManuallySetLockOnTarget(shouldLock ? CurrentTarget.transform : null, true); } if (CurrentTarget != null) { ApplyHighlight(CurrentTarget, true); OnTargetSelected?.Invoke(CurrentTarget); } } public void ExecuteRotationTowardsCurrentTarget(float deltaTime) { if (_playerController != null && !_playerController.enabled) { return; } if (CurrentTarget == null || _playerTransform == null) return; Vector3 directionToTarget = CurrentTarget.transform.position - _playerTransform.position; directionToTarget.y = 0f; if (directionToTarget.sqrMagnitude < 0.0001f) return; Quaternion targetRotation = Quaternion.LookRotation(directionToTarget.normalized); _playerTransform.rotation = Quaternion.Slerp(_playerTransform.rotation, targetRotation, deltaTime * playerRotationSpeed); } #endregion #region Lock-On System Sync private void HandleLockOnSystemTargetChanged(Transform lockedTargetTransform) { if (lockedTargetTransform == null || targetLockSystem == null) return; _manualSwitchCooldownActive = true; _manualSwitchCooldownTimer = manualSwitchCooldownDuration; vFSMBehaviourController fsmTarget = lockedTargetTransform.GetComponentInParent(); if (fsmTarget == null) fsmTarget = lockedTargetTransform.GetComponentInChildren(true); if (CurrentTarget != null && CurrentTarget.transform == lockedTargetTransform) return; if (fsmTarget != null) SetNewTarget(fsmTarget, true); else SetNewTarget(null, true); } private void HandleLockOnSystemTargetUnlocked(Transform previouslyLockedTargetTransform) { if (targetLockSystem == null) return; _manualSwitchCooldownActive = true; _manualSwitchCooldownTimer = manualSwitchCooldownDuration; if (CurrentTarget != null && previouslyLockedTargetTransform == CurrentTarget.transform) { SetNewTarget(null, true); } } #endregion #region Helper Methods public bool IsTargetInAngle(Transform sourceTransform, vFSMBehaviourController targetAI, float angleThreshold) { if (targetAI == null || sourceTransform == null) return false; Vector3 directionToTarget = (targetAI.transform.position - sourceTransform.position); directionToTarget.y = 0; if (directionToTarget.sqrMagnitude < 0.0001f) return true; return Vector3.Angle(sourceTransform.forward, directionToTarget.normalized) <= angleThreshold; } private bool IsTargetValid(vFSMBehaviourController target) { if (target == null || !target.gameObject.activeInHierarchy || target.aiController.currentHealth <= 0) return false; if (_playerTransform == null) return false; if (!IsTargetInAngle(_playerTransform, target, targetingAngleThreshold)) return false; float distSqr = (target.transform.position - _playerTransform.position).sqrMagnitude; return distSqr <= (maxTargetingDistance * maxTargetingDistance); } public float GetCurrentTargetHealth() { if (CurrentTarget != null && CurrentTarget.aiController != null) { return CurrentTarget.aiController.currentHealth; } return -1f; } public void ClearTarget(bool findNewOneImmediately) { SetNewTarget(null, true); if (findNewOneImmediately && _gameStateManager != null && _gameStateManager.CurrentState == GameStateManager.State.COMBAT) { if (!_manualSwitchCooldownActive) { UpdateTarget(); } } } #endregion #region Visuals private Renderer[] GetTargetRenderers(vFSMBehaviourController targetController) { if (targetController == null) return new Renderer[0]; return targetController.GetComponentsInChildren(true).Distinct().ToArray(); } private void ApplyHighlight(vFSMBehaviourController targetController, bool fadeIn) { if (targetController == null || string.IsNullOrEmpty(materialHighlightPropertyName)) return; Renderer[] renderers = GetTargetRenderers(targetController); foreach (Renderer rend in renderers) { if (rend == null) continue; foreach (Material mat in rend.materials) { if (mat == null || !mat.HasProperty(materialHighlightPropertyName)) continue; if (_materialToFadeCoroutineMap.TryGetValue(mat, out Coroutine existingCoroutine) && existingCoroutine != null) StopCoroutine(existingCoroutine); Color currentColor = mat.GetColor(materialHighlightPropertyName); Color targetColor = fadeIn ? highlightColor : deselectHighlightColor; Coroutine newFadeCoroutine = StartCoroutine(FadeMaterialPropertyCoroutine(mat, currentColor, targetColor, highlightFadeDuration)); _materialToFadeCoroutineMap[mat] = newFadeCoroutine; } } } private IEnumerator FadeMaterialPropertyCoroutine(Material material, Color fromValue, Color toValue, float duration) { float timer = 0f; while (timer < duration) { if (material == null) yield break; timer += Time.deltaTime; material.SetColor(materialHighlightPropertyName, Color.Lerp(fromValue, toValue, Mathf.Clamp01(timer / duration))); yield return null; } if (material != null) material.SetColor(materialHighlightPropertyName, toValue); } #endregion } }