Rework interactables into a flatter hierarchy, reenable puzzles as well
This commit is contained in:
@@ -1,41 +1,230 @@
|
||||
using UnityEngine;
|
||||
using Input;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
using UnityEngine.Events;
|
||||
|
||||
/// <summary>
|
||||
/// Represents an interactable object that can respond to tap input events.
|
||||
/// </summary>
|
||||
public class Interactable : MonoBehaviour, ITouchInputConsumer
|
||||
namespace Interactions
|
||||
{
|
||||
public event Action StartedInteraction;
|
||||
public event Action<bool> InteractionComplete;
|
||||
|
||||
/// <summary>
|
||||
/// Handles tap input. Triggers interaction logic.
|
||||
/// </summary>
|
||||
public void OnTap(Vector2 worldPosition)
|
||||
public enum CharacterToInteract
|
||||
{
|
||||
Debug.Log($"[Interactable] OnTap at {worldPosition} on {gameObject.name}");
|
||||
StartedInteraction?.Invoke();
|
||||
Trafalgar,
|
||||
Pulver
|
||||
}
|
||||
|
||||
// No hold behavior for interactables.
|
||||
public void OnHoldStart(Vector2 worldPosition) { }
|
||||
public void OnHoldMove(Vector2 worldPosition) { }
|
||||
public void OnHoldEnd(Vector2 worldPosition) { }
|
||||
|
||||
/// <summary>
|
||||
/// Called to interact with this object by a character (player, follower, etc).
|
||||
/// Represents an interactable object that can respond to tap input events.
|
||||
/// </summary>
|
||||
public virtual void OnInteract(Character character)
|
||||
public class Interactable : MonoBehaviour, ITouchInputConsumer
|
||||
{
|
||||
// In the new architecture, requirements and step checks will be handled by orchestrator.
|
||||
StartedInteraction?.Invoke();
|
||||
// For now, immediately complete interaction as success (can be extended later).
|
||||
InteractionComplete?.Invoke(true);
|
||||
}
|
||||
[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;
|
||||
|
||||
public void CompleteInteraction(bool success)
|
||||
{
|
||||
InteractionComplete?.Invoke(success);
|
||||
// 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!", 2f);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user