2144 lines
76 KiB
C#
2144 lines
76 KiB
C#
|
using UnityEngine;
|
|||
|
using System.Collections;
|
|||
|
using System.Collections.Generic;
|
|||
|
using System.IO;
|
|||
|
using UnityEngine.UI;
|
|||
|
using UnityEngine.Events;
|
|||
|
using RPGTALK.Texts;
|
|||
|
using RPGTALK.Helper;
|
|||
|
using RPGTALK.Localization;
|
|||
|
using RPGTALK.Dub;
|
|||
|
using RPGTALK.Snippets;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
[AddComponentMenu("Seize Studios/RPGTalk/RPGTalk")]
|
|||
|
public class RPGTalk : MonoBehaviour {
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Should the talk be initiated when the script starts?
|
|||
|
/// </summary>
|
|||
|
public bool startOnAwake = true;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// An array of objects that will be shown or hidden with the text.
|
|||
|
/// Usually, the canvas with the text UI is set here.
|
|||
|
/// </summary>
|
|||
|
public GameObject[] showWithDialog;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The object setted by the user to see if we can obtain a TMP_Translator out of it
|
|||
|
/// </summary>
|
|||
|
public GameObject textUIObj;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The UI element that holds a Text component. TMP_Translator deal with differences in regular UI to TMP
|
|||
|
/// </summary>
|
|||
|
public TMP_Translator textUI;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// This dialog have the name of the talker? The dialoger?
|
|||
|
/// </summary>
|
|||
|
public bool dialoger;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The object setted by the user to see if we can obtain a TMP_Translator out of it
|
|||
|
/// </summary>
|
|||
|
public GameObject dialogerObj;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// To show the name of the talker, another UI. TMP_Translater deal with differences in regular UI to TMP
|
|||
|
/// </summary>
|
|||
|
public TMP_Translator dialogerUI;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Should the element follow someone?
|
|||
|
/// </summary>
|
|||
|
public bool shouldFollow;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Who am I currently following
|
|||
|
/// </summary>
|
|||
|
public Transform following;
|
|||
|
/// <summary>
|
|||
|
/// With what offset am I following someone
|
|||
|
/// </summary>
|
|||
|
public Vector3 followingOffset;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The objects in showWithDialog should be Billboard?
|
|||
|
/// </summary>
|
|||
|
public bool billboard = true;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// If billboard is set to true, should it be based on the main camera?
|
|||
|
/// </summary>
|
|||
|
public bool mainCamera = true;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// If billboard is set to true but not the mainCamera, should it be based on what camera?
|
|||
|
/// </summary>
|
|||
|
public Camera otherCamera;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The text file that contains all the talks to be parsed.
|
|||
|
/// </summary>
|
|||
|
public TextAsset txtToParse;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// If the player hits the intercation button, should the text be skipped to the end?
|
|||
|
/// </summary>
|
|||
|
public bool enableQuickSkip = true;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// You can assign events to be called when the talk ends
|
|||
|
/// </summary>
|
|||
|
public UnityEvent callback;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// An animator that some parameters can be set by RPGTalk to help animating while the talk is running
|
|||
|
/// </summary>
|
|||
|
public Animator animatorWhenTalking;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The actual animator. This can be different from animatorWhenTalking if this options was set on the Character Settings option.
|
|||
|
/// </summary>
|
|||
|
public Animator actualAnimator;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Name of a boolean property in the animatorWhenTalking that will be set to true when the text is running.
|
|||
|
/// </summary>
|
|||
|
public string animatorBooleanName;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Name of an int property in animator that represents the talker (based on the characters array).
|
|||
|
/// </summary>
|
|||
|
public string animatorIntName;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Wich position of the talk are we?
|
|||
|
/// </summary>
|
|||
|
public int cutscenePosition = 0;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Speed of the text, in characters per second
|
|||
|
/// </summary>
|
|||
|
public float textSpeed = 50.0f;
|
|||
|
/// <summary>
|
|||
|
/// wich character of the current line are we?
|
|||
|
/// </summary>
|
|||
|
public float currentChar = 0.0f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// a list with every element of the Talk. Each element is a line on the text
|
|||
|
/// </summary>
|
|||
|
public List<RpgtalkElement> rpgtalkElements;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// An array that can contain any variable and what is its value to be replaced in the talk
|
|||
|
/// </summary>
|
|||
|
public RPGTalkVariable[] variables;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Should there be photos of the dialogers?
|
|||
|
/// </summary>
|
|||
|
public bool shouldUsePhotos;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// A list of all Characters available in the talks with settings of stuff that should be on the scene
|
|||
|
/// </summary>
|
|||
|
public RPGTalkCharacterSettings[] characters;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// An UI element with the Image property that the photo should be applied to
|
|||
|
/// </summary>
|
|||
|
public Image UIPhoto;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The dialog and everything in showWithDialog should stay on screen even if the text has ended?
|
|||
|
/// </summary>
|
|||
|
public bool shouldStayOnScreen;
|
|||
|
|
|||
|
//Are we expecting a click?
|
|||
|
bool lookForClick = true;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Audio to be played while the character is talking
|
|||
|
/// </summary>
|
|||
|
public AudioClip textAudio;
|
|||
|
/// <summary>
|
|||
|
/// Audio to be played when player passes the Talk
|
|||
|
/// </summary>
|
|||
|
public AudioClip passAudio;
|
|||
|
//The AudioSource that will be used to play the SFXs above
|
|||
|
AudioSource rpgAudioSorce;
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Pass the text with mouse Click?
|
|||
|
/// </summary>
|
|||
|
public bool passWithMouse = true;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Pass the text with some button set on Project Settings > Input
|
|||
|
/// </summary>
|
|||
|
public string passWithInputButton;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// What is the key that should be used to interact? You can override the Update function and write your
|
|||
|
/// own conditions, if needed
|
|||
|
/// </summary>
|
|||
|
public KeyCode passWithKey = KeyCode.None;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The user can currently pass the talk?
|
|||
|
/// </summary>
|
|||
|
public bool enablePass = true;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Should the talk pass itself?
|
|||
|
/// </summary>
|
|||
|
public bool autoPass = false;
|
|||
|
/// <summary>
|
|||
|
/// How many seconds should RPGTalk wait after the animation stopped to autoPass
|
|||
|
/// </summary>
|
|||
|
public float secondsAutoPass = 3f;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Line to start reading the text. Should not be below 1.
|
|||
|
/// Can be a string that the RPGTalk will look for in the text by the pattern [title=MyString]
|
|||
|
/// </summary>
|
|||
|
public string lineToStart = "1";
|
|||
|
/// <summary>
|
|||
|
/// Line to stop reading the text. If it is -1 it will read until the end of the file.
|
|||
|
/// Can be a string that the RPGTalk will look for in the text by the pattern [title=MyString]
|
|||
|
/// </summary>
|
|||
|
public string lineToBreak = "-1";
|
|||
|
|
|||
|
//After some calculations, keep the actual line to start or break
|
|||
|
private int actualLineToStart;
|
|||
|
private int actualLineToBreak;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Should the RPGTalk try to break long lines into several little ones?
|
|||
|
/// </summary>
|
|||
|
public bool wordWrap = true;
|
|||
|
/// <summary>
|
|||
|
/// If wordWrap is set to true, RPGTalk will only accept a line with maxCharInWidth * maxCharInHeight characters.
|
|||
|
/// If the line in the text passes it, it will be broken into another line.
|
|||
|
/// </summary>
|
|||
|
public int maxCharInWidth = 50;
|
|||
|
/// <summary>
|
|||
|
/// If wordWrap is set to true, RPGTalk will only accept a line with maxCharInWidth * maxCharInHeight characters.
|
|||
|
/// If the line in the text passes it, it will be broken into another line.
|
|||
|
/// </summary>
|
|||
|
public int maxCharInHeight = 4;
|
|||
|
|
|||
|
//Any RichTexts around here?
|
|||
|
private List<RPGTalkRichText> richText;
|
|||
|
private List<string> unclosedTags;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The sprites that can be used in this talk
|
|||
|
/// </summary>
|
|||
|
public List<RPGTalkSprite> sprites;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The sprites that are being used in this talk
|
|||
|
/// </summary>
|
|||
|
public List<RPGTalkSprite> spritesUsed;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The sprite atlas from Text Mesh Pro that should be used in the text
|
|||
|
/// </summary>
|
|||
|
public string tmpSpriteAtlas = "Default Sprite Asset";
|
|||
|
|
|||
|
//Any dubs around here?
|
|||
|
List<RPGTalkDub> dubs;
|
|||
|
RPGTalkDubSounds dubSounds;
|
|||
|
|
|||
|
//Any speed changes around here?
|
|||
|
List<RPGTalkSpeed> speeds;
|
|||
|
|
|||
|
//Any questions around here?
|
|||
|
List<RPGTalkQuestion> questions;
|
|||
|
|
|||
|
//Any jitter changes around here?
|
|||
|
List<RPGTalkJitter> jitters;
|
|||
|
Coroutine jitterRoutine;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The actual speed that the text will be scrolled. This usually is equal to textSpeed
|
|||
|
/// but can be changed within the text with the [speed=X] tag
|
|||
|
/// </summary>
|
|||
|
public float actualTextSpeed;
|
|||
|
|
|||
|
//Event to be called when a New Talk Start
|
|||
|
public delegate void NewTalkAction();
|
|||
|
public event NewTalkAction OnNewTalk;
|
|||
|
|
|||
|
//Event to be called when RPGTalk play next line in the talk
|
|||
|
public delegate void PlayNextAction();
|
|||
|
public event PlayNextAction OnPlayNext;
|
|||
|
|
|||
|
//Event to be called when a talk ends
|
|||
|
public delegate void EndTalkAction();
|
|||
|
public event EndTalkAction OnEndTalk;
|
|||
|
|
|||
|
//Event to be called when a line finish animating
|
|||
|
public delegate void EndAnimatingAction();
|
|||
|
public event EndAnimatingAction OnEndAnimating;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Is the RPGTalk currently playing the text?
|
|||
|
/// </summary>
|
|||
|
public bool isPlaying;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Is the RPGTalk currently animating the text?
|
|||
|
/// </summary>
|
|||
|
public bool isAnimating;
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The prefab of a Button that will be the choice in case of questions in the text
|
|||
|
/// </summary>
|
|||
|
public GameObject choicePrefab;
|
|||
|
/// <summary>
|
|||
|
/// A parent that each choice will be instantiated to in case of questions
|
|||
|
/// </summary>
|
|||
|
public Transform choicesParent;
|
|||
|
|
|||
|
//Event to be called when a it play next line in the talk
|
|||
|
public delegate void MadeAChoiceAction(string questionID, int choiceNumber);
|
|||
|
public event MadeAChoiceAction OnMadeChoice;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// The Expression that this character is expressing
|
|||
|
/// </summary>
|
|||
|
public Expression expressing;
|
|||
|
|
|||
|
//if we will change talks in the middle our talk, these variables will be set
|
|||
|
string changeToStart;
|
|||
|
string changeToBreak;
|
|||
|
|
|||
|
List<RPGtalkSaveStatement> saves;
|
|||
|
/// <summary>
|
|||
|
/// The RPGTalkSaveInstance element, if there is any
|
|||
|
/// </summary>
|
|||
|
public RPGTalkSaveInstance saveInstance;
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Sometimes the line to start and break may be changing during a talk. With this option marked, end the talk finish it will return to the original ones
|
|||
|
/// </summary>
|
|||
|
public bool goBackToOriginalStartAndBreak = true;
|
|||
|
string originalLineToStart;
|
|||
|
string originalLineToBreak;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void Start(){
|
|||
|
//Get the TMP_Translate Object
|
|||
|
textUI = new TMP_Translator(textUIObj);
|
|||
|
if (dialogerObj != null)
|
|||
|
{
|
|||
|
dialogerUI = new TMP_Translator(dialogerObj);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//If it is set to start on awake, start it! If not, make sure that we hide anything that shouldn't be there
|
|||
|
if (startOnAwake) {
|
|||
|
NewTalk ();
|
|||
|
} else {
|
|||
|
foreach (GameObject GO in showWithDialog) {
|
|||
|
GO.SetActive (false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
saveInstance = GetComponent<RPGTalkSaveInstance>();
|
|||
|
}
|
|||
|
|
|||
|
//Change txtToParse to be the correct for other language
|
|||
|
TextAsset CheckCurrentLanguage(){
|
|||
|
if (RPGTalkLocalization.singleton != null) {
|
|||
|
return RPGTalkLocalization.singleton.CheckForCorrectLanguage (txtToParse);
|
|||
|
}
|
|||
|
return txtToParse;
|
|||
|
}
|
|||
|
|
|||
|
void CreateAudioSource()
|
|||
|
{
|
|||
|
AudioSource aS = gameObject.GetComponent<AudioSource>();
|
|||
|
if (aS && aS.clip == textAudio)
|
|||
|
{
|
|||
|
rpgAudioSorce = aS;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
rpgAudioSorce = gameObject.AddComponent<AudioSource>();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#region newtalk
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Before start a New Talk, change the values
|
|||
|
/// </summary>
|
|||
|
/// <param name="_lineToStart">Line to start reading the text.</param>
|
|||
|
/// <param name="_lineToBreak">Line to stop reading the text.</param>
|
|||
|
public void NewTalk(string _lineToStart,string _lineToBreak){
|
|||
|
lineToStart = _lineToStart;
|
|||
|
lineToBreak = _lineToBreak;
|
|||
|
NewTalk ();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Before start a New Talk, change the values
|
|||
|
/// </summary>
|
|||
|
/// <param name="_lineToStart">Line to start reading the text.</param>
|
|||
|
/// <param name="_lineToBreak">Line to stop reading the text.</param>
|
|||
|
/// <param name="_txtToParse">Text to read from</param>
|
|||
|
public void NewTalk(string _lineToStart,string _lineToBreak,TextAsset _txtToParse){
|
|||
|
lineToStart = _lineToStart;
|
|||
|
lineToBreak = _lineToBreak;
|
|||
|
txtToParse = _txtToParse;
|
|||
|
NewTalk ();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Before start a New Talk, change the values
|
|||
|
/// </summary>
|
|||
|
/// <param name="_lineToStart">Line to start reading the text.</param>
|
|||
|
/// <param name="_lineToBreak">Line to stop reading the text.</param>
|
|||
|
/// <param name="_txtToParse">Text to read from</param>
|
|||
|
/// <param name="_callback">Events to be called when the talk ends</param>
|
|||
|
public void NewTalk(string _lineToStart,string _lineToBreak,TextAsset _txtToParse, UnityEvent _callback){
|
|||
|
lineToStart = _lineToStart;
|
|||
|
lineToBreak = _lineToBreak;
|
|||
|
txtToParse = _txtToParse;
|
|||
|
callback = _callback;
|
|||
|
NewTalk ();
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Starts a new Talk.
|
|||
|
/// </summary>
|
|||
|
public void NewTalk(){
|
|||
|
//call the event
|
|||
|
if(OnNewTalk != null){
|
|||
|
OnNewTalk ();
|
|||
|
}
|
|||
|
|
|||
|
//Check if we are using the right txtToParse based on the language
|
|||
|
TextAsset internalTxtToParse = CheckCurrentLanguage ();
|
|||
|
|
|||
|
//check if we have the dubsounds component on
|
|||
|
if (dubSounds == null) {
|
|||
|
dubSounds = GetComponent<RPGTalkDubSounds> ();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//save the original lines to start and break if we want to revert to them later
|
|||
|
if (string.IsNullOrEmpty(originalLineToStart))
|
|||
|
{
|
|||
|
originalLineToStart = lineToStart;
|
|||
|
originalLineToBreak = lineToBreak;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//reduce one for the line to Start and break, if they were ints
|
|||
|
//return the default lines to -2 if they were not ints
|
|||
|
if (int.TryParse (lineToStart, out actualLineToStart)) {
|
|||
|
actualLineToStart -= 1;
|
|||
|
} else {
|
|||
|
actualLineToStart = -2;
|
|||
|
}
|
|||
|
if (int.TryParse (lineToBreak, out actualLineToBreak)) {
|
|||
|
if (lineToBreak != "-1") {
|
|||
|
actualLineToBreak -= 1;
|
|||
|
}
|
|||
|
} else {
|
|||
|
actualLineToBreak = -2;
|
|||
|
}
|
|||
|
|
|||
|
if (textAudio != null) {
|
|||
|
if (rpgAudioSorce == null) {
|
|||
|
CreateAudioSource();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
lookForClick = true;
|
|||
|
|
|||
|
//reset positions
|
|||
|
cutscenePosition = 1;
|
|||
|
currentChar = 0;
|
|||
|
|
|||
|
|
|||
|
//create a new CutsCeneElement
|
|||
|
rpgtalkElements = new List<RpgtalkElement>();
|
|||
|
|
|||
|
//Resets the Rich Texts list
|
|||
|
richText = new List<RPGTalkRichText> ();
|
|||
|
|
|||
|
//If there was any unclosed tags... Reset it
|
|||
|
unclosedTags = new List<string>();
|
|||
|
|
|||
|
//if there was any sprites used... reset it
|
|||
|
spritesUsed = new List<RPGTalkSprite>();
|
|||
|
CleanDirtySprites ();
|
|||
|
|
|||
|
//if there was any speeds used... reset it
|
|||
|
speeds = new List<RPGTalkSpeed>();
|
|||
|
|
|||
|
//if there was any dubs used... reset it
|
|||
|
dubs = new List<RPGTalkDub>();
|
|||
|
|
|||
|
//if there was any questions used... reset it
|
|||
|
questions = new List<RPGTalkQuestion>();
|
|||
|
|
|||
|
//the speed at the start is the default
|
|||
|
actualTextSpeed = textSpeed;
|
|||
|
|
|||
|
//Zero saves
|
|||
|
saves = new List<RPGtalkSaveStatement>();
|
|||
|
|
|||
|
//The jitters that could be in this lines
|
|||
|
jitters = new List<RPGTalkJitter>();
|
|||
|
|
|||
|
|
|||
|
//resets any text that might have been left from previous talks
|
|||
|
if(textUI == null)
|
|||
|
{
|
|||
|
if(textUIObj == null)
|
|||
|
{
|
|||
|
Debug.LogError("You need to set an UI Element to be the text!");
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
textUI = new TMP_Translator(textUIObj);
|
|||
|
}
|
|||
|
}
|
|||
|
textUI.ChangeTextTo("");
|
|||
|
|
|||
|
|
|||
|
if(internalTxtToParse != null) {
|
|||
|
// read the TXT file into the elements list
|
|||
|
StringReader reader = new StringReader (internalTxtToParse.text);
|
|||
|
|
|||
|
string line = reader.ReadLine();
|
|||
|
int currentLine = 0;
|
|||
|
|
|||
|
if(line == null)
|
|||
|
{
|
|||
|
Debug.LogError("There was an error reading your file! Check your encoding settings.");
|
|||
|
EndTalk();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
while (line != null) {
|
|||
|
//if the lineToStart or lineToBreak were strings, find out what line they actually were
|
|||
|
if (actualLineToStart == -2) {
|
|||
|
if (line.IndexOf("[title="+lineToStart+"]") != -1) {
|
|||
|
actualLineToStart = currentLine+1;
|
|||
|
} else {
|
|||
|
line = reader.ReadLine();
|
|||
|
currentLine++;
|
|||
|
continue;
|
|||
|
}
|
|||
|
}
|
|||
|
if (actualLineToBreak == -2) {
|
|||
|
if (line.IndexOf("[title="+lineToBreak+"]") != -1) {
|
|||
|
actualLineToBreak = currentLine-1;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (currentLine >= actualLineToStart) {
|
|||
|
if (actualLineToBreak < 0 || currentLine <= actualLineToBreak) {
|
|||
|
//If this line was a choice, we don't want to keep track of it
|
|||
|
if (LookForChoices (line)) {
|
|||
|
line = reader.ReadLine();
|
|||
|
currentLine++;
|
|||
|
continue;
|
|||
|
}
|
|||
|
//If this line was a save, we don't want to keep track of it
|
|||
|
if (LookForSave(line))
|
|||
|
{
|
|||
|
line = reader.ReadLine();
|
|||
|
currentLine++;
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (wordWrap) {
|
|||
|
CheckIfTheTextFits (line);
|
|||
|
} else {
|
|||
|
rpgtalkElements.Add (readSceneElement (line));
|
|||
|
}
|
|||
|
} else {
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
line = reader.ReadLine();
|
|||
|
currentLine++;
|
|||
|
}
|
|||
|
|
|||
|
if(rpgtalkElements.Count == 0){
|
|||
|
Debug.LogError ("The Line To Start and the Line To Break are not fit for the given TXT");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//After reading all the elements in the talk, let's check if the text should be ready to fit some sprites
|
|||
|
if (textUIObj.GetComponent<ITextWithIcon> () != null) {
|
|||
|
textUIObj.GetComponent<ITextWithIcon> ().RepopulateImages ();
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//show what need to be shown
|
|||
|
textUI.Enabled(true);
|
|||
|
if (dialoger) {
|
|||
|
if (dialogerObj) {
|
|||
|
dialogerUI.Enabled(true);
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
for (int i = 0; i < showWithDialog.Length; i++) {
|
|||
|
showWithDialog[i].SetActive(true);
|
|||
|
}
|
|||
|
|
|||
|
//Set the speaker name and photo
|
|||
|
if (dialoger) {
|
|||
|
if (dialogerObj) {
|
|||
|
dialogerUI.ChangeTextTo(rpgtalkElements [0].speakerName);
|
|||
|
}
|
|||
|
if (shouldUsePhotos) {
|
|||
|
for (int i = 0; i < characters.Length; i++) {
|
|||
|
//If we fond the character that is talking
|
|||
|
if (characters[i].character.dialoger == rpgtalkElements [0].originalSpeakerName) {
|
|||
|
//Change its photo
|
|||
|
if (UIPhoto) {
|
|||
|
UIPhoto.sprite = characters [i].character.photo;
|
|||
|
}
|
|||
|
//Change its animator
|
|||
|
if(characters[i].animatorOverwrite != null)
|
|||
|
{
|
|||
|
actualAnimator = characters[i].animatorOverwrite;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
actualAnimator = animatorWhenTalking;
|
|||
|
}
|
|||
|
//animate it
|
|||
|
if (actualAnimator && animatorIntName != ""){
|
|||
|
actualAnimator.SetInteger (animatorIntName, i);
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//Check if and who the elements should follow
|
|||
|
CheckWhoToFollow (rpgtalkElements [0]);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//check if there should be any dubs in this line
|
|||
|
CheckDubsInThisLine();
|
|||
|
|
|||
|
//check if we are expressing something
|
|||
|
expressing = IsExpressing(rpgtalkElements[0]);
|
|||
|
|
|||
|
//if we have an animator.. play it
|
|||
|
PlayAnimator(rpgtalkElements[0]);
|
|||
|
|
|||
|
//check if after this line we should start another talk
|
|||
|
rpgtalkElements[0].dialogText = LookForNewTalk(rpgtalkElements[0].dialogText);
|
|||
|
|
|||
|
isPlaying = true;
|
|||
|
isAnimating = true;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
private RpgtalkElement readSceneElement(string line) {
|
|||
|
|
|||
|
RpgtalkElement newElement = new RpgtalkElement();
|
|||
|
|
|||
|
newElement.originalSpeakerName = line;
|
|||
|
|
|||
|
//replace any variable that may exist on the text
|
|||
|
for (int i = 0; i < variables.Length; i++) {
|
|||
|
if (line.Contains (variables[i].variableName)) {
|
|||
|
line = line.Replace (variables[i].variableName, variables[i].variableValue);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//If we want to show the dialoger's name, slipt the line at the ':'
|
|||
|
if (dialoger) {
|
|||
|
|
|||
|
if (line.IndexOf (':') != -1) {
|
|||
|
|
|||
|
string[] splitLine = line.Split (new char[] { ':' }, 2);
|
|||
|
|
|||
|
newElement.speakerName = splitLine [0].Trim ();
|
|||
|
|
|||
|
//newElement.dialogText = LookForRichTexts(splitLine [1].Trim ());
|
|||
|
line = splitLine [1].Trim ();
|
|||
|
|
|||
|
string[] originalSplitLine = newElement.originalSpeakerName.Split (new char[] { ':' },2);
|
|||
|
|
|||
|
newElement.originalSpeakerName = originalSplitLine [0].Trim ();
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//Check for any question that should come along with the text
|
|||
|
line = LookForQuestions(line);
|
|||
|
|
|||
|
//Check for any dubs that should come along with the text
|
|||
|
line = LookForDubs (line);
|
|||
|
|
|||
|
//Check for any speed changes that should be on the text
|
|||
|
line = LookForSpeed(line);
|
|||
|
|
|||
|
//Check for any sprites that should be on the text
|
|||
|
line = LookForSprites (line);
|
|||
|
|
|||
|
//Check for any expressions to play with the text
|
|||
|
newElement.expression = LookForExpression(line);
|
|||
|
line = LookForExpression(line, true);
|
|||
|
|
|||
|
//Check for any jitters on the text
|
|||
|
line = LookForJitter(line);
|
|||
|
|
|||
|
//Check for any rich texts on the text
|
|||
|
line = LookForRichTexts(line);
|
|||
|
|
|||
|
|
|||
|
//Finally apply the text to the new element
|
|||
|
newElement.dialogText = line;
|
|||
|
newElement.hasDialog = true;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
return newElement;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#region sprites
|
|||
|
|
|||
|
private void CheckTextUIScript(){
|
|||
|
//If we are using sprites inside the text, the regular Text script need to be changed.
|
|||
|
if (textUIObj.GetComponent<ITextWithIcon> () == null && textUI.hasUIText) {
|
|||
|
//Lets create a copy of the Text that the user created
|
|||
|
GameObject tempGO = new GameObject ();
|
|||
|
ITextWithIcon newText = textUI.AddTextWithIconComponent(tempGO);
|
|||
|
RPGTalkHelper.CopyTextParameters (textUI.GetTextObject(), newText as Object);
|
|||
|
|
|||
|
//now remove the previous one
|
|||
|
DestroyImmediate(textUI.GetTextObject());
|
|||
|
|
|||
|
//finally, add the new text to the ancient Game Object
|
|||
|
textUI.AddTextWithIconComponent(textUIObj);
|
|||
|
textUI = new TMP_Translator( textUIObj);
|
|||
|
textUIObj.GetComponent<ITextWithIcon> ().rpgtalk = this;
|
|||
|
RPGTalkHelper.CopyTextParameters (newText as Object, textUI.GetTextObject());
|
|||
|
|
|||
|
Destroy (tempGO);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
private string LookForSprites(string line){
|
|||
|
//check if the user have some sprites and the line asks for one
|
|||
|
if (sprites.Count > 0 && line.IndexOf("[sprite=")!=-1 && line.IndexOf("]",line.IndexOf("[sprite="))!= -1) {
|
|||
|
bool thereAreSpritesLeft = true;
|
|||
|
|
|||
|
//There is at least one sprite in this line! Let's check if our UI uses the correct script
|
|||
|
CheckTextUIScript();
|
|||
|
|
|||
|
|
|||
|
//repeat as long as we find a sprite
|
|||
|
while (thereAreSpritesLeft) {
|
|||
|
int initialBracket = line.IndexOf ("[sprite=");
|
|||
|
int finalBracket = -1;
|
|||
|
if (initialBracket != -1) {
|
|||
|
finalBracket = line.IndexOf ("]", initialBracket);
|
|||
|
}
|
|||
|
|
|||
|
//There still are any '[sprite=' and it is before a ']'?
|
|||
|
if (initialBracket < finalBracket) {
|
|||
|
//Ok, new sprite around! Let's get its number
|
|||
|
int spriteNum = -1;
|
|||
|
//Check if the number was a valid int
|
|||
|
if (int.TryParse (line.Substring (initialBracket + 8, finalBracket - (initialBracket + 8)), out spriteNum) &&
|
|||
|
sprites.Count > spriteNum) {
|
|||
|
|
|||
|
//Change the line differently if we have TMP
|
|||
|
line = textUI.GetCorrectSpriteLine(line, ref sprites, ref spritesUsed, spriteNum, initialBracket, finalBracket, rpgtalkElements.Count, tmpSpriteAtlas);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
Debug.LogWarning ("Found a [sprite=x] variable in the text but something is wrong with it. Check The spelling and check if the number used exists in the 'Sprites' section");
|
|||
|
thereAreSpritesLeft = false;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
thereAreSpritesLeft = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return line;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void CleanDirtySprites()
|
|||
|
{
|
|||
|
if (textUI == null)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
foreach (Transform child in textUIObj.transform)
|
|||
|
{
|
|||
|
DestroyImmediate(child.gameObject);
|
|||
|
StopCoroutine(TryToPutImageOnText(0));
|
|||
|
}
|
|||
|
if (textUIObj.GetComponent<ITextWithIcon>() != null)
|
|||
|
{
|
|||
|
textUIObj.GetComponent<ITextWithIcon>().RepopulateImages();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//Tries to force image to be put on the text. If it cant, wait half a second so that the text mesh can be updated
|
|||
|
IEnumerator TryToPutImageOnText(int spriteToUse)
|
|||
|
{
|
|||
|
spritesUsed[spriteToUse].alreadyInPlace = textUIObj.GetComponent<ITextWithIcon>().FitImagesOnText(spriteToUse);
|
|||
|
if (!spritesUsed[spriteToUse].alreadyInPlace)
|
|||
|
{
|
|||
|
yield return new WaitForSeconds(0.2f);
|
|||
|
if (spritesUsed[spriteToUse].lineWithSprite == cutscenePosition - 1)
|
|||
|
{
|
|||
|
spritesUsed[spriteToUse].alreadyInPlace = textUIObj.GetComponent<ITextWithIcon>().FitImagesOnText(spriteToUse);
|
|||
|
}
|
|||
|
}
|
|||
|
yield return null;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region dubs
|
|||
|
|
|||
|
private string LookForDubs(string line){
|
|||
|
//check if the user have some dubs and the line asks for one
|
|||
|
if (line.IndexOf("[dub=")!=-1 && line.IndexOf("]",line.IndexOf("[dub="))!= -1) {
|
|||
|
|
|||
|
bool thereAreDubsLeft = true;
|
|||
|
|
|||
|
//repeat as long as we find a dub
|
|||
|
while (thereAreDubsLeft) {
|
|||
|
int initialBracket = line.IndexOf ("[dub=");
|
|||
|
int finalBracket = -1;
|
|||
|
if (initialBracket != -1) {
|
|||
|
finalBracket = line.IndexOf ("]", initialBracket);
|
|||
|
}
|
|||
|
|
|||
|
//There still are any '[dub=' and it is before a ']'?
|
|||
|
if (initialBracket < finalBracket) {
|
|||
|
//Ok, new dub around! Let's get its number
|
|||
|
int dubNum = -1;
|
|||
|
//Check if the number was a valid int
|
|||
|
if (int.TryParse (line.Substring (initialBracket + 5, finalBracket - (initialBracket + 5)), out dubNum)) {
|
|||
|
//Neat, we definely have a dub with a valid number. Time to keep track of it
|
|||
|
RPGTalkDub newDub = new RPGTalkDub();
|
|||
|
newDub.dubNumber = dubNum;
|
|||
|
//Make sure that the the sprite only work for that next line to be added to RpgTalkElements
|
|||
|
newDub.lineWithDub = rpgtalkElements.Count;
|
|||
|
|
|||
|
dubs.Add (newDub);
|
|||
|
|
|||
|
//Looking good! We found out that a dub should be there and we are already keeping track of it
|
|||
|
//But now we should remove the [dub=X] from the line.
|
|||
|
line = line.Substring(0,initialBracket) +
|
|||
|
line.Substring(finalBracket+1);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
Debug.LogWarning ("Found a [dub=x] variable in the text but something is wrong with it. Check The spelling");
|
|||
|
thereAreDubsLeft = false;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
thereAreDubsLeft = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return line;
|
|||
|
}
|
|||
|
|
|||
|
void CheckDubsInThisLine()
|
|||
|
{
|
|||
|
for (int i = 0; i < dubs.Count; i++)
|
|||
|
{
|
|||
|
if (dubs[i].lineWithDub == cutscenePosition - 1)
|
|||
|
{
|
|||
|
if (dubSounds == null)
|
|||
|
{
|
|||
|
Debug.LogError("A dub was found in this line but there is no RPGTalkDubSounds component added to the object");
|
|||
|
return;
|
|||
|
}
|
|||
|
dubSounds.PlayDubClip(dubs[i].dubNumber);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region questions
|
|||
|
|
|||
|
private string LookForQuestions(string line){
|
|||
|
//check if the user have some question and the line asks for one
|
|||
|
if (line.IndexOf("[question=")!=-1 && line.IndexOf("]",line.IndexOf("[question="))!= -1) {
|
|||
|
int initialBracket = line.IndexOf ("[question=");
|
|||
|
int finalBracket = line.IndexOf ("]", initialBracket);
|
|||
|
|
|||
|
//Ok, new question around! Let's get its id
|
|||
|
string questionID = line.Substring(initialBracket + 10, finalBracket - (initialBracket + 10));
|
|||
|
if (questionID.Length > 0) {
|
|||
|
//Neat, we definely have a question with a valid number. Time to keep track of it
|
|||
|
RPGTalkQuestion newQuestion = new RPGTalkQuestion();
|
|||
|
newQuestion.questionID = questionID;
|
|||
|
newQuestion.lineWithQuestion = rpgtalkElements.Count;
|
|||
|
|
|||
|
questions.Add (newQuestion);
|
|||
|
|
|||
|
//Looking good! We found out that a question should be there and we are already keeping track of it
|
|||
|
//But now we should remove the [question=X] from the line.
|
|||
|
line = line.Substring(0,initialBracket) +
|
|||
|
line.Substring(finalBracket+1);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
Debug.LogWarning ("Found a [question=x] variable in the text but something is wrong with it. Check The spelling");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return line;
|
|||
|
}
|
|||
|
|
|||
|
public bool LookForChoices(string line){
|
|||
|
//check if the user have some choice and the line asks for one
|
|||
|
if (line.IndexOf("[choice]")!=-1) {
|
|||
|
int initialBracket = line.IndexOf ("[choice]");
|
|||
|
|
|||
|
//Ok! Let's isolate its string
|
|||
|
line = line.Substring(initialBracket+8);
|
|||
|
|
|||
|
//Add it to the last question found
|
|||
|
if (questions.Count > 0) {
|
|||
|
questions [questions.Count - 1].choices.Add (line);
|
|||
|
return true;
|
|||
|
} else {
|
|||
|
Debug.LogWarning ("Found a [choice] in the text but there was no [question=x] in a line before it");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region speed
|
|||
|
|
|||
|
private string LookForSpeed(string line){
|
|||
|
//check if the user have some speed changes and the line asks for one
|
|||
|
if ((line.IndexOf("[speed=")!=-1 && line.IndexOf("]",line.IndexOf("[speed="))!= -1) || line.IndexOf ("[/speed]") != -1) {
|
|||
|
bool thereAreSpeedsLeft = true;
|
|||
|
|
|||
|
//There is at least one sprite in this line! Let's check if our UI uses the correct script
|
|||
|
CheckTextUIScript();
|
|||
|
|
|||
|
|
|||
|
//repeat as long as we find a sprite
|
|||
|
while (thereAreSpeedsLeft) {
|
|||
|
int initialBracket = line.IndexOf ("[speed=");
|
|||
|
int closingBracket = line.IndexOf ("[/speed]");
|
|||
|
int finalBracket = -1;
|
|||
|
if (initialBracket != -1) {
|
|||
|
finalBracket = line.IndexOf ("]", initialBracket);
|
|||
|
}
|
|||
|
|
|||
|
//There still are any '[speed=' and it is before a ']'?
|
|||
|
if (initialBracket < finalBracket || closingBracket != -1) {
|
|||
|
//Ok, new speed chang around! Let's get its number
|
|||
|
int speedNum = 0;
|
|||
|
|
|||
|
//it was a opening [speed=
|
|||
|
if (closingBracket == -1 || (initialBracket < closingBracket && initialBracket != -1 && finalBracket != -1 && initialBracket < finalBracket)) {
|
|||
|
//Check if the number was a valid int
|
|||
|
if (int.TryParse (line.Substring (initialBracket + 7, finalBracket - (initialBracket + 7)), out speedNum)) {
|
|||
|
//Neat, we definely have a speed with a valid number. Time to keep track of it
|
|||
|
RPGTalkSpeed newSpeed = new RPGTalkSpeed ();
|
|||
|
newSpeed.speed = Mathf.Abs(speedNum);
|
|||
|
//subtract from the speed position any rpgtalk tag that might have come before it
|
|||
|
newSpeed.speedPosition = initialBracket - RPGTalkHelper.CountRPGTalkTagCharacters (line.Substring (0, initialBracket));
|
|||
|
newSpeed.lineWithSpeed = rpgtalkElements.Count;
|
|||
|
speeds.Add (newSpeed);
|
|||
|
|
|||
|
//Looking good! We found out that a speed should be there and we are already keeping track of it
|
|||
|
//But now we should remove the [speed=X] from the line.
|
|||
|
line = line.Substring (0, initialBracket) +
|
|||
|
line.Substring (finalBracket + 1);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
Debug.LogWarning ("Found a [speed=x] variable in the text but something is wrong with it. Check The spelling.");
|
|||
|
thereAreSpeedsLeft = false;
|
|||
|
}
|
|||
|
} else {
|
|||
|
//it was a closgin [/speed]
|
|||
|
RPGTalkSpeed newSpeed = new RPGTalkSpeed ();
|
|||
|
newSpeed.speed = 0;
|
|||
|
//subtract from the speed position any rpgtalk tag that might have come before it
|
|||
|
newSpeed.speedPosition = closingBracket - RPGTalkHelper.CountRPGTalkTagCharacters (line.Substring (0, closingBracket));
|
|||
|
newSpeed.lineWithSpeed = rpgtalkElements.Count;
|
|||
|
speeds.Add (newSpeed);
|
|||
|
|
|||
|
line = line.Substring (0, closingBracket) +
|
|||
|
line.Substring (closingBracket + 8);
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
thereAreSpeedsLeft = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return line;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region richtext
|
|||
|
|
|||
|
private string LookForRichTexts(string line){
|
|||
|
//If you had any sprites added to your line... I'm sorry, you need to enable Rich Text
|
|||
|
if (spritesUsed.Count > 0) {
|
|||
|
textUI.ChangeRichText(true);
|
|||
|
}
|
|||
|
|
|||
|
//check for any rich text (only it is marked as so on the UI element)
|
|||
|
if (textUI.RichText() && line.IndexOf('<') != -1)
|
|||
|
{
|
|||
|
bool thereIsRichTextLeft = true;
|
|||
|
|
|||
|
//repeat for as long as we find a tag
|
|||
|
while (thereIsRichTextLeft) {
|
|||
|
int inicialBracket = line.IndexOf ('<');
|
|||
|
int finalBracket = line.IndexOf ('>');
|
|||
|
//Here comes the tricky part... First check if there are any '<' before a '>'
|
|||
|
if (inicialBracket < finalBracket) {
|
|||
|
//Ok, there is! It should be a tag. But first let's check if it isn't a closing one
|
|||
|
//This could happen because the text was automatically clipped with word wrap to fit the UI
|
|||
|
if (line.Substring (inicialBracket + 1, 1) == "/") {
|
|||
|
//Oh! It is a closing tag! Who would say?
|
|||
|
//If there wasn't some unclosed tags in some other line in this talk, it was just a mistake.
|
|||
|
if (unclosedTags.Count == 0) {
|
|||
|
thereIsRichTextLeft = false;
|
|||
|
}
|
|||
|
//Let's check the openned tags in previous lines.
|
|||
|
for (int i = unclosedTags.Count-1; i >= 0; i--) {
|
|||
|
line = unclosedTags [i] + line;
|
|||
|
}
|
|||
|
//After that... Let's reset the unclosed tags, shall we? No infinity loops wanted
|
|||
|
unclosedTags = new List<string>();
|
|||
|
|
|||
|
//Cool, we openned the tags... Let's try this search again
|
|||
|
inicialBracket = line.IndexOf ('<');
|
|||
|
finalBracket = line.IndexOf ('>');
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//Ok, we got an openning tag. Let's found out the name of it
|
|||
|
int endOfTag = line.IndexOf (' ', inicialBracket);
|
|||
|
//Let's check if the tag ends (>) before a ' ' is found
|
|||
|
if (finalBracket < endOfTag || endOfTag == -1) {
|
|||
|
endOfTag = finalBracket;
|
|||
|
}
|
|||
|
//Now let's check if there was an '=' before the '>' or ' '.
|
|||
|
int equalSign = line.IndexOf ('=', inicialBracket);
|
|||
|
if (equalSign < endOfTag && equalSign != -1) {
|
|||
|
endOfTag = equalSign;
|
|||
|
}
|
|||
|
|
|||
|
string tagName = line.Substring (inicialBracket + 1, endOfTag - inicialBracket - 1);
|
|||
|
//Good! We know the tag name. Now let's find its closing point
|
|||
|
string closedTag = "</" + tagName + '>';
|
|||
|
int closedTagLine = line.IndexOf (closedTag, finalBracket);
|
|||
|
|
|||
|
if(closedTagLine == -1){
|
|||
|
//Well would you look at that... We found a tag, but not its closing point (</tag>)... What to do?
|
|||
|
//This could have happened because the text was automatically clipped with word wrap to fit the UI,
|
|||
|
//Or you just forgot to put a </tag> somewhere...
|
|||
|
//Anyway, we will forcelly add the closing tag at the end of the line
|
|||
|
//And keep track of it, so if the tag is closed in another line, we know how it started
|
|||
|
//But we don't want to add it if it is a "sprite" tag from TMP
|
|||
|
if (tagName == "sprite")
|
|||
|
{
|
|||
|
closedTag = "";
|
|||
|
closedTagLine = finalBracket +1;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
line += closedTag;
|
|||
|
unclosedTags.Add(line.Substring(inicialBracket, finalBracket - inicialBracket + 1));
|
|||
|
|
|||
|
closedTagLine = line.IndexOf(closedTag, finalBracket);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//Ok, we found (or forcelly added) the closing tag, there is definely a rich text here.
|
|||
|
//Let's add it to a list so we can read later on.
|
|||
|
RPGTalkRichText newRichText = new RPGTalkRichText();
|
|||
|
newRichText.initialTagPosition = inicialBracket;
|
|||
|
newRichText.initialTag = line.Substring (inicialBracket, finalBracket-inicialBracket+1);
|
|||
|
newRichText.finalTagPosition = closedTagLine;
|
|||
|
newRichText.finalTag = closedTag;
|
|||
|
//Make sure that the the rich text only work for that next line to be added to RpgTalkElements
|
|||
|
newRichText.lineWithTheRichText = rpgtalkElements.Count;
|
|||
|
richText.Add (newRichText);
|
|||
|
|
|||
|
//Good! Now finaly, remove it from the original text
|
|||
|
string textWithoutRichText = line.Substring (0, inicialBracket);
|
|||
|
textWithoutRichText += line.Substring (finalBracket + 1, closedTagLine - finalBracket - 1);
|
|||
|
textWithoutRichText += line.Substring (closedTagLine + closedTag.Length);
|
|||
|
line = textWithoutRichText;
|
|||
|
|
|||
|
} else {
|
|||
|
thereIsRichTextLeft = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return line;
|
|||
|
}
|
|||
|
|
|||
|
return line;
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region expressions
|
|||
|
|
|||
|
private string LookForExpression(string line, bool returnLine = false)
|
|||
|
{
|
|||
|
//check if the user have some expression and the line asks for one
|
|||
|
if (line.IndexOf("[expression=") != -1 && line.IndexOf("]") != -1)
|
|||
|
{
|
|||
|
//We do have one!
|
|||
|
int initialBracket = line.IndexOf("[expression=");
|
|||
|
int finalBracket = -1;
|
|||
|
if (initialBracket != -1)
|
|||
|
{
|
|||
|
finalBracket = line.IndexOf("]", initialBracket);
|
|||
|
}
|
|||
|
|
|||
|
//There still are any '[expression=' and it is before a ']'?
|
|||
|
if (initialBracket < finalBracket)
|
|||
|
{
|
|||
|
if (line.Substring(initialBracket + 12, finalBracket - (initialBracket + 12)).Length > 0)
|
|||
|
{
|
|||
|
if (returnLine)
|
|||
|
{
|
|||
|
return line.Substring(0, initialBracket) +
|
|||
|
line.Substring(finalBracket + 1);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return line.Substring(initialBracket + 12, finalBracket - (initialBracket + 12));
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogWarning("Found a [expression=x] variable in the text but something is wrong with it. Check The spelling");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
if (returnLine)
|
|||
|
{
|
|||
|
return line;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return "";
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Expression IsExpressing(RpgtalkElement element)
|
|||
|
{
|
|||
|
if(!string.IsNullOrEmpty(element.expression))
|
|||
|
{
|
|||
|
foreach(RPGTalkCharacterSettings character in characters)
|
|||
|
{
|
|||
|
if(character.character.dialoger == element.originalSpeakerName)
|
|||
|
{
|
|||
|
foreach(Expression expression in character.character.expressions)
|
|||
|
{
|
|||
|
if(expression.name == element.expression)
|
|||
|
{
|
|||
|
|
|||
|
//if we have to change a photo, let's change it already
|
|||
|
if(shouldUsePhotos && UIPhoto != null)
|
|||
|
{
|
|||
|
UIPhoto.sprite = expression.photo;
|
|||
|
}
|
|||
|
|
|||
|
//if we have a default audio for this expression, let's play it too
|
|||
|
if(expression.audio != null)
|
|||
|
{
|
|||
|
if(rpgAudioSorce == null)
|
|||
|
{
|
|||
|
CreateAudioSource();
|
|||
|
}
|
|||
|
rpgAudioSorce.PlayOneShot(expression.audio);
|
|||
|
}
|
|||
|
|
|||
|
return expression;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
Debug.LogError("An expression was used in your TXT, but that expression wasn't found on the character talking");
|
|||
|
return null;
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
return null;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
#region NewTalkTag
|
|||
|
|
|||
|
private string LookForNewTalk(string line)
|
|||
|
{
|
|||
|
changeToBreak = "";
|
|||
|
changeToStart = "";
|
|||
|
|
|||
|
|
|||
|
//check if the user have some newtalk and the line asks for one
|
|||
|
if (line.IndexOf("[newtalk") != -1 && line.IndexOf("]") != -1)
|
|||
|
{
|
|||
|
//We do have one!
|
|||
|
int initialBracket = line.IndexOf("[newtalk");
|
|||
|
int finalBracket = -1;
|
|||
|
if (initialBracket != -1)
|
|||
|
{
|
|||
|
finalBracket = line.IndexOf("]", initialBracket);
|
|||
|
}
|
|||
|
|
|||
|
//There still are any '[newtalk' and it is before a ']'?
|
|||
|
if (initialBracket < finalBracket)
|
|||
|
{
|
|||
|
|
|||
|
//Everything fine until now. Now let's check the start and break variables
|
|||
|
int indexOfStart = line.IndexOf("start=", initialBracket + 8);
|
|||
|
int endOfStart = line.IndexOf(" ", indexOfStart);
|
|||
|
if(endOfStart == -1)
|
|||
|
{
|
|||
|
endOfStart = line.IndexOf("]", indexOfStart);
|
|||
|
}
|
|||
|
int indexOfBreak = line.IndexOf("break=", initialBracket + 8);
|
|||
|
int endOfBreak = line.IndexOf(" ", indexOfBreak);
|
|||
|
if (endOfBreak == -1)
|
|||
|
{
|
|||
|
endOfBreak = line.IndexOf("]", indexOfBreak);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (indexOfStart != -1 && indexOfBreak != -1 && endOfBreak != -1 && endOfStart != -1)
|
|||
|
{
|
|||
|
string newLineToStart = line.Substring(indexOfStart + 6, endOfStart - (indexOfStart + 6));
|
|||
|
string newLineToBreak = line.Substring(indexOfBreak + 6, endOfBreak - (indexOfBreak + 6));
|
|||
|
|
|||
|
if (newLineToStart.Length > 0 && newLineToBreak.Length > 0)
|
|||
|
{
|
|||
|
changeToStart = newLineToStart;
|
|||
|
changeToBreak = newLineToBreak;
|
|||
|
|
|||
|
return line.Substring(0, initialBracket) +
|
|||
|
line.Substring(finalBracket + 1);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogWarning("There was a problem in your start=x or break=y. Check The spelling");
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogWarning("Found a [newtalk] variable in the text but it didn't had start=x or break=y. Check The spelling");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return line;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
|
|||
|
#region Save
|
|||
|
|
|||
|
private bool LookForSave(string line)
|
|||
|
{
|
|||
|
//check if the user have some newtalk and the line asks for one
|
|||
|
if (line.IndexOf("[save") != -1 && line.IndexOf("]") != -1)
|
|||
|
{
|
|||
|
//We do have one!
|
|||
|
int initialBracket = line.IndexOf("[save");
|
|||
|
int finalBracket = -1;
|
|||
|
if (initialBracket != -1)
|
|||
|
{
|
|||
|
finalBracket = line.IndexOf("]", initialBracket);
|
|||
|
}
|
|||
|
|
|||
|
//There still are any '[save' and it is before a ']'?
|
|||
|
if (initialBracket < finalBracket)
|
|||
|
{
|
|||
|
|
|||
|
//Everything fine until now. Now let's check the start and break variables
|
|||
|
int indexOfStart = line.IndexOf("start=", initialBracket + 5);
|
|||
|
int endOfStart = line.IndexOf(" ", indexOfStart);
|
|||
|
if (endOfStart == -1)
|
|||
|
{
|
|||
|
endOfStart = line.IndexOf("]", indexOfStart);
|
|||
|
}
|
|||
|
int indexOfBreak = line.IndexOf("break=", initialBracket + 5);
|
|||
|
int endOfBreak = line.IndexOf(" ", indexOfBreak);
|
|||
|
if (endOfBreak == -1)
|
|||
|
{
|
|||
|
endOfBreak = line.IndexOf("]", indexOfBreak);
|
|||
|
}
|
|||
|
int indexOfSavedData = line.IndexOf("data=", initialBracket + 5);
|
|||
|
int endOfSavedData = line.IndexOf(" ", indexOfSavedData);
|
|||
|
if (endOfSavedData == -1)
|
|||
|
{
|
|||
|
endOfSavedData = line.IndexOf("]", indexOfSavedData);
|
|||
|
}
|
|||
|
int indexOfModifier = line.IndexOf("mod=", initialBracket + 5);
|
|||
|
int endOfModifier = line.IndexOf(" ", indexOfModifier);
|
|||
|
if (endOfModifier == -1)
|
|||
|
{
|
|||
|
endOfModifier = line.IndexOf("]", indexOfModifier);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
if (indexOfStart != -1 && indexOfBreak != -1 && endOfBreak != -1 && endOfStart != -1
|
|||
|
&& indexOfSavedData != -1 && endOfSavedData != -1 && indexOfModifier != -1 && endOfModifier != -1)
|
|||
|
{
|
|||
|
string newLineToStart = line.Substring(indexOfStart + 6, endOfStart - (indexOfStart + 6));
|
|||
|
string newLineToBreak = line.Substring(indexOfBreak + 6, endOfBreak - (indexOfBreak + 6));
|
|||
|
string newSavedData = line.Substring(indexOfSavedData + 5, endOfSavedData - (indexOfSavedData + 5));
|
|||
|
string newModfier = line.Substring(indexOfModifier + 4, endOfModifier - (indexOfModifier + 4));
|
|||
|
int intModifier;
|
|||
|
|
|||
|
if (newLineToStart.Length > 0 && newLineToBreak.Length > 0 && newSavedData.Length > 0 && int.TryParse(newModfier, out intModifier))
|
|||
|
{
|
|||
|
saves.Add(new RPGtalkSaveStatement());
|
|||
|
saves[saves.Count - 1].lineToStart = newLineToStart;
|
|||
|
saves[saves.Count - 1].lineToBreak = newLineToBreak;
|
|||
|
saves[saves.Count - 1].savedData = newSavedData;
|
|||
|
saves[saves.Count - 1].modifier = intModifier;
|
|||
|
|
|||
|
return true;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogWarning("There was a problem in your save variables. Check The spelling");
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogWarning("Found a [save] variable in the text but it didn't had a variable. Check The spelling");
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return false;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
|
|||
|
#region jitter
|
|||
|
|
|||
|
private string LookForJitter(string line)
|
|||
|
{
|
|||
|
//check if the user have some jitter and the line asks for one
|
|||
|
if ((line.IndexOf("[jitter=") != -1 && line.IndexOf("]", line.IndexOf("[jitter=")) != -1))
|
|||
|
{
|
|||
|
bool thereAreJittersLeft = true;
|
|||
|
|
|||
|
//repeat as long as we find a jitter
|
|||
|
while (thereAreJittersLeft)
|
|||
|
{
|
|||
|
int initialBracket = line.IndexOf("[jitter=");
|
|||
|
int finalBracket = -1;
|
|||
|
int endOfJitter = -1;
|
|||
|
int spaceInJitter = -1;
|
|||
|
if (initialBracket != -1)
|
|||
|
{
|
|||
|
finalBracket = line.IndexOf("]", initialBracket);
|
|||
|
spaceInJitter = line.IndexOf(" ", initialBracket);
|
|||
|
}
|
|||
|
if (finalBracket != -1)
|
|||
|
{
|
|||
|
endOfJitter = line.IndexOf("[/jitter]", finalBracket);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//There still are any '[jitter=' and it is before a ']'?
|
|||
|
if (initialBracket < finalBracket)
|
|||
|
{
|
|||
|
if (endOfJitter == -1)
|
|||
|
{
|
|||
|
Debug.LogError("A [jitter] tag was found. But not a [/jitter] !");
|
|||
|
return line;
|
|||
|
}
|
|||
|
|
|||
|
//Ok, new jitter chang around! Let's get its number
|
|||
|
float jitterNum = 0;
|
|||
|
|
|||
|
int finalJitterNum = finalBracket;
|
|||
|
if(spaceInJitter != -1 && spaceInJitter < finalBracket)
|
|||
|
{
|
|||
|
finalJitterNum = spaceInJitter;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//Check if the number was a valid float
|
|||
|
if (float.TryParse(line.Substring(initialBracket + 8, finalJitterNum - (initialBracket + 8)), out jitterNum))
|
|||
|
{
|
|||
|
//Neat, we definely have a jitter with a valid number. Time to keep track of it
|
|||
|
RPGTalkJitter newJitter = new RPGTalkJitter();
|
|||
|
newJitter.jitter = Mathf.Abs(jitterNum);
|
|||
|
//subtract from the jitter position any rpgtalk tag that might have come before it
|
|||
|
newJitter.jitterPosition = initialBracket - RPGTalkHelper.CountRPGTalkTagCharacters(line.Substring(0, initialBracket));
|
|||
|
newJitter.lineWithJitter = rpgtalkElements.Count;
|
|||
|
|
|||
|
//ok, looking good! Now, we want to check if we have an angle set in this jitter tag.
|
|||
|
int anglePos = line.IndexOf("angle=", initialBracket);
|
|||
|
float angle = 1;
|
|||
|
if(anglePos != -1)
|
|||
|
{
|
|||
|
if (float.TryParse(line.Substring(anglePos + 6, finalBracket - (anglePos + 6)), out angle))
|
|||
|
{
|
|||
|
newJitter.angle = angle;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogWarning("Found a angle in a [jitter] variable but something is wrong with it. Check The spelling.");
|
|||
|
thereAreJittersLeft = false;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//let's look for the number of characters that should recieve the jitter
|
|||
|
newJitter.numberOfCharacters = endOfJitter - (finalBracket + 1);
|
|||
|
|
|||
|
jitters.Add(newJitter);
|
|||
|
|
|||
|
|
|||
|
//Looking good! We found out that a jitter should be there and we are already keeping track of it
|
|||
|
//But now we should remove the [jitter=X] from the line.
|
|||
|
line = line.Substring(0, initialBracket) +
|
|||
|
line.Substring(finalBracket + 1, endOfJitter - (finalBracket + 1)) +
|
|||
|
line.Substring(endOfJitter+9);
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogWarning("Found a [jitter=x] variable in the text but something is wrong with it. Check The spelling.");
|
|||
|
thereAreJittersLeft = false;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
thereAreJittersLeft = false;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return line;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
#endregion
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// Update is called once per frame
|
|||
|
void Update () {
|
|||
|
//We don't want to do nothing if the text isn't even showing
|
|||
|
if (!textUIObj.activeInHierarchy) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
if (textUI!= null && textUI.Enabled() &&
|
|||
|
currentChar >= rpgtalkElements [cutscenePosition - 1].dialogText.Length) {
|
|||
|
//if we hit the end of the talk, but we should stay on screen, return.
|
|||
|
//but if we have a callback, he can click on it once more.
|
|||
|
if (cutscenePosition >= rpgtalkElements.Count && shouldStayOnScreen) {
|
|||
|
if(lookForClick && (
|
|||
|
(passWithMouse && Input.GetMouseButtonDown (0)) ||
|
|||
|
(passWithInputButton != "" && Input.GetButtonDown(passWithInputButton)) || (passWithKey != KeyCode.None && Input.GetKeyDown(passWithKey))
|
|||
|
)){
|
|||
|
//if have an audio... playit
|
|||
|
if (passAudio != null && !rpgAudioSorce.isPlaying) {
|
|||
|
rpgAudioSorce.clip = passAudio;
|
|||
|
rpgAudioSorce.Play ();
|
|||
|
}
|
|||
|
if(callback.GetPersistentEventCount() > 0){
|
|||
|
callback.Invoke();
|
|||
|
}
|
|||
|
lookForClick = false;
|
|||
|
|
|||
|
//Let's call the endtalk methods
|
|||
|
if (OnEndTalk != null)
|
|||
|
OnEndTalk.Invoke();
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//if we reached the end of the line and click on the screen...
|
|||
|
if (
|
|||
|
enablePass && (
|
|||
|
(passWithMouse && Input.GetMouseButtonDown (0)) ||
|
|||
|
(passWithInputButton != "" && Input.GetButtonDown(passWithInputButton)) || (passWithKey != KeyCode.None && Input.GetKeyDown(passWithKey))
|
|||
|
|
|||
|
) ){//if have an audio... playit
|
|||
|
if (passAudio != null) {
|
|||
|
rpgAudioSorce.clip = passAudio;
|
|||
|
rpgAudioSorce.Play ();
|
|||
|
}
|
|||
|
textUI.Enabled(false);
|
|||
|
PlayNext ();
|
|||
|
|
|||
|
}
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//if we're currently showing dialog, then start scrolling it
|
|||
|
if(textUI.Enabled()) {
|
|||
|
// if there's still text left to show
|
|||
|
if(currentChar < rpgtalkElements[cutscenePosition - 1].dialogText.Length) {
|
|||
|
|
|||
|
//ensure that we don't accidentally blow past the end of the string
|
|||
|
currentChar = Mathf.Min(currentChar + actualTextSpeed * Time.deltaTime,
|
|||
|
rpgtalkElements[cutscenePosition - 1].dialogText.Length);
|
|||
|
|
|||
|
//Do what we have to do if the the text just ended
|
|||
|
if(currentChar >= rpgtalkElements[cutscenePosition - 1].dialogText.Length){
|
|||
|
TextEnded();
|
|||
|
}
|
|||
|
|
|||
|
//Get the current char and the text and put it into the U
|
|||
|
PutRightTextToShow ();
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if(enableQuickSkip == true &&
|
|||
|
(
|
|||
|
(passWithMouse && Input.GetMouseButtonDown (0)) ||
|
|||
|
(passWithInputButton != "" && Input.GetButtonDown(passWithInputButton)) || (passWithKey != KeyCode.None && Input.GetKeyDown(passWithKey))
|
|||
|
&& currentChar > 3)) {
|
|||
|
|
|||
|
currentChar = rpgtalkElements[cutscenePosition - 1].dialogText.Length;
|
|||
|
PutRightTextToShow ();
|
|||
|
//Do what we have to do if the the text just ended
|
|||
|
TextEnded();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//The text just ended to be written on the screen
|
|||
|
void TextEnded(){
|
|||
|
|
|||
|
isAnimating = false;
|
|||
|
|
|||
|
//If we want the talk to auto pass... Auto pass it
|
|||
|
if (autoPass)
|
|||
|
{
|
|||
|
Invoke("AutoPass", secondsAutoPass);
|
|||
|
}
|
|||
|
|
|||
|
//call the event
|
|||
|
if (OnEndAnimating != null)
|
|||
|
{
|
|||
|
OnEndAnimating();
|
|||
|
}
|
|||
|
|
|||
|
//if we have an animator.. stop it
|
|||
|
if (actualAnimator != null) {
|
|||
|
actualAnimator.SetBool (animatorBooleanName, false);
|
|||
|
}
|
|||
|
|
|||
|
//Check if this text was a question
|
|||
|
if (questions.Count > 0) {
|
|||
|
if (choicePrefab == null) {
|
|||
|
Debug.LogError ("There was a question here, but no object was set in choicePrefab to be the answer");
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
foreach (RPGTalkQuestion q in questions) {
|
|||
|
if(q.lineWithQuestion == cutscenePosition-1 && !q.alreadyHappen){
|
|||
|
//This line was a question! Put the correct answers here
|
|||
|
enablePass = false;
|
|||
|
|
|||
|
for (int i = 0; i < q.choices.Count; i++) {
|
|||
|
GameObject newChoice = (GameObject)Instantiate (choicePrefab, choicesParent);
|
|||
|
Button newChoiceBtn = newChoice.GetComponent<Button>();
|
|||
|
if (newChoiceBtn) {
|
|||
|
string thisText = q.choices[i];
|
|||
|
string correctText = thisText;
|
|||
|
//make sure we will not want to make it to a new talk
|
|||
|
correctText = LookForNewTalk(correctText);
|
|||
|
|
|||
|
newChoice.GetComponentInChildren<Text> ().text = correctText;
|
|||
|
int choiceNumber = i;
|
|||
|
newChoiceBtn.onClick.AddListener (delegate{MadeAChoice (q.questionID, choiceNumber, thisText);});
|
|||
|
if (i == 0) {
|
|||
|
StartCoroutine(SelectButton(newChoiceBtn));
|
|||
|
}
|
|||
|
} else {
|
|||
|
Debug.LogWarning ("RPGTalk can only put the choice's text correctly if choicePrefab is a button with a child of type Text.");
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IEnumerator SelectButton(Button button)
|
|||
|
{
|
|||
|
yield return new WaitForSeconds(0.3f);
|
|||
|
button.Select();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
void AutoPass()
|
|||
|
{
|
|||
|
PlayNext();
|
|||
|
}
|
|||
|
|
|||
|
//When we make a choice, we don't want it to skip the next line, so we will wait to the end of the frame
|
|||
|
//so that the events will be resetted by then
|
|||
|
IEnumerator ReenableSkip(bool originalSkip)
|
|||
|
{
|
|||
|
enableQuickSkip = false;
|
|||
|
yield return new WaitForEndOfFrame();
|
|||
|
enableQuickSkip = originalSkip;
|
|||
|
}
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Function to be called by the buttons when the user makes a choice.
|
|||
|
/// This passes the talk and call the OnMadeChoice event
|
|||
|
/// </summary>
|
|||
|
public void MadeAChoice(string questionID, int choiceNumber, string text){
|
|||
|
foreach (RPGTalkQuestion q in questions) {
|
|||
|
if (q.questionID == questionID) {
|
|||
|
q.alreadyHappen = true;
|
|||
|
LookForNewTalk(text);
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
enablePass = true;
|
|||
|
|
|||
|
StartCoroutine(ReenableSkip(enableQuickSkip));
|
|||
|
|
|||
|
PlayNext ();
|
|||
|
if (OnMadeChoice != null) {
|
|||
|
OnMadeChoice (questionID, choiceNumber);
|
|||
|
}
|
|||
|
//delete all the buttons (and other childs) in the buttons parent
|
|||
|
foreach (Transform child in choicesParent) {
|
|||
|
Destroy (child.gameObject);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Given the current character, look for variables or rich text and put everything in the textUI.
|
|||
|
/// </summary>
|
|||
|
public void PutRightTextToShow(){
|
|||
|
//ensure that we don't accidentally blow past the end of the string
|
|||
|
currentChar = Mathf.Min(currentChar, rpgtalkElements[cutscenePosition - 1].dialogText.Length);
|
|||
|
|
|||
|
//Did we reach the point where a speed should be changed?
|
|||
|
for (int i = 0; i < speeds.Count; i++) {
|
|||
|
if (speeds [i].speedPosition <= currentChar && !speeds [i].alreadyGone && speeds [i].lineWithSpeed == cutscenePosition - 1) {
|
|||
|
//Change the actual speed of the text. if it is 0 or below, change back to the default
|
|||
|
actualTextSpeed = speeds [i].speed;
|
|||
|
if (actualTextSpeed <= 0) {
|
|||
|
actualTextSpeed = textSpeed;
|
|||
|
}
|
|||
|
speeds [i].alreadyGone = true;
|
|||
|
|
|||
|
//Change currentChar to the position of the [speed] tag so no word gets jumped
|
|||
|
currentChar = speeds [i].speedPosition;
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//Did we reach the point where a jitter should happen?
|
|||
|
for (int i = 0; i < jitters.Count; i++)
|
|||
|
{
|
|||
|
if (jitters[i].jitterPosition <= currentChar && !jitters[i].alreadyGone && jitters[i].lineWithJitter == cutscenePosition - 1)
|
|||
|
{
|
|||
|
jitterRoutine = StartCoroutine(textUI.Jitter(jitters[i]));
|
|||
|
|
|||
|
jitters[i].alreadyGone = true;
|
|||
|
|
|||
|
//Change currentChar to the position of the [speed] tag so no word gets jumped
|
|||
|
currentChar = jitters[i].jitterPosition;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//Check if a line break is starting to appear
|
|||
|
if (rpgtalkElements[cutscenePosition - 1].dialogText.Length > currentChar + 2 &&
|
|||
|
rpgtalkElements[cutscenePosition - 1].dialogText.Substring((int)currentChar, 2) == "\\n")
|
|||
|
{
|
|||
|
currentChar += 2;
|
|||
|
}
|
|||
|
|
|||
|
//Select the right text to display
|
|||
|
string textToDisplay = rpgtalkElements[cutscenePosition - 1].dialogText.Substring(0, (int)currentChar);
|
|||
|
|
|||
|
//Check if there should be any rich text text beggining or ending at this position
|
|||
|
//Check from the bottom, because a tag might have been openned inside another
|
|||
|
for (int i = richText.Count-1; i >= 0; i--) {
|
|||
|
if (richText[i].lineWithTheRichText != cutscenePosition - 1 || currentChar < richText [i].initialTagPosition) {
|
|||
|
continue;
|
|||
|
}
|
|||
|
string beforeTextToDisplay = textToDisplay;
|
|||
|
textToDisplay = textToDisplay.Substring (0,richText [i].initialTagPosition);
|
|||
|
textToDisplay += richText [i].initialTag;
|
|||
|
|
|||
|
if (currentChar + richText [i].initialTag.Length >= richText [i].finalTagPosition) {
|
|||
|
textToDisplay += beforeTextToDisplay.Substring (richText [i].initialTagPosition,
|
|||
|
richText [i].finalTagPosition-richText [i].initialTagPosition-richText [i].initialTag.Length);
|
|||
|
textToDisplay += richText [i].finalTag;
|
|||
|
textToDisplay += beforeTextToDisplay.Substring (richText[i].finalTagPosition - richText[i].initialTag.Length);
|
|||
|
} else {
|
|||
|
textToDisplay += beforeTextToDisplay.Substring(richText [i].initialTagPosition);
|
|||
|
textToDisplay += richText [i].finalTag;
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
textToDisplay = textToDisplay.Replace("\\n","\n");
|
|||
|
|
|||
|
|
|||
|
//if have an audio... playit
|
|||
|
if (textAudio != null && !rpgAudioSorce.isPlaying)
|
|||
|
{
|
|||
|
if (textToDisplay.Length > textUI.GetCurrentText().Length)
|
|||
|
{
|
|||
|
rpgAudioSorce.clip = textAudio;
|
|||
|
rpgAudioSorce.Play();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//Put the text in the UI
|
|||
|
textUI.ChangeTextTo(textToDisplay);
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//Keep the amount of rich text in the string so we can count them later on
|
|||
|
int RichTextUntilNow = RPGTalkHelper.CountRichTextCharacters(textToDisplay);
|
|||
|
|
|||
|
//Did we reach the point where an image should be shown?
|
|||
|
for (int i = 0; i < spritesUsed.Count; i++) {
|
|||
|
if (spritesUsed [i].spritePosition <= currentChar + RichTextUntilNow && !spritesUsed [i].alreadyInPlace && spritesUsed[i].lineWithSprite == cutscenePosition - 1) {
|
|||
|
spritesUsed [i].alreadyInPlace = textUIObj.GetComponent<ITextWithIcon> ().FitImagesOnText (i);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//check again, at the end of the text, if there should be any images on it
|
|||
|
if (currentChar >= rpgtalkElements [cutscenePosition - 1].dialogText.Length) {
|
|||
|
for (int i = 0; i < spritesUsed.Count; i++) {
|
|||
|
if (spritesUsed[i].lineWithSprite == cutscenePosition - 1 && !spritesUsed[i].alreadyInPlace) {
|
|||
|
StartCoroutine (TryToPutImageOnText (i));
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
void CheckIfTheTextFits(string line){
|
|||
|
|
|||
|
int maxCharsOnUI = maxCharInWidth * maxCharInHeight;
|
|||
|
if (line.Length > maxCharsOnUI) {
|
|||
|
|
|||
|
//how many talks would be necessary to fit this text?
|
|||
|
int howMuchMore = Mathf.CeilToInt((float)line.Length / (float)maxCharsOnUI);
|
|||
|
string newLine = "";
|
|||
|
int cuttedInSpace = -1;
|
|||
|
|
|||
|
for (int i = 0; i < howMuchMore; i++) {
|
|||
|
//get the characeter that we should start saying
|
|||
|
int startChar = i * maxCharsOnUI;
|
|||
|
if(cuttedInSpace != -1){
|
|||
|
startChar = cuttedInSpace;
|
|||
|
cuttedInSpace = -1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//if the new line fits the talk...
|
|||
|
if (line.Substring (startChar,
|
|||
|
line.Length - (startChar)).Length < maxCharsOnUI) {
|
|||
|
newLine = line.Substring (startChar,
|
|||
|
line.Length - (startChar));
|
|||
|
} else {
|
|||
|
//if it not, search for spaces near to the last word and cut it
|
|||
|
cuttedInSpace = line.IndexOf (" ", startChar+ (maxCharsOnUI - 20));
|
|||
|
if(cuttedInSpace != -1){
|
|||
|
newLine = line.Substring (startChar, cuttedInSpace-startChar);
|
|||
|
}else{
|
|||
|
newLine = line.Substring (startChar, maxCharsOnUI);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
rpgtalkElements.Add (readSceneElement (newLine));
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
rpgtalkElements.Add (readSceneElement (line));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Finish the talk, skipping every dialog. The callback function will still going to be called
|
|||
|
/// </summary>
|
|||
|
/// <param name="jumpQuestions">If set to <c>true</c> goes to the end of the talk, even if there were questions between it</param>
|
|||
|
public void EndTalk(bool jumpQuestions = false) {
|
|||
|
if (textUIObj && textUIObj.activeInHierarchy) {
|
|||
|
cutscenePosition = rpgtalkElements.Count - 1;
|
|||
|
if (!shouldStayOnScreen) {
|
|||
|
CleanDirtySprites ();
|
|||
|
cutscenePosition = rpgtalkElements.Count;
|
|||
|
}
|
|||
|
|
|||
|
if(!jumpQuestions){
|
|||
|
//Look for questions that hasn't already happen
|
|||
|
foreach(RPGTalkQuestion q in questions){
|
|||
|
if (!q.alreadyHappen) {
|
|||
|
cutscenePosition = q.lineWithQuestion;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
PlayNext (true);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
/// <summary>
|
|||
|
/// Plays the next dialog in the current Talk.
|
|||
|
/// </summary>
|
|||
|
/// <param name="forcePlay">If set to <c>true</c> play the next talk in line even if "enablePass" is false.</param>
|
|||
|
public void PlayNext(bool forcePlay = false) {
|
|||
|
if (!enablePass && !forcePlay) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//We won't be able to pass if you have to answer a question
|
|||
|
foreach (RPGTalkQuestion q in questions)
|
|||
|
{
|
|||
|
if (q.lineWithQuestion == cutscenePosition - 1 && !q.alreadyHappen)
|
|||
|
{
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//call the event
|
|||
|
if (OnPlayNext != null){
|
|||
|
OnPlayNext ();
|
|||
|
}
|
|||
|
|
|||
|
//If we had auto pass, cancel it
|
|||
|
if (autoPass)
|
|||
|
{
|
|||
|
CancelInvoke("AutoPass");
|
|||
|
}
|
|||
|
|
|||
|
//stop any dubs
|
|||
|
if (dubSounds != null) {
|
|||
|
dubSounds.StopCurrentDub ();
|
|||
|
}
|
|||
|
|
|||
|
//Let's set to false any expression left
|
|||
|
if(actualAnimator != null)
|
|||
|
{
|
|||
|
if (expressing != null && !string.IsNullOrEmpty(expressing.boolInAnimator))
|
|||
|
{
|
|||
|
actualAnimator.SetBool(expressing.boolInAnimator, false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
//if there was any jitter here.. Let's remove it!
|
|||
|
if (jitterRoutine != null)
|
|||
|
{
|
|||
|
StopCoroutine(jitterRoutine);
|
|||
|
jitterRoutine = null;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// increment the cutscene counter
|
|||
|
cutscenePosition++;
|
|||
|
currentChar = 0;
|
|||
|
|
|||
|
//if there was any sprite in the text, deactivate it
|
|||
|
if (spritesUsed.Count > 0) {
|
|||
|
foreach (Transform child in textUIObj.transform) {
|
|||
|
child.gameObject.SetActive (false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if(cutscenePosition <= rpgtalkElements.Count) {
|
|||
|
|
|||
|
textUI.Enabled(true);
|
|||
|
|
|||
|
RpgtalkElement currentRpgtalkElement = rpgtalkElements[cutscenePosition - 1];
|
|||
|
|
|||
|
if (dialoger) {
|
|||
|
if (dialogerObj) {
|
|||
|
dialogerUI.Enabled(true);
|
|||
|
|
|||
|
dialogerUI.ChangeTextTo(currentRpgtalkElement.speakerName);
|
|||
|
}
|
|||
|
if (shouldUsePhotos) {
|
|||
|
for (int i = 0; i < characters.Length; i++) {
|
|||
|
if (characters [i].character.dialoger == currentRpgtalkElement.originalSpeakerName) {
|
|||
|
if (UIPhoto) {
|
|||
|
UIPhoto.sprite = characters [i].character.photo;
|
|||
|
}
|
|||
|
//Change its animator
|
|||
|
if (characters[i].animatorOverwrite != null)
|
|||
|
{
|
|||
|
actualAnimator = characters[i].animatorOverwrite;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
actualAnimator = animatorWhenTalking;
|
|||
|
}
|
|||
|
if (actualAnimator && animatorIntName != ""){
|
|||
|
actualAnimator.SetInteger (animatorIntName, i);
|
|||
|
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CheckWhoToFollow (currentRpgtalkElement);
|
|||
|
|
|||
|
|
|||
|
//check if there should be any dubs in this line
|
|||
|
CheckDubsInThisLine();
|
|||
|
|
|||
|
//check if we are expressing something
|
|||
|
expressing = IsExpressing(rpgtalkElements[cutscenePosition - 1]);
|
|||
|
|
|||
|
//if we have an animator.. play it
|
|||
|
PlayAnimator(rpgtalkElements[cutscenePosition - 1]);
|
|||
|
|
|||
|
//check if after this line we should start another talk
|
|||
|
currentRpgtalkElement.dialogText = LookForNewTalk(currentRpgtalkElement.dialogText);
|
|||
|
|
|||
|
|
|||
|
} else {
|
|||
|
//The talk has finished
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//check if we are supposed to be playing another talk
|
|||
|
if (!string.IsNullOrEmpty(changeToStart) && !string.IsNullOrEmpty(changeToBreak))
|
|||
|
{
|
|||
|
NewTalk(changeToStart, changeToBreak);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//check if we are saved something that should take me to another talk
|
|||
|
foreach(RPGtalkSaveStatement saved in saves)
|
|||
|
{
|
|||
|
if(saveInstance != null)
|
|||
|
{
|
|||
|
if (saveInstance.GetSavedData(saved.savedData, saved.modifier))
|
|||
|
{
|
|||
|
NewTalk(saved.lineToStart, saved.lineToBreak);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
Debug.LogWarning("Found a save but you didn't had the RPGTalkSaveInstance on this object");
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
//call the event
|
|||
|
if (OnEndTalk != null){
|
|||
|
OnEndTalk ();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
StartCoroutine(GoBackIsPlaying());
|
|||
|
|
|||
|
if (!shouldStayOnScreen) {
|
|||
|
textUI.Enabled(false);
|
|||
|
if (dialoger) {
|
|||
|
if (dialogerObj) {
|
|||
|
dialogerUI.Enabled(false);
|
|||
|
}
|
|||
|
}
|
|||
|
for (int i = 0; i < showWithDialog.Length; i++) {
|
|||
|
showWithDialog [i].SetActive (false);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
callback.Invoke();
|
|||
|
|
|||
|
|
|||
|
//if we want to go back to the original talk lines
|
|||
|
if (goBackToOriginalStartAndBreak)
|
|||
|
{
|
|||
|
lineToStart = originalLineToStart;
|
|||
|
lineToBreak = originalLineToBreak;
|
|||
|
originalLineToStart = "";
|
|||
|
originalLineToBreak = "";
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
//Wait a frame to make the isPlaying false, so we try not to play a area and pass a talk at the same time
|
|||
|
IEnumerator GoBackIsPlaying()
|
|||
|
{
|
|||
|
yield return new WaitForEndOfFrame();
|
|||
|
isPlaying = false;
|
|||
|
}
|
|||
|
|
|||
|
//This function play the right bools on the animator.
|
|||
|
void PlayAnimator(RpgtalkElement whoIsPlaying)
|
|||
|
{
|
|||
|
if (actualAnimator != null)
|
|||
|
{
|
|||
|
if (expressing != null)
|
|||
|
{
|
|||
|
actualAnimator.SetBool(expressing.boolInAnimator, true);
|
|||
|
}
|
|||
|
|
|||
|
actualAnimator.SetBool(animatorBooleanName, true);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
void CheckWhoToFollow(RpgtalkElement element){
|
|||
|
//Set it to follow someone
|
|||
|
//resets anyone that is being followed
|
|||
|
following = null;
|
|||
|
followingOffset = Vector3.zero;
|
|||
|
if (shouldFollow && characters.Length > 0) {
|
|||
|
foreach (RPGTalkCharacterSettings character in characters) {
|
|||
|
//if the character in the follow array have the same name as the talker or an empty name, follow it!
|
|||
|
if(character.character.dialoger == element.speakerName ||
|
|||
|
character.character.dialoger == ""){
|
|||
|
following = character.follow;
|
|||
|
followingOffset = character.followOffset;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
}
|