├── VGPrompter ├── Script │ ├── Internal │ │ ├── Script.VGPReturn.cs │ │ ├── Script.VGPPass.cs │ │ ├── Base │ │ │ ├── Script.IWrappable.cs │ │ │ ├── Script.ITextual.cs │ │ │ ├── Script.DummyLine.cs │ │ │ ├── Script.IConditional.cs │ │ │ ├── Script.PickableContainer.cs │ │ │ ├── Script.VGPBaseReference.cs │ │ │ ├── Script.Line.cs │ │ │ ├── Script.ChildContainer.cs │ │ │ ├── Script.Conditional.cs │ │ │ ├── Script.Container.cs │ │ │ └── Script.IterableContainer.cs │ │ ├── Script.VGPReference.cs │ │ ├── Script.VGPGoTo.cs │ │ ├── Script.VGPDefine.cs │ │ ├── Script.VGPBlock.cs │ │ ├── Script.VGPDialogueLine.cs │ │ ├── Script.VGPWhile.cs │ │ ├── Script.VGPIfElse.cs │ │ ├── Script.VGPMenu.cs │ │ ├── Script.VGPFunction.cs │ │ └── Script.VGPChoice.cs │ ├── Public │ │ ├── Base │ │ │ └── Script.IScriptLineWrapper.cs │ │ ├── Script.Reference.cs │ │ ├── Script.DialogueLine.cs │ │ ├── Script.Choice.cs │ │ ├── Script.Menu.cs │ │ └── Character.cs │ ├── Parser │ │ ├── Script.Parser.Node.cs │ │ └── Script.Parser.cs │ ├── Script.TextManager.cs │ └── Script.cs ├── Logger.cs ├── Properties │ └── AssemblyInfo.cs ├── Misc.cs └── VGPrompter.csproj ├── Tests ├── Data │ ├── insc_test1_tab.rpy │ ├── insc_test1.rpy │ └── demo.rpy ├── Properties │ └── AssemblyInfo.cs ├── Tests.csproj └── UnitTest1.cs ├── LICENSE.md ├── README.md ├── VGPrompter.sln ├── .gitattributes └── .gitignore /VGPrompter/Script/Internal/Script.VGPReturn.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | class VGPReturn : DummyLine { } 9 | 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPPass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | class VGPPass : DummyLine { } 9 | 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /VGPrompter/Script/Public/Base/Script.IScriptLineWrapper.cs: -------------------------------------------------------------------------------- 1 | namespace VGPrompter { 2 | 3 | public partial class Script { 4 | 5 | public interface IScriptLine { 6 | string ToString(); 7 | } 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.IWrappable.cs: -------------------------------------------------------------------------------- 1 | namespace VGPrompter { 2 | 3 | public partial class Script { 4 | 5 | interface IWrappable { 6 | IScriptLine ToWrapper(Script script); 7 | } 8 | 9 | } 10 | 11 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.ITextual.cs: -------------------------------------------------------------------------------- 1 | namespace VGPrompter { 2 | 3 | public partial class Script { 4 | 5 | interface ITextual { 6 | bool ToInterpolate { get; } 7 | string GetInterpolatedText(Script script); 8 | } 9 | 10 | } 11 | 12 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.DummyLine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | abstract class DummyLine : Line { 9 | 10 | public override bool IsValid() { return true; } 11 | 12 | } 13 | 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.IConditional.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | interface IConditional { 8 | 9 | string Tag { get; } 10 | Func Condition { get; set; } 11 | bool IsTrue { get; } 12 | 13 | } 14 | 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.PickableContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | abstract class PickableContainer : ChildContainer where T : Line { 9 | 10 | public abstract T GetContent(); 11 | 12 | } 13 | 14 | } 15 | 16 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.VGPBaseReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | abstract class VGPBaseReference : Line, IWrappable { 9 | 10 | public string Tag { get; private set; } 11 | 12 | public VGPBaseReference(string label) { 13 | Tag = label; 14 | } 15 | 16 | public abstract override bool IsValid(); 17 | 18 | public abstract IScriptLine ToWrapper(Script script = null); 19 | 20 | } 21 | 22 | } 23 | 24 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Public/Script.Reference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | public class Reference : IScriptLine { 8 | 9 | public string Tag { get; private set; } 10 | public Action Action { get; private set; } 11 | 12 | public Reference(string tag, Action action) { 13 | Tag = tag; 14 | Action = action; 15 | } 16 | 17 | public new string ToString() { 18 | return "REFERENCE: " + Tag; 19 | } 20 | 21 | } 22 | 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /VGPrompter/Script/Public/Script.DialogueLine.cs: -------------------------------------------------------------------------------- 1 | namespace VGPrompter { 2 | 3 | public partial class Script { 4 | 5 | public class DialogueLine : IScriptLine { 6 | 7 | public string Tag { get; private set; } 8 | public string Text { get; private set; } 9 | 10 | public DialogueLine(string text, string tag = null) { 11 | Tag = tag ?? string.Empty; 12 | Text = text; 13 | } 14 | 15 | public new string ToString() { 16 | return (string.IsNullOrEmpty(Tag) ? "" : Tag) + ": " + Text; 17 | } 18 | 19 | } 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPReference.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | class VGPReference : VGPBaseReference { 9 | 10 | [NonSerialized] 11 | Action _action; 12 | 13 | public Action Action { get { return _action; } set { _action = value; } } 14 | 15 | public VGPReference(string label) : base(label) { } 16 | 17 | public override bool IsValid() { 18 | return Action != null; 19 | } 20 | 21 | public override IScriptLine ToWrapper(Script script = null) { 22 | return new Reference(Tag, Action); 23 | } 24 | 25 | } 26 | 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.Line.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | abstract class Line { 9 | 10 | public abstract bool IsValid(); 11 | 12 | // protected abstract string ValidationErrorMessage { get; } 13 | 14 | public virtual void Validate() { 15 | if (!IsValid()) { 16 | var msg = string.Format("Invalid '{0}'!", ToString()); 17 | /* if (!string.IsNullOrEmpty(ValidationErrorMessage)) 18 | msg += '\n' + ValidationErrorMessage;*/ 19 | throw new Exception(msg); 20 | } 21 | } 22 | 23 | } 24 | 25 | } 26 | 27 | } -------------------------------------------------------------------------------- /Tests/Data/insc_test1_tab.rpy: -------------------------------------------------------------------------------- 1 | define xyz = "XYZXYZXYZ" 2 | define abc = "ABCABCABC" 3 | 4 | label start: 5 | 6 | "First [abc] anonymous line [xyz]." 7 | char1 "Second line." 8 | char1 "Third line." # Test quoted 9 | 10 | menu: # Test unquoted 11 | "First anonymous choice [xyz]": 12 | char1 "First choice line." 13 | tag "Second named choice": 14 | char1 "Second choice line." 15 | "Third conditional anonymous choice" if False: 16 | char1 "Third choice line." 17 | tag "Fourth conditional named choice" if True: 18 | char1 "Fourth choice line." 19 | 20 | if True: 21 | char1 "Is true." 22 | else: 23 | char1 "Is false." 24 | 25 | while True: 26 | char1 "While line." 27 | 28 | menu: 29 | "asd": 30 | call first 31 | "sdf": 32 | jump first 33 | 34 | label first: 35 | char "asfasdf" 36 | return -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPGoTo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.Serialization; 3 | 4 | namespace VGPrompter { 5 | 6 | public partial class Script { 7 | 8 | [Serializable] 9 | class VGPGoTo : Line { 10 | 11 | string _target; 12 | public bool IsCall { get; private set; } 13 | public VGPBlock Target { get; private set; } 14 | 15 | public VGPGoTo(string target_label, bool is_call) { 16 | _target = target_label; 17 | IsCall = is_call; 18 | } 19 | 20 | public void SetTarget(Script script) { 21 | Target = script.Blocks[_target]; 22 | } 23 | 24 | public override bool IsValid() { 25 | return Target != null; 26 | } 27 | 28 | } 29 | 30 | } 31 | 32 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Public/Script.Choice.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | public class Choice : IScriptLine { 8 | 9 | public int Index { get; private set; } 10 | public string Tag { get; private set; } 11 | public string Text { get; private set; } 12 | public bool IsTrue { get; set; } 13 | 14 | public Choice(int index, string text, bool is_true, string tag = null) { 15 | 16 | Index = index; 17 | Tag = tag ?? string.Empty; 18 | Text = text; 19 | IsTrue = is_true; 20 | 21 | } 22 | 23 | public new string ToString() { 24 | return (string.IsNullOrEmpty(Tag) ? string.Empty : (Tag + ": ")) + Text; 25 | } 26 | 27 | } 28 | 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /VGPrompter/Logger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | [Serializable] 6 | public class Logger { 7 | 8 | const string 9 | DEFAULT_NAME = "Logger", 10 | NULL = "Null"; 11 | 12 | public bool Enabled { get; set; } 13 | public string Name { get; private set; } 14 | Action _logger; 15 | 16 | public Logger(bool enabled = true) : this(DEFAULT_NAME, enabled: enabled) { } 17 | 18 | public Logger(string name, Action f = null, bool enabled = true) { 19 | Enabled = enabled; 20 | Name = name; 21 | _logger = f ?? Console.WriteLine; 22 | } 23 | 24 | public void Log(object s, bool logger_prefix = true) { 25 | if (Enabled) 26 | _logger(logger_prefix ? string.Format("[{0}] {1}", Name, s ?? NULL) : s); 27 | } 28 | 29 | } 30 | 31 | } -------------------------------------------------------------------------------- /Tests/Data/insc_test1.rpy: -------------------------------------------------------------------------------- 1 | define xyz = "XYZXYZXYZ" 2 | 3 | label start: 4 | 5 | "First anonymous line [xyz]." 6 | char1 "Second line." 7 | char1 "Third line." 8 | 9 | menu: 10 | "First anonymous choice [xyz]": 11 | char1 "First choice line [xyz]." 12 | tag "Second named choice": 13 | char1 "Second choice line." 14 | "Third conditional anonymous choice" if False: 15 | char1 "Third choice line." 16 | tag "Fourth conditional named choice" if True: 17 | char1 "Fourth choice line." 18 | 19 | if True: 20 | char1 "Is true." 21 | else: 22 | char1 "Is false." 23 | 24 | while True: 25 | char1 "While line." 26 | 27 | menu: 28 | "asd": 29 | call first 30 | "sdf": 31 | jump first 32 | 33 | label first: 34 | char "asfasdf" 35 | return -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPDefine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | class VGPDefine : Line, ITextual { 9 | 10 | public string Key { get; private set; } 11 | public string Value { get; private set; } 12 | public bool ToInterpolate { get; internal set; } 13 | 14 | public VGPDefine(string key, string value, bool to_interpolate) { 15 | Key = key; 16 | Value = value; 17 | ToInterpolate = to_interpolate; 18 | } 19 | 20 | public override bool IsValid() { 21 | return !string.IsNullOrEmpty(Value); 22 | } 23 | 24 | public string GetInterpolatedText(Script script) { 25 | return script._text_manager.GetGlobalText(Key, ToInterpolate); 26 | } 27 | 28 | } 29 | 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Tests/Data/demo.rpy: -------------------------------------------------------------------------------- 1 | define noun = "color" 2 | define b = "Blue" 3 | define adj = "spinning" 4 | define sth = "cube" 5 | define things = "[adj] [sth]s" 6 | 7 | label start: 8 | if False: #asdfadf 9 | "Whatever!" 10 | eu "Hi there." 11 | eu "Let me show you the power of [things]!" 12 | eu "Pick a [noun], would you?" #asdfadf 13 | 14 | while True: 15 | menu 5000: # [3, 2, 1]: Introduce a way to set the order of the choices programmatically? 16 | "Green" if CurrentColorNotGreen: 17 | TurnCubeGreen 18 | "[b]": 19 | TurnCubeBlue() 20 | "The prohibited color!" if False: 21 | #SomeOtherDelegate("test") 22 | pass 23 | "Not interested, thanks": 24 | eu "That's enough for today." #[exp:sad, gesture:pray] 25 | SomeDelegate(1, 2) 26 | Test(True, False, "s1", 's2', 1.0, 12, 2.0f) 27 | jump another_scene 28 | #else: 29 | #jump ignavus 30 | 31 | "Come on, another one!" 32 | 33 | eu "Cool, ah?" 34 | 35 | label another_scene: 36 | return 37 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # VGPrompter 2 | 3 | VGPrompter is a C# library which parses an extended subset of [Ren'Py](https://www.renpy.org/) script syntax and allows to iterate over the lines according to: 4 | - flow control statements (conditions are C# parameterless methods referenced in the script); 5 | - user input provided to interactive menus. 6 | 7 | The compiled library is available as a [Unity3D managed plugin](https://www.assetstore.unity3d.com/en/#!/content/69665) in the Asset Store. 8 | 9 | The project targets the .NET 3.5 framework and has no dependencies. 10 | 11 | ## Disclaimer 12 | **It's still a work in progress!** 13 | 14 | What it is **NOT**: 15 | - a Ren'Py C# port; 16 | - a visual novel engine. 17 | 18 | Anything related to graphics and sound is purposefully excluded from this project. 19 | 20 | Nothing in the script ever gets evaluated; logic can only be embedded by referencing C# methods via aliases. 21 | 22 | ## Documentation 23 | See the [wiki](https://github.com/eugeniusfox/vgprompter/wiki) or jump straight to the [quickstart](https://github.com/eugeniusfox/vgprompter/wiki/Quickstart)! 24 | 25 | ## Development & Future plans 26 | Check my blog: https://eugeniusfox.wordpress.com/ 27 | -------------------------------------------------------------------------------- /VGPrompter/Script/Public/Script.Menu.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | 4 | namespace VGPrompter { 5 | 6 | public partial class Script { 7 | 8 | public class Menu : IScriptLine { 9 | 10 | const string MENU = "MENU"; 11 | 12 | public int Count { 13 | get { return Choices != null ? Choices.Count : 0; } 14 | } 15 | 16 | public int TrueCount { 17 | get { return Choices != null ? Choices.Count(x => x.IsTrue) : 0; } 18 | } 19 | 20 | public List Choices { get; private set; } 21 | 22 | public List TrueChoices { 23 | get { return Choices.Where(x => x.IsTrue).ToList(); } 24 | } 25 | 26 | public Menu(List choices) { 27 | Choices = choices; 28 | } 29 | 30 | public new string ToString() { 31 | return string.Format("{0}: {1}", MENU, 32 | string.Join(COMMA, Choices.Select( 33 | (x, i) => string.Format("[{0}] {1}", i, x.ToString())).ToArray())); 34 | } 35 | 36 | } 37 | 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.ChildContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | abstract class ChildContainer : Container where T : Line { 9 | 10 | Script _script; 11 | VGPBlock _parent; 12 | 13 | public VGPBlock Parent { 14 | get { 15 | return _parent; 16 | } 17 | set { 18 | if (_parent == null) { 19 | _parent = value; 20 | } else { 21 | throw new Exception("The Parent of a SubBlock cannot be changed!"); 22 | } 23 | } 24 | } 25 | 26 | public override Script Script { 27 | get { 28 | return _parent != null ? _parent.Script : _script; 29 | } 30 | set { 31 | if (_parent == null) { 32 | _script = value; 33 | } else { 34 | throw new Exception("Script is already derived from parent!"); 35 | } 36 | } 37 | } 38 | 39 | } 40 | 41 | } 42 | 43 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Public/Character.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace VGPrompter { 7 | 8 | public interface IEntityType { } 9 | 10 | public interface ICharacter : IEntityType { } 11 | public interface ILocation : IEntityType { } 12 | public interface IItem : IEntityType { } 13 | 14 | 15 | public abstract class Entity where T : IEntityType { 16 | 17 | public string Id { get; private set; } 18 | public string Name { get; private set; } 19 | 20 | public Entity(string id, string name = null) { 21 | Id = id; 22 | Name = name; 23 | } 24 | 25 | } 26 | 27 | public class Tag { 28 | 29 | public string Id { get; private set; } 30 | public List Children { get; private set; } 31 | 32 | public Tag(string id, List children = null) { 33 | Id = id; 34 | Children = children; 35 | } 36 | 37 | } 38 | 39 | public class EntityManager { 40 | 41 | public List> Characters { get; private set; } 42 | 43 | public List GetIds() { 44 | return Characters.Select(c => c.Id).ToList(); 45 | } 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPBlock.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | using System.Linq; 4 | 5 | namespace VGPrompter { 6 | 7 | public partial class Script { 8 | 9 | [Serializable] 10 | class VGPBlock : IterableContainer { 11 | 12 | public string Label { get; private set; } 13 | public List FromInstanceIDs { get; private set; } 14 | 15 | public VGPBlock() { 16 | FromInstanceIDs = new List(); 17 | Contents = new List(); 18 | } 19 | 20 | public VGPBlock(string label) 21 | : this() { 22 | Label = label; 23 | } 24 | 25 | public VGPBlock(string label, List contents) 26 | : this(label) { 27 | Contents = contents ?? throw new Exception("Block contents can't be null!"); 28 | } 29 | 30 | public void RegisterID(int id) { 31 | FromInstanceIDs.Add(id); 32 | } 33 | 34 | public override bool IsValid() { 35 | return !string.IsNullOrEmpty(Label); 36 | } 37 | 38 | public new string ToString() { 39 | return "BLOCK " + Label + ": " + string.Join(COMMA, Contents.Select(x => x.ToString()).ToArray()); 40 | } 41 | 42 | } 43 | 44 | } 45 | 46 | } -------------------------------------------------------------------------------- /VGPrompter.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 14 4 | VisualStudioVersion = 14.0.25420.1 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "VGPrompter", "VGPrompter\VGPrompter.csproj", "{2229A66D-6DE9-4664-BBE2-77B56BF91E25}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{03D03C5E-71C4-4B89-BB87-66F1A635F0E6}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {2229A66D-6DE9-4664-BBE2-77B56BF91E25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {2229A66D-6DE9-4664-BBE2-77B56BF91E25}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {2229A66D-6DE9-4664-BBE2-77B56BF91E25}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {2229A66D-6DE9-4664-BBE2-77B56BF91E25}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {03D03C5E-71C4-4B89-BB87-66F1A635F0E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {03D03C5E-71C4-4B89-BB87-66F1A635F0E6}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {03D03C5E-71C4-4B89-BB87-66F1A635F0E6}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {03D03C5E-71C4-4B89-BB87-66F1A635F0E6}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /Tests/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("Tests")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("Tests")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("03d03c5e-71c4-4b89-bb87-66f1a635f0e6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /VGPrompter/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("InteractiveNovelScriptCompiler")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("InteractiveNovelScriptCompiler")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("2229a66d-6de9-4664-bbe2-77b56bf91e25")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPDialogueLine.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | class VGPDialogueLine : Line, IWrappable, ITextual { 9 | 10 | public bool ToInterpolate { get; private set; } 11 | public string Tag { get; private set; } 12 | public string ParentLabel { get; private set; } 13 | public string TextHash { get; private set; } 14 | 15 | public VGPDialogueLine() { } 16 | 17 | public VGPDialogueLine(string label, string hash, string tag, bool to_interpolate) { 18 | Tag = tag; 19 | ParentLabel = label; 20 | TextHash = hash; 21 | ToInterpolate = to_interpolate; 22 | } 23 | 24 | public override bool IsValid() { return !string.IsNullOrEmpty(TextHash); } 25 | 26 | public new string ToString() { 27 | return (string.IsNullOrEmpty(Tag) ? "" : Tag) + ": " + TextHash; 28 | } 29 | 30 | public IScriptLine ToWrapper(Script script) { 31 | //var text = script._text_manager.GetText(ParentLabel, TextHash, ToInterpolate); 32 | var text = GetInterpolatedText(script); 33 | return new DialogueLine(text, Tag); 34 | } 35 | 36 | public string GetInterpolatedText(Script script) { 37 | return script._text_manager.GetText(ParentLabel, TextHash, ToInterpolate); 38 | } 39 | 40 | } 41 | 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.Conditional.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | class Conditional : IterableContainer, IConditional { 9 | 10 | [NonSerialized] 11 | Func _condition; 12 | 13 | public string Tag { get; private set; } 14 | public Func Condition { get { return _condition; } set { _condition = value; } } 15 | public bool IsTrue { get { return Condition(); } } 16 | 17 | [Serializable] 18 | public class If : Conditional { 19 | public If(string label, VGPBlock parent = null) : base(label, parent) { } 20 | } 21 | 22 | [Serializable] 23 | public class ElseIf : Conditional { 24 | public ElseIf(string label, VGPBlock parent = null) : base(label, parent) { } 25 | } 26 | 27 | [Serializable] 28 | public class Else : Conditional { 29 | public Else(VGPBlock parent = null) : base(TRUE, parent) { } 30 | } 31 | 32 | public Conditional() { 33 | InitializeContainer(); 34 | } 35 | 36 | Conditional(string label, VGPBlock parent = null) 37 | : this() { 38 | Tag = label; 39 | Parent = parent; 40 | } 41 | 42 | public override bool IsValid() { 43 | if (Condition == null) Script.Logger.Log(Tag); 44 | return Condition != null; 45 | } 46 | 47 | public override void Prime() { 48 | Condition = Script.GetCondition(Tag); 49 | Script.Logger.Log(Condition); 50 | base.Prime(); 51 | } 52 | 53 | } 54 | 55 | } 56 | 57 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPWhile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace VGPrompter { 4 | 5 | public partial class Script { 6 | 7 | [Serializable] 8 | class VGPWhile : IterableContainer, IConditional { 9 | 10 | [NonSerialized] 11 | Func _condition; 12 | 13 | public string Tag { get; private set; } 14 | public static uint MaxIterations = 100; 15 | public bool IsTrue { get { return Condition(); } } 16 | 17 | public uint Iterations { get; private set; } 18 | public Func Condition { get { return _condition; } set { _condition = value; } } 19 | 20 | public VGPWhile() { 21 | InitializeContainer(); 22 | } 23 | 24 | public VGPWhile(string label, VGPBlock parent) 25 | : this() { 26 | Tag = label; 27 | Parent = parent; 28 | } 29 | 30 | public bool Evaluate() { 31 | 32 | Validate(); 33 | 34 | /* So that MaxIterations is reset when the loop exits 35 | * on its own (non pathological state). 36 | */ 37 | 38 | if (IsTrue) { 39 | if (++Iterations < MaxIterations) { 40 | return true; 41 | } else { 42 | throw new Exception("While loop exceed the maximum of iterations allowed!"); 43 | } 44 | 45 | } else { 46 | Iterations = 0; 47 | } 48 | 49 | return false; 50 | } 51 | 52 | public override bool IsValid() { return Condition != null; } 53 | 54 | public new string ToString() { 55 | return "WHILE: " + Tag; 56 | } 57 | 58 | } 59 | 60 | } 61 | 62 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPIfElse.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | using System.Linq; 4 | 5 | namespace VGPrompter { 6 | 7 | public partial class Script { 8 | 9 | [Serializable] 10 | class VGPIfElse : PickableContainer { 11 | 12 | public VGPIfElse(VGPBlock parent) { 13 | Parent = parent; 14 | Contents = new List(); 15 | } 16 | 17 | public bool IsClosed { get { return !IsEmpty && Contents.Last() is Conditional.Else; } } 18 | 19 | public VGPIfElse(Conditional first_condition, VGPBlock parent) 20 | : this(parent) { 21 | Contents.Add(first_condition); 22 | } 23 | 24 | public VGPIfElse(List conditions, VGPBlock parent) 25 | : this(parent) { 26 | Contents = conditions; 27 | } 28 | 29 | public override bool IsEmpty { 30 | get { return Contents == null || Contents.Count == 0; } 31 | } 32 | 33 | public void AddCondition(Conditional condition) { 34 | 35 | if (IsClosed) throw new Exception("The IfElse block is finalized already!"); 36 | 37 | if (condition is Conditional.If) { 38 | if (!IsEmpty) throw new Exception("Invalid If block position!"); 39 | } else if (condition is Conditional.ElseIf) { 40 | if (IsEmpty) throw new Exception("Invalid ElseIf block position!"); 41 | } else if (condition is Conditional.Else) { 42 | if (IsEmpty) throw new Exception("Invalid Else block position!"); 43 | } 44 | 45 | Contents.Add(condition); 46 | } 47 | 48 | public override Conditional GetContent() { 49 | Validate(); 50 | return Contents.FirstOrDefault(x => x.Condition()); 51 | } 52 | 53 | public override bool IsValid() { 54 | //return Contents != null && Contents.All(x => x.IsValid()); 55 | return true; 56 | } 57 | 58 | public new void Prime() { 59 | foreach (var item in Contents) 60 | item.Prime(); 61 | } 62 | 63 | } 64 | 65 | } 66 | 67 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Parser/Script.Parser.Node.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | using System.Linq; 4 | 5 | namespace VGPrompter { 6 | 7 | public partial class Script { 8 | 9 | public static partial class Parser { 10 | 11 | class Node { 12 | 13 | public static int _indent; 14 | public static int? IndentSpan { 15 | get { return _indent; } 16 | set { 17 | if (value == null || value.Value < 0) throw new Exception("Invalid indentation span!"); 18 | _indent = value.Value; 19 | } 20 | } 21 | 22 | RawLine _line; 23 | 24 | public int Level { get; set; } 25 | 26 | public string Label => _line.Text; 27 | 28 | public RawLine Line => _line; 29 | 30 | //public int Index => _line.Index; 31 | //public string Source => _line.Source; 32 | //public string ExceptionString => _line.ExceptionString; 33 | 34 | public List Children { get; set; } 35 | 36 | public Node LastChild { get { return Children.LastOrDefault(); } } 37 | public bool IsEmpty { get { return Children.Count == 0; } } 38 | 39 | public static Node Root { 40 | get { return new Node(); } 41 | } 42 | 43 | Node() { 44 | _line = new RawLine(); 45 | Children = new List(); 46 | Level = -1; 47 | } 48 | 49 | public Node(RawLine line, char indent, List children = null) { 50 | 51 | if (IndentSpan == null) throw new Exception("Indentation span not set!"); 52 | 53 | Level = GetIndent(line.Text, indent) / _indent; 54 | Children = children ?? new List(); 55 | 56 | _line = line.Trim(); 57 | 58 | } 59 | 60 | public void Add(Node x) { 61 | Children.Add(x); 62 | } 63 | 64 | public new string ToString() { 65 | return IsEmpty ? Label : string.Format("{0} [{1}]: ({2})", 66 | Label, Children.Count, string.Join(COMMA, Children.Select(x => x.ToString()).ToArray())); 67 | } 68 | 69 | } 70 | 71 | } 72 | 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPMenu.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | using System.Linq; 4 | 5 | namespace VGPrompter { 6 | 7 | public partial class Script { 8 | 9 | [Serializable] 10 | class VGPMenu : PickableContainer, IWrappable { 11 | 12 | public int? Duration { get; private set; } 13 | 14 | public VGPMenu(VGPBlock parent, int? duration = null) { 15 | Parent = parent; 16 | Duration = duration; 17 | InitializeContainer(); 18 | } 19 | 20 | public VGPMenu(List choices, VGPBlock parent, int? duration = null) 21 | : this(parent, duration) { 22 | Contents = choices ?? new List(); 23 | } 24 | 25 | public VGPMenu FilteredMenu() { 26 | return IsEmpty() ? null : new VGPMenu(Contents.FindAll( 27 | x => x.Condition == null || x.Condition() == true), Parent); 28 | } 29 | 30 | public new bool IsEmpty() { 31 | return Contents.TrueForAll( 32 | x => x.Condition != null && x.Condition() == false); 33 | } 34 | 35 | public override VGPChoice GetContent() { 36 | return null; 37 | } 38 | 39 | public new string ToString() { 40 | return "MENU: " + string.Join(COMMA, Contents.Select(x => x.TextHash).ToArray()); 41 | } 42 | 43 | public new void Prime() { 44 | Script.NumberOfMenus++; 45 | foreach (var item in Contents) 46 | item.Prime(); 47 | } 48 | 49 | public IScriptLine ToWrapper(Script script = null) { 50 | 51 | return new Menu(Contents 52 | .Select((x, i) => x.ToWrapper(script, i)) 53 | .ToList()); 54 | 55 | /*var choices = new List(); 56 | 57 | for (int i = 0; i < Count; i++) 58 | if (Contents[i].IsTrue) 59 | choices.Add(Contents[i].ToWrapper(i)); 60 | 61 | return new MenuWrapper(choices);*/ 62 | 63 | /*return new MenuWrapper(Contents 64 | .Where(x => x.IsTrue) 65 | .Select(x => x.ToWrapper()) 66 | .ToList());*/ 67 | } 68 | 69 | public override bool IsValid() { return true; } 70 | 71 | } 72 | 73 | } 74 | 75 | } -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Base/Script.Container.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | 4 | namespace VGPrompter { 5 | 6 | public partial class Script { 7 | 8 | [Serializable] 9 | abstract class Container : Line where T : Line { 10 | 11 | public abstract Script Script { get; set; } 12 | 13 | public List Contents { get; set; } 14 | 15 | public int Count { get { return Contents != null ? Contents.Count : 0; } } 16 | 17 | public void InitializeContainer() { 18 | Contents = new List(); 19 | } 20 | 21 | public virtual bool IsEmpty { 22 | get { return Contents == null || Contents.Count == 0; } 23 | } 24 | 25 | public override void Validate() { 26 | if (Contents == null || Contents.Count == 0) 27 | throw new Exception(string.Format("Empty container '{0}'!", ToString())); 28 | 29 | foreach (var x in Contents) 30 | x.Validate(); 31 | 32 | base.Validate(); 33 | } 34 | 35 | public virtual void Prime() { 36 | if (Contents == null) 37 | Script.Logger.Log("Null contents!"); 38 | 39 | foreach (var item in Contents) { 40 | 41 | if (item is VGPReference) { 42 | var reference = item as VGPReference; 43 | reference.Action = Script.GetAction(reference.Tag); 44 | Script.Logger.Log(reference.Action); 45 | 46 | } else if (item is VGPFunction) { 47 | var f = item as VGPFunction; 48 | f.Delegate = Script.GetFunction(f.Tag); 49 | Script.Logger.Log(f.Delegate); 50 | 51 | } else if (item is Conditional) { 52 | (item as Conditional).Prime(); 53 | 54 | } else if (item is VGPChoice) { 55 | (item as VGPChoice).Prime(); 56 | 57 | } else if (item is VGPGoTo) { 58 | var gt = item as VGPGoTo; 59 | gt.SetTarget(Script); 60 | 61 | } else if (item is VGPMenu) { 62 | (item as VGPMenu).Prime(); 63 | 64 | } else if (item is VGPIfElse) { 65 | var ifelse = item as VGPIfElse; 66 | ifelse.Prime(); 67 | 68 | } else if (item is IterableContainer) { 69 | if (item is IConditional) { 70 | var conditional = item as IConditional; 71 | conditional.Condition = Script.GetCondition(conditional.Tag); 72 | } 73 | (item as IterableContainer).Prime(); 74 | } 75 | 76 | } 77 | } 78 | } 79 | } 80 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPFunction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | namespace VGPrompter { 6 | 7 | public partial class Script { 8 | 9 | [Serializable] 10 | class VGPFunction : VGPBaseReference { 11 | 12 | [NonSerialized] 13 | Delegate _delegate; 14 | 15 | object[] _argv; 16 | 17 | Type[] ArgumentTypes { get => Delegate.GetType().GetGenericArguments(); } 18 | 19 | public Delegate Delegate { get { return _delegate; } set { _delegate = value; } } 20 | public bool HasContext { get => ArgumentTypes[0] == typeof(Script); } 21 | 22 | public VGPFunction(string label, params object[] argv) : base(label) { 23 | _argv = argv; 24 | } 25 | 26 | int ArgumentIndexOffset { get => HasContext ? 1 : 0; } 27 | bool HasDelegate { get => Delegate != null; } 28 | bool IsArityValid { get => ArgumentTypes.Count() == _argv.Length + ArgumentIndexOffset; } 29 | bool AreArgumentsValid { get => _argv.Select((x, i) => x.GetType() == ArgumentTypes[i + ArgumentIndexOffset]).All(x => x); } 30 | 31 | protected string ValidationErrorMessage { get { 32 | 33 | if (!HasDelegate) return "Null Delegate!"; 34 | 35 | if (!IsArityValid) return string.Format( 36 | "Expected {0} arguments, got {1}!", 37 | ArgumentTypes.Count() - ArgumentIndexOffset, _argv.Length); 38 | 39 | if (!AreArgumentsValid) return string.Format( 40 | "Expected argument types <{1}>, got <{0}>!", 41 | string.Join(", ", _argv.Select(x => x.GetType().ToString()).ToArray()), 42 | string.Join(", ", ArgumentTypes.Select(x => x.ToString()).ToArray())); 43 | 44 | return string.Empty; 45 | 46 | } 47 | } 48 | 49 | public override bool IsValid() { 50 | var result = HasDelegate && IsArityValid && AreArgumentsValid; 51 | if (result) { 52 | return true; 53 | } else { 54 | throw new Exception(Tag + ": " + ValidationErrorMessage); 55 | } 56 | } 57 | 58 | Action GetAction(Script script = null) { 59 | 60 | if (HasContext) { 61 | if (script == null) throw new Exception("Missing Script reference!"); 62 | object[] args = { script }; 63 | args = args.Concat(_argv).ToArray(); 64 | return () => Delegate.DynamicInvoke(args.ToArray()); 65 | } else { 66 | return () => Delegate.DynamicInvoke(_argv); 67 | } 68 | 69 | } 70 | 71 | public override IScriptLine ToWrapper(Script script = null) { 72 | return new Reference(Tag, GetAction(script)); 73 | } 74 | 75 | } 76 | 77 | } 78 | 79 | } -------------------------------------------------------------------------------- /VGPrompter/Script/Internal/Script.VGPChoice.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | using System.Text.RegularExpressions; 4 | 5 | namespace VGPrompter { 6 | 7 | public partial class Script { 8 | 9 | [Serializable] 10 | abstract class VGPChoice : IterableContainer, IConditional, ITextual { 11 | 12 | [Serializable] 13 | public class VGPNamedChoice : VGPChoice { 14 | public string Label { get; private set; } 15 | 16 | public VGPNamedChoice() { } 17 | public VGPNamedChoice(string label, string text, VGPBlock parent, bool to_interpolate, string condition_label = null) 18 | : base(text, parent, to_interpolate, condition_label) { 19 | Label = label; 20 | } 21 | 22 | public override bool IsValid() { 23 | return base.IsValid() && !string.IsNullOrEmpty(Label); 24 | } 25 | 26 | public new string ToString() { 27 | return string.Format(@"{0} ""{1}"" {2}", Label, TextHash, Condition.Method.Name); 28 | } 29 | 30 | public override Choice ToWrapper(Script script, int index) { 31 | return new Choice(index, GetInterpolatedText(script), IsTrue, Label); 32 | } 33 | } 34 | 35 | [Serializable] 36 | public class VGPAnonymousChoice : VGPChoice { 37 | public VGPAnonymousChoice() { } 38 | public VGPAnonymousChoice(string hash, VGPBlock parent, bool to_interpolate, string condition_label = null) 39 | : base(hash, parent, to_interpolate, condition_label) { } 40 | 41 | public new string ToString() { 42 | return string.Format(@"""{1}"" {2}", TextHash, Condition.Method.Name); 43 | } 44 | 45 | public override Choice ToWrapper(Script script, int index) { 46 | return new Choice(index, GetInterpolatedText(script), IsTrue); 47 | } 48 | } 49 | 50 | [NonSerialized] 51 | Func _condition; 52 | 53 | public string Tag { get; private set; } 54 | public Func Condition { get { return _condition; } set { _condition = value; } } 55 | public string TextHash { get; private set; } 56 | public bool IsTrue { get { return Condition == null || Condition(); } } 57 | public bool ToInterpolate { get; private set; } 58 | 59 | public abstract Choice ToWrapper(Script script, int index); 60 | 61 | public string GetInterpolatedText(Script script) { 62 | return script._text_manager.GetText(Parent.Label, TextHash, ToInterpolate); 63 | } 64 | 65 | public VGPChoice() { 66 | Contents = new List(); 67 | } 68 | 69 | public VGPChoice(string hash, VGPBlock parent, bool to_interpolate, string condition_label = null) 70 | : this() { 71 | TextHash = hash; 72 | Tag = condition_label; 73 | Parent = parent; 74 | ToInterpolate = to_interpolate; 75 | } 76 | 77 | public override bool IsValid() { 78 | return !string.IsNullOrEmpty(TextHash) && (string.IsNullOrEmpty(Tag) || Condition != null); 79 | } 80 | 81 | public override void Prime() { 82 | Condition = Script.GetCondition(Tag); 83 | Script.Logger.Log(Condition); 84 | base.Prime(); 85 | } 86 | 87 | } 88 | 89 | } 90 | 91 | } -------------------------------------------------------------------------------- /VGPrompter/Misc.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System; 3 | using System.Linq; 4 | using System.IO; 5 | using System.Runtime.Serialization.Formatters.Binary; 6 | 7 | namespace VGPrompter { 8 | 9 | public static class Utils { 10 | 11 | const string 12 | COMMA = ", ", 13 | RPY_EXT = "*.rpy"; 14 | 15 | public static void WriteSerialized(string path, T thing) { 16 | var formatter = new BinaryFormatter(); 17 | using (var stream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.None)) { 18 | formatter.Serialize(stream, thing); 19 | } 20 | } 21 | 22 | public static T LoadSerialized(string path) { 23 | var formatter = new BinaryFormatter(); 24 | using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) { 25 | return (T)formatter.Deserialize(stream); 26 | } 27 | } 28 | 29 | public static T FromBinary(byte[] bytes) { 30 | var formatter = new BinaryFormatter(); 31 | using (var stream = new MemoryStream(bytes)) { 32 | return (T)formatter.Deserialize(stream); 33 | } 34 | } 35 | 36 | public static byte[] GetBinary(T thing) { 37 | byte[] result; 38 | var formatter = new BinaryFormatter(); 39 | using (var stream = new MemoryStream()) { 40 | formatter.Serialize(stream, thing); 41 | result = stream.ToArray(); 42 | } 43 | return result; 44 | } 45 | 46 | 47 | // http://stackoverflow.com/questions/10443461/c-sharp-array-findallindexof-which-findall-indexof 48 | public static int[] FindAllIndexOf(this IEnumerable values, T val) { 49 | return values.Select((b, i) => object.Equals(b, val) ? i : -1).Where(i => i != -1).ToArray(); 50 | } 51 | 52 | public static int[] FindAllIndexOf2(IEnumerable items, T value) { 53 | int i = 0; 54 | var result = new List(); 55 | foreach (var item in items) { 56 | if (item.Equals(value)) { 57 | result.Add(i); 58 | } 59 | i++; 60 | } 61 | return result.ToArray(); 62 | } 63 | 64 | public static void LogArray(string label, T[] a, Logger logger) { 65 | logger.Log(string.Format("{0}: {1}", label, string.Join(COMMA, a.Select(x => x.ToString()).ToArray()))); 66 | } 67 | 68 | public static string PrintKeys(Dictionary d) { 69 | return "DICT: " + string.Join(COMMA, d.Keys.ToArray()); 70 | } 71 | 72 | public static string[] GetScriptFiles(string dir, bool recursive = false) { 73 | 74 | if (!Directory.Exists(dir)) throw new Exception("Directory not found!"); 75 | 76 | if (recursive) { 77 | var files = new List(); 78 | files.AddRange(Directory.GetFiles(dir, RPY_EXT)); 79 | foreach (var d in Directory.GetDirectories(dir)) 80 | files.AddRange(GetScriptFiles(d, recursive: true)); 81 | return files.ToArray(); 82 | } else { 83 | return Directory.GetFiles(dir, RPY_EXT); 84 | } 85 | } 86 | 87 | public static T GetFromDictionary(string key, Dictionary d, Logger logger = null) { 88 | try { 89 | return !string.IsNullOrEmpty(key) ? d[key] : default(T); 90 | } catch (KeyNotFoundException ex) { 91 | if (logger != null) 92 | logger.Log(string.Format("Tag not found: '{0}'", key)); 93 | throw ex; 94 | } 95 | } 96 | 97 | } 98 | } -------------------------------------------------------------------------------- /VGPrompter/VGPrompter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {2229A66D-6DE9-4664-BBE2-77B56BF91E25} 8 | Library 9 | Properties 10 | VGPrompter 11 | VGPrompter 12 | v3.5 13 | 512 14 | 15 | 16 | true 17 | full 18 | false 19 | ..\..\..\..\..\Public\Documents\Unity Projects\InteractiveNovelDemo\Assets\Plugins\ 20 | DEBUG;TRACE 21 | prompt 22 | 4 23 | 24 | 25 | pdbonly 26 | true 27 | ..\..\..\..\..\Public\Documents\Unity Projects\InteractiveNovelDemo\Assets\Plugins\ 28 | TRACE 29 | prompt 30 | 4 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 88 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | [Xx]64/ 19 | [Xx]86/ 20 | [Bb]uild/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # Visual Studio 2015 cache/options directory 26 | .vs/ 27 | # Uncomment if you have tasks that create the project's static files in wwwroot 28 | #wwwroot/ 29 | 30 | # MSTest test Results 31 | [Tt]est[Rr]esult*/ 32 | [Bb]uild[Ll]og.* 33 | 34 | # NUNIT 35 | *.VisualState.xml 36 | TestResult.xml 37 | 38 | # Build Results of an ATL Project 39 | [Dd]ebugPS/ 40 | [Rr]eleasePS/ 41 | dlldata.c 42 | 43 | # DNX 44 | project.lock.json 45 | artifacts/ 46 | 47 | *_i.c 48 | *_p.c 49 | *_i.h 50 | *.ilk 51 | *.meta 52 | *.obj 53 | *.pch 54 | *.pdb 55 | *.pgc 56 | *.pgd 57 | *.rsp 58 | *.sbr 59 | *.tlb 60 | *.tli 61 | *.tlh 62 | *.tmp 63 | *.tmp_proj 64 | *.log 65 | *.vspscc 66 | *.vssscc 67 | .builds 68 | *.pidb 69 | *.svclog 70 | *.scc 71 | 72 | # Chutzpah Test files 73 | _Chutzpah* 74 | 75 | # Visual C++ cache files 76 | ipch/ 77 | *.aps 78 | *.ncb 79 | *.opendb 80 | *.opensdf 81 | *.sdf 82 | *.cachefile 83 | *.VC.db 84 | 85 | # Visual Studio profiler 86 | *.psess 87 | *.vsp 88 | *.vspx 89 | *.sap 90 | 91 | # TFS 2012 Local Workspace 92 | $tf/ 93 | 94 | # Guidance Automation Toolkit 95 | *.gpState 96 | 97 | # ReSharper is a .NET coding add-in 98 | _ReSharper*/ 99 | *.[Rr]e[Ss]harper 100 | *.DotSettings.user 101 | 102 | # JustCode is a .NET coding add-in 103 | .JustCode 104 | 105 | # TeamCity is a build add-in 106 | _TeamCity* 107 | 108 | # DotCover is a Code Coverage Tool 109 | *.dotCover 110 | 111 | # NCrunch 112 | _NCrunch_* 113 | .*crunch*.local.xml 114 | nCrunchTemp_* 115 | 116 | # MightyMoose 117 | *.mm.* 118 | AutoTest.Net/ 119 | 120 | # Web workbench (sass) 121 | .sass-cache/ 122 | 123 | # Installshield output folder 124 | [Ee]xpress/ 125 | 126 | # DocProject is a documentation generator add-in 127 | DocProject/buildhelp/ 128 | DocProject/Help/*.HxT 129 | DocProject/Help/*.HxC 130 | DocProject/Help/*.hhc 131 | DocProject/Help/*.hhk 132 | DocProject/Help/*.hhp 133 | DocProject/Help/Html2 134 | DocProject/Help/html 135 | 136 | # Click-Once directory 137 | publish/ 138 | 139 | # Publish Web Output 140 | *.[Pp]ublish.xml 141 | *.azurePubxml 142 | 143 | # TODO: Un-comment the next line if you do not want to checkin 144 | # your web deploy settings because they may include unencrypted 145 | # passwords 146 | #*.pubxml 147 | *.publishproj 148 | 149 | # NuGet Packages 150 | *.nupkg 151 | # The packages folder can be ignored because of Package Restore 152 | **/packages/* 153 | # except build/, which is used as an MSBuild target. 154 | !**/packages/build/ 155 | # Uncomment if necessary however generally it will be regenerated when needed 156 | #!**/packages/repositories.config 157 | # NuGet v3's project.json files produces more ignoreable files 158 | *.nuget.props 159 | *.nuget.targets 160 | 161 | # Microsoft Azure Build Output 162 | csx/ 163 | *.build.csdef 164 | 165 | # Microsoft Azure Emulator 166 | ecf/ 167 | rcf/ 168 | 169 | # Microsoft Azure ApplicationInsights config file 170 | ApplicationInsights.config 171 | 172 | # Windows Store app package directory 173 | AppPackages/ 174 | BundleArtifacts/ 175 | 176 | # Visual Studio cache files 177 | # files ending in .cache can be ignored 178 | *.[Cc]ache 179 | # but keep track of directories ending in .cache 180 | !*.[Cc]ache/ 181 | 182 | # Others 183 | ClientBin/ 184 | [Ss]tyle[Cc]op.* 185 | ~$* 186 | *~ 187 | *.dbmdl 188 | *.dbproj.schemaview 189 | *.pfx 190 | *.publishsettings 191 | node_modules/ 192 | orleans.codegen.cs 193 | 194 | # RIA/Silverlight projects 195 | Generated_Code/ 196 | 197 | # Backup & report files from converting an old project file 198 | # to a newer Visual Studio version. Backup files are not needed, 199 | # because we have git ;-) 200 | _UpgradeReport_Files/ 201 | Backup*/ 202 | UpgradeLog*.XML 203 | UpgradeLog*.htm 204 | 205 | # SQL Server files 206 | *.mdf 207 | *.ldf 208 | 209 | # Business Intelligence projects 210 | *.rdl.data 211 | *.bim.layout 212 | *.bim_*.settings 213 | 214 | # Microsoft Fakes 215 | FakesAssemblies/ 216 | 217 | # GhostDoc plugin setting file 218 | *.GhostDoc.xml 219 | 220 | # Node.js Tools for Visual Studio 221 | .ntvs_analysis.dat 222 | 223 | # Visual Studio 6 build log 224 | *.plg 225 | 226 | # Visual Studio 6 workspace options file 227 | *.opt 228 | 229 | # Visual Studio LightSwitch build output 230 | **/*.HTMLClient/GeneratedArtifacts 231 | **/*.DesktopClient/GeneratedArtifacts 232 | **/*.DesktopClient/ModelManifest.xml 233 | **/*.Server/GeneratedArtifacts 234 | **/*.Server/ModelManifest.xml 235 | _Pvt_Extensions 236 | 237 | # LightSwitch generated files 238 | GeneratedArtifacts/ 239 | ModelManifest.xml 240 | 241 | # Paket dependency manager 242 | .paket/paket.exe 243 | 244 | # FAKE - F# Make 245 | .fake/ -------------------------------------------------------------------------------- /Tests/Tests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Debug 5 | AnyCPU 6 | {03D03C5E-71C4-4B89-BB87-66F1A635F0E6} 7 | Library 8 | Properties 9 | Tests 10 | Tests 11 | v3.5 12 | 512 13 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | 10.0 15 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 16 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 17 | False 18 | UnitTest 19 | 20 | 21 | true 22 | full 23 | false 24 | bin\Debug\ 25 | DEBUG;TRACE 26 | prompt 27 | 4 28 | 29 | 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | 37 | 38 | 39 | 40 | 3.5 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {2229a66d-6de9-4664-bbe2-77b56bf91e25} 62 | VGPrompter 63 | 64 | 65 | 66 | 67 | Always 68 | 69 | 70 | Always 71 | 72 | 73 | Always 74 | 75 | 76 | 77 | 78 | 79 | 80 | False 81 | 82 | 83 | False 84 | 85 | 86 | False 87 | 88 | 89 | False 90 | 91 | 92 | 93 | 94 | 95 | 96 | 103 | -------------------------------------------------------------------------------- /VGPrompter/Script/Script.TextManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Security.Cryptography; 5 | using System.Text; 6 | using System.Linq; 7 | 8 | namespace VGPrompter { 9 | 10 | public partial class Script { 11 | 12 | internal class TextManager { 13 | 14 | const string 15 | GLOBAL_TAG = "$", 16 | GLOBAL_FORMAT = GLOBAL_TAG + ";{0};{1}\n", 17 | DIALOGUE_FORMAT = "{0};{1};{2}\n"; 18 | 19 | static readonly char[] SEPARATOR = new char[1] { ';' }; 20 | 21 | MD5 _md5; 22 | 23 | Dictionary> _dialogue_strings; 24 | Dictionary _global_strings; 25 | 26 | internal Dictionary Globals { get { return _global_strings; } } 27 | internal Dictionary> Lines { get { return _dialogue_strings; } } 28 | 29 | public string GetHash(string text) { 30 | 31 | var data = _md5.ComputeHash(Encoding.UTF8.GetBytes(text)); 32 | var sb = new StringBuilder(); 33 | 34 | foreach (var b in data) { 35 | sb.Append(b.ToString("x2")); 36 | } 37 | 38 | return sb.ToString(); 39 | 40 | } 41 | 42 | public TextManager() { 43 | _md5 = MD5.Create(); 44 | _dialogue_strings = new Dictionary>(); 45 | _global_strings = new Dictionary(); 46 | } 47 | 48 | public string GetRawText(string label, string hash) { 49 | return _dialogue_strings[label][hash]; 50 | } 51 | 52 | public string GetRawGlobalText(string alias) { 53 | return _global_strings[alias]; 54 | } 55 | 56 | string GetAnyText(string raw_text, bool to_interpolate) { 57 | var text = Parser.CustomUnescapeString( 58 | to_interpolate ? 59 | InterpolateText(raw_text) : 60 | raw_text); 61 | return text; 62 | } 63 | 64 | public string GetText(string label, string hash, bool to_interpolate) { 65 | var raw_text = GetRawText(label, hash); 66 | return GetAnyText(raw_text, to_interpolate); 67 | } 68 | 69 | public string GetGlobalText(string alias, bool to_interpolate) { 70 | var raw_text = GetRawGlobalText(alias); 71 | return GetAnyText(raw_text, to_interpolate); 72 | } 73 | 74 | public bool TryAddDefinition(string key, string value) { 75 | if (Globals.ContainsKey(key)) { 76 | return false; 77 | } else { 78 | Globals[key] = value; 79 | return true; 80 | } 81 | } 82 | 83 | bool IsToInterpolate(string text) { 84 | return Parser.string_interpolation_re.Matches(text).Count > 0; 85 | } 86 | 87 | string InterpolateText(string text) { 88 | var out_text = text; 89 | 90 | var m = Parser.string_interpolation_re.Matches(text); 91 | var to_interpolate = m.Count > 0; 92 | 93 | string ikey, itext; 94 | 95 | if (to_interpolate) { 96 | 97 | //var groups = m.Cast().Reverse(); 98 | //foreach (var g in groups) { 99 | 100 | foreach (System.Text.RegularExpressions.Group g in m) { 101 | 102 | ikey = g.Value; 103 | 104 | if (_global_strings.TryGetValue(ikey, out itext)) { 105 | 106 | /* The following check is necessary because in the final script 107 | * there is no VGPDefine object to carry the to_interpolate information */ 108 | 109 | if (IsToInterpolate(text)) { 110 | itext = InterpolateText(itext); 111 | } 112 | 113 | out_text = out_text.Replace(string.Format("[{0}]", ikey), itext); 114 | 115 | } else { 116 | 117 | throw new Exception(string.Format("Undefined variable '{0}'!", g.Value)); 118 | 119 | } 120 | 121 | } 122 | } 123 | 124 | return out_text; 125 | } 126 | 127 | public string AddText(string label, string text) { 128 | var hash = GetHash(text); 129 | if (!_dialogue_strings.ContainsKey(label)) { 130 | _dialogue_strings[label] = new Dictionary(); 131 | } 132 | 133 | _dialogue_strings[label][hash] = text; 134 | 135 | return hash; 136 | } 137 | 138 | public void ToCSV(string path) { 139 | 140 | var sb = new StringBuilder(); 141 | 142 | foreach (var s in _global_strings) { 143 | sb.Append(string.Format(GLOBAL_FORMAT, s.Key, s.Value)); 144 | } 145 | 146 | foreach (var label in _dialogue_strings) { 147 | foreach (var s in label.Value) { 148 | sb.Append(string.Format(DIALOGUE_FORMAT, label.Key, s.Key, s.Value)); 149 | } 150 | } 151 | 152 | File.WriteAllText(path, sb.ToString()); 153 | } 154 | 155 | public void FromCSV(string path) { 156 | 157 | _dialogue_strings.Clear(); 158 | _global_strings.Clear(); 159 | 160 | var tokens = new string[3]; 161 | foreach (var row in File.ReadAllLines(path)) { 162 | 163 | tokens = row.Split(SEPARATOR, 3); 164 | var label = tokens[0]; 165 | var hash = tokens[1]; 166 | var text = tokens[2]; 167 | 168 | if (label == GLOBAL_TAG) { 169 | _global_strings.Add(hash, text); 170 | } else { 171 | if (!_dialogue_strings.ContainsKey(label)) { 172 | _dialogue_strings[label] = new Dictionary(); 173 | } 174 | _dialogue_strings[label][hash] = text; 175 | } 176 | 177 | } 178 | 179 | } 180 | 181 | } 182 | 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /Tests/UnitTest1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System.IO; 4 | using VGPrompter; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | 8 | namespace Tests { 9 | 10 | [TestClass] 11 | public class UnitTest1 { 12 | 13 | public static string GetResourcePath(string filename) { 14 | return Path.Combine(AppDomain.CurrentDomain.BaseDirectory, @"Data\" + filename); 15 | } 16 | 17 | public static readonly string TEST_SCRIPT_1 = "insc_test1.rpy"; 18 | public static readonly string TEST_SCRIPT_1_TAB = "insc_test1_tab.rpy"; 19 | public static readonly string DEMO = "demo.rpy"; 20 | 21 | public Script LoadScript(string fp, Script.Parser.IndentChar indent = Script.Parser.IndentChar.Auto) { 22 | 23 | Assert.IsTrue(File.Exists(fp)); 24 | 25 | var actions = new Dictionary() { 26 | { "Nothing", () => { } } 27 | }; 28 | 29 | var conditions = new Dictionary>() { 30 | { "True", () => true }, 31 | { "False", () => false } 32 | }; 33 | 34 | Script.Parser.Logger = new VGPrompter.Logger(); 35 | 36 | var script = Script.FromSource(fp, indent: indent, ignore_unsupported_renpy: false); 37 | script.Conditions = conditions; 38 | 39 | script.Logger = new VGPrompter.Logger(); 40 | 41 | return script; 42 | 43 | } 44 | 45 | [TestMethod] 46 | public void TestScriptPriming() { 47 | 48 | var script = LoadScript(GetResourcePath(TEST_SCRIPT_1)); 49 | script.Prime(); 50 | script.Validate(); 51 | 52 | } 53 | 54 | [TestMethod] 55 | public void TestScriptEnumerator() { 56 | var script = LoadScript(GetResourcePath(TEST_SCRIPT_1_TAB)); 57 | script.Prime(); 58 | script.Validate(); 59 | foreach (var x in script) { 60 | if (x is Script.Menu) 61 | script.CurrentChoiceIndex = (uint)SelectChoice(x as Script.Menu); 62 | Console.WriteLine(x.ToString()); 63 | } 64 | } 65 | 66 | void SomeOtherDelegate(string y) { 67 | Console.WriteLine(y); 68 | } 69 | 70 | delegate void Action(T1 p1, T2 p2, T3 p3, T4 p4, T5 p5, T6 p6, T7 p7); 71 | 72 | void TestFunc(bool a, bool b, string c, string d, double e, int f, float g) { } 73 | 74 | [TestMethod] 75 | public void TestDemoScriptEnumerator() { 76 | var script = LoadScript(GetResourcePath(DEMO)); 77 | var conditions = new Dictionary>() { 78 | { "True", () => true }, 79 | { "False", () => false }, 80 | { "CurrentColorNotGreen", () => false } 81 | }; 82 | 83 | var actions = new Dictionary() { 84 | { "DoNothing", () => { } }, 85 | { "TurnCubeGreen", () => { } }, 86 | { "TurnCubeBlue", () => { } } 87 | }; 88 | 89 | script.SetDelegates(conditions, actions); 90 | script.Functions = new Dictionary() { 91 | { "SomeDelegate", (Action)((s, a, b) => { 92 | Console.WriteLine(s); 93 | Console.WriteLine(a + b); 94 | }) }, 95 | { "SomeOtherDelegate", (Action)SomeOtherDelegate }, 96 | { "Test", (Action)TestFunc } 97 | }; 98 | 99 | script.Prime(); 100 | script.Validate(); 101 | 102 | script.RunFromBeginning( 103 | OnMenu: m => { 104 | var x = m; 105 | var idx = x.TrueChoices.Last().Index; 106 | return idx; 107 | }, 108 | OnLine: l => { Console.WriteLine(l); }); 109 | } 110 | 111 | [TestMethod] 112 | public void TestScriptEnumeratorWhile() { 113 | var script = LoadScript(GetResourcePath(TEST_SCRIPT_1_TAB)); 114 | script.Prime(); 115 | script.Validate(); 116 | 117 | var i = 0; 118 | foreach (var line in script) { 119 | if (i++ > 2) { 120 | break; 121 | } 122 | Console.WriteLine(line.ToString()); 123 | } 124 | 125 | } 126 | 127 | [TestMethod] 128 | public void TestScriptSerialization() { 129 | 130 | var fn = GetResourcePath("serialized.bin"); 131 | var script = LoadScript(GetResourcePath(TEST_SCRIPT_1_TAB)); 132 | 133 | script.Prime(); 134 | script.Validate(); 135 | script.RepeatLastLineOnRecover = false; 136 | 137 | PlayTest(script, 2); 138 | 139 | var bytes = script.ToBinary(); 140 | File.WriteAllBytes(fn, bytes); 141 | 142 | var dscript = Utils.LoadSerialized