public static RootNode ParseFileContents(string[] lines, string fileName)
        {
            CurrentFileBeingParsed = fileName;

            // ERROR HANDLING OUTSIDE OF THIS FUNCTION
            var      balanceStack      = new Stack <UberNode>();
            RootNode root              = new RootNode(null, null);
            RootNode currentRoot       = root;       // spawn nodes are root nodes
            UberNode current           = root;
            UberNode prev              = null;
            int      currentLineNumber = 0;
            int      openCurlyBraces   = 0;

            var cleanedStrings = new List <CleanLine>();

            foreach (string line in lines)
            {
                //Console.WriteLine(line);
                currentLineNumber++;

                StringBuilder sb          = new StringBuilder();
                string        cleanedLine = line.Trim();

                if (cleanedLine.StartsWith("//"))
                {
                    continue;
                }

                bool insideQuotation            = false;
                bool justEscapedBackslash       = false;
                bool possibleComment            = false;
                int  forLoopRemainingSemiColons = 0;

                for (int i = 0; i < cleanedLine.Length; i++)
                {
                    if (cleanedLine[i] == '\t')
                    {
                        continue;                         // always remove tabs... not allowed even in strings..
                    }

                    if (cleanedLine[i] == '\\')
                    {
                        if (!insideQuotation)
                        {
                            throw new UberScriptException(
                                      String.Format(
                                          "Parse: unexpected escape token '\\' in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine));
                        }

                        if (!justEscapedBackslash)
                        {
                            if (i > 0 && cleanedLine[i - 1] == '\\')
                            {
                                justEscapedBackslash = true;
                            }
                        }
                        else
                        {
                            justEscapedBackslash = false;
                        }

                        sb.Append('\\');                         // keep the double backslash in there regardless (it is handled in MathTree.ParseOperand)
                        continue;
                    }

                    if (cleanedLine[i] == '"')
                    {
                        if (!(i > 0 && cleanedLine[i - 1] == '\\' && !justEscapedBackslash))
                        {
                            insideQuotation = !insideQuotation;
                        }

                        sb.Append("\"");                         // add \" to the string
                        continue;
                    }

                    justEscapedBackslash = false;                     // make sure to reset this if you have gotten to this point

                    if (insideQuotation)
                    {
                        sb.Append(cleanedLine[i]);
                        continue;
                    }

                    if (cleanedLine[i] == '/')
                    {
                        if (possibleComment)
                        {
                            sb.Remove(sb.Length - 1, 1);                             // remove the previously added / character
                            break;
                        }

                        possibleComment = true;
                        sb.Append('/');
                        continue;
                    }

                    possibleComment = false;

                    if (cleanedLine[i] == ' ' || cleanedLine[i] == '\t')
                    {
                        continue;
                    }

                    // handle lines with multiple delimiters (e.g. orc { name = "Bob"; } )
                    if (cleanedLine[i] == '{' || cleanedLine[i] == '}')
                    {
                        // check if unbalanced
                        if (cleanedLine[i] == '{')
                        {
                            openCurlyBraces++;
                        }
                        else
                        {
                            openCurlyBraces--;
                        }

                        if (openCurlyBraces < 0)
                        {
                            throw new UberScriptException(
                                      String.Format(
                                          "Parse: brace mismatch detected in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine));
                        }

                        // add everything before the { or } (if there is anything)
                        if (i > 0)
                        {
                            cleanedStrings.Add(new CleanLine(currentLineNumber, sb.ToString()));
                        }

                        cleanedStrings.Add(new CleanLine(currentLineNumber, cleanedLine[i].ToString(CultureInfo.InvariantCulture)));

                        // add { or } on separate "line" (it's processed as a line)
                        cleanedLine = cleanedLine.Substring(i + 1);
                        i           = -1;
                        sb.Clear();

                        continue;
                    }

                    if (cleanedLine[i] == ';')
                    {
                        if (cleanedLine.Length == i + 1)
                        {
                            break;
                        }                         // we are at the end of the loop, ignore lines ending in semicolon

                        string stringPreviousToSemiColon = sb.ToString();

                        if (stringPreviousToSemiColon.Contains("for"))
                        {
                            if (stringPreviousToSemiColon.Contains("foreach("))
                            {
                                forLoopRemainingSemiColons += 1;
                            }
                            else if (stringPreviousToSemiColon.Contains("for("))                             // for loop has 2 semicolons
                            {
                                forLoopRemainingSemiColons += 2;
                            }
                        }

                        if (forLoopRemainingSemiColons == 0)
                        {
                            // we have encountered a statement-separating semi-colon!
                            // add everything before the { or } (if there is anything)
                            if (i > 0)
                            {
                                cleanedStrings.Add(new CleanLine(currentLineNumber, sb.ToString()));
                            }

                            cleanedLine = cleanedLine.Substring(i + 1);
                            i           = -1;
                            sb.Clear();

                            continue;
                        }

                        forLoopRemainingSemiColons--;
                    }

                    sb.Append(cleanedLine[i]);
                }

                cleanedLine = sb.ToString();

                if (!String.IsNullOrWhiteSpace(cleanedLine) && !cleanedLine.StartsWith("//"))
                {
                    cleanedStrings.Add(new CleanLine(currentLineNumber, sb.ToString()));
                }
            }

            foreach (CleanLine cleanedString in cleanedStrings)
            {
                // i have to use this CleanLine object to keep track of line numbers
                // in spite of {, }, and ; characters splitting the lines up!
                currentLineNumber = cleanedString.LineNumber;

                string cleanedLine = cleanedString.Value;

                if (cleanedLine == "" || cleanedLine.StartsWith("//"))
                {
                    continue;
                }

                if (cleanedLine == "{")
                {
                    balanceStack.Push(current);

                    if (prev != null)
                    {
                        current = prev;
                    }

                    continue;
                }

                if (cleanedLine == "}")
                {
                    if (balanceStack.Count > 0)
                    {
                        current = balanceStack.Pop();

                        if (current is RootNode)
                        {
                            // likely we have finished procesinto a spawn nodes (ChildRoots)
                            // and therefore need to go back to the spawn node's root
                            // so triggers are correctly assigned to it
                            currentRoot = current.GetRootNode;
                        }

                        continue;
                    }

                    throw new UberScriptException(
                              String.Format(
                                  "Parse: brace mismatch detected in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine));
                }
                // remove ending ";" characters (doesn't matter if you end with ; or not)
                // CANNOT do more than one statement on a line, though.

                // determine which type of node it is
                string lowerCase = cleanedLine.ToLower();

                if (UberScriptTriggers.HasTrigger(lowerCase))
                {
                    prev = new TriggerNode(current, lowerCase);

                    //Console.WriteLine(current + "->" + prev);

                    currentRoot.TriggerNodes.Add(prev as TriggerNode);
                }
                else if (char.IsDigit(cleanedLine[0]))                 // must be a sequence node
                {
                    int stage;

                    if (!Int32.TryParse(cleanedLine, out stage))
                    {
                        throw new UberScriptException(
                                  String.Format(
                                      "Parse: incorrect sequence format, integer expected at line {0}:\n{1}", currentLineNumber, cleanedLine));
                    }

                    prev = new SequenceNode(current, cleanedLine, stage);
                }
                else if (cleanedLine.StartsWith("foreach"))
                {
                    /*
                     * objs.moblist = GETNEARBYMOBS(TRIGMOB(),5)
                     *
                     * foreach(objs.mob ; objs.moblist)
                     * {
                     * }
                     */

                    prev = new ForEachNode(current, cleanedLine, currentLineNumber);
                }
                else if (cleanedLine.StartsWith("for"))
                {
                    /*
                     * for(ints.i = 0; ints.i < TRIGMOB().hits; ints.i = ints.i + 1)
                     * {
                     * }
                     */

                    prev = new ForLoopNode(current, cleanedLine, currentLineNumber);
                }
                else if (cleanedLine.StartsWith("if") || cleanedLine.StartsWith("elif") || cleanedLine.StartsWith("else") ||
                         cleanedLine.StartsWith("elseif"))
                {
                    prev = new ConditionalNode(current, cleanedLine, currentLineNumber);

                    //Console.WriteLine(current + "->" + prev);
                }
                else if (cleanedLine.StartsWith("return"))
                {
                    prev = new ReturnNode(current, cleanedLine);

                    if (cleanedLine == "returnoverride")
                    {
                        ((ReturnNode)prev).Override = true;
                    }
                }
                else
                {
                    switch (cleanedLine)
                    {
                    case "break":
                    {
                        // check whether it has a foreach / for ancestor
                        UberNode testParent           = current;
                        bool     foundForNodeAncestor = false;

                        while (testParent != null)
                        {
                            if (testParent is ForEachNode || testParent is ForLoopNode)
                            {
                                foundForNodeAncestor = true;
                                break;
                            }
                            testParent = testParent.Parent;
                        }

                        if (!foundForNodeAncestor)
                        {
                            throw new UberScriptException(
                                      String.Format("Parse: unexpected 'break' statement at line {0}:\n{1}", currentLineNumber, cleanedLine));
                        }

                        prev = new BreakNode(current, cleanedLine);
                    }
                    break;

                    case "continue":
                    {
                        // check whether it has a foreach / for ancestor
                        UberNode testParent           = current;
                        bool     foundForNodeAncestor = false;

                        while (testParent != null)
                        {
                            if (testParent is ForEachNode || testParent is ForLoopNode)
                            {
                                foundForNodeAncestor = true;
                                break;
                            }

                            testParent = testParent.Parent;
                        }

                        if (!foundForNodeAncestor)
                        {
                            throw new UberScriptException(
                                      String.Format("Parse: unexpected 'continue' statement at line {0}:\n{1}", currentLineNumber, cleanedLine));
                        }

                        prev = new ContinueNode(current, cleanedLine);
                    }
                    break;

                    default:
                    {
                        if (cleanedLine.StartsWith("pause"))
                        {
                            double pauseDuration;

                            if (!Double.TryParse(cleanedLine.Substring(5), out pauseDuration))
                            {
                                pauseDuration = 0;
                            }

                            prev = new PauseNode(current, cleanedLine, pauseDuration);
                        }
                        else if (cleanedLine.StartsWith("function"))
                        {
                            prev = new UserDefinedFunctionNode(current, lowerCase);
                            //Console.WriteLine(current + "->" + prev);
                            root.UserDefinedFunctionNodes.Add(prev as UserDefinedFunctionNode);
                        }

                        /*
                         * else if (cleanedLine.StartsWith("spawngroup"))
                         * {
                         *      prev = new SpawnGroupNode
                         * }
                         * else if (cleanedLine.StartsWith("spawnregion"))
                         * {
                         *
                         * }
                         * else if (cleanedLine.StartsWith("spawnentry"))
                         * {
                         *
                         * }
                         */
                        else
                        {
                            int equalCount = 0;
                            //char beforeChar = '\0';
                            int  numOpenParentheses = 0;
                            bool inQuotes           = false;

                            for (int i = 0; i < cleanedLine.Length; i++)
                            {
                                if (!inQuotes)
                                {
                                    if (cleanedLine[i] == '=' && numOpenParentheses == 0)
                                    {
                                        equalCount++;
                                    }
                                    else
                                    {
                                        switch (cleanedLine[i])
                                        {
                                        case '(':
                                            numOpenParentheses++;
                                            break;

                                        case ')':
                                        {
                                            numOpenParentheses--;

                                            if (numOpenParentheses < 0)
                                            {
                                                throw new UberScriptException(
                                                          String.Format(
                                                              "Parse: parentheses mismatch detected in '{0}' at line {1}:\n{2}",
                                                              fileName,
                                                              currentLineNumber,
                                                              cleanedLine));
                                            }
                                        }
                                        break;
                                        }
                                    }
                                }

                                if (cleanedLine[i] != '"')
                                {
                                    continue;
                                }

                                inQuotes = !inQuotes;                                         // assume that it's not an escaped quotation

                                // determine if it is an escaped quotation (and that there aren't escaped backslashes preceding it)
                                for (int ix = i - 1; ix >= 0 && cleanedLine[ix] == '\\'; ix--)
                                {
                                    inQuotes = !inQuotes;                                             // for each backslash behind, unescape or escape accordingly
                                }
                            }

                            if (equalCount == 1)                                     // it's a statement node
                            {
                                prev = new StatementNode(current, cleanedLine, currentLineNumber);
                                //Console.WriteLine(current + "->" + prev);

                                if (((StatementNode)prev).ContainsSpawnNode)
                                {
                                    // global_objs.test = orc
                                    // {
                                    //      name = goober
                                    // }
                                    prev.LineNumber = currentLineNumber;                                    // we "continue" here so we set this later
                                    current.Children.Add(prev);                                             // we "continue" here so we don't add it later
                                    //Console.WriteLine(current + "->" + prev + "... adding spawn node to statement node!");

                                    prev = prev.Children[1];                                             // should contain a spawn node
                                    currentRoot.ChildRoots.Add(prev as RootNode);
                                    currentRoot = prev as RootNode;                                      // spawn node become the new root node
                                    continue;
                                }
                            }
                            else if (UberScriptFunctions.IsFunctionString(cleanedLine))
                            {
                                prev = new FunctionNode(current, cleanedLine, currentLineNumber);
                                //Console.WriteLine(current + "->" + prev);
                            }
                            else if (UserDefinedFunctionNode.GetUserDefinedFunctionString(cleanedLine, root) != null)
                            {
                                prev = new UserDefinedFunctionExectueNode(current, cleanedLine, currentLineNumber);

                                ((UserDefinedFunctionExectueNode)prev).UserDefinedFunction =
                                    UserDefinedFunctionNode.GetUserDefinedFunctionString(cleanedLine, root);
                            }
                            else
                            {
                                // the string might have commas (indicating a spawn type with a constructor accepting arguments
                                // such as "static,100"
                                int commaIndex = cleanedLine.IndexOf(',');

                                Type spawnType =
                                    ScriptCompiler.FindTypeByName(commaIndex > 0 ? cleanedLine.Substring(0, commaIndex) : cleanedLine);

                                if (spawnType != null && typeof(Mobile).IsAssignableFrom(spawnType) || typeof(Item).IsAssignableFrom(spawnType))
                                {
                                    prev = new RootNode(current, cleanedLine);
                                    // keep a list of child root nodes so that spawned creatures that have triggers
                                    // specific to them can correctly point to the right place in the script e.g.
                                    // troll
                                    // {
                                    //      onDeath
                                    //      {
                                    //          orc
                                    //          {
                                    //              onDeath
                                    //              {
                                    //                  ...
                                    //              }
                                    //          }
                                    //      }
                                    // }
                                    // --> trigger nodes are only added to their closest root node
                                    currentRoot.ChildRoots.Add(prev as RootNode);
                                    //Console.WriteLine(current + "->" + prev);
                                    currentRoot = prev as RootNode;                                             // spawn node become the new root node
                                }
                                else
                                {
                                    throw new UberScriptException(
                                              String.Format("Parse: could not parse node at line {0}:\n{1}", currentLineNumber, cleanedLine));
                                }
                            }
                        }
                    }
                    break;
                    }
                }

                current.Children.Add(prev);
                prev.LineNumber = currentLineNumber;                 // used for debugging purposes
            }

            if (balanceStack.Count > 0)
            {
                throw new UberScriptException(
                          String.Format("Parse: brace mismatch detected in '{0}' at line {1}", fileName, currentLineNumber));
            }

            return(root);
        }
		public static RootNode ParseFileContents(string[] lines, string fileName)
		{
			CurrentFileBeingParsed = fileName;
			
			// ERROR HANDLING OUTSIDE OF THIS FUNCTION
			var balanceStack = new Stack<UberNode>();
			RootNode root = new RootNode(null, null);
			RootNode currentRoot = root; // spawn nodes are root nodes
			UberNode current = root;
			UberNode prev = null;
			int currentLineNumber = 0;
			int openCurlyBraces = 0;

			var cleanedStrings = new List<CleanLine>();
			
			foreach (string line in lines)
			{
				//Console.WriteLine(line);
				currentLineNumber++;

				StringBuilder sb = new StringBuilder();
				string cleanedLine = line.Trim();
				
				if (cleanedLine.StartsWith("//"))
				{
					continue;
				}

				bool insideQuotation = false;
				bool justEscapedBackslash = false;
				bool possibleComment = false;
				int forLoopRemainingSemiColons = 0;

				for (int i = 0; i < cleanedLine.Length; i++)
				{
					if (cleanedLine[i] == '\t')
					{
						continue; // always remove tabs... not allowed even in strings..
					}

					if (cleanedLine[i] == '\\')
					{
						if (!insideQuotation)
						{
							throw new UberScriptException(
								String.Format(
									"Parse: unexpected escape token '\\' in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine));
						}

						if (!justEscapedBackslash)
						{
							if (i > 0 && cleanedLine[i - 1] == '\\')
							{
								justEscapedBackslash = true;
							}
						}
						else
						{
							justEscapedBackslash = false;
						}

						sb.Append('\\'); // keep the double backslash in there regardless (it is handled in MathTree.ParseOperand)
						continue;
					}

					if (cleanedLine[i] == '"')
					{
						if (!(i > 0 && cleanedLine[i - 1] == '\\' && !justEscapedBackslash))
						{
							insideQuotation = !insideQuotation;
						}
						
						sb.Append("\""); // add \" to the string
						continue;
					}
					
					justEscapedBackslash = false; // make sure to reset this if you have gotten to this point

					if (insideQuotation)
					{
						sb.Append(cleanedLine[i]);
						continue;
					}

					if (cleanedLine[i] == '/')
					{
						if (possibleComment)
						{
							sb.Remove(sb.Length - 1, 1); // remove the previously added / character
							break;
						}
						
						possibleComment = true;
						sb.Append('/');
						continue;
					}
				
					possibleComment = false;

					if (cleanedLine[i] == ' ' || cleanedLine[i] == '\t')
					{
						continue;
					}

					// handle lines with multiple delimiters (e.g. orc { name = "Bob"; } )
					if (cleanedLine[i] == '{' || cleanedLine[i] == '}')
					{
						// check if unbalanced
						if (cleanedLine[i] == '{')
						{
							openCurlyBraces++;
						}
						else
						{
							openCurlyBraces--;
						}

						if (openCurlyBraces < 0)
						{
							throw new UberScriptException(
								String.Format(
									"Parse: brace mismatch detected in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine));
						}

						// add everything before the { or } (if there is anything)
						if (i > 0)
						{
							cleanedStrings.Add(new CleanLine(currentLineNumber, sb.ToString()));
						}
						
						cleanedStrings.Add(new CleanLine(currentLineNumber, cleanedLine[i].ToString(CultureInfo.InvariantCulture)));
						
						// add { or } on separate "line" (it's processed as a line)
						cleanedLine = cleanedLine.Substring(i + 1);
						i = -1;
						sb.Clear();

						continue;
					}
					
					if (cleanedLine[i] == ';')
					{
						if (cleanedLine.Length == i + 1)
						{
							break;
						} // we are at the end of the loop, ignore lines ending in semicolon

						string stringPreviousToSemiColon = sb.ToString();
						
						if (stringPreviousToSemiColon.Contains("for"))
						{
							if (stringPreviousToSemiColon.Contains("foreach("))
							{
								forLoopRemainingSemiColons += 1;
							}
							else if (stringPreviousToSemiColon.Contains("for(")) // for loop has 2 semicolons
							{
								forLoopRemainingSemiColons += 2;
							}
						}
						
						if (forLoopRemainingSemiColons == 0)
						{
							// we have encountered a statement-separating semi-colon!
							// add everything before the { or } (if there is anything)
							if (i > 0)
							{
								cleanedStrings.Add(new CleanLine(currentLineNumber, sb.ToString()));
							}
							
							cleanedLine = cleanedLine.Substring(i + 1);
							i = -1;
							sb.Clear();

							continue;
						}
						
						forLoopRemainingSemiColons--;
					}
					
					sb.Append(cleanedLine[i]);
				}
				
				cleanedLine = sb.ToString();

				if (!String.IsNullOrWhiteSpace(cleanedLine) && !cleanedLine.StartsWith("//"))
				{
					cleanedStrings.Add(new CleanLine(currentLineNumber, sb.ToString()));
				}
			}

			foreach (CleanLine cleanedString in cleanedStrings)
			{
				// i have to use this CleanLine object to keep track of line numbers
				// in spite of {, }, and ; characters splitting the lines up!
				currentLineNumber = cleanedString.LineNumber;
				
				string cleanedLine = cleanedString.Value;
				
				if (cleanedLine == "" || cleanedLine.StartsWith("//"))
				{
					continue;
				}

				if (cleanedLine == "{")
				{
					balanceStack.Push(current);
					
					if (prev != null)
					{
						current = prev;
					}
					
					continue;
				}

				if (cleanedLine == "}")
				{
					if (balanceStack.Count > 0)
					{
						current = balanceStack.Pop();
						
						if (current is RootNode)
						{
							// likely we have finished procesinto a spawn nodes (ChildRoots)
							// and therefore need to go back to the spawn node's root
							// so triggers are correctly assigned to it
							currentRoot = current.GetRootNode;
						}

						continue;
					}

					throw new UberScriptException(
						String.Format(
							"Parse: brace mismatch detected in '{0}' at line {1}:\n{2}", fileName, currentLineNumber, cleanedLine));
				}
				// remove ending ";" characters (doesn't matter if you end with ; or not)
				// CANNOT do more than one statement on a line, though.                

				// determine which type of node it is
				string lowerCase = cleanedLine.ToLower();
				
				if (UberScriptTriggers.HasTrigger(lowerCase))
				{
					prev = new TriggerNode(current, lowerCase);
					
					//Console.WriteLine(current + "->" + prev);
					
					currentRoot.TriggerNodes.Add(prev as TriggerNode);
				}
				else if (char.IsDigit(cleanedLine[0])) // must be a sequence node
				{
					int stage;

					if (!Int32.TryParse(cleanedLine, out stage))
					{
						throw new UberScriptException(
							String.Format(
								"Parse: incorrect sequence format, integer expected at line {0}:\n{1}", currentLineNumber, cleanedLine));
					}

					prev = new SequenceNode(current, cleanedLine, stage);
				}
				else if (cleanedLine.StartsWith("foreach"))
				{
					/*
					objs.moblist = GETNEARBYMOBS(TRIGMOB(),5)
					
					foreach(objs.mob ; objs.moblist)
					{
					}
					*/

					prev = new ForEachNode(current, cleanedLine, currentLineNumber);
				}
				else if (cleanedLine.StartsWith("for"))
				{	
					/*
					for(ints.i = 0; ints.i < TRIGMOB().hits; ints.i = ints.i + 1)
					{
					}
					*/
				
					prev = new ForLoopNode(current, cleanedLine, currentLineNumber);
				}
				else if (cleanedLine.StartsWith("if") || cleanedLine.StartsWith("elif") || cleanedLine.StartsWith("else") ||
						 cleanedLine.StartsWith("elseif"))
				{
					prev = new ConditionalNode(current, cleanedLine, currentLineNumber);
					
					//Console.WriteLine(current + "->" + prev);
				}
				else if (cleanedLine.StartsWith("return"))
				{
					prev = new ReturnNode(current, cleanedLine);
					
					if (cleanedLine == "returnoverride")
					{
						((ReturnNode)prev).Override = true;
					}
				}
				else switch (cleanedLine)
				{
					case "break":
						{
							// check whether it has a foreach / for ancestor
							UberNode testParent = current;
							bool foundForNodeAncestor = false;
							
							while (testParent != null)
							{
								if (testParent is ForEachNode || testParent is ForLoopNode)
								{
									foundForNodeAncestor = true;
									break;
								}
								testParent = testParent.Parent;
							}

							if (!foundForNodeAncestor)
							{
								throw new UberScriptException(
									String.Format("Parse: unexpected 'break' statement at line {0}:\n{1}", currentLineNumber, cleanedLine));
							}

							prev = new BreakNode(current, cleanedLine);
						}
						break;
					case "continue":
						{
							// check whether it has a foreach / for ancestor
							UberNode testParent = current;
							bool foundForNodeAncestor = false;
							
							while (testParent != null)
							{
								if (testParent is ForEachNode || testParent is ForLoopNode)
								{
									foundForNodeAncestor = true;
									break;
								}
								
								testParent = testParent.Parent;
							}

							if (!foundForNodeAncestor)
							{
								throw new UberScriptException(
									String.Format("Parse: unexpected 'continue' statement at line {0}:\n{1}", currentLineNumber, cleanedLine));
							}

							prev = new ContinueNode(current, cleanedLine);
						}
						break;
					default:
						{
							if (cleanedLine.StartsWith("pause"))
							{
								double pauseDuration;

								if (!Double.TryParse(cleanedLine.Substring(5), out pauseDuration))
								{
									pauseDuration = 0;
								}

								prev = new PauseNode(current, cleanedLine, pauseDuration);
							}
							else if (cleanedLine.StartsWith("function"))
							{
								prev = new UserDefinedFunctionNode(current, lowerCase);
								//Console.WriteLine(current + "->" + prev);
								root.UserDefinedFunctionNodes.Add(prev as UserDefinedFunctionNode);
							}
							/*
							else if (cleanedLine.StartsWith("spawngroup"))
							{
								prev = new SpawnGroupNode
							}
							else if (cleanedLine.StartsWith("spawnregion"))
							{

							}
							else if (cleanedLine.StartsWith("spawnentry"))
							{

							}
							*/
							else
							{
								int equalCount = 0;
								//char beforeChar = '\0';
								int numOpenParentheses = 0;
								bool inQuotes = false;

								for (int i = 0; i < cleanedLine.Length; i++)
								{
									if (!inQuotes)
									{
										if (cleanedLine[i] == '=' && numOpenParentheses == 0)
										{
											equalCount++;
										}
										else
											switch (cleanedLine[i])
											{
												case '(':
													numOpenParentheses++;
													break;
												case ')':
													{
														numOpenParentheses--;

														if (numOpenParentheses < 0)
														{
															throw new UberScriptException(
																String.Format(
																	"Parse: parentheses mismatch detected in '{0}' at line {1}:\n{2}",
																	fileName,
																	currentLineNumber,
																	cleanedLine));
														}
													}
													break;
											}
									}

									if (cleanedLine[i] != '"')
									{
										continue;
									}

									inQuotes = !inQuotes; // assume that it's not an escaped quotation

									// determine if it is an escaped quotation (and that there aren't escaped backslashes preceding it)
									for (int ix = i - 1; ix >= 0 && cleanedLine[ix] == '\\'; ix--)
									{
										inQuotes = !inQuotes; // for each backslash behind, unescape or escape accordingly
									}
								}

								if (equalCount == 1) // it's a statement node
								{
									prev = new StatementNode(current, cleanedLine, currentLineNumber);
									//Console.WriteLine(current + "->" + prev);

									if (((StatementNode)prev).ContainsSpawnNode)
									{
										// global_objs.test = orc
										// {
										//      name = goober
										// }
										prev.LineNumber = currentLineNumber; // we "continue" here so we set this later
										current.Children.Add(prev); // we "continue" here so we don't add it later
										//Console.WriteLine(current + "->" + prev + "... adding spawn node to statement node!");

										prev = prev.Children[1]; // should contain a spawn node
										currentRoot.ChildRoots.Add(prev as RootNode);
										currentRoot = prev as RootNode; // spawn node become the new root node
										continue;
									}
								}
								else if (UberScriptFunctions.IsFunctionString(cleanedLine))
								{
									prev = new FunctionNode(current, cleanedLine, currentLineNumber);
									//Console.WriteLine(current + "->" + prev);
								}
								else if (UserDefinedFunctionNode.GetUserDefinedFunctionString(cleanedLine, root) != null)
								{
									prev = new UserDefinedFunctionExectueNode(current, cleanedLine, currentLineNumber);

									((UserDefinedFunctionExectueNode)prev).UserDefinedFunction =
										UserDefinedFunctionNode.GetUserDefinedFunctionString(cleanedLine, root);
								}
								else
								{
									// the string might have commas (indicating a spawn type with a constructor accepting arguments
									// such as "static,100"
									int commaIndex = cleanedLine.IndexOf(',');

									Type spawnType =
										ScriptCompiler.FindTypeByName(commaIndex > 0 ? cleanedLine.Substring(0, commaIndex) : cleanedLine);

									if (spawnType != null && typeof(Mobile).IsAssignableFrom(spawnType) || typeof(Item).IsAssignableFrom(spawnType))
									{
										prev = new RootNode(current, cleanedLine);
										// keep a list of child root nodes so that spawned creatures that have triggers
										// specific to them can correctly point to the right place in the script e.g.
										// troll
										// {
										//      onDeath 
										//      {
										//          orc
										//          {
										//              onDeath
										//              {
										//                  ...
										//              }
										//          }
										//      }
										// }
										// --> trigger nodes are only added to their closest root node
										currentRoot.ChildRoots.Add(prev as RootNode);
										//Console.WriteLine(current + "->" + prev);
										currentRoot = prev as RootNode; // spawn node become the new root node
									}
									else
									{
										throw new UberScriptException(
											String.Format("Parse: could not parse node at line {0}:\n{1}", currentLineNumber, cleanedLine));
									}
								}
							}
						}
						break;
				}

				current.Children.Add(prev);
				prev.LineNumber = currentLineNumber; // used for debugging purposes
			}

			if (balanceStack.Count > 0)
			{
				throw new UberScriptException(
					String.Format("Parse: brace mismatch detected in '{0}' at line {1}", fileName, currentLineNumber));
			}

			return root;
		}
Exemple #3
0
        // returns the added operand node
        public static MathNode ParseOperand(string input)
        {
            if (input == null)
            {
                throw new UberScriptException("AddOperand input was null!");
            }
            if (input == "")
            {
                throw new UberScriptException("AddOperand input was an empty string!");
            }
            Object operand = input;
            OpType opType  = OpType.Unassigned;

            try
            {
                int numCommas = 0;
                for (int i = 0; i < input.Length; i++)
                {
                    if (input[i] == ',')
                    {
                        numCommas++;
                    }
                }
                if (input.StartsWith("0x"))
                {
                    try
                    {
                        if (isNegative) // might fail here
                        {
                            operand = Convert.ToInt32(input.Substring(2), 16);
                            opType  = OpType.Int;
                        }
                        else
                        {
                            operand = Convert.ToUInt64(input.Substring(2), 16);
                            // try to use int if at all possible
                            if ((UInt64)operand < 2147483647) // 2^31 - 1
                            {
                                operand = Convert.ToInt32(input.Substring(2), 16);
                                opType  = OpType.Int;
                            }
                            else
                            {
                                opType = OpType.UInt64;
                            }
                        }
                    }
                    catch // (InvalidCastException e)
                    {
                        // must be a string
                        operand = input;
                        opType  = OpType.String;
                    }
                }
                else if (char.IsNumber(input[0]) || input[0] == '.')
                {
                    double doubleVal;
                    bool   result = double.TryParse(input, out doubleVal);
                    if (result == false)
                    {
                        operand = input; // must be something like 016hello, so just treat it as a string
                        opType  = OpType.String;
                    }
                    else
                    {
                        if (isNegative)
                        {
                            doubleVal = -doubleVal; isNegative = false;
                        }
                        if (!input.Contains('.') && doubleVal % 1 < 0.0009765625) // 2^-10
                        {
                            // close enough to an integer value
                            operand = (int)doubleVal;
                            opType  = OpType.Int;
                        }
                        else
                        {
                            operand = doubleVal;
                            opType  = OpType.Double;
                        }
                    }
                }
                else if (input == "null")
                {
                    opType  = OpType.Null;
                    operand = null;
                }
                else if (input.StartsWith("\""))
                {
                    // inside a quotation--> parse out the innards and set it to a string
                    if (!input.EndsWith("\"") || input.Length == 1)
                    {
                        throw new UberScriptException("ParseOperand input started with \" but did not end with \"... it must be balanced!");
                    }
                    input = input.Substring(1, input.Length - 2); // remove the front and back ""
                    bool justEscapedBackslash = false;
                    for (int ix = 0; ix < input.Length; ix++)
                    {
                        if (ix > 0 && input[ix - 1] == '\\')
                        {
                            // not super efficient, but oh well, it's only run once
                            if (!justEscapedBackslash)
                            {
                                if (input[ix] == '"')
                                {
                                    input = input.Substring(0, ix - 1) + input.Substring(ix); // remove the \, keep the "
                                    ix--;
                                }
                                else if (input[ix] == 't')
                                {
                                    input = input.Substring(0, ix - 1) + "\t" + input.Substring(ix + 1); // remove the \ and add a tab
                                    ix--;
                                }
                                else if (input[ix] == 'n')
                                {
                                    input = input.Substring(0, ix - 1) + "\n" + input.Substring(ix + 1); // remove the \ and add a new line
                                    ix--;
                                }
                                else if (input[ix] == 'r')
                                {
                                    input = input.Substring(0, ix - 1) + "\r" + input.Substring(ix + 1); // remove the \ and add a carriage return
                                    ix--;
                                }
                                else if (input[ix] == '\\')
                                {
                                    input = input.Substring(0, ix - 1) + input.Substring(ix); // keep only one of the \ characters
                                    justEscapedBackslash = true;
                                    continue;
                                }
                            }
                            // otherwise ignore it
                        }
                        justEscapedBackslash = false;
                    }
                    operand = input;
                    opType  = OpType.String;
                }
                else if (UberScriptFunctions.IsFunctionString(input))
                {
                    MathNode latestRootNode = currentRoot;       // currentRoot is replaced when FunctionNode adds a mathtree for each arg
                    currentRoot = null;
                    operand     = new FunctionNode(null, input); // can't add LineNumber here :(
                    if (isNegative)
                    {
                        ((FunctionNode)operand).NegateOutput = true; isNegative = false;
                    }
                    opType      = OpType.Func;
                    currentRoot = latestRootNode;
                }
                // NOTE: While the performance is not as good (perhaps), I'm going to rely on parsing list accession (e.g. objs.mobs[objs.mobs.count - 1])
                // on the fly (as is done in the PropertySetters and PropertyGetters functions) rather than pre-parsing it.

                /*
                 * else if (UberScriptFunctions.IsArrayAccessor(input))
                 * {
                 *  MathNode latestRootNode = currentRoot; // currentRoot is replaced when FunctionNode adds a mathtree for each arg
                 *  currentRoot = null;
                 *  operand = new ListAccessNode(null, input); // can't add LineNumber here :(
                 *  if (isNegative) { ((ListAccessNode)operand).NegateOutput = true; isNegative = false; }
                 *  opType = OpType.ListAccess;
                 *  currentRoot = latestRootNode;
                 * }*/

                /* // I'm not going to handle literal points e.g. (1,2,3)... use the LOC function instead (so you can do math in it)
                 * else if (numCommas > 1) // it must be a location if it is not a function and has 2 commas in it
                 * {
                 *  operand = "(" + input + ")";
                 *  opType = OpType.String;
                 * }*/
                else
                {
                    operand = input;
                    opType  = OpType.String;
                }


                MathNode newOperandNode = new MathNode(null, null, opType, TreePriority.Operand, operand); // assigned into tree in constructor!
                return(newOperandNode);
            }
            catch (Exception e)
            {
                Console.WriteLine("AddOperand error: " + e.Message + "\n" + e.StackTrace);
                throw new UberScriptException("AddOperand error for " + input, e);
            }
        }
Exemple #4
0
        // returns the added operand node
        public static MathNode ParseOperand(string input)
        {
            if (input == null) { throw new UberScriptException("AddOperand input was null!"); }
            if (input == "") { throw new UberScriptException("AddOperand input was an empty string!"); }
            Object operand = input;
            OpType opType = OpType.Unassigned;
            try
            {
                int numCommas = 0;
                for (int i = 0; i < input.Length; i++)
                {
                    if (input[i] == ',') numCommas++;
                }
                if (input.StartsWith("0x"))
                {
                    try
                    {
                        if (isNegative) // might fail here
                        {
                            operand = Convert.ToInt32(input.Substring(2), 16);
                            opType = OpType.Int;
                        }
                        else
                        {
                            operand = Convert.ToUInt64(input.Substring(2), 16);
                            // try to use int if at all possible
                            if ((UInt64)operand < 2147483647) // 2^31 - 1
                            {
                                operand = Convert.ToInt32(input.Substring(2), 16);
                                opType = OpType.Int;
                            }
                            else
                                opType = OpType.UInt64;
                        }
                    }
                    catch // (InvalidCastException e)
                    {
                        // must be a string
                        operand = input;
                        opType = OpType.String;
                    }
                }
                else if (char.IsNumber(input[0]) || input[0] == '.')
                {
                    double doubleVal;
                    bool result = double.TryParse(input, out doubleVal);
                    if (result == false)
                    {
                        operand = input; // must be something like 016hello, so just treat it as a string
                        opType = OpType.String;
                    }
                    else
                    {
                        if (isNegative) { doubleVal = -doubleVal; isNegative = false; }
                        if (!input.Contains('.') && doubleVal % 1 < 0.0009765625) // 2^-10
                        {
                            // close enough to an integer value
                            operand = (int)doubleVal;
                            opType = OpType.Int;
                        }
                        else
                        {
                            operand = doubleVal;
                            opType = OpType.Double;
                        }
                    }
                }
                else if (input == "null")
                {
                    opType = OpType.Null;
                    operand = null;
                }
                else if (input.StartsWith("\""))
                {
                    // inside a quotation--> parse out the innards and set it to a string
                    if (!input.EndsWith("\"") || input.Length == 1)
                    {
                        throw new UberScriptException("ParseOperand input started with \" but did not end with \"... it must be balanced!");
                    }
                    input = input.Substring(1, input.Length - 2); // remove the front and back ""
                    bool justEscapedBackslash = false;
                    for (int ix = 0; ix < input.Length; ix++)
                    {
                        if (ix > 0 && input[ix - 1] == '\\')
                        {
                            // not super efficient, but oh well, it's only run once
                            if (!justEscapedBackslash)
                            {
                                if (input[ix] == '"')
                                {
                                    input = input.Substring(0, ix - 1) + input.Substring(ix); // remove the \, keep the "
                                    ix--;
                                }
                                else if (input[ix] == 't')
                                {
                                    input = input.Substring(0, ix - 1) + "\t" + input.Substring(ix + 1); // remove the \ and add a tab
                                    ix--;
                                }
                                else if (input[ix] == 'n')
                                {
                                    input = input.Substring(0, ix - 1) + "\n" + input.Substring(ix + 1); // remove the \ and add a new line
                                    ix--;
                                }
                                else if (input[ix] == 'r')
                                {
                                    input = input.Substring(0, ix - 1) + "\r" + input.Substring(ix + 1); // remove the \ and add a carriage return
                                    ix--;
                                }
                                else if (input[ix] == '\\')
                                {
                                    input = input.Substring(0, ix - 1) + input.Substring(ix); // keep only one of the \ characters 
                                    justEscapedBackslash = true;
                                    continue;
                                }
                            }
                            // otherwise ignore it
                        }
                        justEscapedBackslash = false;
                    }
                    operand = input;
                    opType = OpType.String;
                }
                else if (UberScriptFunctions.IsFunctionString(input))
                {
                    MathNode latestRootNode = currentRoot; // currentRoot is replaced when FunctionNode adds a mathtree for each arg
                    currentRoot = null;
                    operand = new FunctionNode(null, input); // can't add LineNumber here :(
                    if (isNegative) { ((FunctionNode)operand).NegateOutput = true; isNegative = false; }
                    opType = OpType.Func;
                    currentRoot = latestRootNode;
                }
                // NOTE: While the performance is not as good (perhaps), I'm going to rely on parsing list accession (e.g. objs.mobs[objs.mobs.count - 1]) 
                // on the fly (as is done in the PropertySetters and PropertyGetters functions) rather than pre-parsing it.
                /*
                else if (UberScriptFunctions.IsArrayAccessor(input))
                {
                    MathNode latestRootNode = currentRoot; // currentRoot is replaced when FunctionNode adds a mathtree for each arg
                    currentRoot = null;
                    operand = new ListAccessNode(null, input); // can't add LineNumber here :(
                    if (isNegative) { ((ListAccessNode)operand).NegateOutput = true; isNegative = false; }
                    opType = OpType.ListAccess;
                    currentRoot = latestRootNode;
                }*/

                /* // I'm not going to handle literal points e.g. (1,2,3)... use the LOC function instead (so you can do math in it)
                else if (numCommas > 1) // it must be a location if it is not a function and has 2 commas in it
                {
                    operand = "(" + input + ")";
                    opType = OpType.String;
                }*/
                else
                {
                    operand = input;
                    opType = OpType.String;
                }


                MathNode newOperandNode = new MathNode(null, null, opType, TreePriority.Operand, operand); // assigned into tree in constructor!
                return newOperandNode;
            }
            catch (Exception e)
            {
                Console.WriteLine("AddOperand error: " + e.Message + "\n" + e.StackTrace);
                throw new UberScriptException("AddOperand error for " + input, e);
            }
        }
        public override ProcessResult Process(ProcessResult lastSiblingResult, TriggerObject trigObject)
        {
            // will always have 2 children, either function nodes or MathTree nodes
            Object o        = null;
            string propname = null;
            Object value;
            string globalObjsString;
            string localObjsString;

            // 1st child is the property that will be set
            if (Children[0] is FunctionNode)
            {
                // this presumes that it is a function like THIS() or TRIGMOB()
                FunctionNode fn = Children[0] as FunctionNode;
                o        = fn.Execute(trigObject, true);
                propname = fn.Property;
            }
            else             // Children[0] is argument node
            {
                string scriptString = Children[0].ScriptString;
                int    dotIndex     = scriptString.IndexOf('.');

                if (dotIndex > -1)
                {
                    if (scriptString.StartsWith("global_"))
                    {
                        if (scriptString.StartsWith("global_ints."))
                        {
                            UberTreeParser.global_ints[scriptString.Substring(12)] = (int)((MathTree)Children[1]).Calculate(trigObject);

                            value = ((MathTree)Children[1]).Calculate(trigObject);

                            switch (AssignType)
                            {
                            case AssignmentType.Plus:
                                UberTreeParser.global_ints[scriptString.Substring(12)] =
                                    UberTreeParser.global_ints[scriptString.Substring(12)] + Convert.ToInt32(value);
                                break;

                            case AssignmentType.Minus:
                                UberTreeParser.global_ints[scriptString.Substring(12)] =
                                    UberTreeParser.global_ints[scriptString.Substring(12)] - Convert.ToInt32(value);
                                break;

                            case AssignmentType.Mult:
                                UberTreeParser.global_ints[scriptString.Substring(12)] =
                                    UberTreeParser.global_ints[scriptString.Substring(12)] * Convert.ToInt32(value);
                                break;

                            case AssignmentType.Divide:
                                UberTreeParser.global_ints[scriptString.Substring(12)] =
                                    Convert.ToInt32(UberTreeParser.global_ints[scriptString.Substring(12)] / Convert.ToDouble(value));
                                break;

                            default:
                                UberTreeParser.global_ints[scriptString.Substring(12)] = Convert.ToInt32(value);
                                break;
                            }

                            return(ProcessResult.None);
                        }

                        if (scriptString.StartsWith("global_strings."))
                        {
                            value = GetReturnObject(Children[1], trigObject);

                            if (value != null)
                            {
                                value = value.ToString();

                                switch (AssignType)
                                {
                                case AssignmentType.Plus:
                                    UberTreeParser.global_strings[scriptString.Substring(15)] =
                                        UberTreeParser.global_strings[scriptString.Substring(15)] + (string)value;
                                    break;

                                case AssignmentType.Regular:
                                    UberTreeParser.global_strings[scriptString.Substring(15)] = (string)value;
                                    break;
                                }

                                throw new UberScriptException(
                                          "Line " + LineNumber + ": " + ScriptString + "\nTried to use +=, -=, *=, or /= but this is only supported for " +
                                          value.GetType() + "! It is only supported for int, double, and UInt64");
                                //return ProcessResult.None;
                            }

                            throw new UberScriptException(
                                      "Line " + LineNumber + ": " + ScriptString +
                                      "\nTried to add null value to strings dictionary... this is not allowed!");
                        }

                        if (scriptString.StartsWith("global_doubles."))
                        {
                            value = ((MathTree)Children[1]).Calculate(trigObject);

                            switch (AssignType)
                            {
                            case AssignmentType.Plus:
                                UberTreeParser.global_doubles[scriptString.Substring(15)] =
                                    UberTreeParser.global_doubles[scriptString.Substring(15)] + Convert.ToDouble(value);
                                break;

                            case AssignmentType.Minus:
                                UberTreeParser.global_doubles[scriptString.Substring(15)] =
                                    UberTreeParser.global_doubles[scriptString.Substring(15)] - Convert.ToDouble(value);
                                break;

                            case AssignmentType.Mult:
                                UberTreeParser.global_doubles[scriptString.Substring(15)] =
                                    UberTreeParser.global_doubles[scriptString.Substring(15)] * Convert.ToDouble(value);
                                break;

                            case AssignmentType.Divide:
                                UberTreeParser.global_doubles[scriptString.Substring(15)] =
                                    UberTreeParser.global_doubles[scriptString.Substring(15)] / Convert.ToDouble(value);
                                break;

                            default:
                                UberTreeParser.global_doubles[scriptString.Substring(15)] = Convert.ToDouble(value);
                                break;
                            }

                            return(ProcessResult.None);
                        }

                        if (scriptString.StartsWith("global_objs."))
                        {
                            globalObjsString = scriptString.Substring(12);

                            int secondDotIndex = globalObjsString.IndexOf('.');

                            if (secondDotIndex > -1)                             // we are trying to set a property on an existing object
                            {
                                // will throw error if it doesn't exist yet
                                o        = UberTreeParser.global_objs[globalObjsString.Substring(0, secondDotIndex)];
                                propname = globalObjsString.Substring(secondDotIndex + 1);
                            }
                            else
                            {
                                UberTreeParser.global_objs[globalObjsString] = GetReturnObject(Children[1], trigObject);
                                return(ProcessResult.None);
                            }
                        }
                    }
                    else if (scriptString.StartsWith("ints."))
                    {
                        value = ((MathTree)Children[1]).Calculate(trigObject);

                        switch (AssignType)
                        {
                        case AssignmentType.Plus:
                            trigObject.ints[scriptString.Substring(5)] = trigObject.ints[scriptString.Substring(5)] + Convert.ToInt32(value);
                            break;

                        case AssignmentType.Minus:
                            trigObject.ints[scriptString.Substring(5)] = trigObject.ints[scriptString.Substring(5)] - Convert.ToInt32(value);
                            break;

                        case AssignmentType.Mult:
                            trigObject.ints[scriptString.Substring(5)] = trigObject.ints[scriptString.Substring(5)] * Convert.ToInt32(value);
                            break;

                        case AssignmentType.Divide:
                            trigObject.ints[scriptString.Substring(5)] =
                                Convert.ToInt32(trigObject.ints[scriptString.Substring(5)] / Convert.ToDouble(value));
                            break;

                        default:
                            trigObject.ints[scriptString.Substring(5)] = Convert.ToInt32(value);
                            break;
                        }

                        return(ProcessResult.None);
                    }
                    else if (scriptString.StartsWith("strings."))
                    {
                        value = GetReturnObject(Children[1], trigObject);

                        if (value != null)
                        {
                            value = value.ToString();

                            switch (AssignType)
                            {
                            case AssignmentType.Plus:
                                trigObject.strings[scriptString.Substring(8)] = trigObject.strings[scriptString.Substring(8)] + (string)value;
                                break;

                            case AssignmentType.Regular:
                                trigObject.strings[scriptString.Substring(8)] = (string)value;
                                break;

                            default:
                                throw new UberScriptException(
                                          "Line " + LineNumber + ": " + ScriptString +
                                          "\nTried to use +=, -=, *=, or /= but this is only supported for " + value.GetType() +
                                          "! It is only supported for int, double, and UInt64");
                            }
                            return(ProcessResult.None);
                        }

                        throw new UberScriptException(
                                  "Line " + LineNumber + ": " + ScriptString +
                                  "\nTried to add null value to strings dictionary... this is not allowed!");
                    }
                    else if (scriptString.StartsWith("doubles."))
                    {
                        value = ((MathTree)Children[1]).Calculate(trigObject);

                        switch (AssignType)
                        {
                        case AssignmentType.Plus:
                            trigObject.doubles[scriptString.Substring(8)] = trigObject.doubles[scriptString.Substring(8)] +
                                                                            Convert.ToDouble(value);
                            break;

                        case AssignmentType.Minus:
                            trigObject.doubles[scriptString.Substring(8)] = trigObject.doubles[scriptString.Substring(8)] -
                                                                            Convert.ToDouble(value);
                            break;

                        case AssignmentType.Mult:
                            trigObject.doubles[scriptString.Substring(8)] = trigObject.doubles[scriptString.Substring(8)] *
                                                                            Convert.ToDouble(value);
                            break;

                        case AssignmentType.Divide:
                            trigObject.doubles[scriptString.Substring(8)] = trigObject.doubles[scriptString.Substring(8)] /
                                                                            Convert.ToDouble(value);
                            break;

                        default:
                            trigObject.doubles[scriptString.Substring(8)] = Convert.ToDouble(value);
                            break;
                        }

                        return(ProcessResult.None);
                    }
                    else if (scriptString.StartsWith("objs."))
                    {
                        localObjsString = scriptString.Substring(5);

                        int secondDotIndex = localObjsString.IndexOf('.');

                        if (secondDotIndex > -1)                         // we are trying to set a property on an existing object
                        {
                            if (!trigObject.objs.TryGetValue(localObjsString.Substring(0, secondDotIndex), out o))
                            {
                                // will throw error if it doesn't exist yet
                                throw new UberScriptException(
                                          "local objs dictionary did not have entry: " + localObjsString.Substring(0, secondDotIndex));
                            }

                            propname = localObjsString.Substring(secondDotIndex + 1);
                        }
                        else
                        {
                            trigObject.objs[localObjsString] = GetReturnObject(Children[1], trigObject);
                            return(ProcessResult.None);
                        }
                    }
                }

                // only makes it here if we aren't editing global variables directly
                // (or if the we are returning a global object, e.g. global_objs.goober.hits
                if (o == null)
                {
                    o        = trigObject.Spawn ?? trigObject.This;
                    propname = scriptString;
                }
            }

            // 2nd child is the value, but could have an operation in it and needs to be parsed
            try
            {
                if (AssignType == AssignmentType.Regular)
                {
                    value = GetReturnObject(Children[1], trigObject);
                }
                else
                {
                    Type ptype;

                    value = PropertyGetters.GetObject(trigObject, o, propname, out ptype);

                    if (value is int)
                    {
                        switch (AssignType)
                        {
                        case AssignmentType.Plus:
                            value = (int)value + Convert.ToInt32(GetReturnObject(Children[1], trigObject));
                            break;

                        case AssignmentType.Minus:
                            value = (int)value - Convert.ToInt32(GetReturnObject(Children[1], trigObject));
                            break;

                        case AssignmentType.Mult:
                            value = Convert.ToInt32((int)value * Convert.ToDouble(GetReturnObject(Children[1], trigObject)));
                            break;

                        case AssignmentType.Divide:
                            value = Convert.ToInt32((int)value / Convert.ToDouble(GetReturnObject(Children[1], trigObject)));
                            break;
                        }
                    }
                    else if (value is double)
                    {
                        switch (AssignType)
                        {
                        case AssignmentType.Plus:
                            value = (double)value + Convert.ToDouble(GetReturnObject(Children[1], trigObject));
                            break;

                        case AssignmentType.Minus:
                            value = (double)value - Convert.ToDouble(GetReturnObject(Children[1], trigObject));
                            break;

                        case AssignmentType.Mult:
                            value = (double)value * Convert.ToDouble(GetReturnObject(Children[1], trigObject));
                            break;

                        case AssignmentType.Divide:
                            value = (double)value / Convert.ToDouble(GetReturnObject(Children[1], trigObject));
                            break;
                        }
                    }
                    else if (value is UInt64)
                    {
                        switch (AssignType)
                        {
                        case AssignmentType.Plus:
                            value = (UInt64)value + Convert.ToUInt64(GetReturnObject(Children[1], trigObject));
                            break;

                        case AssignmentType.Minus:
                            value = (UInt64)value - Convert.ToUInt64(GetReturnObject(Children[1], trigObject));
                            break;

                        case AssignmentType.Mult:
                            value = Convert.ToUInt64((UInt64)value * Convert.ToDouble(GetReturnObject(Children[1], trigObject)));
                            break;

                        case AssignmentType.Divide:
                            value = Convert.ToUInt64((UInt64)value / Convert.ToDouble(GetReturnObject(Children[1], trigObject)));
                            break;
                        }
                    }
                    else if (value is string)
                    {
                        if (AssignType == AssignmentType.Plus)
                        {
                            value = (string)value + GetReturnObject(Children[1], trigObject);
                        }
                        else
                        {
                            throw new UberScriptException(
                                      "Tried to use +=, -=, *=, or /= but this is only supported for " + value.GetType() +
                                      "! It is only supported for int, double, and UInt64");
                        }
                    }
                    else
                    {
                        //throw new UberScriptException("Tried to use +=, -=, *=, or /= on a non-numeric left-hand side!");
                        throw new UberScriptException(
                                  "Tried to use +=, -=, *=, or /= but this is only supported for " + value.GetType() +
                                  "! It is only supported for int, double, and UInt64");
                    }
                }
            }
            catch (Exception e)
            {
                throw new UberScriptException("Line " + LineNumber + ": " + ScriptString + "\nGetReturnObject Error:", e);
            }

            try
            {
                PropertySetters.SetPropertyValue(trigObject, o, propname, value);
            }
            catch (Exception e)
            {
                throw new UberScriptException("Line " + LineNumber + ": " + ScriptString + "\nError setting value:", e);
            }

            return(ProcessResult.None);
        }