Skip to main content
misapplied censoring
Source Link
Pimgd
  • 22.3k
  • 5
  • 66
  • 144
/**
 * Parses a single Line of Dialogue from a String.
 * Note that since SDT still parses commented lines, the DialogueChecker has to do so as well.
 * Certain severe issues don't care that a line is unused, for instance.
 * @param   lineText the string that should be converted to a Line object.
 * @param   lineIndex the index of the line in the array - used for determining lineNumber.
 * @return a line object if possible, nothing otherwise.
 */
private function parseLine(lineText:String, lineIndex:uint):Line {
    var lineNumber:uint = lineIndex + 1;
    var lineName:String = "";
    var rawLine:String = lineText;
    var lineContent:Array = new Array();
    var lineExcess:Array = ["", "", ""];
    var lineAttribute:LineAttribute = null;
    var lineAttributeValueArray:Array = new Array();
    var lineAttributes:Array = new Array();
    var lineAttributesString:String = "";
    var lineContentString:String = "";
    var line:Line = null;
    
    //1|intro:"Hi!" {"style":"Him"}
    //2|general : "Hello"
    //3|help"me"{"next":"pls"}
    //4|held:Let go!{"held":"true"}
    //5|under_line"Testing!"
    //6|font:Arial
    var source:String = lineText;
    var nameEnd:uint = 0;
    if (source.indexOf(":") != -1 || source.indexOf("\"") != -1) {
        //valid for all 6 examples.
        //Needs : or " to be a line. Otherwise it's not parsable.
        if (source.indexOf(":") != -1) {
            nameEnd = source.indexOf(":");
        } else {
            nameEnd = source.indexOf("\"");
        }
    } else {
        return null; //Comment Filter!
    }
    lineName = source.substr(0, nameEnd);
    source = source.substring(nameEnd);
    //1|:"Hi!" {"style":"Him"}
    //2|: "Hello"
    //3|:"pls"}
    //4|:Let go!{"held":"true"}
    //5|"Testing!"
    //6|:Arial
    var contentStart:int = 0;
    if (source.indexOf(":") == 0) {
        source = source.substring(1);
        //1|"Hi!" {"style":"Him"}
        //2| "Hello"
        //3|"pls"}
        //4|Let go!{"held":"true"}
        //6|Arial
        contentStart = source.indexOf("\"");
    }
    if (contentStart >= 0 && lineName != "initial_settings") {
        lineExcess[0] = source.substr(0, contentStart);
        source = source.substr(contentStart);
    }
    //1|"Hi!" {"style":"Him"}
    //2|"Hello"
    //3|"pls"}
    //4|"held":"true"}
    //5|"Coming5|"Testing!"
    //6|Arial
    var contentString:String = "";
    if (source.indexOf("\"") == 0 && source.indexOf("\"", 1) != -1 && lineName != "initial_settings") {
        contentString = source.substr(0, source.indexOf("\"", 1) + 1);
        source = source.substr(source.indexOf("\"", 1) + 1);
    }
    //source:
    //1| {"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|Arial
    
    //contentString:
    //1|"Hi!"
    //2|"Hello"
    //3|"pls"
    //4|"held"
    //5|"Coming5|"Testing!"
    //6|
    if (contentString == "" && lineName != "initial_settings") {
        contentString = source;
        source = "";
    }
    lineContentString = contentString;
    //Parse contentString
    //When parsing a contentString, you're treating everything as normal string until...
    //Б - change to delayString (new normal string, keep until different character)
    //% - change to special char (new normal string, parse next 2 chars, keep that as pattern, then parse until pattern is broken)
    //* - change to VariableInsertion/VariableSubstitution (Treat as Insertion until YOU/ME/YOUR/MY/FINISHES is detected)
    //[ - change to Trigger
    var contentParseMode:String = "Normal"; //Remember kids, Stringly-typed code is bad! What I'm doing here is bad! So don't do it! (Even although it makes things easier sometimes...)
    var currentContentSegment:String = "";
    
    while (contentString != "") {
        if (contentParseMode == "Normal") {
            //The problem: 4 functions give 4 values. Which function gives the lowest positive value?
            var checkArray:Array = new Array();
            checkArray.push([contentString.indexOf("Б"), "Б"]);
            checkArray.push([contentString.indexOf("%"), "%"]);
            checkArray.push([contentString.indexOf("*"), "*"]);
            checkArray.push([contentString.indexOf("["), "["]);
            checkArray.sortOn([0], [Array.NUMERIC]);
            var nextIssue:int = -1;
            var nextChar:String = "";
            for (var i:uint = 0, isize:uint = checkArray.length; i < isize; i++) {
                if (checkArray[i][0] != -1) {
                    nextIssue = checkArray[i][0];
                    nextChar = checkArray[i][1];
                    i = isize;
                }
            }
            if (nextChar == "Б" || nextChar == "%" || nextChar == "*" || nextChar == "[") {
                
                if (nextChar == "Б") {
                    contentParseMode = "Delay";
                } else if (nextChar == "*" || (nextChar == "%" && contentString.indexOf("%2A") == nextIssue)) {
                    contentParseMode = "Insertion";
                } else if (nextChar == "%") {
                    //trace(contentString.indexOf("%2A")); //commented because it was just extra noise for me as dev and I don't know why I had put this in (v2.13)
                    contentParseMode = "SpecialChar";
                } else if (nextChar == "[") {
                    contentParseMode = "Trigger";
                }
                currentContentSegment = contentString.substr(0, nextIssue);
                lineContent.push(currentContentSegment);
                currentContentSegment = "";
                contentString = contentString.substr(nextIssue);
            } else {
                currentContentSegment = contentString;
                lineContent.push(currentContentSegment);
                currentContentSegment = "";
                contentString = "";
            }
        } else if (contentParseMode == "Delay") {
            //Loop until next char is not delay char. Add all looped chars to content array, strip from content string. Evaluate next section.
            while (contentString.length > 0 && contentString.indexOf("Б") == 0) {
                currentContentSegment += "Б";
                contentString = contentString.substr(1);
            }
            lineContent.push(currentContentSegment);
            currentContentSegment = "";
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "SpecialChar") {
            //Identify special char type, loop until pattern is not next section of string. Add all looped chars to content array, strip from content string. Evaluate next section.
            var specialCharPattern:String = contentString.substr(0, 3);
            //check if it's actually a percent encoded value here
            var hexString:String = "0123456789ABCDEFabcdef";
            if (!(specialCharPattern.length == 3 && hexString.indexOf(specialCharPattern.charAt(1)) != -1 && hexString.indexOf(specialCharPattern.charAt(2)) != -1)) {
                //it's not a proper percent encoded value.
                //treat the % as %25, and strip only 1 character.
                
                currentContentSegment = specialCharPattern;
                contentString = contentString.substring(1); //strip 1 char
            } else {
                while (contentString.length > 0 && contentString.indexOf(specialCharPattern) == 0) {
                    currentContentSegment += specialCharPattern;
                    contentString = contentString.substr(specialCharPattern.length);
                }
            }
            lineContent.push(currentContentSegment);
            currentContentSegment = "";
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "Trigger") {
            //Find trigger and identify trigger if possible...?
            //TODO: Identify variable insertions within triggers
            if (contentString.indexOf("[") != 0) {
                ErrorManager.showDevelopmentError("Line parsing - trigger doesn't start with [");
                contentString = "";
                break;
            }
            var trigger:String = "";
            if (contentString.indexOf("]") != -1) {
                trigger = contentString.substr(1, contentString.indexOf("]") - 1);
                contentString = contentString.substr(contentString.indexOf("]") + 1);
            } else {
                //user error, so ignore it. Act like rest of line is trigger.
                trigger = contentString.substr(1);
                contentString = "";
            }
            var type:TriggerType = TriggerType.identifyType(trigger);
            var triggerObject:Trigger = new Trigger(trigger, type);
            lineContent.push(triggerObject);
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "Insertion") {
            //Find insertion and determine type.
            if (contentString.indexOf("*") != 0 && contentString.indexOf("%2A") != 0) {
                ErrorManager.showDevelopmentError("Line parsing - insertion doesn't start with *");
                contentString = "";
                break;
            }
            var insertion:String = "";
            var indexOfAsterisk:int = contentString.indexOf("*");
            var indexOfPercent2A:int = contentString.indexOf("%2A");
            var indexOfSecondAsterisk:int = contentString.indexOf("*", 1);
            var indexOfSecondPercent2A:int = contentString.indexOf("%2A", 1);
            if (Math.max(indexOfSecondAsterisk, indexOfSecondPercent2A) != -1) {
                var insertionIndex:int = indexOfAsterisk;
                var insertionEndIndex:int = indexOfSecondAsterisk;
                var insertionStartLength:uint = 1;
                var insertionEndLength:uint = 1;
                if (indexOfAsterisk == -1 || (indexOfPercent2A < indexOfAsterisk && indexOfPercent2A != -1)) {
                    insertionIndex = indexOfPercent2A;
                    insertionStartLength = 3;
                }
                if (indexOfSecondAsterisk == -1 || (indexOfSecondPercent2A < indexOfSecondAsterisk && indexOfSecondPercent2A != -1)) {
                    insertionEndIndex = indexOfSecondPercent2A;
                    insertionEndLength = 3;
                }
                insertion = contentString.substr(insertionStartLength, insertionEndIndex - insertionStartLength);
                contentString = contentString.substr(insertionEndIndex + insertionEndLength);
            } else {
                //user error, so ignore it. Act like rest of line is insertion.
                insertion = contentString.substr(1);
                contentString = "";
            }
            var insertionObject:LineElement = null;
            if (insertion.indexOf("YOU") != -1 || insertion.indexOf("ME") != -1 || insertion.indexOf("MY") != -1 || insertion.indexOf("FINISHES") != -1) { //There's also YOUR, but that's already covered by checking for YOU.
                insertionObject = new VariableSubstitution(insertion);
            } else {
                var variable:Variable = getVariable("\"" + insertion + "\"");
                if (variable == null) {
                    variable = new Variable(VariableType.getEnum("UNKNOWN"), "\"" + insertion + "\"");
                    addVariable(variable);
                }
                variable.setInsertedInDialogue(true);
                insertionObject = new VariableInsertion(variable, insertion);
            }
            lineContent.push(insertionObject);
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else {
            contentString = "";
            ErrorManager.showDevelopmentError("Line parsing infinite loop");
        }
    }
    //source:
    //1| {"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    
    //contentString:
    //1|"Hi!"
    //2|"Hello"
    //3|"pls"
    //4|"held"
    //5|"Testing!"
    //6|Arial
    var attributeStart:int = 0;
    var hasAttributes:Boolean = false;
    if (source.indexOf("{") != -1) {
        hasAttributes = true;
        attributeStart = source.indexOf("{");
        lineExcess[1] = source.substr(0, source.indexOf("{"));
    }
    //source:
    //1|{"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    var attributeString:String = "";
    if (hasAttributes) {
        var endIndex:Number = Math.max(source.lastIndexOf("{"), source.lastIndexOf("}"));
        attributeString = source.substr(attributeStart, endIndex + 1);
        source = source.substr(endIndex + 1);
    }
    //Parse attributeString
    lineAttributesString = attributeString;
    var attributeParseMode:String = "Start"; //Oh no Pim, more stringly-typed coding! What are you doing?! I'm trying to make things work, that's what I'm doing.
    var attributeName:String = "";
    var attributeValue:Object; //LineAttributes can be multiple variable references.
    var attributeName2:String = "";
    var attributeValue2:String = "";
    while (attributeString != "") {
        //I really should have included an initial_settings line in my examples.
        if (attributeParseMode == "Start") {
            if (attributeString.indexOf("{") != 0) {
                //Not sure who is to blame here. Me or the user?
                attributeString = "";
            } else {
                attributeString = attributeString.substr(1);
                attributeParseMode = "NextVar";
            }
        } else if (attributeParseMode == "NextVar") {
            //nextvar, reset.
            attributeName = "";
            attributeValue = null;
            attributeName2 = "";
            attributeValue2 = "";
            var start:int = attributeString.indexOf("\"");
            if (start != -1) {
                var end:int = attributeString.indexOf("\"", start + 1);
                attributeName = attributeString.substring(start, end + 1);
                attributeString = attributeString.substr(end + 1);
                attributeParseMode = "NextVal";
            } else {
                if (attributeString.indexOf("}") == 0) {
                    attributeString = attributeString.substr(1);
                }
                //No more variables, so this is the end.
                //source = attributeString + source; //<- I had that line of code, but lineAttributesString makes it unnecessary. I think. Gotta test it first.
                attributeString = "";
            }
        } else if (attributeParseMode == "NextVal") {
            if (attributeString.indexOf(":") != 0) {
                //user error. Now what.
                //Decided to add string version of lineAttributes, so I can always detect shit like missing }.
                //Same goes for this. whatever.
                attributeString = "";
            } else {
                attributeString = attributeString.substr(1);
                if (attributeString.charAt(0) == "\"") {
                    if (attributeString.indexOf("\"", 1) != -1) {
                        attributeValue = attributeString.substring(0, attributeString.indexOf("\"", 1) + 1);
                        attributeString = attributeString.substring(attributeString.indexOf("\"", 1) + 1);
                    } else {
                        attributeValue = attributeString;
                    }
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            if (attributeValue == "\"false\"" || attributeValue == "\"true\"") {
                                variable = new Variable(VariableType.getEnum("BOOLEAN"), attributeName);
                            } else {
                                variable = new Variable(VariableType.getEnum("STRING"), attributeName);
                            }
                            addVariable(variable);
                        } else if (variable.getType() == VariableType.getEnum("UNKNOWN")) {
                            if (attributeValue == "\"false\"" || attributeValue == "\"true\"") {
                                variable.setType(VariableType.getEnum("BOOLEAN"));
                            } else {
                                variable.setType(VariableType.getEnum("STRING"));
                            }
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                } else if (attributeString.indexOf("{") == 0) {
                    //Array.
                    attributeString = attributeString.substr(1);
                    lineAttributeValueArray = new Array();
                    attributeParseMode = "NextSubVar";
                } else if (attributeString.indexOf(",") != -1 || attributeString.indexOf("}") != -1) {
                    //Numeric.
                    var nextcomma:int = attributeString.indexOf(",");
                    var nextbackaccolade:int = attributeString.indexOf("}");
                    if (nextcomma != -1) {
                        if (nextbackaccolade != -1 && nextbackaccolade < nextcomma) { //I don't know either.
                            attributeValue = attributeString.substr(0, nextbackaccolade);
                            attributeString = attributeString.substr(nextbackaccolade);
                        } else {
                            attributeValue = attributeString.substr(0, nextcomma);
                            attributeString = attributeString.substr(nextcomma);
                        }
                    } else {
                        attributeValue = attributeString.substr(0, nextbackaccolade);
                        attributeString = attributeString.substr(nextbackaccolade);
                    }
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            variable = new Variable(VariableType.getEnum("NUMERIC"), attributeName);
                            addVariable(variable);
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                } else {
                    //Yeaahh... damn users again.
                    //Unknown type, but lets register the attribute anyway.
                    attributeValue = attributeString;
                    attributeString = "";
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            variable = new Variable(VariableType.getEnum("UNKNOWN"), attributeName);
                            addVariable(variable);
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                }
            }
        } else if (attributeParseMode == "NextSubVar") {
            //"name":??? (value)
            attributeName2 = "";
            attributeValue2 = "";
            var nextSegmentArray:Array = new Array();
            nextSegmentArray.push([attributeString.indexOf("}"), "}"]);
            nextSegmentArray.push([attributeString.indexOf("\""), "\""]);
            nextSegmentArray.sortOn([0], [Array.NUMERIC]);
            var nextSegmentLocation:int = -1;
            var nextSegmentChar:String = "";
            for (var i:uint = 0, isize:uint = nextSegmentArray.length; i < isize; i++) {
                if (nextSegmentArray[i][0] != -1) {
                    nextSegmentLocation = nextSegmentArray[i][0];
                    nextSegmentChar = nextSegmentArray[i][1];
                    i = isize;
                }
            }
            if (nextSegmentChar == "\"") {
                attributeString = attributeString.substr(nextSegmentLocation); //I hope that didn't contain anything important.
                if (attributeString.indexOf("\"") == 0) {
                    if (attributeString.indexOf("\"", 1) != -1) {
                        //This segment - NextSubVar name String, correct
                        attributeName2 = attributeString.substr(0, attributeString.indexOf("\"", 1) + 1);
                        attributeString = attributeString.substr(attributeString.indexOf("\"", 1) + 1);
                    } else if (attributeString.indexOf(":", 1) != -1) {
                        //This segment - NextSubVar name String, missing end double quote
                        attributeName2 = attributeString.substr(0, attributeString.indexOf(":", 1));
                        attributeString = attributeString.substr(attributeString.indexOf(":", 1));
                    } else {
                        //fuck you, learn to write dialogues for once.
                        //I mean, how inept do you have to be to keep making all these mistakes - 
                        //given a line like {"set":{"var":"value","var2":0}}
                        //Here's where I have to watch out for...
                        //{"set:{"var":"value","var2":0}}
                        //{"set"{"var":"value","var2":0}}
                        //{"set": {"var":"value","var2":0}}
                        //{"set":{ "var":"value","var2":0}}
                        //{"set":{"var""value","var2":0}}
                        //{"set":{"var":value","var2":0}}
                        //etc. how hard is it to just, yknow, write the line properly?
                        attributeString = "";
                    }
                }
                attributeParseMode = "NextSubVal";
            } else if (nextSegmentChar == "}") {
                attributeString = attributeString.substr(nextSegmentLocation + 1);
                attributeParseMode = "NextVar";
                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                lineAttributes.push(lineAttribute);
            }
        } else if (attributeParseMode == "NextSubVal") {
            //This segment - NextSubVal
            //Does it start with "? Find next ", that is value. Else, find next , or }
            //Does it start with something else? Find next , or }.
            if (attributeString.indexOf(":") == 0) {
                attributeString = attributeString.substr(1);
                var nextSegmentArray:Array = new Array();
                nextSegmentArray.push([attributeString.indexOf("}"), "}"]);
                nextSegmentArray.push([attributeString.indexOf("\""), "\""]);
                nextSegmentArray.push([attributeString.indexOf(","), ","]);
                nextSegmentArray.sortOn([0], [Array.NUMERIC]);
                var nextSegmentLocation:int = -1;
                var nextSegmentChar:String = "";
                for (var i:uint = 0, isize:uint = nextSegmentArray.length; i < isize; i++) {
                    if (nextSegmentArray[i][0] != -1) {
                        nextSegmentLocation = nextSegmentArray[i][0];
                        nextSegmentChar = nextSegmentArray[i][1];
                        i = isize;
                    }
                }
                if (nextSegmentChar == "\"") {
                    attributeValue2 = attributeString.substr(0, nextSegmentLocation);
                    attributeString = attributeString.substr(nextSegmentLocation);
                    if (attributeString.indexOf("\"") == 0) {
                        if (attributeString.indexOf("\"", 1) != -1) {
                            attributeValue2 += attributeString.substr(0, attributeString.indexOf("\"", 1) + 1);
                            attributeString = attributeString.substr(attributeString.indexOf("\"", 1) + 1);
                            var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                            if (attributeName == "\"set\"") {
                                addSetValueToVariable(attributeName2, attributeValue2);
                            } else if (attributeName == "\"check\"") {
                                addCheckedValueToVariable(attributeName2, attributeValue2);
                            }
                            lineAttributeValueArray.push(variableReference);
                            attributeParseMode = "NextSubVar";
                        } else {
                            //find next comma or }
                            var nextCommaIndex:int = attributeString.indexOf(",");
                            var nextBackAccoladeIndex:int = attributeString.indexOf("}");
                            if (nextCommaIndex != -1) {
                                if (nextBackAccoladeIndex != -1 && nextBackAccoladeIndex < nextCommaIndex) {
                                    attributeValue2 += attributeString.substr(0, attributeString.indexOf("}"));
                                    attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                    if (attributeName == "\"set\"") {
                                        addSetValueToVariable(attributeName2, attributeValue2);
                                    } else if (attributeName == "\"check\"") {
                                        addCheckedValueToVariable(attributeName2, attributeValue2);
                                    }
                                    lineAttributeValueArray.push(variableReference);
                                    lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                    lineAttributes.push(lineAttribute);
                                    attributeParseMode = "NextVar";
                                } else {
                                    attributeValue2 += attributeString.substr(0, attributeString.indexOf(","));
                                    attributeString = attributeString.substr(attributeString.indexOf(",") + 1);
                                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                    if (attributeName == "\"set\"") {
                                        addSetValueToVariable(attributeName2, attributeValue2);
                                    } else if (attributeName == "\"check\"") {
                                        addCheckedValueToVariable(attributeName2, attributeValue2);
                                    }
                                    lineAttributeValueArray.push(variableReference);
                                    attributeParseMode = "NextSubVar";
                                }
                            } else if (nextBackAccoladeIndex != -1) {
                                attributeValue2 += attributeString.substr(0, attributeString.indexOf("}"));
                                attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                                var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                if (attributeName == "\"set\"") {
                                    addSetValueToVariable(attributeName2, attributeValue2);
                                } else if (attributeName == "\"check\"") {
                                    addCheckedValueToVariable(attributeName2, attributeValue2);
                                }
                                lineAttributeValueArray.push(variableReference);
                                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                lineAttributes.push(lineAttribute);
                                attributeParseMode = "NextVar";
                            } else {
                                //we fucked
                                attributeValue2 += attributeString;
                                attributeString = "";
                                var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                if (attributeName == "\"set\"") {
                                    addSetValueToVariable(attributeName2, attributeValue2);
                                } else if (attributeName == "\"check\"") {
                                    addCheckedValueToVariable(attributeName2, attributeValue2);
                                }
                                lineAttributeValueArray.push(variableReference);
                                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                lineAttributes.push(lineAttribute);
                                attributeParseMode = "NextVar"; //safety measure
                            }
                        }
                    } else {
                        //yeah so now what
                        attributeString = "";
                    }
                } else if (nextSegmentChar == ",") {
                    attributeValue2 = attributeString.substr(0, attributeString.indexOf(","));
                    attributeString = attributeString.substr(attributeString.indexOf(",") + 1);
                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                    if (attributeName == "\"set\"") {
                        addSetValueToVariable(attributeName2, attributeValue2);
                    } else if (attributeName == "\"check\"") {
                        addCheckedValueToVariable(attributeName2, attributeValue2);
                    }
                    lineAttributeValueArray.push(variableReference);
                    attributeParseMode = "NextSubVar";
                } else if (nextSegmentChar == "}") {
                    attributeValue2 = attributeString.substr(0, attributeString.indexOf("}"));
                    attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                    if (attributeName == "\"set\"") {
                        addSetValueToVariable(attributeName2, attributeValue2);
                    } else if (attributeName == "\"check\"") {
                        addCheckedValueToVariable(attributeName2, attributeValue2);
                    }
                    lineAttributeValueArray.push(variableReference);
                    lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                    lineAttributes.push(lineAttribute);
                    attributeParseMode = "NextVar";
                } else {
                    //stop fucking things up, man
                    attributeString = "";
                }
            } else {
                //I've had it.
                var variableReference:VariableReference = new VariableReference(attributeName2, null);
                lineAttributeValueArray.push(variableReference);
                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                lineAttributes.push(lineAttribute);
                attributeString = "";
            }
            
        }
    }
    //source:
    //1|
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    //atributeString:
    //1|{"style":"Him"}
    //2|
    //3|
    //4|
    //5|
    //6|
    lineExcess[2] = source;
    line = new Line(lineNumber, lineName, rawLine, lineContent, lineContentString, lineExcess, lineAttributes, lineAttributesString);
    return line;
}
/**
 * Parses a single Line of Dialogue from a String.
 * Note that since SDT still parses commented lines, the DialogueChecker has to do so as well.
 * Certain severe issues don't care that a line is unused, for instance.
 * @param   lineText the string that should be converted to a Line object.
 * @param   lineIndex the index of the line in the array - used for determining lineNumber.
 * @return a line object if possible, nothing otherwise.
 */
private function parseLine(lineText:String, lineIndex:uint):Line {
    var lineNumber:uint = lineIndex + 1;
    var lineName:String = "";
    var rawLine:String = lineText;
    var lineContent:Array = new Array();
    var lineExcess:Array = ["", "", ""];
    var lineAttribute:LineAttribute = null;
    var lineAttributeValueArray:Array = new Array();
    var lineAttributes:Array = new Array();
    var lineAttributesString:String = "";
    var lineContentString:String = "";
    var line:Line = null;
    
    //1|intro:"Hi!" {"style":"Him"}
    //2|general : "Hello"
    //3|help"me"{"next":"pls"}
    //4|held:Let go!{"held":"true"}
    //5|under_line"Testing!"
    //6|font:Arial
    var source:String = lineText;
    var nameEnd:uint = 0;
    if (source.indexOf(":") != -1 || source.indexOf("\"") != -1) {
        //valid for all 6 examples.
        //Needs : or " to be a line. Otherwise it's not parsable.
        if (source.indexOf(":") != -1) {
            nameEnd = source.indexOf(":");
        } else {
            nameEnd = source.indexOf("\"");
        }
    } else {
        return null; //Comment Filter!
    }
    lineName = source.substr(0, nameEnd);
    source = source.substring(nameEnd);
    //1|:"Hi!" {"style":"Him"}
    //2|: "Hello"
    //3|:"pls"}
    //4|:Let go!{"held":"true"}
    //5|"Testing!"
    //6|:Arial
    var contentStart:int = 0;
    if (source.indexOf(":") == 0) {
        source = source.substring(1);
        //1|"Hi!" {"style":"Him"}
        //2| "Hello"
        //3|"pls"}
        //4|Let go!{"held":"true"}
        //6|Arial
        contentStart = source.indexOf("\"");
    }
    if (contentStart >= 0 && lineName != "initial_settings") {
        lineExcess[0] = source.substr(0, contentStart);
        source = source.substr(contentStart);
    }
    //1|"Hi!" {"style":"Him"}
    //2|"Hello"
    //3|"pls"}
    //4|"held":"true"}
    //5|"Coming!"
    //6|Arial
    var contentString:String = "";
    if (source.indexOf("\"") == 0 && source.indexOf("\"", 1) != -1 && lineName != "initial_settings") {
        contentString = source.substr(0, source.indexOf("\"", 1) + 1);
        source = source.substr(source.indexOf("\"", 1) + 1);
    }
    //source:
    //1| {"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|Arial
    
    //contentString:
    //1|"Hi!"
    //2|"Hello"
    //3|"pls"
    //4|"held"
    //5|"Coming!"
    //6|
    if (contentString == "" && lineName != "initial_settings") {
        contentString = source;
        source = "";
    }
    lineContentString = contentString;
    //Parse contentString
    //When parsing a contentString, you're treating everything as normal string until...
    //Б - change to delayString (new normal string, keep until different character)
    //% - change to special char (new normal string, parse next 2 chars, keep that as pattern, then parse until pattern is broken)
    //* - change to VariableInsertion/VariableSubstitution (Treat as Insertion until YOU/ME/YOUR/MY/FINISHES is detected)
    //[ - change to Trigger
    var contentParseMode:String = "Normal"; //Remember kids, Stringly-typed code is bad! What I'm doing here is bad! So don't do it! (Even although it makes things easier sometimes...)
    var currentContentSegment:String = "";
    
    while (contentString != "") {
        if (contentParseMode == "Normal") {
            //The problem: 4 functions give 4 values. Which function gives the lowest positive value?
            var checkArray:Array = new Array();
            checkArray.push([contentString.indexOf("Б"), "Б"]);
            checkArray.push([contentString.indexOf("%"), "%"]);
            checkArray.push([contentString.indexOf("*"), "*"]);
            checkArray.push([contentString.indexOf("["), "["]);
            checkArray.sortOn([0], [Array.NUMERIC]);
            var nextIssue:int = -1;
            var nextChar:String = "";
            for (var i:uint = 0, isize:uint = checkArray.length; i < isize; i++) {
                if (checkArray[i][0] != -1) {
                    nextIssue = checkArray[i][0];
                    nextChar = checkArray[i][1];
                    i = isize;
                }
            }
            if (nextChar == "Б" || nextChar == "%" || nextChar == "*" || nextChar == "[") {
                
                if (nextChar == "Б") {
                    contentParseMode = "Delay";
                } else if (nextChar == "*" || (nextChar == "%" && contentString.indexOf("%2A") == nextIssue)) {
                    contentParseMode = "Insertion";
                } else if (nextChar == "%") {
                    //trace(contentString.indexOf("%2A")); //commented because it was just extra noise for me as dev and I don't know why I had put this in (v2.13)
                    contentParseMode = "SpecialChar";
                } else if (nextChar == "[") {
                    contentParseMode = "Trigger";
                }
                currentContentSegment = contentString.substr(0, nextIssue);
                lineContent.push(currentContentSegment);
                currentContentSegment = "";
                contentString = contentString.substr(nextIssue);
            } else {
                currentContentSegment = contentString;
                lineContent.push(currentContentSegment);
                currentContentSegment = "";
                contentString = "";
            }
        } else if (contentParseMode == "Delay") {
            //Loop until next char is not delay char. Add all looped chars to content array, strip from content string. Evaluate next section.
            while (contentString.length > 0 && contentString.indexOf("Б") == 0) {
                currentContentSegment += "Б";
                contentString = contentString.substr(1);
            }
            lineContent.push(currentContentSegment);
            currentContentSegment = "";
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "SpecialChar") {
            //Identify special char type, loop until pattern is not next section of string. Add all looped chars to content array, strip from content string. Evaluate next section.
            var specialCharPattern:String = contentString.substr(0, 3);
            //check if it's actually a percent encoded value here
            var hexString:String = "0123456789ABCDEFabcdef";
            if (!(specialCharPattern.length == 3 && hexString.indexOf(specialCharPattern.charAt(1)) != -1 && hexString.indexOf(specialCharPattern.charAt(2)) != -1)) {
                //it's not a proper percent encoded value.
                //treat the % as %25, and strip only 1 character.
                
                currentContentSegment = specialCharPattern;
                contentString = contentString.substring(1); //strip 1 char
            } else {
                while (contentString.length > 0 && contentString.indexOf(specialCharPattern) == 0) {
                    currentContentSegment += specialCharPattern;
                    contentString = contentString.substr(specialCharPattern.length);
                }
            }
            lineContent.push(currentContentSegment);
            currentContentSegment = "";
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "Trigger") {
            //Find trigger and identify trigger if possible...?
            //TODO: Identify variable insertions within triggers
            if (contentString.indexOf("[") != 0) {
                ErrorManager.showDevelopmentError("Line parsing - trigger doesn't start with [");
                contentString = "";
                break;
            }
            var trigger:String = "";
            if (contentString.indexOf("]") != -1) {
                trigger = contentString.substr(1, contentString.indexOf("]") - 1);
                contentString = contentString.substr(contentString.indexOf("]") + 1);
            } else {
                //user error, so ignore it. Act like rest of line is trigger.
                trigger = contentString.substr(1);
                contentString = "";
            }
            var type:TriggerType = TriggerType.identifyType(trigger);
            var triggerObject:Trigger = new Trigger(trigger, type);
            lineContent.push(triggerObject);
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "Insertion") {
            //Find insertion and determine type.
            if (contentString.indexOf("*") != 0 && contentString.indexOf("%2A") != 0) {
                ErrorManager.showDevelopmentError("Line parsing - insertion doesn't start with *");
                contentString = "";
                break;
            }
            var insertion:String = "";
            var indexOfAsterisk:int = contentString.indexOf("*");
            var indexOfPercent2A:int = contentString.indexOf("%2A");
            var indexOfSecondAsterisk:int = contentString.indexOf("*", 1);
            var indexOfSecondPercent2A:int = contentString.indexOf("%2A", 1);
            if (Math.max(indexOfSecondAsterisk, indexOfSecondPercent2A) != -1) {
                var insertionIndex:int = indexOfAsterisk;
                var insertionEndIndex:int = indexOfSecondAsterisk;
                var insertionStartLength:uint = 1;
                var insertionEndLength:uint = 1;
                if (indexOfAsterisk == -1 || (indexOfPercent2A < indexOfAsterisk && indexOfPercent2A != -1)) {
                    insertionIndex = indexOfPercent2A;
                    insertionStartLength = 3;
                }
                if (indexOfSecondAsterisk == -1 || (indexOfSecondPercent2A < indexOfSecondAsterisk && indexOfSecondPercent2A != -1)) {
                    insertionEndIndex = indexOfSecondPercent2A;
                    insertionEndLength = 3;
                }
                insertion = contentString.substr(insertionStartLength, insertionEndIndex - insertionStartLength);
                contentString = contentString.substr(insertionEndIndex + insertionEndLength);
            } else {
                //user error, so ignore it. Act like rest of line is insertion.
                insertion = contentString.substr(1);
                contentString = "";
            }
            var insertionObject:LineElement = null;
            if (insertion.indexOf("YOU") != -1 || insertion.indexOf("ME") != -1 || insertion.indexOf("MY") != -1 || insertion.indexOf("FINISHES") != -1) { //There's also YOUR, but that's already covered by checking for YOU.
                insertionObject = new VariableSubstitution(insertion);
            } else {
                var variable:Variable = getVariable("\"" + insertion + "\"");
                if (variable == null) {
                    variable = new Variable(VariableType.getEnum("UNKNOWN"), "\"" + insertion + "\"");
                    addVariable(variable);
                }
                variable.setInsertedInDialogue(true);
                insertionObject = new VariableInsertion(variable, insertion);
            }
            lineContent.push(insertionObject);
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else {
            contentString = "";
            ErrorManager.showDevelopmentError("Line parsing infinite loop");
        }
    }
    //source:
    //1| {"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    
    //contentString:
    //1|"Hi!"
    //2|"Hello"
    //3|"pls"
    //4|"held"
    //5|"Testing!"
    //6|Arial
    var attributeStart:int = 0;
    var hasAttributes:Boolean = false;
    if (source.indexOf("{") != -1) {
        hasAttributes = true;
        attributeStart = source.indexOf("{");
        lineExcess[1] = source.substr(0, source.indexOf("{"));
    }
    //source:
    //1|{"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    var attributeString:String = "";
    if (hasAttributes) {
        var endIndex:Number = Math.max(source.lastIndexOf("{"), source.lastIndexOf("}"));
        attributeString = source.substr(attributeStart, endIndex + 1);
        source = source.substr(endIndex + 1);
    }
    //Parse attributeString
    lineAttributesString = attributeString;
    var attributeParseMode:String = "Start"; //Oh no Pim, more stringly-typed coding! What are you doing?! I'm trying to make things work, that's what I'm doing.
    var attributeName:String = "";
    var attributeValue:Object; //LineAttributes can be multiple variable references.
    var attributeName2:String = "";
    var attributeValue2:String = "";
    while (attributeString != "") {
        //I really should have included an initial_settings line in my examples.
        if (attributeParseMode == "Start") {
            if (attributeString.indexOf("{") != 0) {
                //Not sure who is to blame here. Me or the user?
                attributeString = "";
            } else {
                attributeString = attributeString.substr(1);
                attributeParseMode = "NextVar";
            }
        } else if (attributeParseMode == "NextVar") {
            //nextvar, reset.
            attributeName = "";
            attributeValue = null;
            attributeName2 = "";
            attributeValue2 = "";
            var start:int = attributeString.indexOf("\"");
            if (start != -1) {
                var end:int = attributeString.indexOf("\"", start + 1);
                attributeName = attributeString.substring(start, end + 1);
                attributeString = attributeString.substr(end + 1);
                attributeParseMode = "NextVal";
            } else {
                if (attributeString.indexOf("}") == 0) {
                    attributeString = attributeString.substr(1);
                }
                //No more variables, so this is the end.
                //source = attributeString + source; //<- I had that line of code, but lineAttributesString makes it unnecessary. I think. Gotta test it first.
                attributeString = "";
            }
        } else if (attributeParseMode == "NextVal") {
            if (attributeString.indexOf(":") != 0) {
                //user error. Now what.
                //Decided to add string version of lineAttributes, so I can always detect shit like missing }.
                //Same goes for this. whatever.
                attributeString = "";
            } else {
                attributeString = attributeString.substr(1);
                if (attributeString.charAt(0) == "\"") {
                    if (attributeString.indexOf("\"", 1) != -1) {
                        attributeValue = attributeString.substring(0, attributeString.indexOf("\"", 1) + 1);
                        attributeString = attributeString.substring(attributeString.indexOf("\"", 1) + 1);
                    } else {
                        attributeValue = attributeString;
                    }
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            if (attributeValue == "\"false\"" || attributeValue == "\"true\"") {
                                variable = new Variable(VariableType.getEnum("BOOLEAN"), attributeName);
                            } else {
                                variable = new Variable(VariableType.getEnum("STRING"), attributeName);
                            }
                            addVariable(variable);
                        } else if (variable.getType() == VariableType.getEnum("UNKNOWN")) {
                            if (attributeValue == "\"false\"" || attributeValue == "\"true\"") {
                                variable.setType(VariableType.getEnum("BOOLEAN"));
                            } else {
                                variable.setType(VariableType.getEnum("STRING"));
                            }
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                } else if (attributeString.indexOf("{") == 0) {
                    //Array.
                    attributeString = attributeString.substr(1);
                    lineAttributeValueArray = new Array();
                    attributeParseMode = "NextSubVar";
                } else if (attributeString.indexOf(",") != -1 || attributeString.indexOf("}") != -1) {
                    //Numeric.
                    var nextcomma:int = attributeString.indexOf(",");
                    var nextbackaccolade:int = attributeString.indexOf("}");
                    if (nextcomma != -1) {
                        if (nextbackaccolade != -1 && nextbackaccolade < nextcomma) { //I don't know either.
                            attributeValue = attributeString.substr(0, nextbackaccolade);
                            attributeString = attributeString.substr(nextbackaccolade);
                        } else {
                            attributeValue = attributeString.substr(0, nextcomma);
                            attributeString = attributeString.substr(nextcomma);
                        }
                    } else {
                        attributeValue = attributeString.substr(0, nextbackaccolade);
                        attributeString = attributeString.substr(nextbackaccolade);
                    }
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            variable = new Variable(VariableType.getEnum("NUMERIC"), attributeName);
                            addVariable(variable);
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                } else {
                    //Yeaahh... damn users again.
                    //Unknown type, but lets register the attribute anyway.
                    attributeValue = attributeString;
                    attributeString = "";
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            variable = new Variable(VariableType.getEnum("UNKNOWN"), attributeName);
                            addVariable(variable);
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                }
            }
        } else if (attributeParseMode == "NextSubVar") {
            //"name":??? (value)
            attributeName2 = "";
            attributeValue2 = "";
            var nextSegmentArray:Array = new Array();
            nextSegmentArray.push([attributeString.indexOf("}"), "}"]);
            nextSegmentArray.push([attributeString.indexOf("\""), "\""]);
            nextSegmentArray.sortOn([0], [Array.NUMERIC]);
            var nextSegmentLocation:int = -1;
            var nextSegmentChar:String = "";
            for (var i:uint = 0, isize:uint = nextSegmentArray.length; i < isize; i++) {
                if (nextSegmentArray[i][0] != -1) {
                    nextSegmentLocation = nextSegmentArray[i][0];
                    nextSegmentChar = nextSegmentArray[i][1];
                    i = isize;
                }
            }
            if (nextSegmentChar == "\"") {
                attributeString = attributeString.substr(nextSegmentLocation); //I hope that didn't contain anything important.
                if (attributeString.indexOf("\"") == 0) {
                    if (attributeString.indexOf("\"", 1) != -1) {
                        //This segment - NextSubVar name String, correct
                        attributeName2 = attributeString.substr(0, attributeString.indexOf("\"", 1) + 1);
                        attributeString = attributeString.substr(attributeString.indexOf("\"", 1) + 1);
                    } else if (attributeString.indexOf(":", 1) != -1) {
                        //This segment - NextSubVar name String, missing end double quote
                        attributeName2 = attributeString.substr(0, attributeString.indexOf(":", 1));
                        attributeString = attributeString.substr(attributeString.indexOf(":", 1));
                    } else {
                        //fuck you, learn to write dialogues for once.
                        //I mean, how inept do you have to be to keep making all these mistakes - 
                        //given a line like {"set":{"var":"value","var2":0}}
                        //Here's where I have to watch out for...
                        //{"set:{"var":"value","var2":0}}
                        //{"set"{"var":"value","var2":0}}
                        //{"set": {"var":"value","var2":0}}
                        //{"set":{ "var":"value","var2":0}}
                        //{"set":{"var""value","var2":0}}
                        //{"set":{"var":value","var2":0}}
                        //etc. how hard is it to just, yknow, write the line properly?
                        attributeString = "";
                    }
                }
                attributeParseMode = "NextSubVal";
            } else if (nextSegmentChar == "}") {
                attributeString = attributeString.substr(nextSegmentLocation + 1);
                attributeParseMode = "NextVar";
                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                lineAttributes.push(lineAttribute);
            }
        } else if (attributeParseMode == "NextSubVal") {
            //This segment - NextSubVal
            //Does it start with "? Find next ", that is value. Else, find next , or }
            //Does it start with something else? Find next , or }.
            if (attributeString.indexOf(":") == 0) {
                attributeString = attributeString.substr(1);
                var nextSegmentArray:Array = new Array();
                nextSegmentArray.push([attributeString.indexOf("}"), "}"]);
                nextSegmentArray.push([attributeString.indexOf("\""), "\""]);
                nextSegmentArray.push([attributeString.indexOf(","), ","]);
                nextSegmentArray.sortOn([0], [Array.NUMERIC]);
                var nextSegmentLocation:int = -1;
                var nextSegmentChar:String = "";
                for (var i:uint = 0, isize:uint = nextSegmentArray.length; i < isize; i++) {
                    if (nextSegmentArray[i][0] != -1) {
                        nextSegmentLocation = nextSegmentArray[i][0];
                        nextSegmentChar = nextSegmentArray[i][1];
                        i = isize;
                    }
                }
                if (nextSegmentChar == "\"") {
                    attributeValue2 = attributeString.substr(0, nextSegmentLocation);
                    attributeString = attributeString.substr(nextSegmentLocation);
                    if (attributeString.indexOf("\"") == 0) {
                        if (attributeString.indexOf("\"", 1) != -1) {
                            attributeValue2 += attributeString.substr(0, attributeString.indexOf("\"", 1) + 1);
                            attributeString = attributeString.substr(attributeString.indexOf("\"", 1) + 1);
                            var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                            if (attributeName == "\"set\"") {
                                addSetValueToVariable(attributeName2, attributeValue2);
                            } else if (attributeName == "\"check\"") {
                                addCheckedValueToVariable(attributeName2, attributeValue2);
                            }
                            lineAttributeValueArray.push(variableReference);
                            attributeParseMode = "NextSubVar";
                        } else {
                            //find next comma or }
                            var nextCommaIndex:int = attributeString.indexOf(",");
                            var nextBackAccoladeIndex:int = attributeString.indexOf("}");
                            if (nextCommaIndex != -1) {
                                if (nextBackAccoladeIndex != -1 && nextBackAccoladeIndex < nextCommaIndex) {
                                    attributeValue2 += attributeString.substr(0, attributeString.indexOf("}"));
                                    attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                    if (attributeName == "\"set\"") {
                                        addSetValueToVariable(attributeName2, attributeValue2);
                                    } else if (attributeName == "\"check\"") {
                                        addCheckedValueToVariable(attributeName2, attributeValue2);
                                    }
                                    lineAttributeValueArray.push(variableReference);
                                    lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                    lineAttributes.push(lineAttribute);
                                    attributeParseMode = "NextVar";
                                } else {
                                    attributeValue2 += attributeString.substr(0, attributeString.indexOf(","));
                                    attributeString = attributeString.substr(attributeString.indexOf(",") + 1);
                                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                    if (attributeName == "\"set\"") {
                                        addSetValueToVariable(attributeName2, attributeValue2);
                                    } else if (attributeName == "\"check\"") {
                                        addCheckedValueToVariable(attributeName2, attributeValue2);
                                    }
                                    lineAttributeValueArray.push(variableReference);
                                    attributeParseMode = "NextSubVar";
                                }
                            } else if (nextBackAccoladeIndex != -1) {
                                attributeValue2 += attributeString.substr(0, attributeString.indexOf("}"));
                                attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                                var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                if (attributeName == "\"set\"") {
                                    addSetValueToVariable(attributeName2, attributeValue2);
                                } else if (attributeName == "\"check\"") {
                                    addCheckedValueToVariable(attributeName2, attributeValue2);
                                }
                                lineAttributeValueArray.push(variableReference);
                                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                lineAttributes.push(lineAttribute);
                                attributeParseMode = "NextVar";
                            } else {
                                //we fucked
                                attributeValue2 += attributeString;
                                attributeString = "";
                                var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                if (attributeName == "\"set\"") {
                                    addSetValueToVariable(attributeName2, attributeValue2);
                                } else if (attributeName == "\"check\"") {
                                    addCheckedValueToVariable(attributeName2, attributeValue2);
                                }
                                lineAttributeValueArray.push(variableReference);
                                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                lineAttributes.push(lineAttribute);
                                attributeParseMode = "NextVar"; //safety measure
                            }
                        }
                    } else {
                        //yeah so now what
                        attributeString = "";
                    }
                } else if (nextSegmentChar == ",") {
                    attributeValue2 = attributeString.substr(0, attributeString.indexOf(","));
                    attributeString = attributeString.substr(attributeString.indexOf(",") + 1);
                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                    if (attributeName == "\"set\"") {
                        addSetValueToVariable(attributeName2, attributeValue2);
                    } else if (attributeName == "\"check\"") {
                        addCheckedValueToVariable(attributeName2, attributeValue2);
                    }
                    lineAttributeValueArray.push(variableReference);
                    attributeParseMode = "NextSubVar";
                } else if (nextSegmentChar == "}") {
                    attributeValue2 = attributeString.substr(0, attributeString.indexOf("}"));
                    attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                    if (attributeName == "\"set\"") {
                        addSetValueToVariable(attributeName2, attributeValue2);
                    } else if (attributeName == "\"check\"") {
                        addCheckedValueToVariable(attributeName2, attributeValue2);
                    }
                    lineAttributeValueArray.push(variableReference);
                    lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                    lineAttributes.push(lineAttribute);
                    attributeParseMode = "NextVar";
                } else {
                    //stop fucking things up, man
                    attributeString = "";
                }
            } else {
                //I've had it.
                var variableReference:VariableReference = new VariableReference(attributeName2, null);
                lineAttributeValueArray.push(variableReference);
                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                lineAttributes.push(lineAttribute);
                attributeString = "";
            }
            
        }
    }
    //source:
    //1|
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    //atributeString:
    //1|{"style":"Him"}
    //2|
    //3|
    //4|
    //5|
    //6|
    lineExcess[2] = source;
    line = new Line(lineNumber, lineName, rawLine, lineContent, lineContentString, lineExcess, lineAttributes, lineAttributesString);
    return line;
}
/**
 * Parses a single Line of Dialogue from a String.
 * Note that since SDT still parses commented lines, the DialogueChecker has to do so as well.
 * Certain severe issues don't care that a line is unused, for instance.
 * @param   lineText the string that should be converted to a Line object.
 * @param   lineIndex the index of the line in the array - used for determining lineNumber.
 * @return a line object if possible, nothing otherwise.
 */
private function parseLine(lineText:String, lineIndex:uint):Line {
    var lineNumber:uint = lineIndex + 1;
    var lineName:String = "";
    var rawLine:String = lineText;
    var lineContent:Array = new Array();
    var lineExcess:Array = ["", "", ""];
    var lineAttribute:LineAttribute = null;
    var lineAttributeValueArray:Array = new Array();
    var lineAttributes:Array = new Array();
    var lineAttributesString:String = "";
    var lineContentString:String = "";
    var line:Line = null;
    
    //1|intro:"Hi!" {"style":"Him"}
    //2|general : "Hello"
    //3|help"me"{"next":"pls"}
    //4|held:Let go!{"held":"true"}
    //5|under_line"Testing!"
    //6|font:Arial
    var source:String = lineText;
    var nameEnd:uint = 0;
    if (source.indexOf(":") != -1 || source.indexOf("\"") != -1) {
        //valid for all 6 examples.
        //Needs : or " to be a line. Otherwise it's not parsable.
        if (source.indexOf(":") != -1) {
            nameEnd = source.indexOf(":");
        } else {
            nameEnd = source.indexOf("\"");
        }
    } else {
        return null; //Comment Filter!
    }
    lineName = source.substr(0, nameEnd);
    source = source.substring(nameEnd);
    //1|:"Hi!" {"style":"Him"}
    //2|: "Hello"
    //3|:"pls"}
    //4|:Let go!{"held":"true"}
    //5|"Testing!"
    //6|:Arial
    var contentStart:int = 0;
    if (source.indexOf(":") == 0) {
        source = source.substring(1);
        //1|"Hi!" {"style":"Him"}
        //2| "Hello"
        //3|"pls"}
        //4|Let go!{"held":"true"}
        //6|Arial
        contentStart = source.indexOf("\"");
    }
    if (contentStart >= 0 && lineName != "initial_settings") {
        lineExcess[0] = source.substr(0, contentStart);
        source = source.substr(contentStart);
    }
    //1|"Hi!" {"style":"Him"}
    //2|"Hello"
    //3|"pls"}
    //4|"held":"true"}
    //5|"Testing!"
    //6|Arial
    var contentString:String = "";
    if (source.indexOf("\"") == 0 && source.indexOf("\"", 1) != -1 && lineName != "initial_settings") {
        contentString = source.substr(0, source.indexOf("\"", 1) + 1);
        source = source.substr(source.indexOf("\"", 1) + 1);
    }
    //source:
    //1| {"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|Arial
    
    //contentString:
    //1|"Hi!"
    //2|"Hello"
    //3|"pls"
    //4|"held"
    //5|"Testing!"
    //6|
    if (contentString == "" && lineName != "initial_settings") {
        contentString = source;
        source = "";
    }
    lineContentString = contentString;
    //Parse contentString
    //When parsing a contentString, you're treating everything as normal string until...
    //Б - change to delayString (new normal string, keep until different character)
    //% - change to special char (new normal string, parse next 2 chars, keep that as pattern, then parse until pattern is broken)
    //* - change to VariableInsertion/VariableSubstitution (Treat as Insertion until YOU/ME/YOUR/MY/FINISHES is detected)
    //[ - change to Trigger
    var contentParseMode:String = "Normal"; //Remember kids, Stringly-typed code is bad! What I'm doing here is bad! So don't do it! (Even although it makes things easier sometimes...)
    var currentContentSegment:String = "";
    
    while (contentString != "") {
        if (contentParseMode == "Normal") {
            //The problem: 4 functions give 4 values. Which function gives the lowest positive value?
            var checkArray:Array = new Array();
            checkArray.push([contentString.indexOf("Б"), "Б"]);
            checkArray.push([contentString.indexOf("%"), "%"]);
            checkArray.push([contentString.indexOf("*"), "*"]);
            checkArray.push([contentString.indexOf("["), "["]);
            checkArray.sortOn([0], [Array.NUMERIC]);
            var nextIssue:int = -1;
            var nextChar:String = "";
            for (var i:uint = 0, isize:uint = checkArray.length; i < isize; i++) {
                if (checkArray[i][0] != -1) {
                    nextIssue = checkArray[i][0];
                    nextChar = checkArray[i][1];
                    i = isize;
                }
            }
            if (nextChar == "Б" || nextChar == "%" || nextChar == "*" || nextChar == "[") {
                
                if (nextChar == "Б") {
                    contentParseMode = "Delay";
                } else if (nextChar == "*" || (nextChar == "%" && contentString.indexOf("%2A") == nextIssue)) {
                    contentParseMode = "Insertion";
                } else if (nextChar == "%") {
                    //trace(contentString.indexOf("%2A")); //commented because it was just extra noise for me as dev and I don't know why I had put this in (v2.13)
                    contentParseMode = "SpecialChar";
                } else if (nextChar == "[") {
                    contentParseMode = "Trigger";
                }
                currentContentSegment = contentString.substr(0, nextIssue);
                lineContent.push(currentContentSegment);
                currentContentSegment = "";
                contentString = contentString.substr(nextIssue);
            } else {
                currentContentSegment = contentString;
                lineContent.push(currentContentSegment);
                currentContentSegment = "";
                contentString = "";
            }
        } else if (contentParseMode == "Delay") {
            //Loop until next char is not delay char. Add all looped chars to content array, strip from content string. Evaluate next section.
            while (contentString.length > 0 && contentString.indexOf("Б") == 0) {
                currentContentSegment += "Б";
                contentString = contentString.substr(1);
            }
            lineContent.push(currentContentSegment);
            currentContentSegment = "";
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "SpecialChar") {
            //Identify special char type, loop until pattern is not next section of string. Add all looped chars to content array, strip from content string. Evaluate next section.
            var specialCharPattern:String = contentString.substr(0, 3);
            //check if it's actually a percent encoded value here
            var hexString:String = "0123456789ABCDEFabcdef";
            if (!(specialCharPattern.length == 3 && hexString.indexOf(specialCharPattern.charAt(1)) != -1 && hexString.indexOf(specialCharPattern.charAt(2)) != -1)) {
                //it's not a proper percent encoded value.
                //treat the % as %25, and strip only 1 character.
                
                currentContentSegment = specialCharPattern;
                contentString = contentString.substring(1); //strip 1 char
            } else {
                while (contentString.length > 0 && contentString.indexOf(specialCharPattern) == 0) {
                    currentContentSegment += specialCharPattern;
                    contentString = contentString.substr(specialCharPattern.length);
                }
            }
            lineContent.push(currentContentSegment);
            currentContentSegment = "";
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "Trigger") {
            //Find trigger and identify trigger if possible...?
            //TODO: Identify variable insertions within triggers
            if (contentString.indexOf("[") != 0) {
                ErrorManager.showDevelopmentError("Line parsing - trigger doesn't start with [");
                contentString = "";
                break;
            }
            var trigger:String = "";
            if (contentString.indexOf("]") != -1) {
                trigger = contentString.substr(1, contentString.indexOf("]") - 1);
                contentString = contentString.substr(contentString.indexOf("]") + 1);
            } else {
                //user error, so ignore it. Act like rest of line is trigger.
                trigger = contentString.substr(1);
                contentString = "";
            }
            var type:TriggerType = TriggerType.identifyType(trigger);
            var triggerObject:Trigger = new Trigger(trigger, type);
            lineContent.push(triggerObject);
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "Insertion") {
            //Find insertion and determine type.
            if (contentString.indexOf("*") != 0 && contentString.indexOf("%2A") != 0) {
                ErrorManager.showDevelopmentError("Line parsing - insertion doesn't start with *");
                contentString = "";
                break;
            }
            var insertion:String = "";
            var indexOfAsterisk:int = contentString.indexOf("*");
            var indexOfPercent2A:int = contentString.indexOf("%2A");
            var indexOfSecondAsterisk:int = contentString.indexOf("*", 1);
            var indexOfSecondPercent2A:int = contentString.indexOf("%2A", 1);
            if (Math.max(indexOfSecondAsterisk, indexOfSecondPercent2A) != -1) {
                var insertionIndex:int = indexOfAsterisk;
                var insertionEndIndex:int = indexOfSecondAsterisk;
                var insertionStartLength:uint = 1;
                var insertionEndLength:uint = 1;
                if (indexOfAsterisk == -1 || (indexOfPercent2A < indexOfAsterisk && indexOfPercent2A != -1)) {
                    insertionIndex = indexOfPercent2A;
                    insertionStartLength = 3;
                }
                if (indexOfSecondAsterisk == -1 || (indexOfSecondPercent2A < indexOfSecondAsterisk && indexOfSecondPercent2A != -1)) {
                    insertionEndIndex = indexOfSecondPercent2A;
                    insertionEndLength = 3;
                }
                insertion = contentString.substr(insertionStartLength, insertionEndIndex - insertionStartLength);
                contentString = contentString.substr(insertionEndIndex + insertionEndLength);
            } else {
                //user error, so ignore it. Act like rest of line is insertion.
                insertion = contentString.substr(1);
                contentString = "";
            }
            var insertionObject:LineElement = null;
            if (insertion.indexOf("YOU") != -1 || insertion.indexOf("ME") != -1 || insertion.indexOf("MY") != -1 || insertion.indexOf("FINISHES") != -1) { //There's also YOUR, but that's already covered by checking for YOU.
                insertionObject = new VariableSubstitution(insertion);
            } else {
                var variable:Variable = getVariable("\"" + insertion + "\"");
                if (variable == null) {
                    variable = new Variable(VariableType.getEnum("UNKNOWN"), "\"" + insertion + "\"");
                    addVariable(variable);
                }
                variable.setInsertedInDialogue(true);
                insertionObject = new VariableInsertion(variable, insertion);
            }
            lineContent.push(insertionObject);
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else {
            contentString = "";
            ErrorManager.showDevelopmentError("Line parsing infinite loop");
        }
    }
    //source:
    //1| {"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    
    //contentString:
    //1|"Hi!"
    //2|"Hello"
    //3|"pls"
    //4|"held"
    //5|"Testing!"
    //6|Arial
    var attributeStart:int = 0;
    var hasAttributes:Boolean = false;
    if (source.indexOf("{") != -1) {
        hasAttributes = true;
        attributeStart = source.indexOf("{");
        lineExcess[1] = source.substr(0, source.indexOf("{"));
    }
    //source:
    //1|{"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    var attributeString:String = "";
    if (hasAttributes) {
        var endIndex:Number = Math.max(source.lastIndexOf("{"), source.lastIndexOf("}"));
        attributeString = source.substr(attributeStart, endIndex + 1);
        source = source.substr(endIndex + 1);
    }
    //Parse attributeString
    lineAttributesString = attributeString;
    var attributeParseMode:String = "Start"; //Oh no Pim, more stringly-typed coding! What are you doing?! I'm trying to make things work, that's what I'm doing.
    var attributeName:String = "";
    var attributeValue:Object; //LineAttributes can be multiple variable references.
    var attributeName2:String = "";
    var attributeValue2:String = "";
    while (attributeString != "") {
        //I really should have included an initial_settings line in my examples.
        if (attributeParseMode == "Start") {
            if (attributeString.indexOf("{") != 0) {
                //Not sure who is to blame here. Me or the user?
                attributeString = "";
            } else {
                attributeString = attributeString.substr(1);
                attributeParseMode = "NextVar";
            }
        } else if (attributeParseMode == "NextVar") {
            //nextvar, reset.
            attributeName = "";
            attributeValue = null;
            attributeName2 = "";
            attributeValue2 = "";
            var start:int = attributeString.indexOf("\"");
            if (start != -1) {
                var end:int = attributeString.indexOf("\"", start + 1);
                attributeName = attributeString.substring(start, end + 1);
                attributeString = attributeString.substr(end + 1);
                attributeParseMode = "NextVal";
            } else {
                if (attributeString.indexOf("}") == 0) {
                    attributeString = attributeString.substr(1);
                }
                //No more variables, so this is the end.
                //source = attributeString + source; //<- I had that line of code, but lineAttributesString makes it unnecessary. I think. Gotta test it first.
                attributeString = "";
            }
        } else if (attributeParseMode == "NextVal") {
            if (attributeString.indexOf(":") != 0) {
                //user error. Now what.
                //Decided to add string version of lineAttributes, so I can always detect shit like missing }.
                //Same goes for this. whatever.
                attributeString = "";
            } else {
                attributeString = attributeString.substr(1);
                if (attributeString.charAt(0) == "\"") {
                    if (attributeString.indexOf("\"", 1) != -1) {
                        attributeValue = attributeString.substring(0, attributeString.indexOf("\"", 1) + 1);
                        attributeString = attributeString.substring(attributeString.indexOf("\"", 1) + 1);
                    } else {
                        attributeValue = attributeString;
                    }
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            if (attributeValue == "\"false\"" || attributeValue == "\"true\"") {
                                variable = new Variable(VariableType.getEnum("BOOLEAN"), attributeName);
                            } else {
                                variable = new Variable(VariableType.getEnum("STRING"), attributeName);
                            }
                            addVariable(variable);
                        } else if (variable.getType() == VariableType.getEnum("UNKNOWN")) {
                            if (attributeValue == "\"false\"" || attributeValue == "\"true\"") {
                                variable.setType(VariableType.getEnum("BOOLEAN"));
                            } else {
                                variable.setType(VariableType.getEnum("STRING"));
                            }
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                } else if (attributeString.indexOf("{") == 0) {
                    //Array.
                    attributeString = attributeString.substr(1);
                    lineAttributeValueArray = new Array();
                    attributeParseMode = "NextSubVar";
                } else if (attributeString.indexOf(",") != -1 || attributeString.indexOf("}") != -1) {
                    //Numeric.
                    var nextcomma:int = attributeString.indexOf(",");
                    var nextbackaccolade:int = attributeString.indexOf("}");
                    if (nextcomma != -1) {
                        if (nextbackaccolade != -1 && nextbackaccolade < nextcomma) { //I don't know either.
                            attributeValue = attributeString.substr(0, nextbackaccolade);
                            attributeString = attributeString.substr(nextbackaccolade);
                        } else {
                            attributeValue = attributeString.substr(0, nextcomma);
                            attributeString = attributeString.substr(nextcomma);
                        }
                    } else {
                        attributeValue = attributeString.substr(0, nextbackaccolade);
                        attributeString = attributeString.substr(nextbackaccolade);
                    }
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            variable = new Variable(VariableType.getEnum("NUMERIC"), attributeName);
                            addVariable(variable);
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                } else {
                    //Yeaahh... damn users again.
                    //Unknown type, but lets register the attribute anyway.
                    attributeValue = attributeString;
                    attributeString = "";
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            variable = new Variable(VariableType.getEnum("UNKNOWN"), attributeName);
                            addVariable(variable);
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                }
            }
        } else if (attributeParseMode == "NextSubVar") {
            //"name":??? (value)
            attributeName2 = "";
            attributeValue2 = "";
            var nextSegmentArray:Array = new Array();
            nextSegmentArray.push([attributeString.indexOf("}"), "}"]);
            nextSegmentArray.push([attributeString.indexOf("\""), "\""]);
            nextSegmentArray.sortOn([0], [Array.NUMERIC]);
            var nextSegmentLocation:int = -1;
            var nextSegmentChar:String = "";
            for (var i:uint = 0, isize:uint = nextSegmentArray.length; i < isize; i++) {
                if (nextSegmentArray[i][0] != -1) {
                    nextSegmentLocation = nextSegmentArray[i][0];
                    nextSegmentChar = nextSegmentArray[i][1];
                    i = isize;
                }
            }
            if (nextSegmentChar == "\"") {
                attributeString = attributeString.substr(nextSegmentLocation); //I hope that didn't contain anything important.
                if (attributeString.indexOf("\"") == 0) {
                    if (attributeString.indexOf("\"", 1) != -1) {
                        //This segment - NextSubVar name String, correct
                        attributeName2 = attributeString.substr(0, attributeString.indexOf("\"", 1) + 1);
                        attributeString = attributeString.substr(attributeString.indexOf("\"", 1) + 1);
                    } else if (attributeString.indexOf(":", 1) != -1) {
                        //This segment - NextSubVar name String, missing end double quote
                        attributeName2 = attributeString.substr(0, attributeString.indexOf(":", 1));
                        attributeString = attributeString.substr(attributeString.indexOf(":", 1));
                    } else {
                        //fuck you, learn to write dialogues for once.
                        //I mean, how inept do you have to be to keep making all these mistakes - 
                        //given a line like {"set":{"var":"value","var2":0}}
                        //Here's where I have to watch out for...
                        //{"set:{"var":"value","var2":0}}
                        //{"set"{"var":"value","var2":0}}
                        //{"set": {"var":"value","var2":0}}
                        //{"set":{ "var":"value","var2":0}}
                        //{"set":{"var""value","var2":0}}
                        //{"set":{"var":value","var2":0}}
                        //etc. how hard is it to just, yknow, write the line properly?
                        attributeString = "";
                    }
                }
                attributeParseMode = "NextSubVal";
            } else if (nextSegmentChar == "}") {
                attributeString = attributeString.substr(nextSegmentLocation + 1);
                attributeParseMode = "NextVar";
                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                lineAttributes.push(lineAttribute);
            }
        } else if (attributeParseMode == "NextSubVal") {
            //This segment - NextSubVal
            //Does it start with "? Find next ", that is value. Else, find next , or }
            //Does it start with something else? Find next , or }.
            if (attributeString.indexOf(":") == 0) {
                attributeString = attributeString.substr(1);
                var nextSegmentArray:Array = new Array();
                nextSegmentArray.push([attributeString.indexOf("}"), "}"]);
                nextSegmentArray.push([attributeString.indexOf("\""), "\""]);
                nextSegmentArray.push([attributeString.indexOf(","), ","]);
                nextSegmentArray.sortOn([0], [Array.NUMERIC]);
                var nextSegmentLocation:int = -1;
                var nextSegmentChar:String = "";
                for (var i:uint = 0, isize:uint = nextSegmentArray.length; i < isize; i++) {
                    if (nextSegmentArray[i][0] != -1) {
                        nextSegmentLocation = nextSegmentArray[i][0];
                        nextSegmentChar = nextSegmentArray[i][1];
                        i = isize;
                    }
                }
                if (nextSegmentChar == "\"") {
                    attributeValue2 = attributeString.substr(0, nextSegmentLocation);
                    attributeString = attributeString.substr(nextSegmentLocation);
                    if (attributeString.indexOf("\"") == 0) {
                        if (attributeString.indexOf("\"", 1) != -1) {
                            attributeValue2 += attributeString.substr(0, attributeString.indexOf("\"", 1) + 1);
                            attributeString = attributeString.substr(attributeString.indexOf("\"", 1) + 1);
                            var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                            if (attributeName == "\"set\"") {
                                addSetValueToVariable(attributeName2, attributeValue2);
                            } else if (attributeName == "\"check\"") {
                                addCheckedValueToVariable(attributeName2, attributeValue2);
                            }
                            lineAttributeValueArray.push(variableReference);
                            attributeParseMode = "NextSubVar";
                        } else {
                            //find next comma or }
                            var nextCommaIndex:int = attributeString.indexOf(",");
                            var nextBackAccoladeIndex:int = attributeString.indexOf("}");
                            if (nextCommaIndex != -1) {
                                if (nextBackAccoladeIndex != -1 && nextBackAccoladeIndex < nextCommaIndex) {
                                    attributeValue2 += attributeString.substr(0, attributeString.indexOf("}"));
                                    attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                    if (attributeName == "\"set\"") {
                                        addSetValueToVariable(attributeName2, attributeValue2);
                                    } else if (attributeName == "\"check\"") {
                                        addCheckedValueToVariable(attributeName2, attributeValue2);
                                    }
                                    lineAttributeValueArray.push(variableReference);
                                    lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                    lineAttributes.push(lineAttribute);
                                    attributeParseMode = "NextVar";
                                } else {
                                    attributeValue2 += attributeString.substr(0, attributeString.indexOf(","));
                                    attributeString = attributeString.substr(attributeString.indexOf(",") + 1);
                                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                    if (attributeName == "\"set\"") {
                                        addSetValueToVariable(attributeName2, attributeValue2);
                                    } else if (attributeName == "\"check\"") {
                                        addCheckedValueToVariable(attributeName2, attributeValue2);
                                    }
                                    lineAttributeValueArray.push(variableReference);
                                    attributeParseMode = "NextSubVar";
                                }
                            } else if (nextBackAccoladeIndex != -1) {
                                attributeValue2 += attributeString.substr(0, attributeString.indexOf("}"));
                                attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                                var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                if (attributeName == "\"set\"") {
                                    addSetValueToVariable(attributeName2, attributeValue2);
                                } else if (attributeName == "\"check\"") {
                                    addCheckedValueToVariable(attributeName2, attributeValue2);
                                }
                                lineAttributeValueArray.push(variableReference);
                                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                lineAttributes.push(lineAttribute);
                                attributeParseMode = "NextVar";
                            } else {
                                //we fucked
                                attributeValue2 += attributeString;
                                attributeString = "";
                                var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                if (attributeName == "\"set\"") {
                                    addSetValueToVariable(attributeName2, attributeValue2);
                                } else if (attributeName == "\"check\"") {
                                    addCheckedValueToVariable(attributeName2, attributeValue2);
                                }
                                lineAttributeValueArray.push(variableReference);
                                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                lineAttributes.push(lineAttribute);
                                attributeParseMode = "NextVar"; //safety measure
                            }
                        }
                    } else {
                        //yeah so now what
                        attributeString = "";
                    }
                } else if (nextSegmentChar == ",") {
                    attributeValue2 = attributeString.substr(0, attributeString.indexOf(","));
                    attributeString = attributeString.substr(attributeString.indexOf(",") + 1);
                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                    if (attributeName == "\"set\"") {
                        addSetValueToVariable(attributeName2, attributeValue2);
                    } else if (attributeName == "\"check\"") {
                        addCheckedValueToVariable(attributeName2, attributeValue2);
                    }
                    lineAttributeValueArray.push(variableReference);
                    attributeParseMode = "NextSubVar";
                } else if (nextSegmentChar == "}") {
                    attributeValue2 = attributeString.substr(0, attributeString.indexOf("}"));
                    attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                    if (attributeName == "\"set\"") {
                        addSetValueToVariable(attributeName2, attributeValue2);
                    } else if (attributeName == "\"check\"") {
                        addCheckedValueToVariable(attributeName2, attributeValue2);
                    }
                    lineAttributeValueArray.push(variableReference);
                    lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                    lineAttributes.push(lineAttribute);
                    attributeParseMode = "NextVar";
                } else {
                    //stop fucking things up, man
                    attributeString = "";
                }
            } else {
                //I've had it.
                var variableReference:VariableReference = new VariableReference(attributeName2, null);
                lineAttributeValueArray.push(variableReference);
                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                lineAttributes.push(lineAttribute);
                attributeString = "";
            }
            
        }
    }
    //source:
    //1|
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    //atributeString:
    //1|{"style":"Him"}
    //2|
    //3|
    //4|
    //5|
    //6|
    lineExcess[2] = source;
    line = new Line(lineNumber, lineName, rawLine, lineContent, lineContentString, lineExcess, lineAttributes, lineAttributesString);
    return line;
}
Source Link
Pimgd
  • 22.3k
  • 5
  • 66
  • 144

Parsing a Dialogue Script - fixing my 620 line mess

Given a Dialogue Script for SDT (NSFW! Google at own risk), parse said dialogue script for validation with checkers. A dialogue script consists of a set of lines.

There's a special line, initial_settings, formatted like such:

initial_settings:{"variable1":numericvalue,"variable2":"stringvalue"}

The rest of the lines follow this format:

linename:"content[TRIGGER]*insertion*" {"attribute":"value","complexattribute":{"variable":numericvalue,"variable2":"stringvalue"}}

Class variables:

//The lines of the dialogue, stored in order of appearance.
private var lines:Array;
//A dictionary of the lines. Structured like so:
//Indexed by linename. Contents are an array of indexes for the lines array.
private var linesDictionary:Array;
//The entire dialogue in string format, if available.
private var dialogue:String;
//String-indexed.
private var variables:Array;

Parsing a dialogue...

public function parseDialogueFromString(dialogueText:String):void {
    var a:Array = dialogueText.split("\r"); //Maybe I should split by \r\n first, then by \n\r, then by \r, then by \n?
    if (a.length == 1) {
        a = dialogueText.split("\n");
    }
    //Parse each line individually...
    for (var i:uint = 0, isize:uint = a.length; i < isize; i++) {
        var line:Line = parseLine(a[i], i);
        //Then place them in the dictionary and main array.
        if (line != null) {
            if (linesDictionary[line.getLineName()] == null) {
                linesDictionary[line.getLineName()] = new Array();
            }
            linesDictionary[line.getLineName()].push(lines.push(line) - 1);
        }
    }
}

line by line.

/**
 * Parses a single Line of Dialogue from a String.
 * Note that since SDT still parses commented lines, the DialogueChecker has to do so as well.
 * Certain severe issues don't care that a line is unused, for instance.
 * @param   lineText the string that should be converted to a Line object.
 * @param   lineIndex the index of the line in the array - used for determining lineNumber.
 * @return a line object if possible, nothing otherwise.
 */
private function parseLine(lineText:String, lineIndex:uint):Line {
    var lineNumber:uint = lineIndex + 1;
    var lineName:String = "";
    var rawLine:String = lineText;
    var lineContent:Array = new Array();
    var lineExcess:Array = ["", "", ""];
    var lineAttribute:LineAttribute = null;
    var lineAttributeValueArray:Array = new Array();
    var lineAttributes:Array = new Array();
    var lineAttributesString:String = "";
    var lineContentString:String = "";
    var line:Line = null;
    
    //1|intro:"Hi!" {"style":"Him"}
    //2|general : "Hello"
    //3|help"me"{"next":"pls"}
    //4|held:Let go!{"held":"true"}
    //5|under_line"Testing!"
    //6|font:Arial
    var source:String = lineText;
    var nameEnd:uint = 0;
    if (source.indexOf(":") != -1 || source.indexOf("\"") != -1) {
        //valid for all 6 examples.
        //Needs : or " to be a line. Otherwise it's not parsable.
        if (source.indexOf(":") != -1) {
            nameEnd = source.indexOf(":");
        } else {
            nameEnd = source.indexOf("\"");
        }
    } else {
        return null; //Comment Filter!
    }
    lineName = source.substr(0, nameEnd);
    source = source.substring(nameEnd);
    //1|:"Hi!" {"style":"Him"}
    //2|: "Hello"
    //3|:"pls"}
    //4|:Let go!{"held":"true"}
    //5|"Testing!"
    //6|:Arial
    var contentStart:int = 0;
    if (source.indexOf(":") == 0) {
        source = source.substring(1);
        //1|"Hi!" {"style":"Him"}
        //2| "Hello"
        //3|"pls"}
        //4|Let go!{"held":"true"}
        //6|Arial
        contentStart = source.indexOf("\"");
    }
    if (contentStart >= 0 && lineName != "initial_settings") {
        lineExcess[0] = source.substr(0, contentStart);
        source = source.substr(contentStart);
    }
    //1|"Hi!" {"style":"Him"}
    //2|"Hello"
    //3|"pls"}
    //4|"held":"true"}
    //5|"Coming!"
    //6|Arial
    var contentString:String = "";
    if (source.indexOf("\"") == 0 && source.indexOf("\"", 1) != -1 && lineName != "initial_settings") {
        contentString = source.substr(0, source.indexOf("\"", 1) + 1);
        source = source.substr(source.indexOf("\"", 1) + 1);
    }
    //source:
    //1| {"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|Arial
    
    //contentString:
    //1|"Hi!"
    //2|"Hello"
    //3|"pls"
    //4|"held"
    //5|"Coming!"
    //6|
    if (contentString == "" && lineName != "initial_settings") {
        contentString = source;
        source = "";
    }
    lineContentString = contentString;
    //Parse contentString
    //When parsing a contentString, you're treating everything as normal string until...
    //Б - change to delayString (new normal string, keep until different character)
    //% - change to special char (new normal string, parse next 2 chars, keep that as pattern, then parse until pattern is broken)
    //* - change to VariableInsertion/VariableSubstitution (Treat as Insertion until YOU/ME/YOUR/MY/FINISHES is detected)
    //[ - change to Trigger
    var contentParseMode:String = "Normal"; //Remember kids, Stringly-typed code is bad! What I'm doing here is bad! So don't do it! (Even although it makes things easier sometimes...)
    var currentContentSegment:String = "";
    
    while (contentString != "") {
        if (contentParseMode == "Normal") {
            //The problem: 4 functions give 4 values. Which function gives the lowest positive value?
            var checkArray:Array = new Array();
            checkArray.push([contentString.indexOf("Б"), "Б"]);
            checkArray.push([contentString.indexOf("%"), "%"]);
            checkArray.push([contentString.indexOf("*"), "*"]);
            checkArray.push([contentString.indexOf("["), "["]);
            checkArray.sortOn([0], [Array.NUMERIC]);
            var nextIssue:int = -1;
            var nextChar:String = "";
            for (var i:uint = 0, isize:uint = checkArray.length; i < isize; i++) {
                if (checkArray[i][0] != -1) {
                    nextIssue = checkArray[i][0];
                    nextChar = checkArray[i][1];
                    i = isize;
                }
            }
            if (nextChar == "Б" || nextChar == "%" || nextChar == "*" || nextChar == "[") {
                
                if (nextChar == "Б") {
                    contentParseMode = "Delay";
                } else if (nextChar == "*" || (nextChar == "%" && contentString.indexOf("%2A") == nextIssue)) {
                    contentParseMode = "Insertion";
                } else if (nextChar == "%") {
                    //trace(contentString.indexOf("%2A")); //commented because it was just extra noise for me as dev and I don't know why I had put this in (v2.13)
                    contentParseMode = "SpecialChar";
                } else if (nextChar == "[") {
                    contentParseMode = "Trigger";
                }
                currentContentSegment = contentString.substr(0, nextIssue);
                lineContent.push(currentContentSegment);
                currentContentSegment = "";
                contentString = contentString.substr(nextIssue);
            } else {
                currentContentSegment = contentString;
                lineContent.push(currentContentSegment);
                currentContentSegment = "";
                contentString = "";
            }
        } else if (contentParseMode == "Delay") {
            //Loop until next char is not delay char. Add all looped chars to content array, strip from content string. Evaluate next section.
            while (contentString.length > 0 && contentString.indexOf("Б") == 0) {
                currentContentSegment += "Б";
                contentString = contentString.substr(1);
            }
            lineContent.push(currentContentSegment);
            currentContentSegment = "";
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "SpecialChar") {
            //Identify special char type, loop until pattern is not next section of string. Add all looped chars to content array, strip from content string. Evaluate next section.
            var specialCharPattern:String = contentString.substr(0, 3);
            //check if it's actually a percent encoded value here
            var hexString:String = "0123456789ABCDEFabcdef";
            if (!(specialCharPattern.length == 3 && hexString.indexOf(specialCharPattern.charAt(1)) != -1 && hexString.indexOf(specialCharPattern.charAt(2)) != -1)) {
                //it's not a proper percent encoded value.
                //treat the % as %25, and strip only 1 character.
                
                currentContentSegment = specialCharPattern;
                contentString = contentString.substring(1); //strip 1 char
            } else {
                while (contentString.length > 0 && contentString.indexOf(specialCharPattern) == 0) {
                    currentContentSegment += specialCharPattern;
                    contentString = contentString.substr(specialCharPattern.length);
                }
            }
            lineContent.push(currentContentSegment);
            currentContentSegment = "";
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "Trigger") {
            //Find trigger and identify trigger if possible...?
            //TODO: Identify variable insertions within triggers
            if (contentString.indexOf("[") != 0) {
                ErrorManager.showDevelopmentError("Line parsing - trigger doesn't start with [");
                contentString = "";
                break;
            }
            var trigger:String = "";
            if (contentString.indexOf("]") != -1) {
                trigger = contentString.substr(1, contentString.indexOf("]") - 1);
                contentString = contentString.substr(contentString.indexOf("]") + 1);
            } else {
                //user error, so ignore it. Act like rest of line is trigger.
                trigger = contentString.substr(1);
                contentString = "";
            }
            var type:TriggerType = TriggerType.identifyType(trigger);
            var triggerObject:Trigger = new Trigger(trigger, type);
            lineContent.push(triggerObject);
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else if (contentParseMode == "Insertion") {
            //Find insertion and determine type.
            if (contentString.indexOf("*") != 0 && contentString.indexOf("%2A") != 0) {
                ErrorManager.showDevelopmentError("Line parsing - insertion doesn't start with *");
                contentString = "";
                break;
            }
            var insertion:String = "";
            var indexOfAsterisk:int = contentString.indexOf("*");
            var indexOfPercent2A:int = contentString.indexOf("%2A");
            var indexOfSecondAsterisk:int = contentString.indexOf("*", 1);
            var indexOfSecondPercent2A:int = contentString.indexOf("%2A", 1);
            if (Math.max(indexOfSecondAsterisk, indexOfSecondPercent2A) != -1) {
                var insertionIndex:int = indexOfAsterisk;
                var insertionEndIndex:int = indexOfSecondAsterisk;
                var insertionStartLength:uint = 1;
                var insertionEndLength:uint = 1;
                if (indexOfAsterisk == -1 || (indexOfPercent2A < indexOfAsterisk && indexOfPercent2A != -1)) {
                    insertionIndex = indexOfPercent2A;
                    insertionStartLength = 3;
                }
                if (indexOfSecondAsterisk == -1 || (indexOfSecondPercent2A < indexOfSecondAsterisk && indexOfSecondPercent2A != -1)) {
                    insertionEndIndex = indexOfSecondPercent2A;
                    insertionEndLength = 3;
                }
                insertion = contentString.substr(insertionStartLength, insertionEndIndex - insertionStartLength);
                contentString = contentString.substr(insertionEndIndex + insertionEndLength);
            } else {
                //user error, so ignore it. Act like rest of line is insertion.
                insertion = contentString.substr(1);
                contentString = "";
            }
            var insertionObject:LineElement = null;
            if (insertion.indexOf("YOU") != -1 || insertion.indexOf("ME") != -1 || insertion.indexOf("MY") != -1 || insertion.indexOf("FINISHES") != -1) { //There's also YOUR, but that's already covered by checking for YOU.
                insertionObject = new VariableSubstitution(insertion);
            } else {
                var variable:Variable = getVariable("\"" + insertion + "\"");
                if (variable == null) {
                    variable = new Variable(VariableType.getEnum("UNKNOWN"), "\"" + insertion + "\"");
                    addVariable(variable);
                }
                variable.setInsertedInDialogue(true);
                insertionObject = new VariableInsertion(variable, insertion);
            }
            lineContent.push(insertionObject);
            if (contentString.charAt(0) == "%" && contentString.indexOf("%2A") != 0) {
                contentParseMode = "SpecialChar";
            } else if (contentString.charAt(0) == "[") {
                contentParseMode = "Trigger";
            } else if (contentString.charAt(0) == "*" || contentString.indexOf("%2A") == 0) {
                contentParseMode = "Insertion";
            } else if (contentString.charAt(0) == "Б") {
                contentParseMode = "Delay";
            } else {
                contentParseMode = "Normal";
            }
        } else {
            contentString = "";
            ErrorManager.showDevelopmentError("Line parsing infinite loop");
        }
    }
    //source:
    //1| {"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    
    //contentString:
    //1|"Hi!"
    //2|"Hello"
    //3|"pls"
    //4|"held"
    //5|"Testing!"
    //6|Arial
    var attributeStart:int = 0;
    var hasAttributes:Boolean = false;
    if (source.indexOf("{") != -1) {
        hasAttributes = true;
        attributeStart = source.indexOf("{");
        lineExcess[1] = source.substr(0, source.indexOf("{"));
    }
    //source:
    //1|{"style":"Him"}
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    var attributeString:String = "";
    if (hasAttributes) {
        var endIndex:Number = Math.max(source.lastIndexOf("{"), source.lastIndexOf("}"));
        attributeString = source.substr(attributeStart, endIndex + 1);
        source = source.substr(endIndex + 1);
    }
    //Parse attributeString
    lineAttributesString = attributeString;
    var attributeParseMode:String = "Start"; //Oh no Pim, more stringly-typed coding! What are you doing?! I'm trying to make things work, that's what I'm doing.
    var attributeName:String = "";
    var attributeValue:Object; //LineAttributes can be multiple variable references.
    var attributeName2:String = "";
    var attributeValue2:String = "";
    while (attributeString != "") {
        //I really should have included an initial_settings line in my examples.
        if (attributeParseMode == "Start") {
            if (attributeString.indexOf("{") != 0) {
                //Not sure who is to blame here. Me or the user?
                attributeString = "";
            } else {
                attributeString = attributeString.substr(1);
                attributeParseMode = "NextVar";
            }
        } else if (attributeParseMode == "NextVar") {
            //nextvar, reset.
            attributeName = "";
            attributeValue = null;
            attributeName2 = "";
            attributeValue2 = "";
            var start:int = attributeString.indexOf("\"");
            if (start != -1) {
                var end:int = attributeString.indexOf("\"", start + 1);
                attributeName = attributeString.substring(start, end + 1);
                attributeString = attributeString.substr(end + 1);
                attributeParseMode = "NextVal";
            } else {
                if (attributeString.indexOf("}") == 0) {
                    attributeString = attributeString.substr(1);
                }
                //No more variables, so this is the end.
                //source = attributeString + source; //<- I had that line of code, but lineAttributesString makes it unnecessary. I think. Gotta test it first.
                attributeString = "";
            }
        } else if (attributeParseMode == "NextVal") {
            if (attributeString.indexOf(":") != 0) {
                //user error. Now what.
                //Decided to add string version of lineAttributes, so I can always detect shit like missing }.
                //Same goes for this. whatever.
                attributeString = "";
            } else {
                attributeString = attributeString.substr(1);
                if (attributeString.charAt(0) == "\"") {
                    if (attributeString.indexOf("\"", 1) != -1) {
                        attributeValue = attributeString.substring(0, attributeString.indexOf("\"", 1) + 1);
                        attributeString = attributeString.substring(attributeString.indexOf("\"", 1) + 1);
                    } else {
                        attributeValue = attributeString;
                    }
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            if (attributeValue == "\"false\"" || attributeValue == "\"true\"") {
                                variable = new Variable(VariableType.getEnum("BOOLEAN"), attributeName);
                            } else {
                                variable = new Variable(VariableType.getEnum("STRING"), attributeName);
                            }
                            addVariable(variable);
                        } else if (variable.getType() == VariableType.getEnum("UNKNOWN")) {
                            if (attributeValue == "\"false\"" || attributeValue == "\"true\"") {
                                variable.setType(VariableType.getEnum("BOOLEAN"));
                            } else {
                                variable.setType(VariableType.getEnum("STRING"));
                            }
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                } else if (attributeString.indexOf("{") == 0) {
                    //Array.
                    attributeString = attributeString.substr(1);
                    lineAttributeValueArray = new Array();
                    attributeParseMode = "NextSubVar";
                } else if (attributeString.indexOf(",") != -1 || attributeString.indexOf("}") != -1) {
                    //Numeric.
                    var nextcomma:int = attributeString.indexOf(",");
                    var nextbackaccolade:int = attributeString.indexOf("}");
                    if (nextcomma != -1) {
                        if (nextbackaccolade != -1 && nextbackaccolade < nextcomma) { //I don't know either.
                            attributeValue = attributeString.substr(0, nextbackaccolade);
                            attributeString = attributeString.substr(nextbackaccolade);
                        } else {
                            attributeValue = attributeString.substr(0, nextcomma);
                            attributeString = attributeString.substr(nextcomma);
                        }
                    } else {
                        attributeValue = attributeString.substr(0, nextbackaccolade);
                        attributeString = attributeString.substr(nextbackaccolade);
                    }
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            variable = new Variable(VariableType.getEnum("NUMERIC"), attributeName);
                            addVariable(variable);
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                } else {
                    //Yeaahh... damn users again.
                    //Unknown type, but lets register the attribute anyway.
                    attributeValue = attributeString;
                    attributeString = "";
                    lineAttribute = new LineAttribute(attributeName, attributeValue);
                    lineAttributes.push(lineAttribute);
                    if (lineName == "initial_settings") {
                        var variable:Variable = getVariable(attributeName);
                        if (variable == null) {
                            variable = new Variable(VariableType.getEnum("UNKNOWN"), attributeName);
                            addVariable(variable);
                        }
                        variable.setInitialValue(attributeValue);
                    }
                    attributeParseMode = "NextVar";
                }
            }
        } else if (attributeParseMode == "NextSubVar") {
            //"name":??? (value)
            attributeName2 = "";
            attributeValue2 = "";
            var nextSegmentArray:Array = new Array();
            nextSegmentArray.push([attributeString.indexOf("}"), "}"]);
            nextSegmentArray.push([attributeString.indexOf("\""), "\""]);
            nextSegmentArray.sortOn([0], [Array.NUMERIC]);
            var nextSegmentLocation:int = -1;
            var nextSegmentChar:String = "";
            for (var i:uint = 0, isize:uint = nextSegmentArray.length; i < isize; i++) {
                if (nextSegmentArray[i][0] != -1) {
                    nextSegmentLocation = nextSegmentArray[i][0];
                    nextSegmentChar = nextSegmentArray[i][1];
                    i = isize;
                }
            }
            if (nextSegmentChar == "\"") {
                attributeString = attributeString.substr(nextSegmentLocation); //I hope that didn't contain anything important.
                if (attributeString.indexOf("\"") == 0) {
                    if (attributeString.indexOf("\"", 1) != -1) {
                        //This segment - NextSubVar name String, correct
                        attributeName2 = attributeString.substr(0, attributeString.indexOf("\"", 1) + 1);
                        attributeString = attributeString.substr(attributeString.indexOf("\"", 1) + 1);
                    } else if (attributeString.indexOf(":", 1) != -1) {
                        //This segment - NextSubVar name String, missing end double quote
                        attributeName2 = attributeString.substr(0, attributeString.indexOf(":", 1));
                        attributeString = attributeString.substr(attributeString.indexOf(":", 1));
                    } else {
                        //fuck you, learn to write dialogues for once.
                        //I mean, how inept do you have to be to keep making all these mistakes - 
                        //given a line like {"set":{"var":"value","var2":0}}
                        //Here's where I have to watch out for...
                        //{"set:{"var":"value","var2":0}}
                        //{"set"{"var":"value","var2":0}}
                        //{"set": {"var":"value","var2":0}}
                        //{"set":{ "var":"value","var2":0}}
                        //{"set":{"var""value","var2":0}}
                        //{"set":{"var":value","var2":0}}
                        //etc. how hard is it to just, yknow, write the line properly?
                        attributeString = "";
                    }
                }
                attributeParseMode = "NextSubVal";
            } else if (nextSegmentChar == "}") {
                attributeString = attributeString.substr(nextSegmentLocation + 1);
                attributeParseMode = "NextVar";
                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                lineAttributes.push(lineAttribute);
            }
        } else if (attributeParseMode == "NextSubVal") {
            //This segment - NextSubVal
            //Does it start with "? Find next ", that is value. Else, find next , or }
            //Does it start with something else? Find next , or }.
            if (attributeString.indexOf(":") == 0) {
                attributeString = attributeString.substr(1);
                var nextSegmentArray:Array = new Array();
                nextSegmentArray.push([attributeString.indexOf("}"), "}"]);
                nextSegmentArray.push([attributeString.indexOf("\""), "\""]);
                nextSegmentArray.push([attributeString.indexOf(","), ","]);
                nextSegmentArray.sortOn([0], [Array.NUMERIC]);
                var nextSegmentLocation:int = -1;
                var nextSegmentChar:String = "";
                for (var i:uint = 0, isize:uint = nextSegmentArray.length; i < isize; i++) {
                    if (nextSegmentArray[i][0] != -1) {
                        nextSegmentLocation = nextSegmentArray[i][0];
                        nextSegmentChar = nextSegmentArray[i][1];
                        i = isize;
                    }
                }
                if (nextSegmentChar == "\"") {
                    attributeValue2 = attributeString.substr(0, nextSegmentLocation);
                    attributeString = attributeString.substr(nextSegmentLocation);
                    if (attributeString.indexOf("\"") == 0) {
                        if (attributeString.indexOf("\"", 1) != -1) {
                            attributeValue2 += attributeString.substr(0, attributeString.indexOf("\"", 1) + 1);
                            attributeString = attributeString.substr(attributeString.indexOf("\"", 1) + 1);
                            var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                            if (attributeName == "\"set\"") {
                                addSetValueToVariable(attributeName2, attributeValue2);
                            } else if (attributeName == "\"check\"") {
                                addCheckedValueToVariable(attributeName2, attributeValue2);
                            }
                            lineAttributeValueArray.push(variableReference);
                            attributeParseMode = "NextSubVar";
                        } else {
                            //find next comma or }
                            var nextCommaIndex:int = attributeString.indexOf(",");
                            var nextBackAccoladeIndex:int = attributeString.indexOf("}");
                            if (nextCommaIndex != -1) {
                                if (nextBackAccoladeIndex != -1 && nextBackAccoladeIndex < nextCommaIndex) {
                                    attributeValue2 += attributeString.substr(0, attributeString.indexOf("}"));
                                    attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                    if (attributeName == "\"set\"") {
                                        addSetValueToVariable(attributeName2, attributeValue2);
                                    } else if (attributeName == "\"check\"") {
                                        addCheckedValueToVariable(attributeName2, attributeValue2);
                                    }
                                    lineAttributeValueArray.push(variableReference);
                                    lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                    lineAttributes.push(lineAttribute);
                                    attributeParseMode = "NextVar";
                                } else {
                                    attributeValue2 += attributeString.substr(0, attributeString.indexOf(","));
                                    attributeString = attributeString.substr(attributeString.indexOf(",") + 1);
                                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                    if (attributeName == "\"set\"") {
                                        addSetValueToVariable(attributeName2, attributeValue2);
                                    } else if (attributeName == "\"check\"") {
                                        addCheckedValueToVariable(attributeName2, attributeValue2);
                                    }
                                    lineAttributeValueArray.push(variableReference);
                                    attributeParseMode = "NextSubVar";
                                }
                            } else if (nextBackAccoladeIndex != -1) {
                                attributeValue2 += attributeString.substr(0, attributeString.indexOf("}"));
                                attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                                var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                if (attributeName == "\"set\"") {
                                    addSetValueToVariable(attributeName2, attributeValue2);
                                } else if (attributeName == "\"check\"") {
                                    addCheckedValueToVariable(attributeName2, attributeValue2);
                                }
                                lineAttributeValueArray.push(variableReference);
                                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                lineAttributes.push(lineAttribute);
                                attributeParseMode = "NextVar";
                            } else {
                                //we fucked
                                attributeValue2 += attributeString;
                                attributeString = "";
                                var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                                if (attributeName == "\"set\"") {
                                    addSetValueToVariable(attributeName2, attributeValue2);
                                } else if (attributeName == "\"check\"") {
                                    addCheckedValueToVariable(attributeName2, attributeValue2);
                                }
                                lineAttributeValueArray.push(variableReference);
                                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                                lineAttributes.push(lineAttribute);
                                attributeParseMode = "NextVar"; //safety measure
                            }
                        }
                    } else {
                        //yeah so now what
                        attributeString = "";
                    }
                } else if (nextSegmentChar == ",") {
                    attributeValue2 = attributeString.substr(0, attributeString.indexOf(","));
                    attributeString = attributeString.substr(attributeString.indexOf(",") + 1);
                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                    if (attributeName == "\"set\"") {
                        addSetValueToVariable(attributeName2, attributeValue2);
                    } else if (attributeName == "\"check\"") {
                        addCheckedValueToVariable(attributeName2, attributeValue2);
                    }
                    lineAttributeValueArray.push(variableReference);
                    attributeParseMode = "NextSubVar";
                } else if (nextSegmentChar == "}") {
                    attributeValue2 = attributeString.substr(0, attributeString.indexOf("}"));
                    attributeString = attributeString.substr(attributeString.indexOf("}") + 1);
                    var variableReference:VariableReference = new VariableReference(attributeName2, attributeValue2);
                    if (attributeName == "\"set\"") {
                        addSetValueToVariable(attributeName2, attributeValue2);
                    } else if (attributeName == "\"check\"") {
                        addCheckedValueToVariable(attributeName2, attributeValue2);
                    }
                    lineAttributeValueArray.push(variableReference);
                    lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                    lineAttributes.push(lineAttribute);
                    attributeParseMode = "NextVar";
                } else {
                    //stop fucking things up, man
                    attributeString = "";
                }
            } else {
                //I've had it.
                var variableReference:VariableReference = new VariableReference(attributeName2, null);
                lineAttributeValueArray.push(variableReference);
                lineAttribute = new LineAttribute(attributeName, lineAttributeValueArray);
                lineAttributes.push(lineAttribute);
                attributeString = "";
            }
            
        }
    }
    //source:
    //1|
    //2|
    //3|}
    //4|:"true"}
    //5|
    //6|
    //atributeString:
    //1|{"style":"Him"}
    //2|
    //3|
    //4|
    //5|
    //6|
    lineExcess[2] = source;
    line = new Line(lineNumber, lineName, rawLine, lineContent, lineContentString, lineExcess, lineAttributes, lineAttributesString);
    return line;
}

I'm looking for a way to split up my MASSIVE parse function into something more manageable. Improvements on a smaller scale are also welcomed.