using Input; using UnityEngine; using System; using UnityEngine.Events; namespace Interactions { public enum CharacterToInteract { Trafalgar, Pulver } /// /// Represents an interactable object that can respond to tap input events. /// public class Interactable : MonoBehaviour, ITouchInputConsumer { [Header("Interaction Settings")] public bool isOneTime = false; public float cooldown = -1f; public CharacterToInteract characterToInteract = CharacterToInteract.Pulver; [Header("Interaction Events")] public UnityEvent interactionStarted; public UnityEvent interactionInterrupted; public UnityEvent characterArrived; public UnityEvent interactionComplete; // Helpers for managing interaction state private bool _interactionInProgress; private PlayerTouchController _playerRef; private FollowerController _followerController; private bool _isActive = true; private void Awake() { // Subscribe to interactionComplete event interactionComplete.AddListener(OnInteractionComplete); } /// /// Handles tap input. Triggers interaction logic. /// public void OnTap(Vector2 worldPosition) { if (!_isActive) { Debug.Log($"[Interactable] Is disabled!"); return; } Debug.Log($"[Interactable] OnTap at {worldPosition} on {gameObject.name}"); // Broadcast interaction started event TryInteract(); } public void TryInteract() { _interactionInProgress = true; _playerRef = FindFirstObjectByType(); _followerController = FindFirstObjectByType(); interactionStarted?.Invoke(_playerRef, _followerController); if (_playerRef == null) { Debug.Log($"[Interactable] Player character could not be found. Aborting interaction."); interactionInterrupted.Invoke(); return; } // Compute closest point on the interaction radius Vector3 interactablePos = transform.position; Vector3 playerPos = _playerRef.transform.position; float stopDistance = characterToInteract == CharacterToInteract.Pulver ? GameManager.Instance.PlayerStopDistance : GameManager.Instance.PlayerStopDistanceDirectInteraction; Vector3 toPlayer = (playerPos - interactablePos).normalized; Vector3 stopPoint = interactablePos + toPlayer * stopDistance; // Unsubscribe previous to avoid duplicate calls _playerRef.OnArrivedAtTarget -= OnPlayerArrived; _playerRef.OnMoveToCancelled -= OnPlayerMoveCancelled; _playerRef.OnArrivedAtTarget += OnPlayerArrived; _playerRef.OnMoveToCancelled += OnPlayerMoveCancelled; _playerRef.MoveToAndNotify(stopPoint); } private void OnPlayerMoveCancelled() { _interactionInProgress = false; interactionInterrupted?.Invoke(); } private void OnPlayerArrived() { if (!_interactionInProgress) return; // Unsubscribe to avoid memory leaks _playerRef.OnArrivedAtTarget -= OnPlayerArrived; if (characterToInteract == CharacterToInteract.Pulver) { _followerController.OnPickupArrived -= OnFollowerArrived; _followerController.OnPickupArrived += OnFollowerArrived; _followerController.GoToPointAndReturn(transform.position, _playerRef.transform); } else if (characterToInteract == CharacterToInteract.Trafalgar) { BroadcastCharacterArrived(); } } private void OnFollowerArrived() { if (!_interactionInProgress) return; // Unsubscribe to avoid memory leaks _followerController.OnPickupArrived -= OnFollowerArrived; BroadcastCharacterArrived(); } private void BroadcastCharacterArrived() { // Check for ObjectiveStepBehaviour and lock state var step = GetComponent(); if (step != null && !step.IsStepUnlocked()) { DebugUIMessage.Show("This step is locked!", Color.yellow); BroadcastInteractionComplete(false); // Reset variables for next time _interactionInProgress = false; _playerRef = null; _followerController = null; return; } // Broadcast appropriate event characterArrived?.Invoke(); // Reset variables for next time _interactionInProgress = false; _playerRef = null; _followerController = null; } private void OnInteractionComplete(bool success) { if (success) { if (isOneTime) { _isActive = false; } else if (cooldown >= 0f) { StartCoroutine(HandleCooldown()); } } } private System.Collections.IEnumerator HandleCooldown() { _isActive = false; yield return new WaitForSeconds(cooldown); _isActive = true; } public void OnHoldStart(Vector2 position) { throw new NotImplementedException(); } public void OnHoldMove(Vector2 position) { throw new NotImplementedException(); } public void OnHoldEnd(Vector2 position) { throw new NotImplementedException(); } public void BroadcastInteractionComplete(bool success) { interactionComplete?.Invoke(success); } #if UNITY_EDITOR /// /// Draws gizmos for pickup interaction range in the editor. /// void OnDrawGizmos() { float playerStopDistance; if (Application.isPlaying) { playerStopDistance = characterToInteract == CharacterToInteract.Trafalgar ? GameManager.Instance.PlayerStopDistanceDirectInteraction : GameManager.Instance.PlayerStopDistance; } else { // Load settings directly from asset path in editor var settings = UnityEditor.AssetDatabase.LoadAssetAtPath( "Assets/Data/Settings/DefaultSettings.asset"); playerStopDistance = settings != null ? (characterToInteract == CharacterToInteract.Trafalgar ? settings.playerStopDistanceDirectInteraction : settings.playerStopDistance) : 1.0f; } Gizmos.color = Color.yellow; Gizmos.DrawWireSphere(transform.position, playerStopDistance); GameObject playerObj = GameObject.FindGameObjectWithTag("Player"); if (playerObj != null) { Vector3 stopPoint = transform.position + (playerObj.transform.position - transform.position).normalized * playerStopDistance; Gizmos.color = Color.cyan; Gizmos.DrawSphere(stopPoint, 0.15f); } } #endif } }