Example #1
0
        public void GenericOperationConverterTest()
        {
            OperationConverter gOpCnv = new OperationConverter();
            NameValueContext   c      = new NameValueContext();
            InvokeMethod       op     = new InvokeMethod(this, "TestMethod", new object[] { c });
            TestGenOp          genOp  = new TestGenOp();

            Assert.AreEqual(true, gOpCnv.CanConvert(genOp.GetType(), typeof(IOperation <object>)));
            Convert.ToString(gOpCnv.Convert(genOp, typeof(IOperation <object>)).GetType());
            Assert.AreEqual(true, gOpCnv.Convert(genOp, typeof(IOperation <object>)) is IOperation <object>);
            ((IOperation <object>)gOpCnv.Convert(genOp, typeof(IOperation <object>))).Execute(c);
            Assert.AreEqual("a", c["b"]);

            Assert.AreEqual(true, gOpCnv.CanConvert(op.GetType(), typeof(IOperation <NameValueContext>)));
            Assert.AreEqual(true, gOpCnv.Convert(op, typeof(IOperation <NameValueContext>)) is IOperation <NameValueContext>);
            ((IOperation <NameValueContext>)gOpCnv.Convert(op, typeof(IOperation <NameValueContext>))).Execute(c);
            Assert.AreEqual("b", c["a"]);

            // compatible conversion between IOperation<>
            Assert.AreEqual(true, gOpCnv.CanConvert(typeof(IOperation <Context>), typeof(IOperation <NameValueContext>)));
            Assert.AreEqual(true, gOpCnv.CanConvert(typeof(IOperation <NameValueContext>), typeof(IOperation <Context>)));
            IOperation <NameValueExContext> genOpEx = (IOperation <NameValueExContext>)gOpCnv.Convert(genOp, typeof(IOperation <NameValueExContext>));
            NameValueExContext cEx = new NameValueExContext();

            genOpEx.Execute(cEx);
            Assert.AreEqual("a", cEx["b"]);
        }
Example #2
0
        private void ExportScene(Scene scene, string path)
        {
            StreamWriter writer = File.CreateText(path);

            var graphNodes = new List <GraphNode>(scene.Nodes.Count);

            for (int i = 0; i < scene.Nodes.Count; i++)
            {
                graphNodes.Add(new GraphNode());
            }
            var maxId = graphNodes.Capacity + 1;

            for (int i = 0; i < scene.Nodes.Count; i++)
            {
                var node = scene.Nodes[i];
                graphNodes[i].Node = node;

                switch (node)
                {
                case NodeJump nj:
                    var targetId = scene.Nodes.IndexOf(nj.Target);
                    graphNodes[i].Text = $"Jump from {i} to {targetId}";
                    graphNodes[i].NextNodes.Add(graphNodes[targetId], null);
                    break;

                case NodeVariableJump nvj:
                    graphNodes[i].Text = FormatVariableJump(nvj);
                    var targetId1 = scene.Nodes.IndexOf(nvj.Target);
                    var targetId2 = i + 1;

                    if (nvj.Variable.Binary && nvj.Value == 0)
                    {
                        graphNodes[i].NextNodes.Add(graphNodes[targetId1], "false");
                        graphNodes[i].NextNodes.Add(graphNodes[targetId2], "true");
                    }
                    else
                    {
                        graphNodes[i].NextNodes.Add(graphNodes[targetId1], "true");
                        graphNodes[i].NextNodes.Add(graphNodes[targetId2], "false");
                    }

                    break;

                case NodeResponseDialogue nrd:
                    graphNodes[i].Text = $"{nrd.Character.Name}\n\"{nrd.Dialogue.WordWrap(70)}\"";
                    foreach (var response in nrd.ResponseMap)
                    {
                        var targetIdr = scene.Nodes.IndexOf(response.Target);
                        if (graphNodes[i].NextNodes.ContainsKey(graphNodes[targetIdr]))
                        {
                            graphNodes[i].NextNodes[graphNodes[targetIdr]] += $"\n{response.Text}";
                        }
                        else
                        {
                            graphNodes[i].NextNodes.Add(graphNodes[targetIdr], response.Text);
                        }
                    }
                    break;

                case NodeCall nc:
                    graphNodes[i].Text = nc.Target.Name;
                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                case NodeSetVariable nsv:
                    graphNodes[i].Text = $"{nsv.Variable.Name} {_converter.Convert(nsv.Type, null, null, null)} {nsv.Value}";
                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                case NodeDialogue nd:
                    graphNodes[i].Text = $"{nd.Character.Name}\n\"{nd.Text.WordWrap(70)}\"";
                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                case NodeMovement nm:
                    graphNodes[i].Text = $"Movement\n{nm.Character.Name} {nm.MovementDirection} {nm.MovementType}";
                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                case NodeChangeBackground ncb:
                    graphNodes[i].Text = $"Background change\n{ncb.Background.Name}";
                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                case NodeFadeMusic nfm:
                    switch (nfm.AudioFadeType)
                    {
                    case NodeFadeMusic.AudioFadeTypeEnum.All:
                        graphNodes[i].Text = $"Sound fade";
                        break;

                    case NodeFadeMusic.AudioFadeTypeEnum.Music:
                        graphNodes[i].Text = $"Music fade";
                        break;

                    case NodeFadeMusic.AudioFadeTypeEnum.SFX:
                        graphNodes[i].Text = $"SFX fade";
                        break;
                    }

                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                case NodeChangePose ncp:
                    graphNodes[i].Text = $"Pose change\n{ncp.Character.Name} -> {ncp.Pose.Name}";
                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                case NodeWait nw:
                    graphNodes[i].Text = $"Wait {nw.Duration}s";
                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                case NodePlaySound nps:
                    switch (nps.SoundType)
                    {
                    case NodePlaySound.SoundTypeEnum.Music:
                        graphNodes[i].Text = $"Play music\n{nps.Sound.Name}";
                        break;

                    case NodePlaySound.SoundTypeEnum.SFX:
                        graphNodes[i].Text = $"Play sound\n{nps.Sound.Name}";
                        break;
                    }

                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                case NodeRet _:
                    graphNodes[i].Text = "End\n" + scene.Name;
                    break;

                case NodeTodo nt:
                    graphNodes[i].Text = $"TODO\n{nt.Note}";
                    graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    break;

                default:
                    graphNodes[i].Text = node.GetType().Name;
                    if (i != scene.Nodes.Count - 1)
                    {
                        graphNodes[i].NextNodes.Add(graphNodes[i + 1], null);
                    }
                    break;
                }
            }

            foreach (var node in graphNodes)
            {
                foreach (var target in node.NextNodes)
                {
                    var targetId = graphNodes.IndexOf(target.Key);
                    if (targetId < 0)
                    {
                        continue;
                    }
                    graphNodes[targetId].PreviousNodes.Add(node);
                }
            }

            foreach (var node in graphNodes)
            {
                node.Critical = ShowEntireGraph ||
                                node.NextNodes.Count >= 2 ||
                                node.PreviousNodes.Count >= 2 ||
                                IsBranching(node.Node) ||
                                node.PreviousNodes.Any(n => IsBranching(n.Node));

                var sign = IsCharacterVariable(node.Node);
                if (sign > 0)
                {
                    node.Style = "style=filled fillcolor=green";
                }
                else if (sign < 0)
                {
                    node.Style = "style=filled fillcolor=red";
                }
            }

            var startNode = graphNodes.First();

            startNode.Critical = true;

            var endNode = graphNodes.Last();

            endNode.Critical = true;
            endNode.Text     = "End\n" + scene.Name;

            if (!ShowEntireGraph)
            {
                // Collapse 1-input, 1-output jump nodes
                foreach (var jumpNode in graphNodes.Where(n => n.Node is NodeJump))
                {
                    if (jumpNode.PreviousNodes.Count == 1 && jumpNode.NextNodes.Count == 1)
                    {
                        var prevNode = jumpNode.PreviousNodes.First();
                        var nextNode = jumpNode.NextNodes.First();

                        GraphNode nextCriticalNode = null;
                        if (nextNode.Key.Critical)
                        {
                            nextCriticalNode = nextNode.Key;
                        }
                        else
                        {
                            nextCriticalNode = graphNodes[GetNextNode(graphNodes, nextNode.Key)];
                        }

                        jumpNode.Critical = false;
                        prevNode.Critical = true;

                        var oldNext = prevNode.NextNodes[jumpNode];
                        prevNode.NextNodes.Remove(jumpNode);
                        prevNode.NextNodes.Add(nextCriticalNode, oldNext);

                        nextCriticalNode.PreviousNodes.Remove(jumpNode);
                        nextCriticalNode.PreviousNodes.Add(prevNode);
                    }
                }
            }

            writer.WriteLine("digraph {" +
                             $"start [label=\"Start\n{scene.Name}\" shape=rarrow margin=0.2];\n" +
                             "start -> 0;");

            for (int i = 0; i < graphNodes.Count; i++)
            {
                GraphNode node = graphNodes[i];
                if (!node.Critical)
                {
                    continue;
                }

                if (node.Node is NodeRet)
                {
                    writer.WriteLine($"{i} [label=\"{node.Text?.Replace("\"","\\\"")}\" shape=larrow margin=0.2];");
                }
                else if (node.Node is NodeVariableJump)
                {
                    writer.WriteLine($"{i} [label=\"{node.Text?.Replace("\"", "\\\"")}\" shape=diamond];");
                }
                else if (node.Node is NodeCall)
                {
                    writer.WriteLine($"{i} [label=\"{node.Text?.Replace("\"", "\\\"")}\" shape=rarrow];");
                }
                else if (node.Node is NodeTodo)
                {
                    writer.WriteLine($"{i} [label=\"{node.Text?.Replace("\"", "\\\"")}\" shape=box color=green ];");
                }
                else if (node.Node is NodeAchievement)
                {
                    writer.WriteLine($"{i} [label=\"{node.Text?.Replace("\"", "\\\"")}\" shape=box color=yellow ];");
                }
                else if (node.Node is NodeLua)
                {
                    writer.WriteLine($"{i} [label=\"{node.Text?.Replace("\"", "\\\"")}\\nPotential branch\" shape=box color=blue ];");
                }
                else
                {
                    writer.WriteLine($"{i} [label=\"{node.Text?.Replace("\"", "\\\"")}\" shape=box {node.Style}];");
                }

                if (node.PreviousNodes.Count == 0 && i != 0)
                {
                    if (scene.Nodes.Any(n => n is NodeLua))
                    {
                        Debug.WriteLine($"Node with no incoming edges: {scene.Name} ({i}) - {node.Node} | But scene contains Lua");
                    }
                    else
                    {
                        Debug.WriteLine($"Node with no incoming edges: {scene.Name} ({i}) - {node.Node}");
                    }
                }

                foreach (var nextNode in node.NextNodes)
                {
                    if (node != graphNodes.Last())
                    {
                        if (!nextNode.Key.Critical)
                        {
                            var skipNodeId = GetNextNode(graphNodes, node);
                            writer.WriteLine($"{i} -> {skipNodeId};");
                            continue;
                        }
                        ;

                        writer.WriteLine($"{i} -> {graphNodes.IndexOf(nextNode.Key)} [ label=\"{nextNode.Value?.Replace("\"", "\\\"")}\" ];");
                    }
                }

                if (node.NextNodes.Count >= 2)
                {
                    writer.Write("{rank = same;");
                    foreach (var nextNode in node.NextNodes)
                    {
                        var nextNodeId = graphNodes.IndexOf(nextNode.Key);
                        writer.Write($"{nextNodeId};");
                    }
                    writer.WriteLine("}");
                }
            }

            writer.WriteLine("}");
            writer.Close();
        }