/// author: Torben Borghoff
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using wssdk;
using System.Linq;
using UnityEngine.SceneManagement;

public class DialogueController : MonoBehaviour
{
	/// <summary>
	/// Struct holding the information for the dialogue. This is what the writer has to fill
	/// </summary>
	[System.Serializable]
	public struct Dialogue
	{
		[Tooltip("Language the text is written in")]
		public publicEnums.Language language;
		[Tooltip("The circumstance under that the text triggers. Note: IF THE CIRCUMSTANCE IS STORYCUTSCENE, THERE CAN ONLY BE ONE PER POTATOSTEP")]
		public publicEnums.TriggerCircumstance trigger;
		[Tooltip("How big does the potato have to be minimally, to be triggered")]
		public uint potatoStepTriggerMinimum;
		[Tooltip("How big does the potato have to be maximally, to be triggered")]
		public uint potatoStepTriggerMaximum;
		[Tooltip("ONLY FOR TUTORIAL, which step in the tutorial is to be triggered. Note: HAS TO BE 0 IF NOT IN THE TUTORIAL")]
		public uint tutorialStepTrigger;
		[Tooltip("The name that should be displayed. Note: ALL ARRAYS NEED TO BE THE SAME SIZE")]
		public string[] names;
		[Tooltip("The text that should be displayed. Note: ALL ARRAYS NEED TO BE THE SAME SIZE")]
		[TextArea(10, 3)]
		public string[] sentences;
		[Tooltip("The sprites that should be displayed. Note: ALL ARRAYS NEED TO BE THE SAME SIZE")]
		public Sprite[] sprites;
		[Tooltip("Only for Story and Tutorial: Point where the prefab spawns, that is selected")]
		public Transform spawnPoint;
		[Tooltip("Only for Story and Tutorial: spawn prefab, for the story (if you want to see Alberta)")]
		public GameObject spawnPrefab;
	}
	[SerializeField]
	private List<Dialogue> m_Dialogues = new List<Dialogue>();
	/// <summary>
	/// Trigger that has been activated last
	/// </summary>
	[System.NonSerialized]
	public DialogueTrigger m_LastTrigger;
	[Tooltip("Text for the name within the game world.")]
	[SerializeField]
	private TextMeshProUGUI m_NameText;
	[Tooltip("Text for the dialogue within the game world")]
	[SerializeField]
	private TextMeshProUGUI m_DialogueText;
	[Tooltip("Image showing who is speaking within the game world")]
	[SerializeField]
	private Image m_DialogueImage;
	[Tooltip("Point where the image is positioned when the speaker is schown left")]
	[SerializeField]
	private Transform m_ImageLeftTransform;
	[Tooltip("Point where the image is positioned when the speaker is schown right")]
	[SerializeField]
	private Transform m_ImageRightTransform;
	[Tooltip("Animator for the animations of the texts")]
	[SerializeField]
	private Animator m_Animator;
	[Tooltip("Object that has to be spawned in for the dialogue")]
	[SerializeField]
	private GameObject m_SpawnedObject;
	/// <summary>
	/// True if the player has not finished the tutorial (for loading purposes)
	/// </summary>
	private bool m_IsInTutorial = false;
	/// <summary>
	/// True if the text is still typing. This is used to show the full text before the next text is displayed when pressing a button.
	/// </summary>
	private bool m_IsTyping = false;
	/// <summary>
	/// Contains the whole dialogue for the story in english within the game object. Is used to set the queue for the dialogue and names 
	/// </summary>
	private List<Dialogue> m_EngStoryDialogues = new List<Dialogue>();
	/// <summary>
	/// Contains the whole dialogue for the tutorial in english within the game object. Is used to set the queue for the dialogue and names 
	/// </summary>
	private List<Dialogue> m_EngTutorialDialogues = new List<Dialogue>();
	/// <summary>
	/// Contains the whole dialogue for the random in english within the game object. Is used to set the queue for the dialogue and names 
	/// </summary>
	private List<Dialogue> m_EngRandomDialogues = new List<Dialogue>();
	/// <summary>
	/// Contains the whole dialogue for the story in german within the game object. Is used to set the queue for the dialogue and names 
	/// </summary>
	private List<Dialogue> m_GerStoryDialogues = new List<Dialogue>();
	/// <summary>
	/// Contains the whole dialogue for the random in german within the game object. Is used to set the queue for the dialogue and names 
	/// </summary>
	private List<Dialogue> m_GerTutorialDialogues = new List<Dialogue>();
	/// <summary>
	/// Contains the whole dialogue for the random in german within the game object. Is used to set the queue for the dialogue and names 
	/// </summary>
	private List<Dialogue> m_GerRandomDialogues = new List<Dialogue>();
	/// <summary>
	/// Queue of names. Used to display the names of the speakers in the right order
	/// </summary>
	private Queue<string> m_Names;
	/// <summary>
	/// Queue of dialogue text. Used to display the texts of the speakers in the right order
	/// </summary>
	private Queue<string> m_Sentences;
	/// <summary>
	/// Queue of sprites. Used to display the sprites of the speakers in the right order
	/// </summary>
	private Queue<Sprite> m_Sprites;
	/// <summary>
	/// Coroutine that types the sentences one by one, used to have controll over when the coroutine is done (being finished, or pressing a button)
	/// </summary>
	private Coroutine TypesSentence;
	/// <summary>
	/// GameObject of the fastselection bar. Used to activate and deactivate it.
	/// </summary>
	private GameObject m_FastSelectionBarGameObject;
	[Tooltip("List of objects that have to be deactivated in the tutorial, because they would break the game when used too early")]
	[SerializeField]
	private GameObject[] m_GameObjectsToInactiveWhileInTutorial;
	/// <summary>
	/// Sentence that will be displayed when the dialogue box is typing
	/// </summary>
	private string m_Sentence;
	/// <summary>
	/// This bool is used to check if the tutorial is working correctly
	/// </summary>
	bool m_TutHasTools = false;
	/// <summary>
	/// This bool is used to check if the tutorial is working correctly
	/// </summary>
	bool m_TutHasSeeds = false;
	/// <summary>
	/// This bool is used to check if the tutorial is working correctly
	/// </summary>
	bool m_TutHasBerry = false;
	/// <summary>
	/// This bool is used to check if the tutorial is working correctly
	/// </summary>
	bool m_TutHasFertiliser = false;
	/// <summary>
	/// Checks for unused/wrong input, starts the dialogue when the game object is set active.
	/// </summary>
	private void Start()
	{
		RectTransform[] uI_Elements = GameManager.Instance?.GetFastSelectionBarController()?.gameObject.GetComponentsInChildren<RectTransform>();
		foreach (RectTransform element in uI_Elements)
		{
			if (element.gameObject.name == "FastSelectionBar")
			{
				m_FastSelectionBarGameObject = element.gameObject;
			}
		}
		InputManager.Instance.RegisterButtonListener(InputContext.Dialogue, InputPhase.Down, "Fire1", DisplayNextSentence);
		m_SpawnedObject = GameObject.FindWithTag("AlbertaTutorial");
		if (!m_NameText) { Debug.LogWarning("NameText is not set in " + gameObject.name); }
		if (!m_DialogueText) { Debug.LogWarning("" + gameObject.name); }
		if (!m_DialogueImage) { Debug.LogWarning("" + gameObject.name); }
		if (!m_ImageLeftTransform) { Debug.LogWarning("" + gameObject.name); }
		if (!m_ImageRightTransform) { Debug.LogWarning("" + gameObject.name); }
		if (!m_Animator) { Debug.LogWarning("Animator is not set in " + gameObject.name); }
		if (m_Dialogues.Count == 0) { Debug.LogWarning("There are no dialogues found in " + gameObject.name); }

		PreSelectDialogues();

		m_Names = new Queue<string>();
		m_Sentences = new Queue<string>();
		m_Sprites = new Queue<Sprite>();
		StartStoryDialogue();

	}

	private void OnDestroy()
	{
		if (InputManager.Instance)
			InputManager.Instance.UnregisterButtonListener(InputContext.Dialogue, InputPhase.Down, "Fire1");
	}

	/// <summary>
	/// Sorts the dialogues from m_Dialogues to their respective trigger and language variable dependent on the current growth of the potato
	/// </summary>
	private void PreSelectDialogues()
	{
		foreach (Dialogue dialogue in m_Dialogues)
		{
			if (dialogue.potatoStepTriggerMinimum > dialogue.potatoStepTriggerMaximum)
			{
				Debug.LogWarning("Maximum is smaller than minimum for " + dialogue.sentences[0]);
			}
			switch (dialogue.language)
			{
				case publicEnums.Language.English:
					switch (dialogue.trigger)
					{
						case publicEnums.TriggerCircumstance.Random:
							if (GameManager.Instance.m_CurrentPotatoStep >= dialogue.potatoStepTriggerMinimum &&
								GameManager.Instance.m_CurrentPotatoStep <= dialogue.potatoStepTriggerMaximum)
							{
								m_EngRandomDialogues.Add(dialogue);
							}
							break;
						case publicEnums.TriggerCircumstance.StoryCutScene:
							if (GameManager.Instance.m_CurrentPotatoStep >= dialogue.potatoStepTriggerMinimum &&
								GameManager.Instance.m_CurrentPotatoStep <= dialogue.potatoStepTriggerMaximum)
							{
								m_EngStoryDialogues.Add(dialogue);
							}
							break;
						case publicEnums.TriggerCircumstance.Tutorial:
							if (GameManager.Instance.m_CurrentPotatoStep <= 1)
							{
								m_EngTutorialDialogues.Add(dialogue);
							}
							break;
						default:
							break;
					}
					break;
				case publicEnums.Language.German:
					switch (dialogue.trigger)
					{
						case publicEnums.TriggerCircumstance.Random:
							if (GameManager.Instance.m_CurrentPotatoStep > dialogue.potatoStepTriggerMinimum &&
								GameManager.Instance.m_CurrentPotatoStep < dialogue.potatoStepTriggerMaximum)
							{
								m_GerRandomDialogues.Add(dialogue);
							}
							break;
						case publicEnums.TriggerCircumstance.StoryCutScene:
							if (GameManager.Instance.m_CurrentPotatoStep > dialogue.potatoStepTriggerMinimum &&
								GameManager.Instance.m_CurrentPotatoStep < dialogue.potatoStepTriggerMaximum)
							{
								m_GerStoryDialogues.Add(dialogue);
							}
							break;
						case publicEnums.TriggerCircumstance.Tutorial:
							if (GameManager.Instance.m_CurrentPotatoStep < 1)
							{
								m_GerTutorialDialogues.Add(dialogue);
							}
							break;
						default:
							break;
					}
					break;
				default:
					break;
			}
		}
	}
	/// <summary>
	/// Selects a dialogue when triggered from within the game without having the player sleep.
	/// </summary>
	/// <param name="trigger">The trigger which is used to trigger the dialogue</param>
	public void SelectDialogue(publicEnums.TriggerCircumstance trigger)
	{
		// selects dialogue dependent on how big the crop is and if it has the right trigger circumstance
		Dialogue dialogue = new Dialogue { names = null, sentences = null, sprites = null };
		if (m_SpawnedObject == null)
		{
			m_SpawnedObject = GameObject.FindWithTag("AlbertaTutorial");
		}
		switch (trigger)
		{
			case publicEnums.TriggerCircumstance.Random:
				switch (GameManager.Instance.m_LanguageSetting)
				{
					// Does selection of random dialogue dependent on Potato size
					case publicEnums.Language.English:
						List<Dialogue> selectedEngDialogues = new List<Dialogue>();
						foreach (Dialogue unselectedDialogue in m_EngRandomDialogues)
						{
							if (unselectedDialogue.potatoStepTriggerMinimum == GameManager.Instance.m_CurrentPotatoStep)
							{
								selectedEngDialogues.Add(unselectedDialogue);
							}
						}
						dialogue = selectedEngDialogues.PickRandom();
						break;
					case publicEnums.Language.German:
						List<Dialogue> selectedGerDialogues = new List<Dialogue>();
						foreach (Dialogue unselectedDialogue in m_GerRandomDialogues)
						{
							if (unselectedDialogue.potatoStepTriggerMinimum == GameManager.Instance.m_CurrentPotatoStep)
							{
								selectedGerDialogues.Add(unselectedDialogue);
							}
						}
						dialogue = selectedGerDialogues.PickRandom();
						break;
					default:
						break;
				}
				break;
			case publicEnums.TriggerCircumstance.StoryCutScene:
				// Does selection of story dialogue dependent on Potato size
				switch (GameManager.Instance.m_LanguageSetting)
				{
					case publicEnums.Language.English:
						foreach (Dialogue unselectedDialogue in m_EngStoryDialogues)
						{
							if (GameManager.Instance.m_LastStorySegment < GameManager.Instance.m_CurrentPotatoStep && unselectedDialogue.potatoStepTriggerMinimum == GameManager.Instance.m_CurrentPotatoStep)
							{
								GameManager.Instance.m_LastStorySegment = (uint)GameManager.Instance.m_CurrentPotatoStep;
								dialogue = unselectedDialogue;
								if (dialogue.spawnPoint && dialogue.spawnPrefab)
								{
									m_SpawnedObject.transform.position = dialogue.spawnPoint.position;
								}
								break;
							}
						}
						break;
					case publicEnums.Language.German:
						foreach (Dialogue unselectedDialogue in m_GerStoryDialogues)
						{
							if (GameManager.Instance.m_LastStorySegment < GameManager.Instance.m_CurrentPotatoStep && unselectedDialogue.potatoStepTriggerMinimum == GameManager.Instance.m_CurrentPotatoStep)
							{
								GameManager.Instance.m_LastStorySegment = (uint)GameManager.Instance.m_CurrentPotatoStep;
								dialogue = unselectedDialogue;
								if (dialogue.spawnPoint && dialogue.spawnPrefab)
								{
									m_SpawnedObject.transform.position = dialogue.spawnPoint.position;
								}
								break;
							}
						}
						break;
					default:
						break;
				}
				break;
			case publicEnums.TriggerCircumstance.Tutorial:
				if (GameManager.Instance.m_CurrentTutorialStep < m_EngTutorialDialogues.Count)
				{
					switch (GameManager.Instance.m_LanguageSetting)
					{
						case publicEnums.Language.English:
							// Does selection of tutorial dialogue dependent on tutorial advancements
							foreach (Dialogue unselectedDialogue in m_EngTutorialDialogues)
							{
								if (unselectedDialogue.tutorialStepTrigger == GameManager.Instance.m_CurrentTutorialStep)
								{
									dialogue = unselectedDialogue;
									if (dialogue.spawnPoint && dialogue.spawnPrefab)
									{
										m_IsInTutorial = true;
										m_SpawnedObject.transform.position = dialogue.spawnPoint.position;
									}
									break;
								}
							}
							break;
						case publicEnums.Language.German:
							foreach (Dialogue unselectedDialogue in m_GerTutorialDialogues)
							{
								if (unselectedDialogue.tutorialStepTrigger == GameManager.Instance.m_CurrentTutorialStep)
								{
									dialogue = unselectedDialogue;
									if (dialogue.spawnPoint && dialogue.spawnPrefab)
									{
										m_IsInTutorial = true;
										m_SpawnedObject.transform.position = dialogue.spawnPoint.position;
									}
									break;
								}
							}
							break;
						default:
							break;
					}
				}
				break;
			default:
				break;
		}
		if (m_EngTutorialDialogues.Count == GameManager.Instance.m_CurrentTutorialStep + 1)
		{
			m_IsInTutorial = false;
		}
		if (dialogue.names == null)
		{
			return;
		}
		StartDialogue(dialogue);
	}
	/// <summary>
	/// Clears the last read dialogue and adds the texts and sprites to the queue to display them later
	/// </summary>
	/// <param name="dialogue">Dialogue of the current speaker</param>
	private void StartDialogue(Dialogue dialogue)
	{
		if (m_FastSelectionBarGameObject)
		{
			m_FastSelectionBarGameObject.SetActive(false);
		}
		// So no time is lost when in dialogue (time is a precious recource in this game)
		GameManager.Instance?.GetDayNightManager()?.SetFreezeTime(true);
		// Changes the controls, so the player can not move
		InputManager.Instance.CurrentInputContext = InputContext.Dialogue;
		// Starts the animation for the text to appear
		m_Animator.SetBool("IsOpen", true);

		m_Sentences.Clear();
		m_Sprites.Clear();

		foreach (string name in dialogue.names)
		{
			m_Names.Enqueue(name);
		}
		foreach (string sentence in dialogue.sentences)
		{
			m_Sentences.Enqueue(sentence);
		}
		foreach (Sprite sprite in dialogue.sprites)
		{
			m_Sprites.Enqueue(sprite);
		}
		if (!m_IsTyping)
			DisplayNextSentence();
	}
	/// <summary>
	/// Displays the next sentence in the queue with names and sprites. Also resets the sound for the dialogue
	/// </summary>
	public void DisplayNextSentence()
	{
		GameManager.Instance.m_SpokeToNPC = true;
		if (m_IsTyping)
		{
			m_DialogueText.text = m_Sentence;
			if (TypesSentence != null)
				StopCoroutine(TypesSentence);
			AkSoundEngine.PostEvent("V_FemVoice_Stop", GameManager.Instance.AK_GameObject);
			AkSoundEngine.PostEvent("V_ManVoice_Stop", GameManager.Instance.AK_GameObject);
			m_IsTyping = false;
			return;
		}

		if (m_Sentences.Count == 0)
		{
			EndDialogue();
			return;
		}
		string name = m_Names.Dequeue();
		m_Sentence = m_Sentences.Dequeue();
		Sprite sprite = null;
		if (m_Sprites.Count() > 0)
		{
			sprite = m_Sprites.Dequeue();
		}
		if (TypesSentence != null)
			StopCoroutine(TypesSentence);
		// Makes sprite invisible when there is none
		if (!sprite)
		{
			Debug.LogWarning("No sprite found for sentence: " + m_Sentence + " in " + gameObject.name);
			m_DialogueImage.color = new Color(0, 0, 0, 0);
		}
		else
		{
			// Since sprites can be invisible, the color is set every time a new sprite is shown
			m_DialogueImage.color = new Color(255, 255, 255, 255);
			m_DialogueImage.sprite = sprite;
			m_DialogueImage.transform.position = m_ImageRightTransform.position;
			if (sprite.name.Contains("Ham"))
			{
				m_DialogueImage.transform.position = m_ImageLeftTransform.position;
			}
			m_DialogueImage.GetComponent<RectTransform>().sizeDelta = new Vector2(m_DialogueImage.sprite.rect.width * (100 / m_DialogueImage.sprite.rect.height), 100);
		}
		m_DialogueText.text = "";
		m_NameText.text = name;
		AkSoundEngine.PostEvent("V_FemVoice_Stop", GameManager.Instance.AK_GameObject);
		AkSoundEngine.PostEvent("V_ManVoice_Stop", GameManager.Instance.AK_GameObject);
		TypesSentence = StartCoroutine(TypeSentence(m_Sentence, name));
	}
	/// <summary>
	/// Types the sentence in the game and starts the audio
	/// </summary>
	/// <param name="sentence">Sentence that is going to be typed</param>
	/// <param name="name">Name of the speaker</param>
	/// <returns>Wait time</returns>
	private IEnumerator TypeSentence(string sentence, string name)
	{
		m_IsTyping = true;
		   yield return new WaitForSecondsRealtime(0.5f);
		// This would have been done in another way, if there were more than two characters
		if (name.Contains("Ham"))
		{
			AkSoundEngine.PostEvent("V_ManVoice_Start", GameManager.Instance.AK_GameObject);
		}
		else
		{
			AkSoundEngine.PostEvent("V_FemVoice_Start", GameManager.Instance.AK_GameObject);
		}
		foreach (char letter in sentence.ToCharArray())
		{
			m_DialogueText.text += letter;
			if (!char.IsWhiteSpace(letter))
			{
				yield return new WaitForSecondsRealtime(0.05f);
			}
			else
			{
				yield return null;
			}
		}
		AkSoundEngine.PostEvent("V_FemVoice_Stop", GameManager.Instance.AK_GameObject);
		AkSoundEngine.PostEvent("V_ManVoice_Stop", GameManager.Instance.AK_GameObject);
		m_IsTyping = false;
	}
	/// <summary>
	/// Ends the dialogue and closes the dialogue box / resets the sound and gives the tutorial relevant items
	/// </summary>
	private void EndDialogue()
	{
		if (m_FastSelectionBarGameObject)
		{
			m_FastSelectionBarGameObject.SetActive(true);
		}
		if (GameManager.Instance.m_CurrentTutorialStep == 0 && !m_TutHasTools)
		{
			GameManager.Instance?.GetInventoryController()?.GiveTools();
			m_TutHasTools = true;
		}
		if (GameManager.Instance.m_CurrentTutorialStep == 1 && !m_TutHasSeeds)
		{
			GameManager.Instance?.GetInventoryController()?.GiveItem("CornSeed", 6);
			GameManager.Instance?.GetInventoryController()?.GiveItem("CarrotSeed", 2);
			m_TutHasSeeds = true;
		}
		if (GameManager.Instance.m_CurrentTutorialStep == 2 && !m_TutHasBerry)
		{
			GameManager.Instance?.GetInventoryController()?.GiveItem("Strawberry", 1);
			m_TutHasBerry = true;
		}
		if (GameManager.Instance.m_CurrentTutorialStep == 3 && !m_TutHasFertiliser)
		{
			GameManager.Instance?.GetInventoryController()?.GiveItem("Fertiliser", 10);
			m_TutHasFertiliser = true;
		}
		if (GameManager.Instance.m_CurrentTutorialStep == 4)
		{
			GameManager.Instance.m_CurrentTutorialStep = 99;
		}
		m_IsTyping = false;
		AkSoundEngine.PostEvent("V_FemVoice_Stop", GameManager.Instance.AK_GameObject);
		AkSoundEngine.PostEvent("V_ManVoice_Stop", GameManager.Instance.AK_GameObject);
		m_Animator.SetBool("IsOpen", false);
		if (m_LastTrigger != null)
		{
			m_LastTrigger = null;
		}
		InputManager.Instance.CurrentInputContext = InputContext.Game;
		if (!m_IsInTutorial)
		{
			if (m_SpawnedObject && !m_IsInTutorial)
			{
				FadeToBlack.Instance.m_ActionOnBlack += DestroySpawnedObject;
				FadeToBlack.Instance.HideAction(0.05f, 0.5f);
			}
		}
		GameManager.Instance?.GetDayNightManager()?.SetFreezeTime(false);

		if (GameManager.Instance?.m_CurrentTutorialStep == 99)
		{
			foreach (GameObject inactiveGameObject in m_GameObjectsToInactiveWhileInTutorial)
			{
				inactiveGameObject.SetActive(true);
			}
		}
	}
	/// <summary>
	/// Destroys the object so it is not standing around in the game world
	/// </summary>
	private void DestroySpawnedObject()
	{
		m_SpawnedObject.transform.position = new Vector3(0, -100, 0);
		FindObjectOfType<PlayerInteraction>().RegisterTryUseCurrentSelectedItemToButton();
		m_SpawnedObject = null;
	}
	/// <summary>
	/// Used to start tutorial dialoge from a trigger
	/// Here implementations for the different languages should have been done (if they were not unused anyway in the end)
	/// </summary>
	public void StartTutorialDialogue()
	{
		if (GameManager.Instance.m_CurrentTutorialStep < m_EngTutorialDialogues.Count)
		{
			if (GameManager.Instance?.m_CurrentTutorialStep == 0)
			{
				foreach (GameObject inactiveGameObject in m_GameObjectsToInactiveWhileInTutorial)
				{
					inactiveGameObject.SetActive(false);
				}
			}
			SelectDialogue(publicEnums.TriggerCircumstance.Tutorial);
		}
	}
	/// <summary>
	/// Used to start story dialoge from a trigger
	/// Here implementations for the different languages should have been done (if they were not unused anyway in the end)
	/// </summary>
	public void StartStoryDialogue()
	{
		foreach (Dialogue dialogue in m_EngStoryDialogues)
		{
			if (GameManager.Instance.m_CurrentPotatoStep == dialogue.potatoStepTriggerMinimum)
			{
				SelectDialogue(publicEnums.TriggerCircumstance.StoryCutScene);
			}
		}
	}
}
loading
×