- Editor dialogue graph - Asset importer for processing the graph into runtime data - DialogueComponent that steers the dialogue interactions - DialogueCanbas with a scalable speech bubble to display everything - Brief README overview of the system Co-authored-by: AlexanderT <alexander@foolhardyhorizons.com> Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com> Reviewed-on: #10
215 lines
7.3 KiB
C#
215 lines
7.3 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 = characterToInteract == CharacterToInteract.Trafalgar
|
|
? AppleHills.SettingsAccess.GetPlayerStopDistanceDirectInteraction()
|
|
: AppleHills.SettingsAccess.GetPlayerStopDistance();
|
|
|
|
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
|
|
}
|
|
}
|