Files
AppleHillsProduction/Assets/Scripts/Interactions/Interactable.cs

231 lines
7.9 KiB
C#

using Input;
using UnityEngine;
using System;
using UnityEngine.Events;
namespace Interactions
{
public enum CharacterToInteract
{
Trafalgar,
Pulver
}
/// <summary>
/// Represents an interactable object that can respond to tap input events.
/// </summary>
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<PlayerTouchController, FollowerController> interactionStarted;
public UnityEvent interactionInterrupted;
public UnityEvent characterArrived;
public UnityEvent<bool> 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);
}
/// <summary>
/// Handles tap input. Triggers interaction logic.
/// </summary>
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<PlayerTouchController>();
_followerController = FindFirstObjectByType<FollowerController>();
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<PuzzleS.ObjectiveStepBehaviour>();
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
/// <summary>
/// Draws gizmos for pickup interaction range in the editor.
/// </summary>
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<GameSettings>(
"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
}
}