Exemple #1
0
 virtual public void Initialize()
 {
     Node = null;
     if (null != Elements)
     {
         Elements.Clear();
     }
     Elements = new List <JossData>();
 }
Exemple #2
0
    virtual public bool PositionAtFirstNodeOfType(string data_type)
    {
        JossData result = GetFirstNodeOfType(data_type);

        if (null != result)
        {
            PositionAtNode(result.ID);
        }

        return(null != result);
    }
Exemple #3
0
 virtual public bool PositionAtLastNode()
 {
     if (Count > 0)
     {
         Node = Elements[Count - 1];
     }
     else
     {
         return(false);
     }
     return(true);
 }
Exemple #4
0
    // MAIN public void 2 ////////////////////////////////////
    // SPEAK /////////////////////////////////////////////////
    // Speak does all the work. It selects the dialogue turns to display,
    // it tests for redirection, automatically redirects if need be, modifies game keys
    // if required and sends messages to the parent object if instructed to do so by
    // the dialogue file. Once the file is loaded, programmers need only call SPEAK
    // until the dialogue has ended and it will do the rest.
    // It selects subsets of dialogue text from large turns and continues on to the next
    // turn when required.

    string[] LocalizedText(JossData turn)
    {
        string[] results;
        if (Language == string.Empty || turn.String(Language) == string.Empty)
        {
            results = new string[turn.data.Count];
            turn.data.CopyTo(results);
        }
        else
        {
            results = turn.String(Language).Replace("\\n", '\n' + string.Empty).Split('\n');
        }
        return(results);
    }
Exemple #5
0
    virtual public bool CopyNode(JossData existing)
    {
        JossData copy = existing.Copy();

        Elements.Add(new JossData(Elements.Count));
        Last.data_type = copy.data_type;
        foreach (var key in copy.defined)
        {
            Last.defined.Add(key.Key, key.Value);
        }
        foreach (string s in copy.data)
        {
            Last.data.Add(s);
        }
        return(true);
    }
Exemple #6
0
    virtual public bool PositionAtID(int id)
    {
        if (id < 0)
        {
            return(false);
        }

        int index = FindNodeByID(id);

        if (index == -1)
        {
            return(false);
        }

        Node = Elements[index];
        return(true);
    }
Exemple #7
0
    //Josscript files are supposed to have sequential nodes but since the
    //data author can override this, do a test to see if the specified
    //node is the one requested or else go look for it.
    //Since a single file can have multiple sections and each section
    //could start it's index from 1, you can also provide the section's
    //node index as an offset from which to start searching
    virtual public bool PositionAtNode(int index, int offset = 0)
    {
        if (index + offset < 0 || index + offset >= Count)
        {
            return(false);
        }

        if (Elements [index + offset].ID == index)
        {
            Node = Elements[index + offset];
            return(true);
        }

        int found_index = FindNodeByID(index, offset);

        if (found_index < 0)
        {
            return(false);
        }
        Node = Elements[found_index];
        return(true);
    }
Exemple #8
0
    virtual public bool RemoveNode(int index)
    {
        if (Count <= index)
        {
            return(false);
        }

        //position Node on the next node
        if (null != Node)
        {
            if (Node.ID == index)
            {
                if (index < Count - 1)
                {
                    Node = Elements[index + 1];
                }
                else
                //if there isn't a "next" node, first check if there is a "previous" node
                //and go there if there is...
                if (Node.ID == 0)
                {
                    Node = null;
                }
                else
                {
                    Node = Elements[index - 1];
                }
            }
        }

        Elements.RemoveAt(index);
        for (int i = index; i < Elements.Count; i++)
        {
            Elements[i].ID = i;
        }

        return(true);
    }
Exemple #9
0
    virtual public bool InsertNode(int index, string data_type, string add_data = "")
    {
        if (data_type == string.Empty)
        {
            return(false);
        }

        if (Count == 0)
        {
            return(AddNode(data_type, add_data));
        }

        Elements.Insert(index, new JossData(index));
        Node           = Elements[index];
        Node.data_type = data_type;
        Node.ProcessCombinedFields(add_data);
        for (int i = index + 1; i < Count; i++)
        {
            Elements[i].ID = i;
        }

        return(true);
    }
Exemple #10
0
    virtual public JossData Copy(JossCopyMode mode = JossCopyMode.no_id, string id_value = "-1")
    {
        JossData result = new JossData();

        result.data_type = this.data_type;
        foreach (var data in this.defined)
        {
            if (data.Key != "id")
            {
                result.Set(data.Key, data.Value);
            }
            else
            {
                switch (mode)
                {
                //keep the original id....
                case JossCopyMode.old_id:
                    result.Set("id", data.Value);
                    break;

                case JossCopyMode.new_id:
                    result.Set("id", id_value);
                    break;

                case JossCopyMode.no_id:
                    result.Remove("id");
                    break;
                }
            }
        }
        foreach (string s in this.data)
        {
            result.data.Add(s);
        }

        return(result);
    }
    //Josscript files are supposed to have sequential nodes but since the
    //data author can override this, do a test to see if the specified
    //node is the one requested or else go look for it.
    //Since a single file can have multiple sections and each section
    //could start it's index from 1, you can also provide the section's
    //node index as an offset from which to start searching
    public virtual bool PositionAtNode(int index, int offset = 0)
    {
        if (index + offset < 0 || index + offset >= Count)
            return false;

        if (Elements [ index + offset ].ID == index) {
            Node = Elements[index + offset];
            return true;
        }

        int found_index = FindNodeByID(index, offset);
        if (found_index < 0)
            return false;
        Node = Elements[ found_index ];
        return true;
    }
 public virtual bool PositionAtLastNode()
 {
     if (Count > 0)
         Node = Elements[Count-1];
     else
         return false;
     return true;
 }
    public virtual bool PositionAtID(int id)
    {
        if (id < 0)
            return false;

        int index = FindNodeByID(id);
        if (index == -1)
            return false;

        Node = Elements[index];
        return true;
    }
    public virtual bool ParseFile(string data)
    {
        if (data == string.Empty)
            return false;

        Initialize();
        string[] lines = data.Split('\n');
        foreach (string line in lines) {
            //find and remove comments from the parser
            //take into account URL strings '://'
            string l = line;
            int commentIndex = l.IndexOf("//");
            if (commentIndex > -1) {
                int urlIndex = 0;
                if (commentIndex > 0) {
                    while (l.IndexOf("://", urlIndex) == commentIndex - 1) {
                        urlIndex = commentIndex + 1;
                        commentIndex = l.IndexOf("//", urlIndex);
                    }
                }

                if (commentIndex > -1)
                    l = l.Substring(0, commentIndex);
            }
            l = l.Trim();

            //if the line is empty, do nothing with it...
            //ignore lines that close off data blocks...
            if (l == string.Empty //|| l.IndexOf("</") == 0
            ) continue;

            //be default add data to the last datatype created
            int tagopen = l.IndexOf('[');
            int tagclose = l.IndexOf(']');
            if (tagclose > 1 && tagopen == 0) {
                if (null != Last) {
                    string keyname = l.Substring(1, tagclose - 1).Trim();
                    l = l.Substring(tagclose+1, l.Length - (tagclose + 1) ).Trim();
                    if (null != Last) {
                        Last.Set(keyname, l);
                    }
                }
            } else {
            //check to see if this is the definition of a new datatype's constant fields
            //or whether this is new data for a particular datatype
                tagopen = l.IndexOf('<');
                tagclose = l.IndexOf('>');
                if (tagclose > 1 && tagopen == 0) {
                    string key_name = l.Substring(1, tagclose - 1).Trim();
                    string key_value = l.Substring(tagclose+1, l.Length - (tagclose + 1) );
                    AddNode(key_name, key_value);
                } else {
                    //else add it as raw data...
                    if (null != Last)
                        Last.AddToData(l);
                }
            }
        }

        Node = (Count > 0) ? First : null;
        return true;
    }
    public virtual bool InsertNode(int index, string data_type, string add_data="")
    {
        if (data_type == string.Empty)
            return false;

        if (Count == 0)
            return AddNode(data_type, add_data);

        Elements.Insert(index, new JossData( index ) );
        Node = Elements[index];
        Node.data_type = data_type;
        Node.ProcessCombinedFields(add_data);
        for (int i = index + 1; i < Count; i++)
            Elements[i].ID = i;

        return true;
    }
 public virtual void Initialize()
 {
     Node = null;
     if (null != Elements)
         Elements.Clear();
     Elements	= new List<JossData>();
 }
 public virtual bool CopyNode(JossData existing)
 {
     JossData copy = existing.Copy();
     Elements.Add( new JossData( Elements.Count ) );
     Last.data_type = copy.data_type;
     foreach (var key in copy.defined)
         Last.defined.Add(key.Key, key.Value);
     foreach (string s in copy.data)
         Last.data.Add(s);
     return true;
 }
Exemple #18
0
    virtual public bool ParseFile(string data)
    {
        if (data == string.Empty)
        {
            return(false);
        }

        Initialize();
        string[] lines = data.Split('\n');
        foreach (string line in lines)
        {
            //find and remove comments from the parser
            //take into account URL strings '://'
            string l            = line;
            int    commentIndex = l.IndexOf("//");
            if (commentIndex > -1)
            {
                int urlIndex = 0;
                if (commentIndex > 0)
                {
                    while (l.IndexOf("://", urlIndex) == commentIndex - 1)
                    {
                        urlIndex     = commentIndex + 1;
                        commentIndex = l.IndexOf("//", urlIndex);
                    }
                }

                if (commentIndex > -1)
                {
                    l = l.Substring(0, commentIndex);
                }
            }
            l = l.Trim();

            //if the line is empty, do nothing with it...
            //ignore lines that close off data blocks...
            if (l == string.Empty             //|| l.IndexOf("</") == 0
                )
            {
                continue;
            }

            //be default add data to the last datatype created
            int tagopen  = l.IndexOf('[');
            int tagclose = l.IndexOf(']');
            if (tagclose > 1 && tagopen == 0)
            {
                if (null != Last)
                {
                    string keyname = l.Substring(1, tagclose - 1).Trim();
                    l = l.Substring(tagclose + 1, l.Length - (tagclose + 1)).Trim();
                    if (null != Last)
                    {
                        Last.Set(keyname, l);
                    }
                }
            }
            else
            {
                //check to see if this is the definition of a new datatype's constant fields
                //or whether this is new data for a particular datatype
                tagopen  = l.IndexOf('<');
                tagclose = l.IndexOf('>');
                if (tagclose > 1 && tagopen == 0)
                {
                    string key_name  = l.Substring(1, tagclose - 1).Trim();
                    string key_value = l.Substring(tagclose + 1, l.Length - (tagclose + 1));
                    AddNode(key_name, key_value);
                }
                else
                {
                    //else add it as raw data...
                    if (null != Last)
                    {
                        Last.AddToData(l);
                    }
                }
            }
        }

        Node = (Count > 0) ? First : null;
        return(true);
    }
    public virtual bool RemoveNode(int index)
    {
        if ( Count <= index)
            return false;

        //position Node on the next node
        if (null != Node)
        if (Node.ID == index)
            if (index < Count - 1)
                Node = Elements[index + 1];
            else
                //if there isn't a "next" node, first check if there is a "previous" node
                //and go there if there is...
                if (Node.ID == 0)
                    Node = null;
                else
                    Node = Elements[index - 1];

        Elements.RemoveAt(index);
        for (int i = index; i < Elements.Count; i++)
            Elements[i].ID = i;

        return true;
    }
    public virtual JossData Copy(JossCopyMode mode = JossCopyMode.no_id, string id_value="-1")
    {
        JossData result = new JossData();
        result.data_type = this.data_type;
        foreach (var data in this.defined) {
            if (data.Key != "id") {
                result.Set(data.Key, data.Value);
            } else {
                switch (mode) {
                    //keep the original id....
                 	case JossCopyMode.old_id:
                        result.Set("id", data.Value);
                        break;

                    case JossCopyMode.new_id :
                        result.Set("id", id_value);
                        break;

                    case JossCopyMode.no_id :
                        result.Remove("id");
                        break;
                }
            }
        }
        foreach (string s in this.data)
            result.data.Add(s);

        return result;
    }
    public int Speak(int id, bool triggerEvent)
    {
        if (triggerEvent) {
            event_data[1].defined.Clear();
            _OnSpeakStart(new DlgEvent(event_data), this);
        }
        JossData tempLine;

        int continueFrom = 0;
        string[] this_turn;

        try {
            //if we are instructed to quit the conversation
            if (id == -1) {
                EndDialogue ();
                return -1;
            }

            //if no line number is provided, try and determine the line
            if (id == -3) {
                //first see if we were reading something the previous turn...
                if (currentlySpeakingLine > -1) {
                    id = currentlySpeakingLine;
                } else {
                    //if not, then assign to first ID
                    tempLine = lines [0];
                    id = tempLine.ID;
                }

                //if we are currently speaking the same line as before, continue to show this id
                if (id == currentlySpeakingLine) {
                    this_turn = LocalizedText(currentLine);
                    startAtIncrement++;
                    if (this_turn.Length > (startAtIncrement * maxLinesToShow)) {
                        continueFrom = id;
                        mustProcessReq = false;
                    } else {
                        continueFrom = IDOfNextLine;
                        startAtIncrement = 0;
                        mustProcessReq = true;
                    }
                } else {
                    //if not, first determine wether we were speaking at all
                    // and if we were, check what comes next...
                    mustProcessReq = true;
                    startAtIncrement = 0;
                    continueFrom = (currentlySpeakingLine == -1) ? lines[0].ID : IDOfNextLine;
                }
            } else {	// if ID was given to us, we HAVE to use it
                // if it is the same as the last ID
                if (id == currentlySpeakingLine) {		//if there is nothing more to read here... This is an error... NAH!!!
                    this_turn = LocalizedText(currentLine);
                    if (this_turn.Length > (startAtIncrement * maxLinesToShow)) {
                        startAtIncrement++;
                        mustProcessReq = false;
                    } else {
                        Debug.LogError ("User provided line number with no dialogue to show: " + id);
                        EndDialogue ();
                        return -1;
                    }
                } else {
                    //if it is a new ID
                    startAtIncrement = 0;
                    mustProcessReq = true;
                }
                continueFrom = id;
            }

            //see if we are being redirected to an end of conversation
            if (continueFrom == -1) {
                EndDialogue ();
                return -1;
            }

            //now that we know what we want, let's go get it...
            if (continueFrom != currentlySpeakingLine) {
                selection = 0;

                currentLine = Find (continueFrom);
                currentlySpeakingLine = currentLine.ID;
                startAtIncrement = 0;
            }

            //first process the requirements for this id
            if (GameKeys != null && mustProcessReq && ( currentLine.defined.ContainsKey("require") )) {
                string[] requirements = TextReplace(currentLine["require"]).Split(',');
                for (int x= 0; x < requirements.Length; x++) {
                    string[] tempReq = requirements[x].Trim().Split(' ');
                    if (tempReq.Length != 4)
                        throw new InvalidReqDefinitionException(currentLine.ID +":"+ requirements[x]);

                    switch (tempReq[0]) {
                    case "+":
                        if (GameKeys.DoesNotHave ( tempReq[1], int.Parse(tempReq[2]) ) ) {
                            Speak ( int.Parse( tempReq[3] ), false );
                            return 0;
                        }
                        break;

                    case "-":
                        if (GameKeys.DoesHave ( tempReq[1], int.Parse(tempReq[2]) )) {
                            Speak ( int.Parse( tempReq[3] ), false );
                            return 0;
                        }
                        break;

                    case "=":
                        if (!GameKeys.HasExactly ( tempReq[1], int.Parse(tempReq[2]) )) {
                            Speak ( int.Parse( tempReq[3] ), false );
                            return 0;
                        }
                        break;
                    }
                }
            }

            //now modify the keys
            if (GameKeys != null && mustProcessReq && (currentLine.defined.ContainsKey("keys"))) {
                string[] keys = TextReplace(currentLine["keys"]).Split(',');
                for (int x= 0; x < keys.Length; x++) {
                    string[] tempKey = keys[x].Trim().Split(' ');
                    if (tempKey.Length != 3)
                        throw new InvalidKeyDefinitionException(currentLine.ID +":"+ keys[x]);

                    switch ( tempKey[0] ) {
                    case "+":
                        GameKeys.Add ( tempKey[1], float.Parse (tempKey[2]) );
                        break;
                    case "-":
                        GameKeys.Subtract ( tempKey[1], float.Parse (tempKey[2]) );
                        break;
                    case "=":
                        GameKeys.Setf ( tempKey[1], float.Parse (tempKey[2]) );
                        break;
                    default:
                        parent.BroadcastMessage ("ProcessKeys", keys[x].Trim(), SendMessageOptions.DontRequireReceiver);
                        break;
                    }
                }
            }

            continueFrom = maxLinesToShow * startAtIncrement;

            this_turn = LocalizedText(currentLine);
            for (int x=0; x < maxLinesToShow; x++) {
                linesToShow [x] = (continueFrom < this_turn.Length) ? TextReplace ((string)this_turn[ continueFrom ] ) : "";
                continueFrom++;
            }

            if ( currentLine.Bool("choice") ) {
                FormattedText = string.Empty;
            } else {
                if (TypewriterMode)
                    GradualDisplay ();
                else
                    FormattedText = CurrentText;
            }
            //prepare the avatar image
            if (null == WhoIsSpeakingAvatar)
                Debug.LogWarning("No avatar was set. This should never happen!");

            event_data[1].defined.Clear();
            event_data[1].Set("turn_id", CurrentLine.ID.ToString());
            event_data[1].Set("text", CurrentText);
            event_data[1].Set("choice", CurrentLine.String("choice"));
            _OnSpeakEnd(new DlgEvent(event_data), this);
            return 1;
        }

        catch (InvalidReqDefinitionException ex) {
            Debug.Log("Detected an incorrectly formatted Requirement field on line: " + ex.Message);
        }

        catch (InvalidKeyDefinitionException ex) {
            Debug.Log("Detected an incorrectly formatted Key field on line: " + ex.Message);
        }

        catch (Exception ex) {
            Debug.Log (ex.Message + ".\n" + ex.StackTrace);
        }

        return 0;
    }
Exemple #22
0
    public int Speak(int id, bool triggerEvent)
    {
        if (triggerEvent)
        {
            event_data[1].defined.Clear();
            _OnSpeakStart(new DlgEvent(event_data), this);
        }
        JossData tempLine;

        int continueFrom = 0;

        string[] this_turn;

        try {
            //if we are instructed to quit the conversation
            if (id == -1)
            {
                EndDialogue();
                return(-1);
            }

            //if no line number is provided, try and determine the line
            if (id == -3)
            {
                //first see if we were reading something the previous turn...
                if (currentlySpeakingLine > -1)
                {
                    id = currentlySpeakingLine;
                }
                else
                {
                    //if not, then assign to first ID
                    tempLine = lines [0];
                    id       = tempLine.ID;
                }

                //if we are currently speaking the same line as before, continue to show this id
                if (id == currentlySpeakingLine)
                {
                    this_turn = LocalizedText(currentLine);
                    startAtIncrement++;
                    if (this_turn.Length > (startAtIncrement * maxLinesToShow))
                    {
                        continueFrom   = id;
                        mustProcessReq = false;
                    }
                    else
                    {
                        continueFrom     = IDOfNextLine;
                        startAtIncrement = 0;
                        mustProcessReq   = true;
                    }
                }
                else
                {
                    //if not, first determine wether we were speaking at all
                    // and if we were, check what comes next...
                    mustProcessReq   = true;
                    startAtIncrement = 0;
                    continueFrom     = (currentlySpeakingLine == -1) ? lines[0].ID : IDOfNextLine;
                }
            }
            else                        // if ID was given to us, we HAVE to use it
            // if it is the same as the last ID
            {
                if (id == currentlySpeakingLine)                                //if there is nothing more to read here... This is an error... NAH!!!
                {
                    this_turn = LocalizedText(currentLine);
                    if (this_turn.Length > (startAtIncrement * maxLinesToShow))
                    {
                        startAtIncrement++;
                        mustProcessReq = false;
                    }
                    else
                    {
                        Debug.LogError("User provided line number with no dialogue to show: " + id);
                        EndDialogue();
                        return(-1);
                    }
                }
                else
                {
                    //if it is a new ID
                    startAtIncrement = 0;
                    mustProcessReq   = true;
                }
                continueFrom = id;
            }

            //see if we are being redirected to an end of conversation
            if (continueFrom == -1)
            {
                EndDialogue();
                return(-1);
            }

            //now that we know what we want, let's go get it...
            if (continueFrom != currentlySpeakingLine)
            {
                selection = 0;

                currentLine           = Find(continueFrom);
                currentlySpeakingLine = currentLine.ID;
                startAtIncrement      = 0;
            }

            //first process the requirements for this id
            if (GameKeys != null && mustProcessReq && (currentLine.defined.ContainsKey("require")))
            {
                string[] requirements = TextReplace(currentLine["require"]).Split(',');
                for (int x = 0; x < requirements.Length; x++)
                {
                    string[] tempReq = requirements[x].Trim().Split(' ');
                    if (tempReq.Length != 4)
                    {
                        throw new InvalidReqDefinitionException(currentLine.ID + ":" + requirements[x]);
                    }

                    switch (tempReq[0])
                    {
                    case "+":
                        if (GameKeys.DoesNotHave(tempReq[1], int.Parse(tempReq[2])))
                        {
                            Speak(int.Parse(tempReq[3]), false);
                            return(0);
                        }
                        break;

                    case "-":
                        if (GameKeys.DoesHave(tempReq[1], int.Parse(tempReq[2])))
                        {
                            Speak(int.Parse(tempReq[3]), false);
                            return(0);
                        }
                        break;

                    case "=":
                        if (!GameKeys.HasExactly(tempReq[1], int.Parse(tempReq[2])))
                        {
                            Speak(int.Parse(tempReq[3]), false);
                            return(0);
                        }
                        break;
                    }
                }
            }

            //now modify the keys
            if (GameKeys != null && mustProcessReq && (currentLine.defined.ContainsKey("keys")))
            {
                string[] keys = TextReplace(currentLine["keys"]).Split(',');
                for (int x = 0; x < keys.Length; x++)
                {
                    string[] tempKey = keys[x].Trim().Split(' ');
                    if (tempKey.Length != 3)
                    {
                        throw new InvalidKeyDefinitionException(currentLine.ID + ":" + keys[x]);
                    }

                    switch (tempKey[0])
                    {
                    case "+":
                        GameKeys.Add(tempKey[1], float.Parse(tempKey[2]));
                        break;

                    case "-":
                        GameKeys.Subtract(tempKey[1], float.Parse(tempKey[2]));
                        break;

                    case "=":
                        GameKeys.Setf(tempKey[1], float.Parse(tempKey[2]));
                        break;

                    default:
                        parent.BroadcastMessage("ProcessKeys", keys[x].Trim(), SendMessageOptions.DontRequireReceiver);
                        break;
                    }
                }
            }

            continueFrom = maxLinesToShow * startAtIncrement;

            this_turn = LocalizedText(currentLine);
            for (int x = 0; x < maxLinesToShow; x++)
            {
                linesToShow [x] = (continueFrom < this_turn.Length) ? TextReplace((string)this_turn[continueFrom]) : "";
                continueFrom++;
            }

            if (currentLine.Bool("choice"))
            {
                FormattedText = string.Empty;
            }
            else
            {
                if (TypewriterMode)
                {
                    GradualDisplay();
                }
                else
                {
                    FormattedText = CurrentText;
                }
            }
            //prepare the avatar image
            if (null == WhoIsSpeakingAvatar)
            {
                Debug.LogWarning("No avatar was set. This should never happen!");
            }

            event_data[1].defined.Clear();
            event_data[1].Set("turn_id", CurrentLine.ID.ToString());
            event_data[1].Set("text", CurrentText);
            event_data[1].Set("choice", CurrentLine.String("choice"));
            _OnSpeakEnd(new DlgEvent(event_data), this);
            return(1);
        }

        catch (InvalidReqDefinitionException ex) {
            Debug.Log("Detected an incorrectly formatted Requirement field on line: " + ex.Message);
        }

        catch (InvalidKeyDefinitionException ex) {
            Debug.Log("Detected an incorrectly formatted Key field on line: " + ex.Message);
        }

        catch (Exception ex) {
            Debug.Log(ex.Message + ".\n" + ex.StackTrace);
        }

        return(0);
    }
 // MAIN public void 2 ////////////////////////////////////
 // SPEAK /////////////////////////////////////////////////
 // Speak does all the work. It selects the dialogue turns to display,
 // it tests for redirection, automatically redirects if need be, modifies game keys
 // if required and sends messages to the parent object if instructed to do so by
 // the dialogue file. Once the file is loaded, programmers need only call SPEAK
 // until the dialogue has ended and it will do the rest.
 // It selects subsets of dialogue text from large turns and continues on to the next
 // turn when required.
 string[] LocalizedText(JossData turn)
 {
     string[] results;
     if (Language == string.Empty || turn.String(Language) == string.Empty) {
         results = new string[turn.data.Count];
         turn.data.CopyTo(results);
     } else	{
         results = turn.String(Language).Replace("\\n",'\n' +string.Empty).Split ('\n');
     }
     return results;
 }