Beispiel #1
0
        // executes a node, and returns either the name of the next node to run
        // or null (indicating the dialogue is over)
        internal IEnumerable <Dialogue.RunnerResult> RunNode(Yarn.Parser.Node node)
        {
            // Clear the list of options when we start a new node
            currentOptions = new List <Parser.OptionStatement> ();

            // Run all of the statements in this node
            foreach (var command in RunStatements(node.statements))
            {
                yield return(command);
            }

            // If we have no options, we're all done
            if (currentOptions.Count == 0)
            {
                yield return(new Dialogue.NodeCompleteResult(null));

                yield break;
            }
            else
            {
                // We have options!

                // If we have precisely one option and it's got no label, jump to it
                if (currentOptions.Count == 1 &&
                    currentOptions[0].label == null)
                {
                    yield return(new Dialogue.NodeCompleteResult(currentOptions [0].destination));

                    yield break;
                }

                // Otherwise, ask which option to pick...
                var optionStrings = new List <string> ();
                foreach (var option in currentOptions)
                {
                    var label = option.label ?? option.destination;
                    optionStrings.Add(label);
                }

                Parser.OptionStatement selectedOption = null;

                yield return(new Dialogue.OptionSetResult(optionStrings, delegate(int selectedOptionIndex) {
                    selectedOption = currentOptions[selectedOptionIndex];
                }));

                if (selectedOption == null)
                {
                    dialogue.LogErrorMessage("Option chooser was never called!");
                    yield break;
                }

                // And jump to its destination!
                yield return(new Dialogue.NodeCompleteResult(selectedOption.destination));
            }
            yield break;
        }
Beispiel #2
0
        // Given either Twine, JSON or XML input, return an array
        // containing info about the nodes in that file
        NodeInfo[] ParseInput(string text)
        {
            // All the nodes we found in this file
            var nodes = new List <NodeInfo> ();

            if (text.IndexOf("//") == 0)
            {
                // If it starts with a comment, treat it as a single-node file
                nodes.Add(new NodeInfo("Start", text, null));
            }
            else
            {
                // Blindly assume it's JSON! \:D/
                try {
                    // First, parse the raw text
                    var loadedJSON = JsonParser.FromJson(text);

                    // Process each item that was found (probably just a single one)
                    foreach (var item in loadedJSON)
                    {
                        // We expect it to be an array of dictionaries
                        var list = item.Value as IList <object>;

                        // For each dictionary in the list..
                        foreach (IDictionary <string, object> nodeJSON in list)
                        {
                            // Pull out the node's title and body, and use that
                            nodes.Add(
                                new NodeInfo(
                                    nodeJSON["title"] as string,
                                    nodeJSON["body"] as string,
                                    nodeJSON["tags"] as string
                                    )
                                );
                        }
                    }
                } catch (InvalidCastException) {
                    dialogue.LogErrorMessage("Error parsing Yarn input: it's valid JSON, but " +
                                             "it didn't match the data layout I was expecting.");
                } catch (InvalidJsonException e) {
                    dialogue.LogErrorMessage("Error parsing Yarn input: " + e.Message);
                }
            }

            // hooray we're done
            return(nodes.ToArray());
        }
		public bool SetNode(string nodeName) {
			if (program.nodes.ContainsKey(nodeName) == false) {

				var error = "No node named " + nodeName;
				dialogue.LogErrorMessage(error);
				executionState = ExecutionState.Stopped;
				return false;
			}

			dialogue.LogDebugMessage ("Running node " + nodeName);

			// Clear the special variables
			dialogue.continuity.SetNumber(SpecialVariables.ShuffleOptions, 0.0f);

			currentNodeInstructions = program.nodes [nodeName].instructions;
			ResetState ();
			state.currentNode = nodeName;

			return true;
		}
        public bool SetNode(string nodeName)
        {
            if (Program.Nodes.ContainsKey(nodeName) == false)
            {
                var error = "No node named " + nodeName;
                dialogue.LogErrorMessage(error);
                executionState = ExecutionState.Stopped;
                return(false);
            }

            dialogue.LogDebugMessage("Running node " + nodeName);

            currentNode = Program.Nodes [nodeName];
            ResetState();
            state.currentNodeName = nodeName;

            return(true);
        }
Beispiel #5
0
        public bool SetNode(string nodeName)
        {
            if (program.nodes.ContainsKey(nodeName) == false)
            {
                var error = "No node named " + nodeName;
                dialogue.LogErrorMessage(error);
                executionState = ExecutionState.Stopped;
                return(false);
            }

            dialogue.LogDebugMessage("Running node " + nodeName);


            // Clear the special variables
            dialogue.continuity.SetValue(SpecialVariables.ShuffleOptions, new Value(false));

            currentNode = program.nodes [nodeName];
            ResetState();
            state.currentNodeName = nodeName;
            ExampleDialogueUI.onNewNode.Invoke();

            return(true);
        }
Beispiel #6
0
        public static void Main(string[] args)
        {
            if (args.Length == 0)
            {
                ShowHelpAndExit();
            }
            bool   showTokens            = false;
            bool   showParseTree         = false;
            bool   waitForLines          = false;
            string onlyConsiderNode      = null;
            bool   showDebugging         = false;
            int    runTimes              = 1;
            bool   compileToBytecodeOnly = false;

            var    inputFiles = new List <string> ();
            string startNode  = Dialogue.DEFAULT_START;

            string[] allowedExtensions = { ".node", ".json" };

            var defaultVariables = new Dictionary <string, float> ();

            foreach (var arg in args)
            {
                // Handle 'start' parameter
                if (arg.IndexOf("-s=") != -1)
                {
                    var startArray = arg.Split(new char[] { '=' });
                    if (startArray.Length != 2)
                    {
                        ShowHelpAndExit();
                    }
                    else
                    {
                        startNode = startArray [1];
                        continue;
                    }
                }

                // Handle variable input
                if (arg.IndexOf("-v") != -1)
                {
                    var variable      = arg.Substring(2);
                    var variableArray = variable.Split(new char[] { '=' });
                    if (variableArray.Length != 2)
                    {
                        ShowHelpAndExit();
                    }
                    else
                    {
                        var varName  = "$" + variableArray [0];
                        var varValue = float.Parse(variableArray [1]);
                        defaultVariables [varName] = varValue;
                        continue;
                    }
                }

                // Handle 'only this node' parameter
                if (arg.IndexOf("-o=") != -1)
                {
                    var startArray = arg.Split(new char[] { '=' });
                    if (startArray.Length != 2)
                    {
                        ShowHelpAndExit();
                    }
                    else
                    {
                        onlyConsiderNode = startArray [1];
                        continue;
                    }
                }

                // Handle 'run times' parameter
                if (arg.IndexOf("-r=") != -1)
                {
                    var argArray = arg.Split('=');
                    if (argArray.Length != 2)
                    {
                        ShowHelpAndExit();
                    }
                    else
                    {
                        runTimes = int.Parse(argArray [1]);
                        continue;
                    }
                }


                switch (arg)
                {
                case "-t":
                    showTokens    = true;
                    showDebugging = true;
                    break;

                case "-p":
                    showParseTree = true;
                    showDebugging = true;
                    break;

                case "-w":
                    waitForLines = true;
                    break;

                case "-d":
                    showDebugging = true;
                    break;

                case "-c":
                    compileToBytecodeOnly = true;
                    break;

                case "-h":
                    ShowHelpAndExit();
                    break;

                default:

                    // only allow one file
                    if (inputFiles.Count > 0)
                    {
                        Console.Error.WriteLine("Error: Too many files specified.");
                        Environment.Exit(1);
                    }

                    var extension = System.IO.Path.GetExtension(arg);
                    if (Array.IndexOf(allowedExtensions, extension) != -1)
                    {
                        inputFiles.Add(arg);
                    }
                    break;
                }
            }

            if (inputFiles.Count == 0)
            {
                Console.Error.WriteLine("Error: No files specified.");
                Environment.Exit(1);
            }

            // Create the object that handles callbacks
            var impl = new ConsoleRunnerImplementation(waitForLines: waitForLines);

            // load the default variables we got on the command line
            foreach (var variable in defaultVariables)
            {
                impl.SetNumber(variable.Key, variable.Value);
            }

            // Load nodes
            var dialogue = new Dialogue(impl);


            // Add some methods for testing
            dialogue.library.RegisterFunction("add_three_operands", 3, delegate(Value[] parameters) {
                var f1 = parameters[0].AsNumber;
                var f2 = parameters[1].AsNumber;
                var f3 = parameters[2].AsNumber;

                return(f1 + f2 + f3);
            });

            dialogue.library.RegisterFunction("last_value", -1, delegate(Value[] parameters) {
                // return the last value
                return(parameters[parameters.Length - 1]);
            });

            dialogue.library.RegisterFunction("is_even", 1, delegate(Value[] parameters) {
                return((int)parameters[0].AsNumber % 2 == 0);
            });

            // Register the "assert" function, which stops execution if its parameter evaluates to false
            dialogue.library.RegisterFunction("assert", 1, delegate(Value[] parameters) {
                if (parameters[0].AsBool == false)
                {
                    // TODO: Include file, node and line number
                    dialogue.LogErrorMessage("ASSERTION FAILED");
                    Environment.Exit(1);
                }
            });


            // Register a function to let test scripts register how many
            // options they expect to send
            dialogue.library.RegisterFunction("prepare_for_options", 2, delegate(Value[] parameters) {
                impl.numberOfExpectedOptions = (int)parameters [0].AsNumber;
                impl.autoSelectOptionNumber  = (int)parameters[1].AsNumber;
            });

            dialogue.library.RegisterFunction("expect_line", 1, delegate(Value[] parameters) {
                impl.expectedNextLine = parameters[0].AsString;
            });

            dialogue.library.RegisterFunction("expect_command", 1, delegate(Value[] parameters) {
                impl.expectedNextCommand = parameters[0].AsString;
            });

            // If debugging is enabled, log debug messages; otherwise, ignore them
            if (showDebugging)
            {
                dialogue.LogDebugMessage = delegate(string message) {
                    Console.WriteLine("Debug: " + message);
                };
            }
            else
            {
                dialogue.LogDebugMessage = delegate(string message) {};
            }

            dialogue.LogErrorMessage = delegate(string message) {
                Console.WriteLine("ERROR: " + message);
            };

            dialogue.LoadFile(inputFiles [0], showTokens, showParseTree, onlyConsiderNode);

            if (compileToBytecodeOnly)
            {
                var result = dialogue.Compile();
                Console.WriteLine(result);
            }

            // Only run the program when we're not emitting debug output of some kind
            var runProgram =
                showTokens == false &&
                showParseTree == false &&
                compileToBytecodeOnly == false;

            if (runProgram)
            {
                // Run the conversation

                for (int run = 0; run < runTimes; run++)
                {
                    foreach (var step in dialogue.Run(startNode))
                    {
                        // It can be one of three types: a line to show, options
                        // to present to the user, or an internal command to run

                        if (step is Dialogue.LineResult)
                        {
                            var lineResult = step as Dialogue.LineResult;
                            impl.RunLine(lineResult.line);
                        }
                        else if (step is Dialogue.OptionSetResult)
                        {
                            var optionsResult = step as Dialogue.OptionSetResult;
                            impl.RunOptions(optionsResult.options, optionsResult.setSelectedOptionDelegate);
                        }
                        else if (step is Dialogue.CommandResult)
                        {
                            var commandResult = step as Dialogue.CommandResult;
                            impl.RunCommand(commandResult.command.text);
                        }
                    }
                    impl.DialogueComplete();
                }
            }
        }
Beispiel #7
0
        // Given either Twine, JSON or XML input, return an array
        // containing info about the nodes in that file
        internal NodeInfo[] GetNodesFromText(string text, NodeFormat format)
        {
            // All the nodes we found in this file
            var nodes = new List <NodeInfo> ();

            switch (format)
            {
            case NodeFormat.SingleNodeText:
                // If it starts with a comment, treat it as a single-node file
                var nodeInfo = new NodeInfo();
                nodeInfo.title = "Start";
                nodeInfo.body  = text;
                nodes.Add(nodeInfo);
                break;

            case NodeFormat.JSON:
                // Parse it as JSON
                try
                {
                    nodes = JsonConvert.DeserializeObject <List <NodeInfo> >(text);
                }
                catch (JsonReaderException e)
                {
                    dialogue.LogErrorMessage("Error parsing Yarn input: " + e.Message);
                }

                break;

            case NodeFormat.Text:

                // check for the existence of at least one "---"+newline sentinel, which divides
                // the headers from the body

                // we use a regex to match either \r\n or \n line endings
                if (System.Text.RegularExpressions.Regex.IsMatch(text, "---.?\n") == false)
                {
                    dialogue.LogErrorMessage("Error parsing input: text appears corrupt (no header sentinel)");
                    break;
                }

                var headerRegex = new System.Text.RegularExpressions.Regex("(?<field>.*): *(?<value>.*)");

                var nodeProperties = typeof(NodeInfo).GetProperties();

                int lineNumber = 0;

                using (var reader = new System.IO.StringReader(text))
                {
                    string line;
                    while ((line = reader.ReadLine()) != null)
                    {
                        // Create a new node
                        NodeInfo node = new NodeInfo();

                        // Read header lines
                        do
                        {
                            lineNumber++;

                            // skip empty lines
                            if (line == null || line.Length == 0)
                            {
                                continue;
                            }

                            // Attempt to parse the header
                            var headerMatches = headerRegex.Match(line);

                            if (headerMatches == null)
                            {
                                dialogue.LogErrorMessage(string.Format("Line {0}: Can't parse header '{1}'", lineNumber, line));
                                continue;
                            }

                            var field = headerMatches.Groups["field"].Value;
                            var value = headerMatches.Groups["value"].Value;

                            // Attempt to set the appropriate property using this field
                            foreach (var property in nodeProperties)
                            {
                                if (property.Name != field)
                                {
                                    continue;
                                }

                                // skip properties that can't be written to
                                if (property.CanWrite == false)
                                {
                                    continue;
                                }
                                try
                                {
                                    var    propertyType = property.PropertyType;
                                    object convertedValue;
                                    if (propertyType.IsAssignableFrom(typeof(string)))
                                    {
                                        convertedValue = value;
                                    }
                                    else if (propertyType.IsAssignableFrom(typeof(int)))
                                    {
                                        convertedValue = int.Parse(value);
                                    }
                                    else if (propertyType.IsAssignableFrom(typeof(NodeInfo.Position)))
                                    {
                                        var components = value.Split(',');

                                        // we expect 2 components: x and y
                                        if (components.Length != 2)
                                        {
                                            throw new FormatException();
                                        }

                                        var position = new NodeInfo.Position();
                                        position.x = int.Parse(components[0]);
                                        position.y = int.Parse(components[1]);

                                        convertedValue = position;
                                    }
                                    else
                                    {
                                        throw new NotSupportedException();
                                    }
                                    // we need to box this because structs are value types,
                                    // so calling SetValue using 'node' would just modify a copy of 'node'
                                    object box = node;
                                    property.SetValue(box, convertedValue, null);
                                    node = (NodeInfo)box;
                                    break;
                                }
                                catch (FormatException)
                                {
                                    dialogue.LogErrorMessage(string.Format("{0}: Error setting '{1}': invalid value '{2}'", lineNumber, field, value));
                                }
                                catch (NotSupportedException)
                                {
                                    dialogue.LogErrorMessage(string.Format("{0}: Error setting '{1}': This property cannot be set", lineNumber, field));
                                }
                            }
                        } while ((line = reader.ReadLine()) != "---");

                        lineNumber++;

                        // We're past the header; read the body

                        var lines = new List <string>();

                        // Read header lines until we hit the end of node sentinel or the end of the file
                        while ((line = reader.ReadLine()) != "===" && line != null)
                        {
                            lineNumber++;
                            lines.Add(line);
                        }
                        // We're done reading the lines! Zip 'em up into a string and
                        // store it in the body
                        node.body = string.Join("\n", lines.ToArray());

                        // And add this node to the list
                        nodes.Add(node);

                        // And now we're ready to move on to the next line!
                    }
                }
                break;

            default:
                throw new InvalidOperationException("Unknown format " + format.ToString());
            }

            // hooray we're done
            return(nodes.ToArray());
        }
Beispiel #8
0
        static internal Dialogue CreateDialogue(ExecutionOptions options, ConsoleRunnerImplementation impl)
        {
            // Load nodes
            var dialogue = new Dialogue(impl);

            // Add some methods for testing
            dialogue.library.RegisterFunction("add_three_operands", 3, delegate(Value[] parameters)
            {
                return(parameters[0] + parameters[1] + parameters[2]);
            });

            dialogue.library.RegisterFunction("last_value", -1, delegate(Value[] parameters)
            {
                // return the last value
                return(parameters[parameters.Length - 1]);
            });

            dialogue.library.RegisterFunction("is_even", 1, delegate(Value[] parameters)
            {
                return((int)parameters[0].AsNumber % 2 == 0);
            });

            // Register the "assert" function, which stops execution if its parameter evaluates to false
            dialogue.library.RegisterFunction("assert", -1, delegate(Value[] parameters)
            {
                if (parameters[0].AsBool == false)
                {
                    // TODO: Include file, node and line number
                    if (parameters.Length > 1 && parameters[1].AsBool)
                    {
                        dialogue.LogErrorMessage("ASSERTION FAILED: " + parameters[1].AsString);
                    }
                    else
                    {
                        dialogue.LogErrorMessage("ASSERTION FAILED");
                    }
                    Environment.Exit(1);
                }
            });


            // Register a function to let test scripts register how many
            // options they expect to send
            dialogue.library.RegisterFunction("prepare_for_options", 2, delegate(Value[] parameters)
            {
                impl.numberOfExpectedOptions = (int)parameters[0].AsNumber;
                impl.autoSelectOptionNumber  = (int)parameters[1].AsNumber;
            });

            dialogue.library.RegisterFunction("expect_line", 1, delegate(Value[] parameters)
            {
                impl.expectedNextLine = parameters[0].AsString;
            });

            dialogue.library.RegisterFunction("expect_command", 1, delegate(Value[] parameters)
            {
                impl.expectedNextCommand = parameters[0].AsString;
            });

            // If debugging is enabled, log debug messages; otherwise, ignore them
            if (options.showDebuggingInfo)
            {
                dialogue.LogDebugMessage = delegate(string message)
                {
                    Note(message);
                };
            }
            else
            {
                dialogue.LogDebugMessage = delegate(string message) { };
            }

            dialogue.LogErrorMessage = delegate(string message)
            {
                Warn("Yarn Error: " + message);
            };

            foreach (var file in options.files)
            {
                try
                {
                    dialogue.LoadFile(file, false, false, options.onlyConsiderNode);
                }
                catch (Yarn.ParseException e)
                {
                    Warn(e.Message);
                }
            }

            // Load string table
            if (options.stringTable != null)
            {
                var parsedTable = new Dictionary <string, string>();

                using (var reader = new System.IO.StreamReader(options.stringTable))
                {
                    using (var csvReader = new CsvReader(reader))
                    {
                        if (csvReader.ReadHeader() == false)
                        {
                            Error(string.Format(CultureInfo.CurrentCulture, "{0} is not a valid string table", options.stringTable));
                        }

                        foreach (var row in csvReader.GetRecords <LocalisedLine>())
                        {
                            parsedTable[row.LineCode] = row.LineText;
                        }
                    }
                }

                dialogue.AddStringTable(parsedTable);
            }

            return(dialogue);
        }
Beispiel #9
0
        public static void Main(string[] args)
        {
            if (args.Length == 0) {
                ShowHelpAndExit ();
            }

            bool showTokens = false;
            bool showParseTree = false;
            bool waitForLines = false;
            string onlyConsiderNode = null;
            bool showDebugging = false;
            int runTimes = 1;
            bool compileToBytecodeOnly = false;
            bool verifyOnly = false;
            bool autoSelectFirstOption = false;
            bool analyseOnly = false;

            var inputFiles = new List<string> ();
            string startNode = Dialogue.DEFAULT_START;

            string[] allowedExtensions = {".node", ".json" };

            var defaultVariables = new Dictionary<string,float> ();

            foreach (var arg in args) {

                // Handle 'start' parameter
                if (arg.IndexOf("-s=") != -1) {
                    var startArray = arg.Split (new char[]{ '=' });
                    if (startArray.Length != 2) {
                        ShowHelpAndExit ();
                    } else {
                        startNode = startArray [1];
                        continue;
                    }
                }

                // Handle variable input
                if (arg.IndexOf("-v") != -1) {
                    var variable = arg.Substring (2);
                    var variableArray = variable.Split (new char[]{ '=' });
                    if (variableArray.Length != 2) {
                        ShowHelpAndExit ();
                    } else {
                        var varName = "$" + variableArray [0];
                        var varValue = float.Parse (variableArray [1]);
                        defaultVariables [varName] = varValue;
                        continue;
                    }

                }

                // Handle 'only this node' parameter
                if (arg.IndexOf("-o=") != -1) {
                    var startArray = arg.Split (new char[]{ '=' });
                    if (startArray.Length != 2) {
                        ShowHelpAndExit ();
                    } else {
                        onlyConsiderNode = startArray [1];
                        continue;
                    }
                }

                // Handle 'run times' parameter
                if (arg.IndexOf("-r=") != -1) {
                    var argArray = arg.Split ('=');
                    if (argArray.Length != 2) {
                        ShowHelpAndExit ();
                    } else {
                        runTimes = int.Parse (argArray [1]);
                        continue;
                    }

                }

                switch (arg) {
                case "-V":
                    verifyOnly = true;
                    break;
                case "-t":
                    showTokens = true;
                    showDebugging = true;
                    break;
                case "-p":
                    showParseTree = true;
                    showDebugging = true;
                    break;
                case "-w":
                    waitForLines = true;
                    break;
                case "-d":
                    showDebugging = true;
                    break;
                case "-c":
                    compileToBytecodeOnly = true;
                    break;
                case "-1":
                    autoSelectFirstOption = true;
                    break;
                case "-h":
                    ShowHelpAndExit ();
                    break;
                case "-a":
                    analyseOnly = true;
                    break;
                default:

                    // only allow one file
                    if (inputFiles.Count > 0) {
                        Console.Error.WriteLine ("Error: Too many files specified.");
                        Environment.Exit (1);
                    }

                    var extension = System.IO.Path.GetExtension (arg);
                    if (Array.IndexOf(allowedExtensions, extension) != -1) {
                        inputFiles.Add (arg);
                    }
                    break;
                }
            }

            if (inputFiles.Count == 0) {
                Console.Error.WriteLine ("Error: No files specified.");
                Environment.Exit (1);
            }

            // Create the object that handles callbacks
            var impl = new ConsoleRunnerImplementation (waitForLines:waitForLines);

            // load the default variables we got on the command line
            foreach (var variable in defaultVariables) {

                impl.SetNumber (variable.Key, variable.Value);
            }

            // Load nodes
            var dialogue = new Dialogue(impl);

            // Add some methods for testing
            dialogue.library.RegisterFunction ("add_three_operands", 3, delegate(Value[] parameters) {
                return parameters[0]+parameters[1]+parameters[2];
            });

            dialogue.library.RegisterFunction ("last_value", -1, delegate(Value[] parameters) {
                // return the last value
                return parameters[parameters.Length-1];
            });

            dialogue.library.RegisterFunction ("is_even", 1, delegate(Value[] parameters) {
                return (int)parameters[0].AsNumber % 2 == 0;
            });

            // Register the "assert" function, which stops execution if its parameter evaluates to false
            dialogue.library.RegisterFunction ("assert", -1, delegate(Value[] parameters) {
                if (parameters[0].AsBool == false) {

                    // TODO: Include file, node and line number
                    if( parameters.Length > 1 && parameters[1].AsBool ) {
                        dialogue.LogErrorMessage ("ASSERTION FAILED: " + parameters[1].AsString);
                    } else {
                        dialogue.LogErrorMessage ("ASSERTION FAILED");
                    }
                    Environment.Exit(1);
                }
            });

            // Register a function to let test scripts register how many
            // options they expect to send
            dialogue.library.RegisterFunction ("prepare_for_options", 2, delegate(Value[] parameters) {
                impl.numberOfExpectedOptions = (int)parameters [0].AsNumber;
                impl.autoSelectOptionNumber = (int)parameters[1].AsNumber;
            });

            dialogue.library.RegisterFunction ("expect_line", 1, delegate(Value[] parameters) {
                impl.expectedNextLine = parameters[0].AsString;
            });

            dialogue.library.RegisterFunction ("expect_command", 1, delegate(Value[] parameters) {
                impl.expectedNextCommand = parameters[0].AsString;
            });

            if (autoSelectFirstOption == true) {
                impl.autoSelectFirstOption = true;
            }

            // If debugging is enabled, log debug messages; otherwise, ignore them
            if (showDebugging) {
                dialogue.LogDebugMessage = delegate(string message) {
                    Console.WriteLine ("Debug: " + message);
                };
            } else {
                dialogue.LogDebugMessage = delegate(string message) {};
            }

            dialogue.LogErrorMessage = delegate(string message) {
                Console.WriteLine ("ERROR: " + message);
            };

            if (verifyOnly) {
                try {
                    dialogue.LoadFile (inputFiles [0],showTokens, showParseTree, onlyConsiderNode);
                } catch (Exception e) {
                    Console.WriteLine ("Error: " + e.Message);
                }
                return;
            }

            dialogue.LoadFile (inputFiles [0],showTokens, showParseTree, onlyConsiderNode);

            if (compileToBytecodeOnly) {
                var result = dialogue.GetByteCode ();
                Console.WriteLine (result);
                return;
            }

            if (analyseOnly) {

                var context = new Yarn.Analysis.Context ();

                dialogue.Analyse (context);

                foreach (var diagnosis in context.FinishAnalysis()) {
                    Console.WriteLine (diagnosis.ToString(showSeverity:true));
                }
                return;
            }

            // Only run the program when we're not emitting debug output of some kind
            var runProgram =
                showTokens == false &&
                showParseTree == false &&
                compileToBytecodeOnly == false &&
                analyseOnly == false;

            if (runProgram) {
                // Run the conversation

                for (int run = 0; run < runTimes; run++) {
                    foreach (var step in dialogue.Run (startNode)) {

                        // It can be one of three types: a line to show, options
                        // to present to the user, or an internal command to run

                        if (step is Dialogue.LineResult) {
                            var lineResult = step as Dialogue.LineResult;
                            impl.RunLine (lineResult.line);
                        } else if (step is Dialogue.OptionSetResult) {
                            var optionsResult = step as Dialogue.OptionSetResult;
                            impl.RunOptions (optionsResult.options, optionsResult.setSelectedOptionDelegate);
                        } else if (step is Dialogue.CommandResult) {
                            var commandResult = step as Dialogue.CommandResult;
                            impl.RunCommand (commandResult.command.text);
                        }
                    }
                    impl.DialogueComplete ();
                }

            }
        }