using Cinemachine; using System; using System.Collections; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using TMPro; using UnityEngine; /// <summary> /// Handles individual Check Utility designs for points, tracking, and speed /// with their necessary dialogues /// </summary> public class CheckCameraUtil : MonoBehaviour { [Header("Points")] [SerializeField] private Transform TrackingPoint; // The point that is tracked by the virtual camera as this transform moves between point one and two [SerializeField] private Transform PointOne; // The beginning point for when a check is initiated [SerializeField] private Transform PointTwo; // The ending point for when a check is completing [Header("Canvas Components")] [SerializeField] private GameObject hpBar; // Reference to the health bar of the enemy [SerializeField] private TextMeshProUGUI enemyName; // Reference to the text of the enemy name [SerializeField] private TextMeshProUGUI checkText; // Reference to the text used for the check dialogue [SerializeField] private GameObject afflictionsBar; // Reference to the bar that handles modifiers and afflictions [Header("Factors")] [SerializeField] private float Speed; // The speed at which the TrackingPoint follows through the two points with the camera [SerializeField] private float Ortho; // The Orthographic size for the camera when looking at the enemy sprite [Header("Dialogue")] [SerializeField] private DialogueSO dialogue; // The collection of dialogue and voice acting for the particular check private bool active; // Trigger indicator if the update method should begin the moving process private bool routineCheck; // Check for when a dialogue is finished and the next can start private Task checkFulfilled; // The task to be fulfilled during the check process private Coroutine dialogueRoutine; // The coroutine holder for the dialogue and voice acting private int counter; // The current index of the dialogue private CinemachineVirtualCamera cam; // The holder for the virtual camera /// <summary> /// Retrieves the tracking point for the starting process of the animation scene /// </summary> /// <returns>The position/transform of the tracking point</returns> public Transform AcquireTrackingPoint() => TrackingPoint; private void Update() { // When a dialogue part is finished the coroutine is free'd while the counter is incremented to prepare for the next dialogue if (routineCheck) { dialogueRoutine = null; counter++; routineCheck = false; } if (active) { if (counter < dialogue.dialogues.Count) dialogueRoutine ??= StartCoroutine(DialogueCapsulation()); float distance = Vector2.Distance(TrackingPoint.position, PointTwo.position); if (distance > 0.1f) TrackingPoint.position = Vector2.MoveTowards(TrackingPoint.position, PointTwo.position, Speed * Time.deltaTime); else checkFulfilled = Task.CompletedTask; } } /// <summary> /// Begins the process of stating the check dialogue for the counter index /// </summary> /// <returns></returns> private IEnumerator DialogueCapsulation() { string text = dialogue.dialogues[counter]; checkText.text = text; float timer = -1f; // Try catch is for when dialogue clips are not yet implemented // timer acts as a secondary check that the dialogue is properly moving through try { if (dialogue.dialogueClips[counter] == null) throw new NullReferenceException(); AudioClip clip = dialogue.dialogueClips[counter]; DialogueManager.Instance.PlayDialogueClip(clip); } catch (NullReferenceException) when (dialogue.dialogueClips[counter] == null) { Debug.LogWarning($"Skipping audio clip in {dialogue.name} at position {counter}."); timer = 5f; } while (DialogueManager.Instance.CheckDialogueStatus() || timer > 0f) { timer -= Time.deltaTime; yield return null; } routineCheck = true; } /// <summary> /// Switches the enemy canvas component on and off while switching the Check Text to the opposite of what the trigger is /// </summary> /// <param name="trigger">Indicator if the canvas components should be on or off</param> private async Task SwitchComponents(bool trigger) { hpBar.SetActive(!trigger); enemyName.enabled = !trigger; afflictionsBar.SetActive(!trigger); checkText.enabled = trigger; cam.gameObject.SetActive(trigger); active = trigger; await Task.CompletedTask; } /// <summary> /// Initializes the start of a check command /// </summary> /// <param name="checkTrackCamera">The camera to manipulate its parameters</param> /// <returns>End of Initialization check</returns> public async Task StartCheckDialogue(CinemachineVirtualCamera checkTrackCamera) => await ProcessCheck(checkTrackCamera); private async Task ProcessCheck(CinemachineVirtualCamera checkTrackCamera) { // Calibrate and set all of the necessary parameters TrackingPoint.position = PointOne.position; counter = 0; checkFulfilled = null; cam = checkTrackCamera; cam.m_Lens.OrthographicSize = Ortho; await SwitchComponents(true); // Freezes this task while the task is not complete while (checkFulfilled is null) await Task.Yield(); // Disables the virtual camera following the tracking point before switching back // to the main camera with its designated ortho size await SwitchComponents(false); Camera.main.orthographicSize = Globals.MainCameraOrtho; Camera.main.transform.position = Globals.MainCameraPosition; await Task.CompletedTask; } }