├── .gitignore ├── Blot-Sample ├── Assets │ ├── Blot.unity │ ├── Blot.unity.meta │ ├── Button.prefab │ ├── Button.prefab.meta │ ├── InkLibrary.asset │ ├── InkLibrary.asset.meta │ ├── Plugins.meta │ ├── Plugins │ │ ├── Ink.meta │ │ └── Ink │ │ │ ├── Editor.meta │ │ │ ├── Editor │ │ │ ├── Compiler.meta │ │ │ ├── Compiler │ │ │ │ ├── Auto Compiler.meta │ │ │ │ ├── Auto Compiler │ │ │ │ │ ├── InkPostProcessor.cs │ │ │ │ │ └── InkPostProcessor.cs.meta │ │ │ │ ├── InkCompiler.cs │ │ │ │ └── InkCompiler.cs.meta │ │ │ ├── Ink Inspector.meta │ │ │ ├── Ink Inspector │ │ │ │ ├── File Icons.meta │ │ │ │ ├── File Icons │ │ │ │ │ ├── InkBrowserIcons.cs │ │ │ │ │ ├── InkBrowserIcons.cs.meta │ │ │ │ │ ├── Resources.meta │ │ │ │ │ └── Resources │ │ │ │ │ │ ├── InkChildIcon.psd │ │ │ │ │ │ ├── InkChildIcon.psd.meta │ │ │ │ │ │ ├── InkErrorIcon.psd │ │ │ │ │ │ ├── InkErrorIcon.psd.meta │ │ │ │ │ │ ├── InkFileIcon-retina.psd │ │ │ │ │ │ ├── InkFileIcon-retina.psd.meta │ │ │ │ │ │ ├── InkFileIcon.psd │ │ │ │ │ │ ├── InkFileIcon.psd.meta │ │ │ │ │ │ ├── InkUnknownFileIcon.psd │ │ │ │ │ │ ├── InkUnknownFileIcon.psd.meta │ │ │ │ │ │ ├── InkWarningIcon.psd │ │ │ │ │ │ └── InkWarningIcon.psd.meta │ │ │ │ ├── Ink Inspector.meta │ │ │ │ └── Ink Inspector │ │ │ │ │ ├── InkInspector.cs │ │ │ │ │ ├── InkInspector.cs.meta │ │ │ │ │ ├── ObjectEditor.cs │ │ │ │ │ ├── ObjectEditor.cs.meta │ │ │ │ │ ├── ObjectInspector.cs │ │ │ │ │ └── ObjectInspector.cs.meta │ │ │ ├── Ink Library.meta │ │ │ ├── Ink Library │ │ │ │ ├── InkFile.cs │ │ │ │ ├── InkFile.cs.meta │ │ │ │ ├── InkLibrary.cs │ │ │ │ ├── InkLibrary.cs.meta │ │ │ │ ├── InkLibraryEditor.cs │ │ │ │ └── InkLibraryEditor.cs.meta │ │ │ ├── Player Window.meta │ │ │ ├── Player Window │ │ │ │ ├── InkPlayerWindow.cs │ │ │ │ └── InkPlayerWindow.cs.meta │ │ │ ├── Tools.meta │ │ │ └── Tools │ │ │ │ ├── InkEditorUtils.cs │ │ │ │ └── InkEditorUtils.cs.meta │ │ │ ├── Extras.meta │ │ │ ├── Extras │ │ │ ├── Sublime3Syntax.zip │ │ │ └── Sublime3Syntax.zip.meta │ │ │ ├── InkRuntime.meta │ │ │ ├── InkRuntime │ │ │ ├── ink-engine.dll │ │ │ └── ink-engine.dll.meta │ │ │ ├── Inklecate.meta │ │ │ ├── Inklecate │ │ │ ├── Newtonsoft.Json.dll │ │ │ ├── Newtonsoft.Json.dll.meta │ │ │ ├── inklecate_mac │ │ │ ├── inklecate_mac.meta │ │ │ ├── inklecate_win.exe │ │ │ └── inklecate_win.exe.meta │ │ │ ├── Readme.md │ │ │ ├── Readme.md.meta │ │ │ ├── Template.meta │ │ │ └── Template │ │ │ ├── Template.txt │ │ │ └── Template.txt.meta │ ├── Script.cs │ ├── Script.cs.meta │ ├── Story.ink │ ├── Story.ink.meta │ ├── Story.json │ ├── Story.json.meta │ ├── Text.prefab │ └── Text.prefab.meta └── ProjectSettings │ ├── AudioManager.asset │ ├── ClusterInputManager.asset │ ├── DynamicsManager.asset │ ├── EditorBuildSettings.asset │ ├── EditorSettings.asset │ ├── GraphicsSettings.asset │ ├── InputManager.asset │ ├── NavMeshAreas.asset │ ├── NetworkManager.asset │ ├── Physics2DSettings.asset │ ├── ProjectSettings.asset │ ├── ProjectVersion.txt │ ├── QualitySettings.asset │ ├── TagManager.asset │ ├── TimeManager.asset │ ├── UnityAdsSettings.asset │ └── UnityConnectSettings.asset ├── README.md └── Script.cs /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .AppleDouble 3 | .LSOverride 4 | 5 | # Icon must end with two \r 6 | Icon 7 | 8 | 9 | # Thumbnails 10 | ._* 11 | 12 | # Files that might appear in the root of a volume 13 | .DocumentRevisions-V100 14 | .fseventsd 15 | .Spotlight-V100 16 | .TemporaryItems 17 | .Trashes 18 | .VolumeIcon.icns 19 | 20 | # Directories potentially created on remote AFP share 21 | .AppleDB 22 | .AppleDesktop 23 | Network Trash Folder 24 | Temporary Items 25 | .apdisk 26 | 27 | [Ll]ibrary/ 28 | [Tt]emp/ 29 | [Oo]bj/ 30 | [Bb]uild/ 31 | [Bb]uilds/ 32 | Assets/AssetStoreTools* 33 | 34 | # Autogenerated VS/MD solution and project files 35 | ExportedObj/ 36 | *.csproj 37 | *.unityproj 38 | *.sln 39 | *.suo 40 | *.tmp 41 | *.user 42 | *.userprefs 43 | *.pidb 44 | *.booproj 45 | *.svd 46 | 47 | 48 | # Unity3D generated meta files 49 | *.pidb.meta 50 | 51 | # Unity3D Generated File On Crash Reports 52 | sysinfo.txt 53 | 54 | # Builds 55 | *.apk 56 | *.unitypackage 57 | 58 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Blot.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Blot.unity -------------------------------------------------------------------------------- /Blot-Sample/Assets/Blot.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: abb15c59bba6746c68267931f81bf730 3 | timeCreated: 1457991739 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Button.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Button.prefab -------------------------------------------------------------------------------- /Blot-Sample/Assets/Button.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 37c108a6c38964f79a6621eccb33ddc1 3 | timeCreated: 1457987203 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/InkLibrary.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/InkLibrary.asset -------------------------------------------------------------------------------- /Blot-Sample/Assets/InkLibrary.asset.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a84a9d66ef17448dcb4f457f7d001e11 3 | timeCreated: 1464240670 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: cdc00c7c87b5641719beb9ef5d81e211 3 | folderAsset: yes 4 | timeCreated: 1462789685 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 644b324b887a8483bb4b50d35a72ed20 3 | folderAsset: yes 4 | timeCreated: 1457765523 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: df28596461d414a1b9f56cb406a23a3f 3 | folderAsset: yes 4 | timeCreated: 1459882215 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Compiler.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ddef5103d5ce1401da019f7df6c472af 3 | folderAsset: yes 4 | timeCreated: 1459882122 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Compiler/Auto Compiler.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: e277a78690c74451084fbfbedc0507e8 3 | folderAsset: yes 4 | timeCreated: 1459941651 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Compiler/Auto Compiler/InkPostProcessor.cs: -------------------------------------------------------------------------------- 1 | // Automatically creates JSON files from an ink placed within the Assets/Ink folder. 2 | using UnityEngine; 3 | using UnityEditor; 4 | using System; 5 | using System.IO; 6 | using Debug = UnityEngine.Debug; 7 | using System.Collections; 8 | using System.Collections.Generic; 9 | 10 | using System.Linq; 11 | using System.Reflection; 12 | using System.Runtime.CompilerServices; 13 | 14 | namespace Ink.UnityIntegration { 15 | class InkPostProcessor : AssetPostprocessor { 16 | 17 | // Recompiles any ink files as a result of an ink file (re)import 18 | private static void OnPostprocessAllAssets (string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths) { 19 | if(deletedAssets.Length > 0) { 20 | OnDeleteAssets(deletedAssets); 21 | } 22 | if(movedAssets.Length > 0) { 23 | OnMoveAssets(movedAssets.Except(importedAssets).ToArray()); 24 | } 25 | if(importedAssets.Length > 0) { 26 | OnImportAssets(importedAssets); 27 | } 28 | if(InkLibrary.created) 29 | InkLibrary.Clean(); 30 | } 31 | 32 | private static void OnDeleteAssets (string[] deletedAssets) { 33 | bool deletedInk = false; 34 | foreach (var deletedAssetPath in deletedAssets) { 35 | if(Path.GetExtension(deletedAssetPath) == InkEditorUtils.inkFileExtension) { 36 | deletedInk = true; 37 | break; 38 | } 39 | } 40 | if(!deletedInk) 41 | return; 42 | 43 | // bool alsoDeleteJSON = false; 44 | // alsoDeleteJSON = EditorUtility.DisplayDialog("Deleting .ink file", "Also delete the JSON file associated with the deleted .ink file?", "Yes", "No")); 45 | List masterFilesAffected = new List(); 46 | for (int i = InkLibrary.Instance.inkLibrary.Count - 1; i >= 0; i--) { 47 | if(InkLibrary.Instance.inkLibrary [i].inkAsset == null) { 48 | if(!InkLibrary.Instance.inkLibrary[i].isMaster && InkLibrary.Instance.inkLibrary[i].master != null && !masterFilesAffected.Contains(InkLibrary.Instance.inkLibrary[i].masterInkFile)) { 49 | masterFilesAffected.Add(InkLibrary.Instance.inkLibrary[i].masterInkFile); 50 | } 51 | if(InkLibrary.Instance.handleJSONFilesAutomatically) 52 | AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(InkLibrary.Instance.inkLibrary[i].jsonAsset)); 53 | InkLibrary.Instance.inkLibrary.RemoveAt(i); 54 | } 55 | } 56 | // After deleting files, we might have broken some include references, so we rebuild them. There's probably a faster way to do this, or we could probably just remove any null references, but this is a bit more robust. 57 | foreach(InkFile inkFile in InkLibrary.Instance.inkLibrary) { 58 | inkFile.FindIncludedFiles(); 59 | } 60 | foreach(InkFile masterFile in masterFilesAffected) { 61 | InkCompiler.CompileInk(masterFile); 62 | } 63 | } 64 | 65 | private static void OnMoveAssets (string[] movedAssets) { 66 | if (!InkLibrary.Instance.handleJSONFilesAutomatically) 67 | return; 68 | for (var i = 0; i < movedAssets.Length; i++) { 69 | if(Path.GetExtension(movedAssets[i]) != InkEditorUtils.inkFileExtension) 70 | continue; 71 | InkFile inkFile = InkLibrary.GetInkFileWithPath(movedAssets[i]); 72 | if(inkFile != null) { 73 | string jsonAssetPath = AssetDatabase.GetAssetPath(inkFile.jsonAsset); 74 | string newPath = Path.Combine(Path.GetDirectoryName(movedAssets[i]), Path.GetFileNameWithoutExtension(Path.GetFileName(movedAssets[i])))+".json"; 75 | AssetDatabase.MoveAsset(jsonAssetPath, newPath); 76 | } 77 | } 78 | } 79 | 80 | private static void OnImportAssets (string[] importedAssets) { 81 | List importedInkAssets = new List(); 82 | string inklecateFileLocation = null; 83 | foreach (var importedAssetPath in importedAssets) { 84 | if(Path.GetExtension(importedAssetPath) == InkEditorUtils.inkFileExtension) 85 | importedInkAssets.Add(importedAssetPath); 86 | else if (Path.GetFileName(importedAssetPath) == "inklecate" && Path.GetExtension(importedAssetPath) == "") 87 | inklecateFileLocation = importedAssetPath; 88 | } 89 | 90 | if(importedInkAssets.Count > 0) 91 | PostprocessInkFiles(importedInkAssets); 92 | if(inklecateFileLocation != null) 93 | PostprocessInklecate(inklecateFileLocation); 94 | 95 | if(PlayerSettings.apiCompatibilityLevel == ApiCompatibilityLevel.NET_2_0_Subset) { 96 | Debug.LogWarning("Detected PlayerSettings.apiCompatibilityLevel is .NET 2.0 Subset. Due to JSON.Net as used by ink-engine.dll, API Compatibility Level must be set to .NET 2.0 for standalone builds to function. Change this in PlayerSettings."); 97 | } 98 | } 99 | 100 | private static void PostprocessInklecate (string inklecateFileLocation) { 101 | Debug.Log("Inklecate updated. Recompiling all Ink files..."); 102 | InkCompiler.RecompileAll(); 103 | } 104 | 105 | private static void PostprocessInkFiles (List importedInkAssets) { 106 | // foreach (var importedAssetPath in importedInkAssets) { 107 | // Debug.Log("Imported Ink: "+importedAssetPath); 108 | // } 109 | CreateOrReadUpdatedInkFiles (importedInkAssets); 110 | foreach (var inkAssetToCompile in GetUniqueMasterInkFilesToCompile (importedInkAssets)) { 111 | InkCompiler.CompileInk(inkAssetToCompile); 112 | } 113 | } 114 | 115 | private static void CreateOrReadUpdatedInkFiles (List importedInkAssets) { 116 | foreach (var importedAssetPath in importedInkAssets) { 117 | InkFile inkFile = InkLibrary.GetInkFileWithPath(importedAssetPath); 118 | if(inkFile == null) { 119 | DefaultAsset asset = AssetDatabase.LoadAssetAtPath(importedAssetPath); 120 | inkFile = new InkFile(asset); 121 | InkLibrary.Instance.inkLibrary.Add(inkFile); 122 | } 123 | inkFile.ParseContent(); 124 | } 125 | // Now we've updated all the include paths for the ink library we can create master/child references between them. 126 | InkLibrary.RebuildInkFileConnections(); 127 | } 128 | 129 | private static List GetUniqueMasterInkFilesToCompile (List importedInkAssets) { 130 | List masterInkFiles = new List(); 131 | foreach (var importedAssetPath in importedInkAssets) { 132 | InkFile inkFile = InkLibrary.GetInkFileWithPath(importedAssetPath); 133 | if(inkFile.isMaster && !masterInkFiles.Contains(inkFile) && (InkLibrary.Instance.compileAutomatically || inkFile.compileAutomatically)) { 134 | masterInkFiles.Add(inkFile); 135 | } else if(!inkFile.isMaster && !masterInkFiles.Contains(inkFile.masterInkFile) && (InkLibrary.Instance.compileAutomatically || inkFile.masterInkFile.compileAutomatically)) { 136 | masterInkFiles.Add(inkFile.masterInkFile); 137 | } 138 | } 139 | return masterInkFiles; 140 | } 141 | } 142 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Compiler/Auto Compiler/InkPostProcessor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 45a9c84618e20498993d11d2bb89946e 3 | timeCreated: 1459667420 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Compiler/InkCompiler.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Diagnostics; 8 | using System.Text.RegularExpressions; 9 | 10 | using Debug = UnityEngine.Debug; 11 | 12 | namespace Ink.UnityIntegration { 13 | [InitializeOnLoad] 14 | public static class InkCompiler { 15 | private const float timeout = 10; 16 | 17 | public static bool compiling { 18 | get { 19 | return InkLibrary.Instance.compilationStack.Count > 0; 20 | } 21 | } 22 | 23 | public delegate void OnCompileInkEvent (InkFile inkFile); 24 | public static event OnCompileInkEvent OnCompileInk; 25 | 26 | [Serializable] 27 | public class CompilationStackItem { 28 | public enum State { 29 | Idle, 30 | Compiling, 31 | Importing 32 | } 33 | 34 | public State state = State.Idle; 35 | public InkFile inkFile; 36 | public string inkAbsoluteFilePath; 37 | public string jsonAbsoluteFilePath; 38 | public string output; 39 | public float startTime; 40 | 41 | public CompilationStackItem () { 42 | startTime = (float)EditorApplication.timeSinceStartup; 43 | } 44 | } 45 | 46 | static InkCompiler () { 47 | EditorApplication.playmodeStateChanged += OnPlayModeChange; 48 | EditorApplication.update += Update; 49 | } 50 | 51 | private static void Update () { 52 | if(!InkLibrary.created) 53 | return; 54 | 55 | for (int i = InkLibrary.Instance.compilationStack.Count - 1; i >= 0; i--) { 56 | var compilingFile = InkLibrary.Instance.compilationStack [i]; 57 | if (EditorApplication.timeSinceStartup-compilingFile.startTime > timeout) { 58 | InkLibrary.Instance.compilationStack.RemoveAt(i); 59 | EditorUtility.ClearProgressBar(); 60 | Debug.LogError("Ink Compiler timed out for "+compilingFile.inkAbsoluteFilePath+". Ink file: "+compilingFile); 61 | } 62 | } 63 | if(InkLibrary.Instance.compilationStack.Count > 0) { 64 | int numCompiling = InkLibrary.FilesInCompilingStackInState(CompilationStackItem.State.Compiling).Count; 65 | string message = "Compiling .Ink File "+(InkLibrary.Instance.compilationStack.Count-numCompiling)+" of "+InkLibrary.Instance.compilationStack.Count; 66 | EditorUtility.DisplayProgressBar("Compiling Ink...", message, (InkLibrary.Instance.compilationStack.Count-numCompiling)/InkLibrary.Instance.compilationStack.Count); 67 | } 68 | } 69 | 70 | private static void OnPlayModeChange () { 71 | if(EditorApplication.isPlayingOrWillChangePlaymode) { 72 | if(compiling) 73 | Debug.LogWarning("Entered Play Mode while Ink was still compiling. Recommend exiting and re-entering play mode."); 74 | } 75 | } 76 | 77 | [MenuItem("Assets/Recompile Ink", false, 60)] 78 | public static void RecompileAll() { 79 | InkLibrary.Rebuild(); 80 | List masterInkFiles = InkLibrary.GetMasterInkFiles (); 81 | foreach(InkFile masterInkFile in masterInkFiles) { 82 | CompileInk(masterInkFile); 83 | } 84 | } 85 | 86 | public static void CompileInk (InkFile inkFile) { 87 | if(inkFile == null) { 88 | Debug.LogError("Tried to compile ink file "+inkFile.filePath+", but input was null."); 89 | return; 90 | } 91 | if(!inkFile.isMaster) 92 | Debug.LogWarning("Compiling InkFile which is an include. Any file created is likely to be invalid. Did you mean to call CompileInk on inkFile.master?"); 93 | if(InkLibrary.GetCompilationStackItem(inkFile) != null) { 94 | UnityEngine.Debug.LogWarning("Tried compiling ink file, but file is already compiling. "+inkFile.filePath); 95 | return; 96 | } 97 | 98 | string inklecatePath = InkEditorUtils.GetInklecateFilePath(); 99 | if(inklecatePath == null) { 100 | UnityEngine.Debug.LogWarning("Inklecate (the ink compiler) not found in assets. This will prevent automatic building of JSON TextAsset files from ink story files."); 101 | return; 102 | } 103 | 104 | string inputPath = Path.Combine(inkFile.absoluteFolderPath, Path.GetFileName(inkFile.filePath)); 105 | inputPath = inputPath.Replace ('\\', '/'); 106 | string outputPath = Path.Combine(inkFile.absoluteFolderPath, Path.GetFileNameWithoutExtension(Path.GetFileName(inkFile.filePath)))+".json"; 107 | outputPath = outputPath.Replace ('\\', '/'); 108 | string inkArguments = "-c -o "+"\""+outputPath +"\" \""+inputPath+"\""; 109 | 110 | CompilationStackItem pendingFile = new CompilationStackItem(); 111 | pendingFile.inkFile = InkLibrary.GetInkFileWithAbsolutePath(inputPath); 112 | pendingFile.inkAbsoluteFilePath = inputPath; 113 | pendingFile.jsonAbsoluteFilePath = outputPath; 114 | pendingFile.state = CompilationStackItem.State.Compiling; 115 | InkLibrary.Instance.compilationStack.Add(pendingFile); 116 | 117 | Process process = new Process(); 118 | process.StartInfo.WorkingDirectory = inkFile.absoluteFolderPath; 119 | process.StartInfo.FileName = inklecatePath; 120 | process.StartInfo.Arguments = inkArguments; 121 | process.StartInfo.RedirectStandardError = true; 122 | process.StartInfo.RedirectStandardOutput = true; 123 | process.StartInfo.UseShellExecute = false; 124 | process.EnableRaisingEvents = true; 125 | process.StartInfo.EnvironmentVariables["inkAbsoluteFilePath"] = inputPath; 126 | process.Exited += OnCompileProcessComplete; 127 | process.ErrorDataReceived += OnProcessError; 128 | process.Start(); 129 | 130 | } 131 | 132 | static void OnProcessError (object sender, DataReceivedEventArgs e) { 133 | Process process = (Process)sender; 134 | Debug.LogError("Fatal Error compiling Ink! Ink failed to process. Please report this as a bug."); 135 | InkFile inkFile = InkLibrary.GetInkFileWithAbsolutePath(process.StartInfo.EnvironmentVariables["inkAbsoluteFilePath"]); 136 | Debug.LogError(inkFile); 137 | CompilationStackItem compilingFile = InkLibrary.GetCompilationStackItem(inkFile); 138 | InkLibrary.Instance.compilationStack.Remove(compilingFile); 139 | } 140 | 141 | static void OnCompileProcessComplete(object sender, System.EventArgs e) { 142 | Process process = (Process)sender; 143 | 144 | CompilationStackItem pendingFile = InkLibrary.GetCompilationStackItem(process.StartInfo.EnvironmentVariables["inkAbsoluteFilePath"]); 145 | pendingFile.state = CompilationStackItem.State.Importing; 146 | pendingFile.output = process.StandardOutput.ReadToEnd(); 147 | 148 | if(InkLibrary.FilesInCompilingStackInState(CompilationStackItem.State.Compiling).Count == 0) { 149 | // This event runs in another thread, preventing us from calling some UnityEditor functions directly. Instead, we delay till the next inspector update. 150 | EditorApplication.delayCall += Delay; 151 | } 152 | } 153 | 154 | private static void Delay () { 155 | if(InkLibrary.FilesInCompilingStackInState(CompilationStackItem.State.Compiling).Count > 0) { 156 | Debug.LogWarning("Delayed, but a file is now compiling! You can ignore this warning."); 157 | return; 158 | } 159 | foreach (var compilingFile in InkLibrary.Instance.compilationStack) { 160 | SetOutputLog(compilingFile); 161 | if(!compilingFile.inkFile.hasErrors) { 162 | string localJSONAssetPath = compilingFile.jsonAbsoluteFilePath.Substring (Application.dataPath.Length - 6); 163 | AssetDatabase.ImportAsset (localJSONAssetPath); 164 | compilingFile.inkFile.jsonAsset = AssetDatabase.LoadAssetAtPath (localJSONAssetPath); 165 | } 166 | } 167 | // InkLibrary.Refresh(); 168 | InkLibrary.Instance.compilationStack.Clear(); 169 | 170 | EditorUtility.ClearProgressBar(); 171 | if(EditorApplication.isPlayingOrWillChangePlaymode) { 172 | Debug.LogWarning("Ink just finished recompiling while in play mode. Your runtime story may not be up to date."); 173 | } 174 | foreach (var compilingFile in InkLibrary.Instance.compilationStack) { 175 | if (OnCompileInk != null) { 176 | OnCompileInk (compilingFile.inkFile); 177 | } 178 | } 179 | } 180 | 181 | private static void SetOutputLog (CompilationStackItem pendingFile) { 182 | pendingFile.inkFile.errors.Clear(); 183 | pendingFile.inkFile.warnings.Clear(); 184 | pendingFile.inkFile.todos.Clear(); 185 | foreach(var child in pendingFile.inkFile.includes) { 186 | if(child == null) { 187 | Debug.LogError("Error compiling ink: Ink file include in "+pendingFile.inkFile.filePath+" is null."); 188 | continue; 189 | } 190 | InkFile childInkFile = InkLibrary.GetInkFileWithFile((DefaultAsset)child); 191 | childInkFile.errors.Clear(); 192 | childInkFile.warnings.Clear(); 193 | childInkFile.todos.Clear(); 194 | } 195 | 196 | string[] splitOutput = pendingFile.output.Split(new string[]{"\n"}, StringSplitOptions.RemoveEmptyEntries); 197 | foreach(string output in splitOutput) { 198 | var match = _errorRegex.Match(output); 199 | if (match.Success) { 200 | string errorType = null; 201 | string filename = null; 202 | int lineNo = -1; 203 | string message = null; 204 | 205 | var errorTypeCapture = match.Groups["errorType"]; 206 | if( errorTypeCapture != null ) { 207 | errorType = errorTypeCapture.Value; 208 | } 209 | 210 | var filenameCapture = match.Groups["filename"]; 211 | if (filenameCapture != null) 212 | filename = filenameCapture.Value; 213 | 214 | var lineNoCapture = match.Groups["lineNo"]; 215 | if (lineNoCapture != null) 216 | lineNo = int.Parse (lineNoCapture.Value); 217 | 218 | var messageCapture = match.Groups["message"]; 219 | if (messageCapture != null) 220 | message = messageCapture.Value.Trim(); 221 | 222 | 223 | string logFilePath = Path.Combine(Path.GetDirectoryName(pendingFile.inkFile.filePath), filename); 224 | logFilePath = logFilePath.Replace ('\\', '/'); 225 | InkFile inkFile = InkLibrary.GetInkFileWithPath(logFilePath); 226 | if(inkFile == null) 227 | inkFile = pendingFile.inkFile; 228 | 229 | string pathAndLineNumberString = "\n"+inkFile.filePath+"("+lineNo+")"; 230 | if(errorType == "ERROR") { 231 | inkFile.errors.Add(new InkFile.InkFileLog(message, lineNo)); 232 | Debug.LogError("INK "+errorType+": "+message + pathAndLineNumberString); 233 | } else if (errorType == "WARNING") { 234 | inkFile.warnings.Add(new InkFile.InkFileLog(message, lineNo)); 235 | Debug.LogWarning("INK "+errorType+": "+message + pathAndLineNumberString); 236 | } else if (errorType == "TODO") { 237 | inkFile.todos.Add(new InkFile.InkFileLog(message, lineNo)); 238 | Debug.Log("INK "+errorType+": "+message + pathAndLineNumberString); 239 | } 240 | } 241 | } 242 | } 243 | 244 | private static Regex _errorRegex = new Regex(@"(?ERROR|WARNING|TODO|RUNTIME ERROR):(?:\s(?:'(?[^']*)'\s)?line (?\d+):)?(?.*)"); 245 | } 246 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Compiler/InkCompiler.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 430cb8f71c23c438cb8b0ce85cc80fa6 3 | timeCreated: 1459463931 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 9dc6bf28e6e4a460a909a665c5bc048a 3 | folderAsset: yes 4 | timeCreated: 1459934446 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 350bb01e156c14508a02fa0ab21edce5 3 | folderAsset: yes 4 | timeCreated: 1459941567 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/InkBrowserIcons.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using UnityEditor; 3 | using UnityEngine; 4 | 5 | /* 6 | * This script allows you to set custom icons for folders in project browser. 7 | * Recommended icon sizes - small: 16x16 px, large: 64x64 px; 8 | */ 9 | 10 | namespace Ink.UnityIntegration { 11 | [InitializeOnLoad] 12 | public class InkBrowserIcons { 13 | private static bool isRetina { 14 | get { 15 | float unityVersion = float.Parse(Application.unityVersion.Substring (0, 3)); 16 | return Application.platform == RuntimePlatform.OSXEditor && unityVersion >= 5.4f; 17 | } 18 | } 19 | private const float largeIconSize = 64f; 20 | 21 | private static Texture2D _inkFileIcon; 22 | public static Texture2D inkFileIcon { 23 | get { 24 | if(_inkFileIcon == null) { 25 | if(isRetina) { 26 | _inkFileIcon = Resources.Load("InkFileIcon-retina"); 27 | } else { 28 | _inkFileIcon = Resources.Load("InkFileIcon"); 29 | } 30 | } 31 | return _inkFileIcon; 32 | } 33 | } 34 | private static Texture2D _errorIcon; 35 | public static Texture2D errorIcon { 36 | get { 37 | if(_errorIcon == null) { 38 | _errorIcon = Resources.Load("InkErrorIcon"); 39 | } 40 | return _errorIcon; 41 | } 42 | } 43 | private static Texture2D _warningIcon; 44 | public static Texture2D warningIcon { 45 | get { 46 | if(_warningIcon == null) { 47 | _warningIcon = Resources.Load("InkWarningIcon"); 48 | } 49 | return _warningIcon; 50 | } 51 | } 52 | private static Texture2D _childIcon; 53 | public static Texture2D childIcon { 54 | get { 55 | if(_childIcon == null) { 56 | _childIcon = Resources.Load("InkChildIcon"); 57 | } 58 | return _childIcon; 59 | } 60 | } 61 | private static Texture2D _unknownFileIcon; 62 | public static Texture2D unknownFileIcon { 63 | get { 64 | if(_unknownFileIcon == null) { 65 | _unknownFileIcon = Resources.Load("InkUnknownFileIcon"); 66 | } 67 | return _unknownFileIcon; 68 | } 69 | } 70 | 71 | static InkBrowserIcons() { 72 | EditorApplication.projectWindowItemOnGUI += OnDrawProjectWindowItem; 73 | } 74 | 75 | static void OnDrawProjectWindowItem(string guid, Rect rect) { 76 | if(!InkLibrary.created) 77 | return; 78 | var path = AssetDatabase.GUIDToAssetPath(guid); 79 | 80 | if (Path.GetExtension(path) == InkEditorUtils.inkFileExtension) { 81 | InkFile inkFile = InkLibrary.GetInkFileWithPath(path); 82 | 83 | var isSmall = rect.width > rect.height; 84 | if (isSmall) { 85 | rect.width = rect.height; 86 | } else { 87 | rect.height = rect.width; 88 | } 89 | 90 | if (rect.width > largeIconSize) { 91 | var offset = (rect.width - largeIconSize) * 0.5f; 92 | var position = new Rect(rect.x + offset, rect.y + offset, largeIconSize, largeIconSize); 93 | if(inkFileIcon != null) 94 | GUI.DrawTexture(position, inkFileIcon); 95 | } 96 | else { 97 | if(inkFileIcon != null) 98 | GUI.DrawTexture(rect, inkFileIcon); 99 | 100 | if(inkFile == null) { 101 | if(unknownFileIcon != null) { 102 | GUI.DrawTexture(new Rect(rect.x, rect.y, unknownFileIcon.width, unknownFileIcon.height), unknownFileIcon); 103 | } 104 | } else { 105 | Rect miniRect = new Rect(rect.center, rect.size * 0.5f); 106 | if(inkFile.hasErrors && errorIcon != null) { 107 | GUI.DrawTexture(miniRect, errorIcon); 108 | } else if(inkFile.hasWarnings && warningIcon != null) { 109 | GUI.DrawTexture(miniRect, warningIcon); 110 | } 111 | if(!inkFile.isMaster && childIcon != null) { 112 | GUI.DrawTexture(new Rect(rect.x, rect.y, childIcon.width, childIcon.height), childIcon); 113 | } 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/InkBrowserIcons.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 38c650d4ee11f47559699f833d29d4b9 3 | timeCreated: 1459341699 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 5081bfd12b67745ab9e8c560e5f4cad1 3 | folderAsset: yes 4 | timeCreated: 1459341721 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkChildIcon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkChildIcon.psd -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkChildIcon.psd.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 53256257fd17e4dfda5f08016551019e 3 | timeCreated: 1461958426 4 | licenseType: Pro 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 1 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -3 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: 0 33 | aniso: 1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 2 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkErrorIcon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkErrorIcon.psd -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkErrorIcon.psd.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: ab12885a47a21456596113fc9233946c 3 | timeCreated: 1461099630 4 | licenseType: Pro 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 1 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -3 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: 0 33 | aniso: 1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 2 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkFileIcon-retina.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkFileIcon-retina.psd -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkFileIcon-retina.psd.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bd3c497264be941e3a935055a433bbb9 3 | timeCreated: 1459953147 4 | licenseType: Pro 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 1 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -3 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: 0 33 | aniso: 1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 2 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkFileIcon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkFileIcon.psd -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkFileIcon.psd.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8e6b218461abe4192baeeabfbe7a9ced 3 | timeCreated: 1459341708 4 | licenseType: Pro 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 1 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -3 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: 0 33 | aniso: 1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 16 48 | alphaIsTransparency: 1 49 | textureType: 2 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkUnknownFileIcon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkUnknownFileIcon.psd -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkUnknownFileIcon.psd.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: b26518ba7d72a4fd3bc42599f621c839 3 | timeCreated: 1462526037 4 | licenseType: Pro 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 1 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -3 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: 0 33 | aniso: 1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 2 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkWarningIcon.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkWarningIcon.psd -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/File Icons/Resources/InkWarningIcon.psd.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: efc1324f6160840a9a4c26231cc2d304 3 | timeCreated: 1461099630 4 | licenseType: Pro 5 | TextureImporter: 6 | fileIDToRecycleName: {} 7 | serializedVersion: 2 8 | mipmaps: 9 | mipMapMode: 0 10 | enableMipMap: 0 11 | linearTexture: 1 12 | correctGamma: 0 13 | fadeOut: 0 14 | borderMipMap: 0 15 | mipMapFadeDistanceStart: 1 16 | mipMapFadeDistanceEnd: 3 17 | bumpmap: 18 | convertToNormalMap: 0 19 | externalNormalMap: 0 20 | heightScale: 0.25 21 | normalMapFilter: 0 22 | isReadable: 0 23 | grayScaleToAlpha: 0 24 | generateCubemap: 0 25 | cubemapConvolution: 0 26 | cubemapConvolutionSteps: 7 27 | cubemapConvolutionExponent: 1.5 28 | seamlessCubemap: 0 29 | textureFormat: -3 30 | maxTextureSize: 2048 31 | textureSettings: 32 | filterMode: 0 33 | aniso: 1 34 | mipBias: -1 35 | wrapMode: 1 36 | nPOTScale: 0 37 | lightmap: 0 38 | rGBM: 0 39 | compressionQuality: 50 40 | allowsAlphaSplitting: 0 41 | spriteMode: 0 42 | spriteExtrude: 1 43 | spriteMeshType: 1 44 | alignment: 0 45 | spritePivot: {x: 0.5, y: 0.5} 46 | spriteBorder: {x: 0, y: 0, z: 0, w: 0} 47 | spritePixelsToUnits: 100 48 | alphaIsTransparency: 1 49 | textureType: 2 50 | buildTargetSettings: [] 51 | spriteSheet: 52 | sprites: [] 53 | outline: [] 54 | spritePackingTag: 55 | userData: 56 | assetBundleName: 57 | assetBundleVariant: 58 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/Ink Inspector.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f99704ec1dd94cc1ba8f76bb71f6ae3 3 | folderAsset: yes 4 | timeCreated: 1460011342 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/Ink Inspector/InkInspector.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Collections; 7 | using System.Collections.Generic; 8 | using Ink.Runtime; 9 | using UnityEditorInternal; 10 | using System.Text.RegularExpressions; 11 | using Object = UnityEngine.Object; 12 | 13 | namespace Ink.UnityIntegration { 14 | public class InkInspector : ObjectInspector { 15 | 16 | private InkFile inkFile; 17 | private ReorderableList includesFileList; 18 | private ReorderableList errorList; 19 | private ReorderableList warningList; 20 | private ReorderableList todosList; 21 | 22 | public override bool IsValid(string assetPath) { 23 | if(Path.GetExtension(assetPath) == InkEditorUtils.inkFileExtension) { 24 | return true; 25 | } 26 | return false; 27 | } 28 | 29 | public override void OnEnable () { 30 | Rebuild(); 31 | InkCompiler.OnCompileInk += OnCompileInk; 32 | } 33 | 34 | public override void OnDisable () { 35 | InkCompiler.OnCompileInk -= OnCompileInk; 36 | } 37 | 38 | void OnCompileInk (InkFile inkFile) { 39 | Rebuild(); 40 | } 41 | 42 | void Rebuild () { 43 | string assetPath = AssetDatabase.GetAssetPath(target); 44 | inkFile = InkLibrary.GetInkFileWithPath(assetPath); 45 | if(inkFile == null) 46 | return; 47 | 48 | if(inkFile.includes.Count > 0) { 49 | CreateIncludeList(); 50 | } 51 | CreateErrorList(); 52 | CreateWarningList(); 53 | CreateTodoList(); 54 | } 55 | 56 | void CreateIncludeList () { 57 | List includeTextAssets = inkFile.includes; 58 | includesFileList = new ReorderableList(includeTextAssets, typeof(DefaultAsset), false, false, false, false); 59 | // includesFileList.elementHeight = 16; 60 | includesFileList.drawHeaderCallback = (Rect rect) => { 61 | EditorGUI.LabelField(rect, "Included Files"); 62 | }; 63 | includesFileList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { 64 | DefaultAsset childAssetFile = ((List)includesFileList.list)[index]; 65 | if(childAssetFile == null) { 66 | Debug.LogError("Ink file in include list is null. This should never occur. Use Assets > Recompile Ink to fix this issue."); 67 | EditorGUI.LabelField(rect, new GUIContent("Warning: Ink File in include list is null. Use Assets > Recompile Ink to fix this issue.")); 68 | return; 69 | } 70 | InkFile childInkFile = InkLibrary.GetInkFileWithFile(childAssetFile); 71 | if(childInkFile == null) { 72 | Debug.LogError("Ink File for included file "+childAssetFile+" not found. This should never occur. Use Assets > Recompile Ink to fix this issue."); 73 | EditorGUI.LabelField(rect, new GUIContent("Warning: Ink File for included file "+childAssetFile+" not found. Use Assets > Recompile Ink to fix this issue.")); 74 | return; 75 | } 76 | Rect iconRect = new Rect(rect.x, rect.y, 0, 16); 77 | if(childInkFile.hasErrors || childInkFile.hasWarnings) { 78 | iconRect.width = 20; 79 | } 80 | Rect objectFieldRect = new Rect(iconRect.xMax, rect.y, rect.width - iconRect.width - 80, 16); 81 | Rect selectRect = new Rect(objectFieldRect.xMax, rect.y, 80, 16); 82 | if(childInkFile.hasErrors) { 83 | EditorGUI.LabelField(iconRect, new GUIContent(InkBrowserIcons.errorIcon)); 84 | } else if(childInkFile.hasWarnings) { 85 | EditorGUI.LabelField(iconRect, new GUIContent(InkBrowserIcons.warningIcon)); 86 | } 87 | EditorGUI.BeginDisabledGroup(true); 88 | EditorGUI.ObjectField(objectFieldRect, childAssetFile, typeof(Object), false); 89 | EditorGUI.EndDisabledGroup(); 90 | if(GUI.Button(selectRect, "Select")) { 91 | Selection.activeObject = childAssetFile; 92 | } 93 | }; 94 | } 95 | 96 | void CreateErrorList () { 97 | errorList = new ReorderableList(inkFile.errors, typeof(string), false, false, false, false); 98 | errorList.elementHeight = 18; 99 | errorList.drawHeaderCallback = (Rect rect) => { 100 | EditorGUI.LabelField(rect, new GUIContent(InkBrowserIcons.errorIcon), new GUIContent("Errors")); 101 | }; 102 | errorList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { 103 | Rect labelRect = new Rect(rect.x, rect.y, rect.width - 80, rect.height); 104 | Rect buttonRect = new Rect(labelRect.xMax, rect.y, 80, rect.height-2); 105 | InkFile.InkFileLog log = ((List)errorList.list)[index]; 106 | string label = log.content; 107 | GUI.Label(labelRect, label); 108 | string openLabel = "Open"+ (log.lineNumber == -1 ? "" : " ("+log.lineNumber+")"); 109 | if(GUI.Button(buttonRect, openLabel)) { 110 | UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(inkFile.filePath, log.lineNumber); 111 | // AssetDatabase.OpenAsset(masterInkFile.inkFile, lineNumber); 112 | } 113 | }; 114 | } 115 | 116 | void CreateWarningList () { 117 | warningList = new ReorderableList(inkFile.warnings, typeof(string), false, false, false, false); 118 | warningList.elementHeight = 18; 119 | warningList.drawHeaderCallback = (Rect rect) => { 120 | EditorGUI.LabelField(rect, new GUIContent(InkBrowserIcons.warningIcon), new GUIContent("Warnings")); 121 | }; 122 | warningList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { 123 | Rect labelRect = new Rect(rect.x, rect.y, rect.width - 80, rect.height); 124 | Rect buttonRect = new Rect(labelRect.xMax, rect.y, 80, rect.height-2); 125 | InkFile.InkFileLog log = ((List)warningList.list)[index]; 126 | string label = log.content; 127 | GUI.Label(labelRect, label); 128 | string openLabel = "Open"+ (log.lineNumber == -1 ? "" : " ("+log.lineNumber+")"); 129 | if(GUI.Button(buttonRect, openLabel)) { 130 | UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(inkFile.filePath, log.lineNumber); 131 | // AssetDatabase.OpenAsset(masterInkFile.inkFile, lineNumber); 132 | } 133 | }; 134 | } 135 | 136 | void CreateTodoList () { 137 | todosList = new ReorderableList(inkFile.todos, typeof(string), false, false, false, false); 138 | todosList.elementHeight = 18; 139 | todosList.drawHeaderCallback = (Rect rect) => { 140 | EditorGUI.LabelField(rect, "To do"); 141 | }; 142 | todosList.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { 143 | Rect labelRect = new Rect(rect.x, rect.y, rect.width - 80, rect.height); 144 | Rect buttonRect = new Rect(labelRect.xMax, rect.y, 80, rect.height-2); 145 | InkFile.InkFileLog log = ((List)todosList.list)[index]; 146 | string label = log.content; 147 | GUI.Label(labelRect, label); 148 | string openLabel = "Open"+ (log.lineNumber == -1 ? "" : " ("+log.lineNumber+")"); 149 | if(GUI.Button(buttonRect, openLabel)) { 150 | UnityEditorInternal.InternalEditorUtility.OpenFileAtLineExternal(inkFile.filePath, log.lineNumber); 151 | // AssetDatabase.OpenAsset(masterInkFile.inkFile, lineNumber); 152 | } 153 | }; 154 | } 155 | 156 | public override void OnInspectorGUI () { 157 | editor.Repaint(); 158 | serializedObject.Update(); 159 | if(inkFile == null) { 160 | EditorGUILayout.HelpBox("Ink File is not in library.", MessageType.Warning); 161 | if(GUILayout.Button("Rebuild Library")) { 162 | InkLibrary.Rebuild(); 163 | Rebuild(); 164 | } 165 | return; 166 | } 167 | 168 | if(InkLibrary.GetCompilationStackItem(inkFile) != null) { 169 | EditorGUILayout.HelpBox("File is compiling...", MessageType.Info); 170 | return; 171 | } 172 | InkFile masterInkFile = inkFile; 173 | if(inkFile.isMaster) { 174 | DrawMasterFileHeader(); 175 | } else { 176 | masterInkFile = InkLibrary.GetInkFileWithFile((DefaultAsset)inkFile.master); 177 | DrawSubFileHeader(masterInkFile); 178 | } 179 | 180 | DrawEditAndCompileDates(masterInkFile); 181 | if(inkFile.isMaster && !editedAfterLastCompile) 182 | DrawCompileButton(masterInkFile); 183 | DrawIncludedFiles(); 184 | 185 | DrawErrors(); 186 | DrawWarnings(); 187 | DrawTODOList(); 188 | DrawFileContents (); 189 | 190 | serializedObject.ApplyModifiedProperties(); 191 | } 192 | 193 | void DrawMasterFileHeader () { 194 | EditorGUILayout.LabelField("Master File", EditorStyles.boldLabel); 195 | if(!InkLibrary.Instance.compileAutomatically) 196 | inkFile.compileAutomatically = EditorGUILayout.Toggle("Compile Automatially", inkFile.compileAutomatically); 197 | EditorGUI.BeginDisabledGroup(true); 198 | EditorGUILayout.ObjectField("JSON Asset", inkFile.jsonAsset, typeof(TextAsset), false); 199 | EditorGUI.EndDisabledGroup(); 200 | 201 | if(inkFile.jsonAsset != null && inkFile.errors.Count == 0 && GUILayout.Button("Play")) { 202 | InkPlayerWindow.LoadAndPlay(inkFile.jsonAsset); 203 | } 204 | 205 | // if(!checkedStoryForErrors) { 206 | // if(GUILayout.Button("Check for errors")) { 207 | // GetStoryErrors(); 208 | // } 209 | // } else { 210 | // if(exception != null) { 211 | // EditorGUILayout.HelpBox("Story is invalid\n"+exception.ToString(), MessageType.Error); 212 | // } else { 213 | // EditorGUILayout.HelpBox("Story is valid", MessageType.Info); 214 | // } 215 | // } 216 | } 217 | 218 | void DrawSubFileHeader(InkFile masterInkFile) { 219 | EditorGUILayout.LabelField("Sub File", EditorStyles.boldLabel); 220 | EditorGUILayout.BeginHorizontal(); 221 | if(masterInkFile.hasErrors) { 222 | GUILayout.Label(new GUIContent(InkBrowserIcons.errorIcon), GUILayout.Width(20)); 223 | } else if(masterInkFile.hasWarnings) { 224 | GUILayout.Label(new GUIContent(InkBrowserIcons.warningIcon), GUILayout.Width(20)); 225 | } 226 | EditorGUI.BeginDisabledGroup(true); 227 | EditorGUILayout.ObjectField("Master Ink File", masterInkFile.inkAsset, typeof(Object), false); 228 | EditorGUI.EndDisabledGroup(); 229 | if(GUILayout.Button("Select", GUILayout.Width(80))) { 230 | Selection.activeObject = masterInkFile.inkAsset; 231 | } 232 | EditorGUILayout.EndHorizontal(); 233 | } 234 | 235 | 236 | bool editedAfterLastCompile = false; 237 | void DrawEditAndCompileDates (InkFile masterInkFile) { 238 | editedAfterLastCompile = false; 239 | string editAndCompileDateString = ""; 240 | DateTime lastEditDate = File.GetLastWriteTime(inkFile.absoluteFilePath); 241 | editAndCompileDateString += "Last edit date "+lastEditDate.ToString(); 242 | if(inkFile.isMaster && inkFile.jsonAsset != null) { 243 | DateTime lastCompileDate = File.GetLastWriteTime(Path.Combine(Application.dataPath, AssetDatabase.GetAssetPath(masterInkFile.jsonAsset).Substring(7))); 244 | editAndCompileDateString += "\nLast compile date "+lastCompileDate.ToString(); 245 | if(lastEditDate > lastCompileDate) { 246 | editedAfterLastCompile = true; 247 | EditorGUILayout.HelpBox(editAndCompileDateString, MessageType.Warning); 248 | if(GUILayout.Button("Recompile")) { 249 | InkCompiler.CompileInk(masterInkFile); 250 | } 251 | } else { 252 | EditorGUILayout.HelpBox(editAndCompileDateString, MessageType.None); 253 | } 254 | } else { 255 | EditorGUILayout.HelpBox(editAndCompileDateString, MessageType.None); 256 | } 257 | } 258 | 259 | void DrawIncludedFiles () { 260 | if(includesFileList != null && includesFileList.count > 0) { 261 | includesFileList.DoLayoutList(); 262 | } 263 | } 264 | 265 | void DrawCompileButton (InkFile masterInkFile) { 266 | bool drawButton = false; 267 | if(masterInkFile.hasErrors) { 268 | EditorGUILayout.HelpBox("Last compiled failed", MessageType.Error); 269 | drawButton = true; 270 | } else if(masterInkFile.hasWarnings) { 271 | EditorGUILayout.HelpBox("Last compile had errors", MessageType.Warning); 272 | drawButton = true; 273 | } else if(masterInkFile.jsonAsset == null) { 274 | EditorGUILayout.HelpBox("Ink file has not been compiled", MessageType.Warning); 275 | drawButton = true; 276 | } 277 | if(drawButton && GUILayout.Button("Compile")) { 278 | InkCompiler.CompileInk(masterInkFile); 279 | } 280 | } 281 | 282 | void DrawErrors () { 283 | if(errorList != null && errorList.count > 0) { 284 | errorList.DoLayoutList(); 285 | } 286 | } 287 | 288 | void DrawWarnings () { 289 | if(warningList != null && warningList.count > 0) { 290 | warningList.DoLayoutList(); 291 | } 292 | } 293 | 294 | void DrawTODOList () { 295 | if(todosList != null && todosList.count > 0) { 296 | todosList.DoLayoutList(); 297 | } 298 | } 299 | 300 | void DrawFileContents () { 301 | int maxCharacters = 16000; 302 | string trimmedStory = inkFile.fileContents.Substring(0, Mathf.Min(inkFile.fileContents.Length, maxCharacters)); 303 | if(inkFile.fileContents.Length >= maxCharacters) 304 | trimmedStory += "...\n\n<...etc...>"; 305 | float width = EditorGUIUtility.currentViewWidth-50; 306 | float height = EditorStyles.wordWrappedLabel.CalcHeight(new GUIContent(trimmedStory), width); 307 | EditorGUILayout.BeginVertical(EditorStyles.textArea); 308 | EditorGUILayout.SelectableLabel(trimmedStory, EditorStyles.wordWrappedLabel, GUILayout.ExpandHeight(true), GUILayout.Width(width), GUILayout.Height(height)); 309 | EditorGUILayout.EndVertical(); 310 | } 311 | } 312 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/Ink Inspector/InkInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6905e53703af64a70aa6eb42f98b047c 3 | timeCreated: 1460011343 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/Ink Inspector/ObjectEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System; 4 | using System.IO; 5 | using System.Collections; 6 | using System.Collections.Generic; 7 | using System.Reflection; 8 | 9 | namespace Ink.UnityIntegration { 10 | [CustomEditor(typeof(UnityEngine.Object), true)] 11 | public class ObjectEditor : Editor { 12 | 13 | private ObjectInspector objectInspector; 14 | 15 | private void OnEnable () { 16 | objectInspector = FindObjectInspector (); 17 | if(objectInspector != null) { 18 | objectInspector.editor = this; 19 | objectInspector.serializedObject = serializedObject; 20 | objectInspector.target = target; 21 | objectInspector.OnEnable(); 22 | } 23 | } 24 | 25 | private void OnDisable () { 26 | if(objectInspector != null) 27 | objectInspector.OnDisable(); 28 | } 29 | 30 | public override void OnInspectorGUI () { 31 | if(objectInspector != null) { 32 | GUI.enabled = true; 33 | objectInspector.OnInspectorGUI(); 34 | } 35 | else if (target.GetType() != typeof(UnityEditor.DefaultAsset)) 36 | base.OnInspectorGUI(); 37 | } 38 | 39 | private ObjectInspector FindObjectInspector () { 40 | List assembliesToCheck = new List{"Assembly-CSharp-Editor", "Assembly-CSharp-Editor-firstpass", "Assembly-UnityScript-Editor", "Assembly-UnityScript-Editor-firstpass"}; 41 | string assetPath = AssetDatabase.GetAssetPath(target); 42 | Assembly[] referencedAssemblies = System.AppDomain.CurrentDomain.GetAssemblies(); 43 | for(int i = 0; i < referencedAssemblies.Length; ++i) { 44 | if(!assembliesToCheck.Contains(referencedAssemblies[i].GetName().Name)) 45 | continue; 46 | foreach(var type in referencedAssemblies[i].GetTypes()) { 47 | if(!type.IsSubclassOf(typeof(ObjectInspector))) 48 | continue; 49 | ObjectInspector objectInspector = (ObjectInspector)Activator.CreateInstance(type); 50 | if(objectInspector.IsValid(assetPath)) { 51 | objectInspector.target = target; 52 | return objectInspector; 53 | } 54 | } 55 | } 56 | return null; 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/Ink Inspector/ObjectEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2e0ac3feecf474715b8c698e9495d833 3 | timeCreated: 1460011342 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/Ink Inspector/ObjectInspector.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | 4 | namespace Ink.UnityIntegration { 5 | public abstract class ObjectInspector { 6 | // Reference to the actual editor we draw to 7 | public Editor editor; 8 | // Shortcut to the target object 9 | public Object target; 10 | // Shortcut to the serializedObject 11 | public SerializedObject serializedObject; 12 | 13 | public abstract bool IsValid(string assetPath); 14 | public virtual void OnEnable () {} 15 | public virtual void OnDisable () {} 16 | public virtual void OnHeaderGUI () {} 17 | public virtual void OnInspectorGUI() {} 18 | } 19 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Inspector/Ink Inspector/ObjectInspector.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 61e383d72b80d4b7eb886e2bf08ca9b7 3 | timeCreated: 1460011342 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Library.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6188b37f5f5824313b73fb964d1b269e 3 | folderAsset: yes 4 | timeCreated: 1459878666 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Library/InkFile.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System; 4 | using System.IO; 5 | using System.Text; 6 | using System.Text.RegularExpressions; 7 | using Debug = UnityEngine.Debug; 8 | using System.Collections; 9 | using System.Collections.Generic; 10 | using System.Diagnostics; 11 | 12 | using System.Linq; 13 | using System.Reflection; 14 | using System.Runtime.CompilerServices; 15 | 16 | using Object = UnityEngine.Object; 17 | 18 | namespace Ink.UnityIntegration { 19 | // Helper class for ink files that maintains INCLUDE connections between ink files 20 | [System.Serializable] 21 | public sealed class InkFile { 22 | private const string includeKey = "INCLUDE "; 23 | 24 | public bool compileAutomatically = false; 25 | 26 | // The full file path 27 | public string absoluteFilePath { 28 | get { 29 | if(inkAsset == null) 30 | return null; 31 | string path = Path.Combine(Application.dataPath, filePath.Substring(7)); 32 | path = path.Replace ('\\', '/'); 33 | return path; 34 | } 35 | } 36 | public string absoluteFolderPath { 37 | get { 38 | return Path.GetDirectoryName(absoluteFilePath); 39 | } 40 | } 41 | // The file path relative to the Assets folder 42 | public string filePath { 43 | get { 44 | return AssetDatabase.GetAssetPath(inkAsset); 45 | } 46 | } 47 | 48 | // The content of the .ink file 49 | public string fileContents; 50 | 51 | // A reference to the ink file 52 | public DefaultAsset inkAsset; 53 | 54 | // If file that contains this file as an include, if one exists. 55 | public DefaultAsset parent; 56 | public InkFile parentInkFile { 57 | get { 58 | if(parent == null) 59 | return null; 60 | else 61 | return InkLibrary.GetInkFileWithFile(parent); 62 | } 63 | } 64 | // Is this ink file a parent file? 65 | public bool isParent { 66 | get { 67 | return includes.Count > 0; 68 | } 69 | } 70 | 71 | public DefaultAsset master; 72 | public InkFile masterInkFile { 73 | get { 74 | if(master == null) 75 | return null; 76 | else 77 | return InkLibrary.GetInkFileWithFile(master); 78 | } 79 | } 80 | // Is this ink file a master file? 81 | public bool isMaster { 82 | get { 83 | return master == null; 84 | } 85 | } 86 | 87 | 88 | // The files included by this file 89 | // We cache the paths of the files to be included for performance, giving us more freedom to refresh the actual includes list without needing to parse all the text. 90 | public List includePaths = new List(); 91 | public List includes = new List(); 92 | 93 | // The compiled json file. Use this to start a story. 94 | public TextAsset jsonAsset; 95 | 96 | 97 | public List errors = new List(); 98 | public bool hasErrors { 99 | get { 100 | return errors.Count > 0; 101 | } 102 | } 103 | 104 | public List warnings = new List(); 105 | public bool hasWarnings { 106 | get { 107 | return warnings.Count > 0; 108 | } 109 | } 110 | 111 | public List todos = new List(); 112 | public bool hasTodos { 113 | get { 114 | return todos.Count > 0; 115 | } 116 | } 117 | 118 | [System.Serializable] 119 | public class InkFileLog { 120 | public string content; 121 | public int lineNumber; 122 | 123 | public InkFileLog (string content, int lineNumber = -1) { 124 | this.content = content; 125 | this.lineNumber = lineNumber; 126 | } 127 | } 128 | 129 | public InkFile (DefaultAsset inkFile) { 130 | Debug.Assert(inkFile != null); 131 | this.inkAsset = inkFile; 132 | } 133 | 134 | public void ParseContent () { 135 | fileContents = File.OpenText(absoluteFilePath).ReadToEnd(); 136 | InkIncludeParser includeParser = new InkIncludeParser(fileContents); 137 | includePaths = includeParser.includeFilenames; 138 | } 139 | 140 | public void FindIncludedFiles () { 141 | includes.Clear(); 142 | foreach(string includePath in includePaths) { 143 | string localIncludePath = Path.Combine(Path.GetDirectoryName(filePath), includePath); 144 | localIncludePath = localIncludePath.Replace ('\\', '/'); 145 | DefaultAsset includedInkFileAsset = AssetDatabase.LoadAssetAtPath(localIncludePath); 146 | InkFile includedInkFile = InkLibrary.GetInkFileWithFile(includedInkFileAsset); 147 | if(includedInkFile == null) { 148 | Debug.LogError("Expected Ink file at "+localIncludePath+" but file was not found."); 149 | } else if (includedInkFile.includes.Contains(inkAsset)) { 150 | Debug.LogError("Circular INCLUDE reference between "+filePath+" and "+includedInkFile.filePath+"."); 151 | } else 152 | includes.Add(includedInkFileAsset); 153 | } 154 | } 155 | 156 | public void FindCompiledJSONAsset () { 157 | string jsonAssetPath = Path.Combine(Path.GetDirectoryName(filePath), Path.GetFileNameWithoutExtension(filePath))+".json"; 158 | jsonAssetPath = jsonAssetPath.Replace ('\\', '/'); 159 | jsonAsset = AssetDatabase.LoadAssetAtPath(jsonAssetPath); 160 | } 161 | 162 | public class InkIncludeParser { 163 | public InkIncludeParser (string inkContents) 164 | { 165 | _text = inkContents; 166 | } 167 | void Process() 168 | { 169 | _text = EliminateComments (_text); 170 | FindIncludes (_text); 171 | } 172 | string EliminateComments(string inkStr) 173 | { 174 | var sb = new StringBuilder (); 175 | int idx = 0; 176 | while(idx < inkStr.Length) { 177 | var commentStarterIdx = inkStr.IndexOf ('/', idx); 178 | // Final string? 179 | if (commentStarterIdx == -1 || commentStarterIdx >= inkStr.Length-2 ) { 180 | sb.Append (inkStr.Substring (idx, inkStr.Length - idx)); 181 | break; 182 | } 183 | sb.Append (inkStr.Substring (idx, commentStarterIdx - idx)); 184 | var commentStarter = inkStr.Substring (commentStarterIdx, 2); 185 | if (commentStarter == "//" || commentStarter == "/*") { 186 | int endOfCommentIdx = -1; 187 | // Single line comments 188 | if (commentStarter == "//") { 189 | endOfCommentIdx = inkStr.IndexOf ('\n', commentStarterIdx); 190 | if (endOfCommentIdx == -1) 191 | endOfCommentIdx = inkStr.Length; 192 | else if (inkStr [endOfCommentIdx - 1] == '\r') 193 | endOfCommentIdx = endOfCommentIdx - 1; 194 | } 195 | // Block comments 196 | else if (commentStarter == "/*") { 197 | endOfCommentIdx = inkStr.IndexOf ("*/", idx); 198 | if (endOfCommentIdx == -1) 199 | endOfCommentIdx = inkStr.Length; 200 | else 201 | endOfCommentIdx += 2; 202 | // If there are *any* newlines, we should add one in here, 203 | // so that lines are spit up correctly 204 | if (inkStr.IndexOf ('\n', commentStarterIdx, endOfCommentIdx - commentStarterIdx) != -1) 205 | sb.Append ("\n"); 206 | } 207 | // Skip over comment 208 | if (endOfCommentIdx > -1) 209 | idx = endOfCommentIdx; 210 | } 211 | // Normal slash we need, not a comment 212 | else { 213 | sb.Append ("/"); 214 | idx = commentStarterIdx + 1; 215 | } 216 | } 217 | return sb.ToString (); 218 | } 219 | void FindIncludes(string str) 220 | { 221 | _includeFilenames = new List (); 222 | var includeRegex = new Regex (@"^\s*INCLUDE\s+(.+)$", RegexOptions.Multiline); 223 | MatchCollection matches = includeRegex.Matches(str); 224 | foreach (Match match in matches) 225 | { 226 | var capture = match.Groups [1].Captures [0]; 227 | _includeFilenames.Add (capture.Value); 228 | } 229 | } 230 | 231 | public List includeFilenames { 232 | get { 233 | if (_includeFilenames == null) { 234 | Process (); 235 | } 236 | return _includeFilenames; 237 | } 238 | } 239 | List _includeFilenames; 240 | string _text; 241 | } 242 | } 243 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Library/InkFile.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: f710753ff9fd84049b8c06fddd9af1af 3 | timeCreated: 1459464092 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Library/InkLibrary.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Diagnostics; 8 | using UnityEditorInternal; 9 | using Debug = UnityEngine.Debug; 10 | using Ink.Runtime; 11 | 12 | /// 13 | /// Holds a reference to an InkFile object for every .ink file detected in the Assets folder. 14 | /// Provides helper functions to easily obtain these files. 15 | /// 16 | namespace Ink.UnityIntegration { 17 | public class InkLibrary : ScriptableObject { 18 | public static bool created { 19 | get { 20 | InkLibrary tmpSettings = AssetDatabase.LoadAssetAtPath(defaultPath); 21 | if(tmpSettings != null) 22 | return true; 23 | string[] GUIDs = AssetDatabase.FindAssets("t:"+typeof(InkLibrary).Name); 24 | if(GUIDs.Length > 0) 25 | return true; 26 | return false; 27 | } 28 | } 29 | private static InkLibrary _Instance; 30 | public static InkLibrary Instance { 31 | get { 32 | if(_Instance == null) 33 | _Instance = FindOrCreateLibrary(); 34 | return _Instance; 35 | } 36 | } 37 | public const string defaultPath = "Assets/InkLibrary.asset"; 38 | 39 | public bool compileAutomatically = true; 40 | public bool handleJSONFilesAutomatically = true; 41 | 42 | public List inkLibrary = new List(); 43 | public List compilationStack = new List(); 44 | 45 | [MenuItem("Edit/Project Settings/Ink", false, 500)] 46 | public static void SelectFromProjectSettings() { 47 | Selection.activeObject = Instance; 48 | } 49 | 50 | /// 51 | /// Removes and null references in the library 52 | /// 53 | public static void Clean () { 54 | for (int i = InkLibrary.Instance.inkLibrary.Count - 1; i >= 0; i--) { 55 | InkFile inkFile = InkLibrary.Instance.inkLibrary[i]; 56 | if (inkFile.inkAsset == null) 57 | InkLibrary.Instance.inkLibrary.RemoveAt(i); 58 | } 59 | } 60 | 61 | private static InkLibrary FindOrCreateLibrary () { 62 | InkLibrary tmpSettings = AssetDatabase.LoadAssetAtPath(defaultPath); 63 | if(tmpSettings == null) { 64 | string[] GUIDs = AssetDatabase.FindAssets("t:"+typeof(InkLibrary).Name); 65 | if(GUIDs.Length > 0) { 66 | string path = AssetDatabase.GUIDToAssetPath(GUIDs[0]); 67 | tmpSettings = AssetDatabase.LoadAssetAtPath(path); 68 | if(GUIDs.Length > 1) { 69 | for(int i = 1; i < GUIDs.Length; i++) { 70 | AssetDatabase.DeleteAsset(AssetDatabase.GUIDToAssetPath(GUIDs[i])); 71 | } 72 | Debug.LogWarning("More than one InkLibrary was found. Deleted excess libraries."); 73 | } 74 | } 75 | } 76 | // If we couldn't find the asset in the project, create a new one. 77 | if(tmpSettings == null) { 78 | tmpSettings = CreateInkLibrary (); 79 | Debug.Log("Created a new ink library at "+defaultPath+" because one was not found."); 80 | InkLibrary.Rebuild(); 81 | } 82 | return tmpSettings; 83 | } 84 | 85 | private static InkLibrary CreateInkLibrary () { 86 | var asset = ScriptableObject.CreateInstance(); 87 | AssetDatabase.CreateAsset (asset, defaultPath); 88 | AssetDatabase.SaveAssets (); 89 | AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(asset)); 90 | return asset; 91 | } 92 | 93 | public static string[] GetAllInkFilePaths () { 94 | string[] inkFilePaths = Directory.GetFiles(Application.dataPath, "*.ink", SearchOption.AllDirectories); 95 | for (int i = 0; i < inkFilePaths.Length; i++) { 96 | inkFilePaths [i] = inkFilePaths [i].Replace('\\', '/'); 97 | } 98 | return inkFilePaths; 99 | } 100 | 101 | /// 102 | /// Updates the ink library. Executed whenever an ink file is changed by InkToJSONPostProcessor 103 | /// Can be called manually, but incurs a performance cost. 104 | /// 105 | public static void Rebuild () { 106 | Debug.Log("Rebuilding Ink Library..."); 107 | string[] inkFilePaths = GetAllInkFilePaths(); 108 | 109 | List newInkLibrary = new List(inkFilePaths.Length); 110 | for (int i = 0; i < inkFilePaths.Length; i++) { 111 | InkFile inkFile = GetInkFileWithAbsolutePath(inkFilePaths [i]); 112 | if(inkFile == null) { 113 | string localAssetPath = inkFilePaths [i].Substring(Application.dataPath.Length-6); 114 | DefaultAsset inkFileAsset = AssetDatabase.LoadAssetAtPath(localAssetPath); 115 | // If the ink file can't be found, it might not yet have been imported. We try to manually import it to fix this. 116 | if(inkFileAsset == null) { 117 | AssetDatabase.ImportAsset(localAssetPath); 118 | inkFileAsset = AssetDatabase.LoadAssetAtPath(localAssetPath); 119 | if(inkFileAsset == null) { 120 | Debug.LogWarning("Ink File Asset not found at "+localAssetPath+". This can occur if the .meta file has not yet been created. This issue should resolve itself, but if unexpected errors occur, rebuild Ink Library using > Recompile Ink"); 121 | continue; 122 | } 123 | } 124 | inkFile = new InkFile(inkFileAsset); 125 | } 126 | newInkLibrary.Add(inkFile); 127 | } 128 | 129 | InkLibrary.Instance.inkLibrary = newInkLibrary; 130 | 131 | foreach (InkFile inkFile in InkLibrary.Instance.inkLibrary) { 132 | inkFile.ParseContent(); 133 | } 134 | RebuildInkFileConnections(); 135 | foreach (InkFile inkFile in InkLibrary.Instance.inkLibrary) { 136 | inkFile.FindCompiledJSONAsset(); 137 | } 138 | 139 | EditorUtility.SetDirty(InkLibrary.Instance); 140 | AssetDatabase.SaveAssets(); 141 | } 142 | 143 | /// 144 | /// Rebuilds which files are master files. 145 | /// 146 | public static void RebuildInkFileConnections () { 147 | foreach (InkFile inkFile in InkLibrary.Instance.inkLibrary) { 148 | inkFile.parent = null; 149 | inkFile.master = null; 150 | inkFile.FindIncludedFiles(); 151 | } 152 | // We now set the master file for ink files. As a file can be in an include hierarchy, we need to do this in two passes. 153 | // First, we set the master file to the file that includes an ink file. 154 | foreach (InkFile inkFile in InkLibrary.Instance.inkLibrary) { 155 | if(inkFile.includes.Count == 0) 156 | continue; 157 | foreach (InkFile otherInkFile in InkLibrary.Instance.inkLibrary) { 158 | if(inkFile == otherInkFile) 159 | continue; 160 | if(inkFile.includes.Contains(otherInkFile.inkAsset)) { 161 | otherInkFile.parent = inkFile.inkAsset; 162 | } 163 | } 164 | } 165 | // Next, we create a list of all the files owned by the actual master file, which we obtain by travelling up the parent tree from each file. 166 | Dictionary> masterChildRelationships = new Dictionary>(); 167 | foreach (InkFile inkFile in InkLibrary.Instance.inkLibrary) { 168 | if(inkFile.parent == null) 169 | continue; 170 | InkFile parent = inkFile.parentInkFile; 171 | while (parent.parent != null) { 172 | parent = parent.parentInkFile; 173 | } 174 | if(!masterChildRelationships.ContainsKey(parent)) { 175 | masterChildRelationships.Add(parent, new List()); 176 | } 177 | masterChildRelationships[parent].Add(inkFile); 178 | } 179 | // Finally, we set the master file of the children 180 | foreach (var inkFileRelationship in masterChildRelationships) { 181 | foreach(InkFile childInkFile in inkFileRelationship.Value) { 182 | childInkFile.master = inkFileRelationship.Key.inkAsset; 183 | if(InkLibrary.Instance.handleJSONFilesAutomatically && childInkFile.jsonAsset != null) { 184 | AssetDatabase.DeleteAsset(AssetDatabase.GetAssetPath(childInkFile.jsonAsset)); 185 | childInkFile.jsonAsset = null; 186 | } 187 | } 188 | } 189 | } 190 | public static List GetMasterInkFiles () { 191 | List masterInkFiles = new List(); 192 | if(InkLibrary.Instance.inkLibrary == null) return masterInkFiles; 193 | foreach (InkFile inkFile in InkLibrary.Instance.inkLibrary) { 194 | if(inkFile.isMaster) { 195 | masterInkFiles.Add(inkFile); 196 | } 197 | } 198 | return masterInkFiles; 199 | } 200 | 201 | /// 202 | /// Gets the ink file from the .ink file reference. 203 | /// 204 | /// The ink file with path. 205 | /// Path. 206 | public static InkFile GetInkFileWithFile (DefaultAsset file) { 207 | if(InkLibrary.Instance.inkLibrary == null) return null; 208 | foreach(InkFile inkFile in InkLibrary.Instance.inkLibrary) { 209 | if(inkFile.inkAsset == file) { 210 | return inkFile; 211 | } 212 | } 213 | return null; 214 | } 215 | 216 | /// 217 | /// Gets the ink file with path relative to Assets folder, for example: "Assets/Ink/myStory.ink". 218 | /// 219 | /// The ink file with path. 220 | /// Path. 221 | public static InkFile GetInkFileWithPath (string path) { 222 | if(InkLibrary.Instance.inkLibrary == null) return null; 223 | foreach(InkFile inkFile in InkLibrary.Instance.inkLibrary) { 224 | if(inkFile.filePath == path) { 225 | return inkFile; 226 | } 227 | } 228 | return null; 229 | } 230 | 231 | /// 232 | /// Gets the ink file with absolute path. 233 | /// 234 | /// The ink file with path. 235 | /// Path. 236 | public static InkFile GetInkFileWithAbsolutePath (string absolutePath) { 237 | if(InkLibrary.Instance.inkLibrary == null) return null; 238 | foreach(InkFile inkFile in InkLibrary.Instance.inkLibrary) { 239 | if(inkFile.absoluteFilePath == absolutePath) { 240 | return inkFile; 241 | } 242 | } 243 | return null; 244 | } 245 | 246 | public static List FilesInCompilingStackInState (InkCompiler.CompilationStackItem.State state) { 247 | List items = new List(); 248 | foreach(var x in InkLibrary.Instance.compilationStack) { 249 | if(x.state == state) 250 | items.Add(x); 251 | } 252 | return items; 253 | } 254 | 255 | public static InkCompiler.CompilationStackItem GetCompilationStackItem (string inkAbsoluteFilePath) { 256 | foreach(var x in InkLibrary.Instance.compilationStack) { 257 | if(x.inkAbsoluteFilePath == inkAbsoluteFilePath) 258 | return x; 259 | } 260 | Debug.LogError("Fatal Error compiling Ink! No file found! Please report this as a bug. "+inkAbsoluteFilePath); 261 | return null; 262 | } 263 | 264 | public static InkCompiler.CompilationStackItem GetCompilationStackItem (InkFile inkFile) { 265 | foreach(var x in InkLibrary.Instance.compilationStack) { 266 | if(x.inkFile == inkFile) 267 | return x; 268 | } 269 | return null; 270 | } 271 | } 272 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Library/InkLibrary.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fb0d2db055db745819018cfdb3d82ea8 3 | timeCreated: 1459876783 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Library/InkLibraryEditor.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using UnityEditorInternal; 6 | 7 | namespace Ink.UnityIntegration { 8 | 9 | [CustomEditor(typeof(InkLibrary))] 10 | public class InkLibraryEditor : Editor { 11 | 12 | private ReorderableList list; 13 | 14 | #pragma warning disable 15 | protected InkLibrary data; 16 | 17 | public void OnEnable() { 18 | data = (InkLibrary) target; 19 | list = new ReorderableList(data.inkLibrary, typeof(InkFile), false, true, false, false); 20 | // list.elementHeight = 60; 21 | list.drawHeaderCallback = (Rect rect) => { 22 | EditorGUI.LabelField(rect, "Ink Library"); 23 | }; 24 | 25 | list.drawElementCallback = (Rect rect, int index, bool isActive, bool isFocused) => { 26 | InkFile inkFile = ((List)list.list)[index]; 27 | Rect objectFieldRect = new Rect(rect.x, rect.y, rect.width - 80, rect.height-4); 28 | if(!inkFile.isMaster) { 29 | objectFieldRect.x += 14; 30 | objectFieldRect.width -= 14; 31 | } 32 | Rect selectRect = new Rect(objectFieldRect.xMax, rect.y, 80, rect.height-4); 33 | // Rect titleRect = new Rect(rect.x, rect.y, rect.width, EditorGUIUtility.singleLineHeight); 34 | // Rect inkAssetRect = new Rect(rect.x, rect.y + EditorGUIUtility.singleLineHeight * 1, rect.width, EditorGUIUtility.singleLineHeight); 35 | // Rect jsonAssetRect = new Rect(rect.x, rect.y + EditorGUIUtility.singleLineHeight * 2, rect.width, EditorGUIUtility.singleLineHeight); 36 | // 37 | // inkFile.compileAutomatically = EditorGUI.Toggle(titleRect, "Compile Automatically", inkFile.compileAutomatically); 38 | EditorGUI.BeginDisabledGroup(true); 39 | inkFile.inkAsset = EditorGUI.ObjectField(objectFieldRect, inkFile.inkAsset, typeof(DefaultAsset)) as DefaultAsset; 40 | EditorGUI.EndDisabledGroup(); 41 | if(GUI.Button(selectRect, "Select")) { 42 | Selection.activeObject = inkFile.inkAsset; 43 | } 44 | // inkFile.jsonAsset = EditorGUI.ObjectField(jsonAssetRect, new GUIContent("JSON File"), inkFile.jsonAsset, typeof(TextAsset)) as TextAsset; 45 | }; 46 | } 47 | 48 | public override void OnInspectorGUI() { 49 | if(PlayerSettings.apiCompatibilityLevel == ApiCompatibilityLevel.NET_2_0_Subset) { 50 | EditorGUILayout.HelpBox("apiCompatibilityLevel is .Net 2.0 Subset. ", MessageType.Warning); 51 | if (GUILayout.Button("Fix")) { 52 | PlayerSettings.apiCompatibilityLevel = ApiCompatibilityLevel.NET_2_0; 53 | } 54 | } 55 | base.OnInspectorGUI(); 56 | serializedObject.Update(); 57 | if(GUI.changed && target != null) { 58 | EditorUtility.SetDirty(target); 59 | } 60 | // list.DoLayoutList(); 61 | serializedObject.ApplyModifiedProperties(); 62 | } 63 | } 64 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Ink Library/InkLibraryEditor.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 16128fd21c644429dae73abe903d2070 3 | timeCreated: 1462020109 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Player Window.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 76d8510b5dffa4afe814a18428526677 3 | folderAsset: yes 4 | timeCreated: 1459882092 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Player Window/InkPlayerWindow.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using Ink.Runtime; 7 | using Debug = UnityEngine.Debug; 8 | 9 | namespace Ink.UnityIntegration { 10 | 11 | /// 12 | /// Ink player window. Tests stories in an editor window. 13 | /// 14 | public class InkPlayerWindow : EditorWindow { 15 | private TextAsset storyJSONTextAsset; 16 | private string storyJSON; 17 | private Story story; 18 | private TextAsset _storyStateTextAsset; 19 | private TextAsset storyStateTextAsset { 20 | get { 21 | return _storyStateTextAsset; 22 | } set { 23 | if(_storyStateTextAsset == value) 24 | return; 25 | _storyStateTextAsset = value; 26 | if(_storyStateTextAsset != null) 27 | storyStateValid = InkEditorUtils.CheckStoryStateIsValid(storyJSONTextAsset.text, storyStateTextAsset.text); 28 | } 29 | } 30 | 31 | private UndoHistory storyStateHistory; 32 | private List storyHistory; 33 | private bool displayChoicesInLog = false; 34 | private bool continueAutomatically = true; 35 | 36 | private Vector2 scrollPosition; 37 | 38 | bool showingContentPanel = true; 39 | private Vector2 contentScrollPosition; 40 | bool showingChoicesPanel = true; 41 | bool showingSaveLoadPanel; 42 | bool showingDivertsPanel; 43 | private Vector2 divertsScrollPosition; 44 | bool showingVariablesPanel; 45 | private Vector2 variablesScrollPosition; 46 | 47 | private string divertCommand; 48 | 49 | private Exception errors; 50 | bool storyStateValid = false; 51 | 52 | [MenuItem("Window/Ink Player %#i", false, 2300)] 53 | public static InkPlayerWindow GetWindow () { 54 | return GetWindow("Ink Player", true); 55 | } 56 | 57 | void OnEnable () { 58 | storyStateHistory = new UndoHistory(); 59 | storyHistory = new List(); 60 | } 61 | 62 | public static void LoadAndPlay (TextAsset storyJSONTextAsset) { 63 | InkPlayerWindow window = GetWindow(); 64 | if(window.story != null) { 65 | if(EditorUtility.DisplayDialog("Story in progress", "The Ink Player Window is already playing a story. Would you like to stop it and load the new story?", "Stop and load", "Cancel")) { 66 | window.Stop(); 67 | window.Play(storyJSONTextAsset); 68 | } 69 | } else { 70 | window.Play(storyJSONTextAsset); 71 | } 72 | } 73 | 74 | public static void LoadAndPlay (string storyJSON) { 75 | InkPlayerWindow window = GetWindow(); 76 | if(window.story != null) { 77 | if(EditorUtility.DisplayDialog("Story in progress", "The Ink Player Window is already playing a story. Would you like to stop it and load the new story?", "Stop and load", "Cancel")) { 78 | window.Stop(); 79 | window.Play(storyJSON); 80 | } 81 | } else { 82 | window.Play(storyJSON); 83 | } 84 | } 85 | 86 | void Play (TextAsset storyJSONTextAsset) { 87 | if(!InkEditorUtils.CheckStoryIsValid(storyJSONTextAsset.text, out errors)) 88 | return; 89 | this.storyJSONTextAsset = storyJSONTextAsset; 90 | storyJSON = this.storyJSONTextAsset.text; 91 | InitStory(); 92 | TryContinue(); 93 | } 94 | 95 | void Play (string storyJSON) { 96 | if(!InkEditorUtils.CheckStoryIsValid(storyJSON, out errors)) 97 | return; 98 | this.storyJSONTextAsset = null; 99 | this.storyJSON = storyJSON; 100 | InitStory(); 101 | TryContinue(); 102 | } 103 | 104 | void InitStory () { 105 | story = new Story(storyJSON); 106 | story.allowExternalFunctionFallbacks = true; 107 | } 108 | 109 | void Stop () { 110 | storyStateHistory.Clear(); 111 | storyHistory.Clear(); 112 | story = null; 113 | } 114 | 115 | void Restart () { 116 | Stop(); 117 | if(storyJSONTextAsset != null) 118 | Play(storyJSONTextAsset); 119 | else 120 | Play(storyJSON); 121 | } 122 | 123 | void ContinueStory () { 124 | story.Continue(); 125 | storyHistory.Add(new InkPlayerHistoryContentItem(story.currentText.Trim())); 126 | ScrollToBottom(); 127 | AddToHistory(); 128 | } 129 | 130 | void AddToHistory () { 131 | InkPlayerHistoryItem historyItem = new InkPlayerHistoryItem(story.state.ToJson(), new List(storyHistory)); 132 | storyStateHistory.AddToUndoHistory(historyItem); 133 | } 134 | 135 | void Undo () { 136 | InkPlayerHistoryItem item = storyStateHistory.Undo(); 137 | story.state.LoadJson(item.inkStateJSON); 138 | storyHistory = new List(item.storyHistory); 139 | ScrollToBottom(); 140 | } 141 | 142 | void Redo () { 143 | InkPlayerHistoryItem item = storyStateHistory.Redo(); 144 | story.state.LoadJson(item.inkStateJSON); 145 | storyHistory = new List(item.storyHistory); 146 | ScrollToBottom(); 147 | } 148 | 149 | void SaveStoryState (string storyStateJSON) { 150 | storyStateTextAsset = InkEditorUtils.CreateStoryStateTextFile(storyStateJSON, System.IO.Path.GetDirectoryName(AssetDatabase.GetAssetPath(storyJSONTextAsset)), storyJSONTextAsset.name+"_SaveState"); 151 | } 152 | 153 | void LoadStoryState (string storyStateJSON) { 154 | storyHistory.Clear(); 155 | storyStateHistory.Clear(); 156 | story.state.LoadJson(storyStateJSON); 157 | } 158 | 159 | void ScrollToBottom () { 160 | contentScrollPosition.y = Mathf.Infinity; 161 | } 162 | 163 | void TryContinue () { 164 | if(!story.canContinue) 165 | return; 166 | if(continueAutomatically) { 167 | while (story.canContinue) { 168 | ContinueStory(); 169 | } 170 | } else { 171 | ContinueStory(); 172 | } 173 | } 174 | 175 | void OnGUI () { 176 | this.Repaint(); 177 | scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition); 178 | DisplayHeader(); 179 | if(errors == null) { 180 | DisplayControlbar(); 181 | } else { 182 | DisplayErrors(); 183 | } 184 | if(story == null) { 185 | EditorGUILayout.EndScrollView(); 186 | return; 187 | } 188 | EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); 189 | showingContentPanel = EditorGUILayout.Foldout(showingContentPanel, "Content"); 190 | EditorGUILayout.EndHorizontal(); 191 | if(showingContentPanel) 192 | DisplayStory (); 193 | 194 | EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); 195 | showingChoicesPanel = EditorGUILayout.Foldout(showingChoicesPanel, "Choices"); 196 | EditorGUILayout.EndHorizontal(); 197 | if(showingChoicesPanel) 198 | DisplayChoices (); 199 | 200 | EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); 201 | showingSaveLoadPanel = EditorGUILayout.Foldout(showingSaveLoadPanel, "Story State"); 202 | EditorGUILayout.EndHorizontal(); 203 | if(showingSaveLoadPanel) 204 | DisplaySaveLoad (); 205 | 206 | EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); 207 | showingDivertsPanel = EditorGUILayout.Foldout(showingDivertsPanel, "Diverts"); 208 | EditorGUILayout.EndHorizontal(); 209 | if(showingDivertsPanel) 210 | DisplayDiverts (); 211 | 212 | if(InkEditorUtils.StoryContainsVariables(story)) { 213 | EditorGUILayout.BeginHorizontal(EditorStyles.toolbar); 214 | showingVariablesPanel = EditorGUILayout.Foldout(showingVariablesPanel, "Variables"); 215 | EditorGUILayout.EndHorizontal(); 216 | if(showingVariablesPanel) 217 | DisplayVariables (); 218 | } 219 | EditorGUILayout.EndScrollView(); 220 | } 221 | 222 | void DisplayHeader () { 223 | EditorGUILayout.BeginVertical(); 224 | EditorGUI.BeginChangeCheck(); 225 | storyJSONTextAsset = EditorGUILayout.ObjectField("Story JSON", storyJSONTextAsset, typeof(TextAsset), false) as TextAsset; 226 | if(EditorGUI.EndChangeCheck()) { 227 | if(storyJSONTextAsset == null) { 228 | story = null; 229 | } else { 230 | Stop(); 231 | Play(storyJSONTextAsset); 232 | } 233 | } 234 | if(storyJSONTextAsset != null && storyJSON != null && storyJSONTextAsset.text != storyJSON) { 235 | EditorGUILayout.HelpBox("Story JSON file has changed. Restart to play updated story.", MessageType.Warning); 236 | } 237 | EditorGUILayout.EndVertical(); 238 | } 239 | 240 | void DisplayErrors () { 241 | EditorGUILayout.HelpBox(errors.Message, MessageType.Error); 242 | } 243 | 244 | void DisplayControlbar () { 245 | EditorGUILayout.BeginHorizontal (EditorStyles.toolbar); 246 | 247 | if(story == null) { 248 | EditorGUI.BeginDisabledGroup(storyJSONTextAsset == null); 249 | if(GUILayout.Button("Start", EditorStyles.toolbarButton)) { 250 | Play(storyJSONTextAsset); 251 | } 252 | EditorGUI.EndDisabledGroup(); 253 | } else { 254 | GUILayout.BeginHorizontal(); 255 | if(GUILayout.Button("Stop", EditorStyles.toolbarButton)) { 256 | Stop(); 257 | } 258 | if(GUILayout.Button("Restart", EditorStyles.toolbarButton)) { 259 | Restart(); 260 | } 261 | EditorGUI.BeginDisabledGroup(!storyStateHistory.canUndo); 262 | if(GUILayout.Button("Undo", EditorStyles.toolbarButton)) { 263 | Undo(); 264 | } 265 | EditorGUI.EndDisabledGroup(); 266 | EditorGUI.BeginDisabledGroup(!storyStateHistory.canRedo); 267 | if(GUILayout.Button("Redo", EditorStyles.toolbarButton)) { 268 | Redo(); 269 | } 270 | EditorGUI.EndDisabledGroup(); 271 | GUILayout.FlexibleSpace(); 272 | EditorGUI.BeginChangeCheck(); 273 | displayChoicesInLog = GUILayout.Toggle(displayChoicesInLog, "Display Choices", EditorStyles.toolbarButton); 274 | if(EditorGUI.EndChangeCheck()) { 275 | ScrollToBottom(); 276 | } 277 | EditorGUI.BeginChangeCheck(); 278 | continueAutomatically = GUILayout.Toggle(continueAutomatically, "Auto-Continue", EditorStyles.toolbarButton); 279 | if(EditorGUI.EndChangeCheck() && continueAutomatically) { 280 | while(story.canContinue) { 281 | ContinueStory(); 282 | } 283 | } 284 | GUILayout.EndHorizontal(); 285 | } 286 | 287 | EditorGUILayout.EndHorizontal (); 288 | } 289 | 290 | void DisplayStory () { 291 | GUILayout.BeginVertical(); 292 | 293 | contentScrollPosition = EditorGUILayout.BeginScrollView(contentScrollPosition); 294 | 295 | foreach(InkPlayerHistoryContentItem content in storyHistory) { 296 | if(content.isChoice) { 297 | if(displayChoicesInLog) 298 | DisplayLine(" > "+content.content); 299 | } else { 300 | DisplayLine(content.content); 301 | } 302 | } 303 | EditorGUILayout.EndScrollView(); 304 | GUILayout.EndVertical(); 305 | } 306 | 307 | void DisplayLine (string content) { 308 | float width = position.width-28; 309 | float height = EditorStyles.wordWrappedLabel.CalcHeight(new GUIContent(content), width); 310 | EditorGUILayout.SelectableLabel(content, EditorStyles.wordWrappedLabel, GUILayout.ExpandHeight(true), GUILayout.Width(width), GUILayout.Height(height)); 311 | } 312 | 313 | void DisplayChoices () { 314 | GUILayout.BeginVertical(); 315 | 316 | if(story.canContinue) { 317 | if(GUILayout.Button("Continue")) { 318 | ContinueStory(); 319 | } 320 | if(GUILayout.Button("Continue Maximally")) { 321 | while(story.canContinue) { 322 | ContinueStory(); 323 | } 324 | } 325 | } else if(story.currentChoices.Count > 0) { 326 | foreach(Choice choice in story.currentChoices) { 327 | if(GUILayout.Button(choice.text.Trim())) { 328 | storyHistory.Add(new InkPlayerHistoryContentItem(choice.text.Trim(), true)); 329 | story.ChooseChoiceIndex(choice.index); 330 | AddToHistory(); 331 | TryContinue(); 332 | } 333 | } 334 | } else { 335 | GUILayout.Label("Reached end of story"); 336 | } 337 | 338 | GUILayout.EndVertical(); 339 | } 340 | 341 | void DisplaySaveLoad () { 342 | GUILayout.BeginVertical(); 343 | 344 | EditorGUILayout.BeginHorizontal(); 345 | string currentStateJSON = story.state.ToJson(); 346 | EditorGUILayout.TextField("Current State JSON", currentStateJSON); 347 | EditorGUI.BeginDisabledGroup(GUIUtility.systemCopyBuffer == currentStateJSON); 348 | // if (GUILayout.Button("Copy To Clipboard")) { 349 | // GUIUtility.systemCopyBuffer = currentStateJSON; 350 | // } 351 | EditorGUI.EndDisabledGroup(); 352 | if (GUILayout.Button("Save As...")) { 353 | SaveStoryState(currentStateJSON); 354 | } 355 | EditorGUILayout.EndHorizontal(); 356 | 357 | EditorGUILayout.BeginHorizontal(); 358 | EditorGUI.BeginChangeCheck(); 359 | storyStateTextAsset = EditorGUILayout.ObjectField("Load Story State JSON File", storyStateTextAsset, typeof(TextAsset), false) as TextAsset; 360 | EditorGUI.BeginDisabledGroup(storyStateTextAsset == null); 361 | if (GUILayout.Button("Load")) { 362 | LoadStoryState(storyStateTextAsset.text); 363 | } 364 | EditorGUI.EndDisabledGroup(); 365 | EditorGUILayout.EndHorizontal(); 366 | if(storyStateTextAsset != null && !storyStateValid) { 367 | EditorGUILayout.HelpBox("Loaded story state file is not valid.", MessageType.Error); 368 | } 369 | GUILayout.EndVertical(); 370 | } 371 | 372 | void DisplayDiverts () { 373 | GUILayout.BeginVertical(); 374 | // divertsScrollPosition = EditorGUILayout.BeginScrollView(divertsScrollPosition); 375 | divertCommand = EditorGUILayout.TextField("Divert command", divertCommand); 376 | EditorGUI.BeginDisabledGroup(divertCommand == null || divertCommand == ""); 377 | if (GUILayout.Button("Divert")) { 378 | story.ChoosePathString(divertCommand); 379 | TryContinue(); 380 | } 381 | EditorGUI.EndDisabledGroup(); 382 | // EditorGUILayout.EndScrollView(); 383 | GUILayout.EndVertical(); 384 | } 385 | 386 | void DisplayVariables () { 387 | GUILayout.BeginVertical(GUI.skin.box, GUILayout.MaxHeight(80)); 388 | variablesScrollPosition = EditorGUILayout.BeginScrollView(variablesScrollPosition); 389 | 390 | string variableToChange = null; 391 | object newVariableValue = null; 392 | foreach(string variable in story.variablesState) { 393 | object variableValue = story.variablesState[variable]; 394 | EditorGUI.BeginChangeCheck(); 395 | if(variableValue is string) { 396 | variableValue = EditorGUILayout.TextField(variable, (string)variableValue); 397 | } else if(variableValue is float) { 398 | variableValue = EditorGUILayout.FloatField(variable, (float)variableValue); 399 | } else if(variableValue is int) { 400 | variableValue = EditorGUILayout.IntField(variable, (int)variableValue); 401 | } 402 | if(EditorGUI.EndChangeCheck()) { 403 | variableToChange = variable; 404 | newVariableValue = variableValue; 405 | } 406 | } 407 | if(variableToChange != null) { 408 | story.variablesState[variableToChange] = newVariableValue; 409 | variableToChange = null; 410 | newVariableValue = null; 411 | } 412 | 413 | EditorGUILayout.EndScrollView(); 414 | GUILayout.EndVertical(); 415 | } 416 | } 417 | 418 | 419 | [System.Serializable] 420 | public class UndoHistory where T : class { 421 | 422 | private int _undoHistoryIndex; 423 | public int undoHistoryIndex { 424 | get { 425 | return _undoHistoryIndex; 426 | } set { 427 | _undoHistoryIndex = Mathf.Clamp(value, 0, undoHistory.Count-1); 428 | if(OnChangeHistoryIndex != null) OnChangeHistoryIndex(undoHistory[undoHistoryIndex]); 429 | } 430 | } 431 | 432 | public List undoHistory; 433 | public int maxHistoryItems = 100; 434 | 435 | public bool canUndo { 436 | get { 437 | return undoHistory.Count > 0 && undoHistoryIndex > 0; 438 | } 439 | } 440 | 441 | public bool canRedo { 442 | get { 443 | return undoHistory.Count > 0 && undoHistoryIndex < undoHistory.Count - 1; 444 | } 445 | } 446 | 447 | public delegate void OnUndoEvent(T historyItem); 448 | public event OnUndoEvent OnUndo; 449 | 450 | public delegate void OnRedoEvent(T historyItem); 451 | public event OnRedoEvent OnRedo; 452 | 453 | public delegate void OnChangeHistoryIndexEvent(T historyItem); 454 | public event OnChangeHistoryIndexEvent OnChangeHistoryIndex; 455 | 456 | public delegate void OnChangeUndoHistoryEvent(); 457 | public event OnChangeUndoHistoryEvent OnChangeUndoHistory; 458 | 459 | public UndoHistory () { 460 | undoHistory = new List(); 461 | _undoHistoryIndex = -1; 462 | } 463 | 464 | public UndoHistory (int maxHistoryItems) : this () { 465 | this.maxHistoryItems = Mathf.Clamp(maxHistoryItems, 1, int.MaxValue); 466 | } 467 | 468 | public virtual void AddToUndoHistory (T state) { 469 | if(undoHistory.Count > 0 && undoHistory.Count - (undoHistoryIndex + 1) > 0) { 470 | undoHistory.RemoveRange(undoHistoryIndex + 1, undoHistory.Count - (undoHistoryIndex + 1)); 471 | } 472 | 473 | if(undoHistory.Count >= maxHistoryItems) { 474 | undoHistory.RemoveAt (0); 475 | _undoHistoryIndex--; 476 | } 477 | 478 | undoHistory.Add (state); 479 | _undoHistoryIndex++; 480 | 481 | if(OnChangeUndoHistory != null) OnChangeUndoHistory(); 482 | } 483 | 484 | public virtual void Clear () { 485 | undoHistory.Clear(); 486 | _undoHistoryIndex = -1; 487 | if(OnChangeUndoHistory != null) OnChangeUndoHistory(); 488 | } 489 | 490 | public virtual T Undo () { 491 | if(!canUndo) { 492 | if(undoHistory.Count > 0) 493 | return default(T); 494 | } else { 495 | undoHistoryIndex--; 496 | if(OnUndo != null) OnUndo(undoHistory[undoHistoryIndex]); 497 | } 498 | return undoHistory[undoHistoryIndex]; 499 | } 500 | 501 | public virtual T Redo () { 502 | if(!canRedo) { 503 | if(undoHistory.Count > 0) 504 | return default(T); 505 | } else { 506 | undoHistoryIndex++; 507 | if(OnRedo != null) OnRedo(undoHistory[undoHistoryIndex]); 508 | } 509 | return undoHistory[undoHistoryIndex]; 510 | } 511 | 512 | protected virtual void ApplyHistoryItem (T historyItem) {} 513 | } 514 | 515 | public class InkPlayerHistoryItem { 516 | public string inkStateJSON; 517 | public List storyHistory; 518 | 519 | public InkPlayerHistoryItem (string inkStateJSON, List storyHistory) { 520 | this.inkStateJSON = inkStateJSON; 521 | this.storyHistory = storyHistory; 522 | } 523 | } 524 | 525 | public class InkPlayerHistoryContentItem { 526 | public string content; 527 | public bool isChoice = false; 528 | 529 | public InkPlayerHistoryContentItem (string text, bool isChoice = false) { 530 | this.content = text; 531 | this.isChoice = isChoice; 532 | } 533 | } 534 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Player Window/InkPlayerWindow.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: bf969d7f2c98e470c9797844e20b20ee 3 | timeCreated: 1459342679 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Tools.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c467be9178a5427983a7344e352e0be 3 | folderAsset: yes 4 | timeCreated: 1459878675 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Tools/InkEditorUtils.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using UnityEditor; 3 | using System; 4 | using System.Collections; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Text; 8 | using System.Diagnostics; 9 | using UnityEditorInternal; 10 | using Debug = UnityEngine.Debug; 11 | using Ink.Runtime; 12 | using UnityEditor.ProjectWindowCallback; 13 | 14 | namespace Ink.UnityIntegration { 15 | 16 | class CreateInkAssetAction : EndNameEditAction { 17 | public override void Action(int instanceId, string pathName, string resourceFile) { 18 | UnityEngine.Object asset = CreateScriptAssetFromTemplate(pathName, resourceFile); 19 | ProjectWindowUtil.ShowCreatedAsset(asset); 20 | } 21 | 22 | internal static UnityEngine.Object CreateScriptAssetFromTemplate(string pathName, string templateFilePath) { 23 | string fullPath = Path.GetFullPath(pathName); 24 | string text = ""; 25 | if(File.Exists(templateFilePath)) { 26 | StreamReader streamReader = new StreamReader(templateFilePath); 27 | text = streamReader.ReadToEnd(); 28 | streamReader.Close(); 29 | } else { 30 | Debug.LogWarning("Could not find .ink template file at expected path "+templateFilePath+". New file will be empty."); 31 | } 32 | UTF8Encoding encoding = new UTF8Encoding(true, false); 33 | bool append = false; 34 | StreamWriter streamWriter = new StreamWriter(fullPath, append, encoding); 35 | streamWriter.Write(text); 36 | streamWriter.Close(); 37 | AssetDatabase.ImportAsset(pathName); 38 | return AssetDatabase.LoadAssetAtPath(pathName, typeof(DefaultAsset)); 39 | } 40 | } 41 | 42 | public static class InkEditorUtils { 43 | public const string inkFileExtension = ".ink"; 44 | private const string templateFileLocation = "Assets/Plugins/Ink/Template/Template.txt"; 45 | 46 | [MenuItem("Assets/Create/Ink", false, 100)] 47 | public static void CreateNewInkFile () { 48 | string fileName = "New Ink.ink"; 49 | string filePath = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(GetSelectedPathOrFallback(), fileName)); 50 | CreateNewInkFile(filePath); 51 | } 52 | 53 | public static void CreateNewInkFile (string filePath) { 54 | ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, ScriptableObject.CreateInstance(), filePath, InkBrowserIcons.inkFileIcon, templateFileLocation); 55 | } 56 | 57 | private static string GetSelectedPathOrFallback() { 58 | string path = "Assets"; 59 | foreach (UnityEngine.Object obj in Selection.GetFiltered(typeof(UnityEngine.Object), SelectionMode.Assets)) { 60 | path = AssetDatabase.GetAssetPath(obj); 61 | if (!string.IsNullOrEmpty(path) && File.Exists(path)) { 62 | path = Path.GetDirectoryName(path); 63 | break; 64 | } 65 | } 66 | return path; 67 | } 68 | 69 | 70 | 71 | [MenuItem("Help/Ink/About")] 72 | public static void OpenAbout() { 73 | Application.OpenURL("https://github.com/inkle/ink#ink"); 74 | } 75 | 76 | [MenuItem("Help/Ink/API Documentation...")] 77 | public static void OpenWritingDocumentation() { 78 | Application.OpenURL("https://github.com/inkle/ink/blob/master/Documentation/RunningYourInk.md"); 79 | } 80 | 81 | public static TextAsset CreateStoryStateTextFile (string jsonStoryState, string defaultPath = "Assets/Ink", string defaultName = "storyState") { 82 | string name = AssetDatabase.GenerateUniqueAssetPath(Path.Combine(defaultPath, defaultName+".json")).Substring(defaultPath.Length+1); 83 | string fullPathName = EditorUtility.SaveFilePanel("Save Story State", defaultPath, name, "json"); 84 | if(fullPathName == "") 85 | return null; 86 | using (StreamWriter outfile = new StreamWriter(fullPathName)) { 87 | outfile.Write(jsonStoryState); 88 | } 89 | AssetDatabase.ImportAsset(fullPathName.Substring(Application.dataPath.Length-6)); 90 | TextAsset textAsset = AssetDatabase.LoadAssetAtPath(fullPathName.Substring(Application.dataPath.Length-6)); 91 | return textAsset; 92 | } 93 | 94 | public static bool StoryContainsVariables (Story story) { 95 | return story.variablesState.GetEnumerator().MoveNext(); 96 | } 97 | 98 | public static bool CheckStoryIsValid (string storyJSON, out Exception exception) { 99 | try { 100 | new Story(storyJSON); 101 | } catch (Exception ex) { 102 | exception = ex; 103 | return false; 104 | } 105 | exception = null; 106 | return true; 107 | } 108 | 109 | public static bool CheckStoryIsValid (string storyJSON, out Story story) { 110 | try { 111 | story = new Story(storyJSON); 112 | } catch { 113 | story = null; 114 | return false; 115 | } 116 | return true; 117 | } 118 | 119 | public static bool CheckStoryIsValid (string storyJSON, out Exception exception, out Story story) { 120 | try { 121 | story = new Story(storyJSON); 122 | } catch (Exception ex) { 123 | exception = ex; 124 | story = null; 125 | return false; 126 | } 127 | exception = null; 128 | return true; 129 | } 130 | 131 | public static bool CheckStoryStateIsValid (string storyJSON, string storyStateJSON) { 132 | Story story; 133 | if(CheckStoryIsValid(storyJSON, out story)) { 134 | try { 135 | story.state.LoadJson(storyStateJSON); 136 | } catch { 137 | return false; 138 | } 139 | } 140 | return true; 141 | } 142 | 143 | public static string GetInklecateFilePath () { 144 | #if UNITY_EDITOR_WIN 145 | string inklecateName = "inklecate_win.exe"; 146 | #endif 147 | 148 | // Unfortunately inklecate's implementation uses newer features of C# that aren't 149 | // available in the version of mono that ships with Unity, so we can't make use of 150 | // it. This means that we need to compile the mono runtime directly into it, inflating 151 | // the size of the executable quite dramatically :-( Hopefully we can improve that 152 | // when Unity ships with a newer version. 153 | #if UNITY_EDITOR_OSX 154 | string inklecateName = "inklecate_mac"; 155 | #endif 156 | 157 | string[] inklecateDirectories = Directory.GetFiles(Application.dataPath, inklecateName, SearchOption.AllDirectories); 158 | if(inklecateDirectories.Length == 0) { 159 | return null; 160 | } else { 161 | return Path.GetFullPath(inklecateDirectories[0]); 162 | } 163 | } 164 | } 165 | } -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Editor/Tools/InkEditorUtils.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: eeaeb991395be4e9e9ab83197c649ad8 3 | timeCreated: 1459944754 4 | licenseType: Pro 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: [] 8 | executionOrder: 0 9 | icon: {instanceID: 0} 10 | userData: 11 | assetBundleName: 12 | assetBundleVariant: 13 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Extras.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: da9154df703de4360bd91ca5ce4ceb2d 3 | folderAsset: yes 4 | timeCreated: 1460814135 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Extras/Sublime3Syntax.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Extras/Sublime3Syntax.zip -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Extras/Sublime3Syntax.zip.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: db1b6d593e1a4418489b76bb1e8b5ddd 3 | timeCreated: 1460814135 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/InkRuntime.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 09ba8a8c2206e48cfa59f4f51335455e 3 | folderAsset: yes 4 | timeCreated: 1460992356 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/InkRuntime/ink-engine.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/InkRuntime/ink-engine.dll -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/InkRuntime/ink-engine.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 6f9d5c0f76cc248079aa37838dbeb524 3 | timeCreated: 1462536115 4 | licenseType: Pro 5 | PluginImporter: 6 | serializedVersion: 1 7 | iconMap: {} 8 | executionOrder: {} 9 | isPreloaded: 0 10 | platformData: 11 | Any: 12 | enabled: 1 13 | settings: {} 14 | Editor: 15 | enabled: 0 16 | settings: 17 | DefaultValueInitialized: true 18 | WindowsStoreApps: 19 | enabled: 0 20 | settings: 21 | CPU: AnyCPU 22 | userData: 23 | assetBundleName: 24 | assetBundleVariant: 25 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Inklecate.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8cab9ecb645d648e69ebb708c7b38f32 3 | folderAsset: yes 4 | timeCreated: 1459802219 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Inklecate/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Inklecate/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Inklecate/Newtonsoft.Json.dll.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: a71af3e798e0f8348a93e9bda083a3d5 3 | timeCreated: 1460400547 4 | licenseType: Free 5 | PluginImporter: 6 | serializedVersion: 1 7 | iconMap: {} 8 | executionOrder: {} 9 | isPreloaded: 0 10 | platformData: 11 | Android: 12 | enabled: 0 13 | settings: 14 | CPU: AnyCPU 15 | Any: 16 | enabled: 1 17 | settings: {} 18 | Editor: 19 | enabled: 0 20 | settings: 21 | CPU: AnyCPU 22 | DefaultValueInitialized: true 23 | OS: AnyOS 24 | Linux: 25 | enabled: 0 26 | settings: 27 | CPU: x86 28 | Linux64: 29 | enabled: 0 30 | settings: 31 | CPU: x86_64 32 | OSXIntel: 33 | enabled: 0 34 | settings: 35 | CPU: AnyCPU 36 | OSXIntel64: 37 | enabled: 0 38 | settings: 39 | CPU: AnyCPU 40 | Win: 41 | enabled: 0 42 | settings: 43 | CPU: AnyCPU 44 | Win64: 45 | enabled: 0 46 | settings: 47 | CPU: AnyCPU 48 | WindowsStoreApps: 49 | enabled: 0 50 | settings: 51 | CPU: AnyCPU 52 | iOS: 53 | enabled: 0 54 | settings: 55 | CompileFlags: 56 | FrameworkDependencies: 57 | userData: 58 | assetBundleName: 59 | assetBundleVariant: 60 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Inklecate/inklecate_mac: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Inklecate/inklecate_mac -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Inklecate/inklecate_mac.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fcee72ed045ae4564bffdf02252459fe 3 | timeCreated: 1459775627 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Inklecate/inklecate_win.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Plugins/Ink/Inklecate/inklecate_win.exe -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Inklecate/inklecate_win.exe.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: fe57173b56f7c4aa588b0425b097c3d7 3 | timeCreated: 1459775627 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Readme.md: -------------------------------------------------------------------------------- 1 | # ink-Unity integration 2 | 3 | This Unity package allows you to integrate inkle's [ink narrative scripting language](http://www.inklestudios.com/ink) with Unity, and provides tools to help quickly test your stories in-editor. 4 | 5 | Features: 6 | 7 | - **Running ink in game**: Allows usage of JSON-compiled ink files in Unity via the included `InkRuntime` source files (and Json.Net dependency in DLLs). 8 | 9 | - **Auto Compilation**: Instantly creates and updates a JSON story file when a `.ink` is updated. 10 | 11 | - **Ink Player**: Provides a powerful player window for playing and debugging stories. 12 | 13 | - **Inspector tools**: Provides an icon for ink files, and a custom inspector that provides information about a file. 14 | 15 | 16 | ## Getting started 17 | 18 | * Download the [latest Unity package release](https://github.com/inkle/ink-unity-integration/releases), and add to your Unity project. 19 | * Select one of the sample `.ink` stories included in the package. In Unity's Inspector window, you should see a *Play* button. Click it to open the **ink player** window, useful for playing (previewing) ink stories. 20 | 21 | For more information on **ink**, see [the documentation in the main ink repo](https://github.com/inkle/ink). For convenience, the package also creates an (**Ink > Help**) menu option. 22 | 23 | ## Using ink in game your game. 24 | 25 | The **ink player** is the core feature of this package; the minimal requirements to actually run a compiled JSON story file are files in the `InkRuntime` folder and `Newtonsoft.Json.dll` library. 26 | 27 | ## Ink Player 28 | 29 | The Ink Player (**Ink > Player Window**) allows you to play stories in an editor window, and provides functionality to edit variables on the fly, save and load states, and divert. 30 | 31 | **Playing a story**: You can play stories by clicking the "Play" button on the inspector of a master ink file, or by manually choosing a compiled ink story TextAsset in the Ink Player window and clicking "Play". You can then use the choice panel to advance the story, and the undo/redo buttons to rewind and test different paths. 32 | 33 | **Saving and restoring story states**: You can save and restore the current state of the story using the save and load buttons in the Story State panel. States are stored as .json files. 34 | 35 | **Diverting to a stitch**: To instantly move to a specific stitch in a story, you can manually enter the path of a stitch in the Divert panel. You need to use the full path of a stitch, including the knot that contains it. For example: "the_orient_express.in_first_class". 36 | 37 | **Editing variables**: The variables panel allows you to view and edit all the story variables. 38 | 39 | ## Automatic compilation 40 | 41 | Ink files must be compiled to JSON before they can be used in-game. 42 | 43 | This package provides tools to automate this process when a .ink file is edited. 44 | 45 | **Disabling auto-compilation**: This option exists via **Edit > Project Settings > Ink** 46 | 47 | **Manual compilation**: If you have disabled auto-compilation, you can manually compile ink via the inspector of an ink file, using the functions in the InkCompiler class, or rebuild all the ink files using the **Assets > Recompile Ink** menu item (this can be a bit slow for larger projects) 48 | 49 | ## Inspector Tools 50 | 51 | This package also replaces the icon for ink files to make them easier to spot, and populates the inspector for a selected ink file. 52 | 53 | **The Inspector**: To replace the inspector for ink files, we've created a system that allows you to provide a custom inspector for any file. If this conflicts with existing behaviour in your project, you can delete the Ink Inspector folder altogether. 54 | 55 | ## Updating Ink manually 56 | 57 | The ink git repo is updated far more frequently than the asset store package. 58 | 59 | If you're interested in keeping up-to-date with cutting edge features, you can download the [latest releases from the GitHub repo](https://github.com/inkle/ink/releases). 60 | 61 | ## Customisation 62 | 63 | The inklecate DLLs used to compile ink are quite large files. You may safely delete the DLLs not corresponding to your current OS, or both of them if not using the compiler. 64 | 65 | ## FAQ 66 | 67 | * Is the Linux Unity Editor supported? 68 | 69 | *We haven't implemented it, although it should be easy enough by running inklecate.exe with mono. Take a look at `InkCompiler.cs` if you want to add it.* 70 | 71 | * I'm getting this error: 72 | 73 | Internal compiler error. See the console log for more information. output was: 74 | Unhandled Exception: System.TypeLoadException: Could not load type 'Newtonsoft.Json.Linq.JContainer' from assembly 'Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed'.` 75 | 76 | You need to change your API compatibility level from .NET 2.0 subset to .NET 2.0. 77 | 78 | * Other errors? 79 | 80 | *First off, recompile the ink library using **Assets > Recompile Ink**. 81 | 82 | If that doesn't work, [report an issue via github](https://github.com/inkle/ink/issues) or [ask us and the comminity for help via hipchat](https://www.hipchat.com/gkq2pSLqU) -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Readme.md.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 85254203c0564478cab552275fc29e5f 3 | timeCreated: 1460473537 4 | licenseType: Pro 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Template.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 56a16e3814a854a1fb67eb0ea4a96df3 3 | folderAsset: yes 4 | timeCreated: 1462289629 5 | licenseType: Pro 6 | DefaultImporter: 7 | userData: 8 | assetBundleName: 9 | assetBundleVariant: 10 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Template/Template.txt: -------------------------------------------------------------------------------- 1 | Hello world! 2 | * Hello back! 3 | Nice to hear from you! -------------------------------------------------------------------------------- /Blot-Sample/Assets/Plugins/Ink/Template/Template.txt.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 0487be7f554cd45a6916f94d2b6baf50 3 | timeCreated: 1462291397 4 | licenseType: Pro 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Script.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using Ink.Runtime; 4 | 5 | public class Script : MonoBehaviour { 6 | [SerializeField] 7 | private TextAsset inkAsset; 8 | private Story _inkStory; 9 | private bool storyNeeded; 10 | 11 | [SerializeField] 12 | private Canvas canvas; 13 | [SerializeField] 14 | private float elementPadding; 15 | 16 | /* UI Prefabs */ 17 | [SerializeField] 18 | private UnityEngine.UI.Text text; 19 | [SerializeField] 20 | private UnityEngine.UI.Button button; 21 | 22 | 23 | void Awake () { 24 | _inkStory = new Story (inkAsset.text); 25 | storyNeeded = true; 26 | } 27 | 28 | // Update is called once per frame 29 | void Update () { 30 | if (storyNeeded == true) { 31 | RemoveChildren (); 32 | 33 | float offset = 0; 34 | while (_inkStory.canContinue) { 35 | UnityEngine.UI.Text storyText = Instantiate (text) as UnityEngine.UI.Text; 36 | storyText.text = _inkStory.Continue (); 37 | storyText.transform.SetParent (canvas.transform, false); 38 | storyText.transform.Translate (new Vector2 (0, offset)); 39 | offset -= (storyText.fontSize + elementPadding); 40 | } 41 | 42 | if(_inkStory.currentChoices.Count > 0) { 43 | for (int ii = 0; ii < _inkStory.currentChoices.Count; ++ii) { 44 | UnityEngine.UI.Button choice = Instantiate (button) as UnityEngine.UI.Button; 45 | choice.transform.SetParent (canvas.transform, false); 46 | choice.transform.Translate (new Vector2 (0, offset)); 47 | 48 | UnityEngine.UI.Text choiceText = choice.GetComponentInChildren (); 49 | choiceText.text = _inkStory.currentChoices [ii].text; 50 | 51 | UnityEngine.UI.HorizontalLayoutGroup layoutGroup = choice.GetComponent (); 52 | 53 | int choiceId = ii; 54 | choice.onClick.AddListener(delegate{ChoiceSelected(choiceId);}); 55 | 56 | offset -= (choiceText.fontSize + layoutGroup.padding.top + layoutGroup.padding.bottom + elementPadding); 57 | } 58 | } 59 | 60 | storyNeeded = false; 61 | } 62 | } 63 | 64 | void RemoveChildren () { 65 | int childCount = canvas.transform.childCount; 66 | for (int i = childCount - 1; i >= 0; --i) { 67 | GameObject.Destroy (canvas.transform.GetChild (i).gameObject); 68 | } 69 | } 70 | 71 | public void ChoiceSelected (int id) { 72 | _inkStory.ChooseChoiceIndex (id); 73 | storyNeeded = true; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Script.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 269672950a82b4085bb08ae32ddb727d 3 | timeCreated: 1457910448 4 | licenseType: Free 5 | MonoImporter: 6 | serializedVersion: 2 7 | defaultReferences: 8 | - inkAsset: {fileID: 4900000, guid: 2c831943b203449bcac0a7c83fde2c64, type: 3} 9 | executionOrder: 0 10 | icon: {instanceID: 0} 11 | userData: 12 | assetBundleName: 13 | assetBundleVariant: 14 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Story.ink: -------------------------------------------------------------------------------- 1 | - I looked at Monsieur Fogg 2 | * ... and I could contain myself no longer. 3 | 'What is the purpose of our journey, Monsieur?' 4 | 'A wager,' he replied. 5 | * * 'A wager!'[] I returned. 6 | He nodded. 7 | * * * 'But surely that is foolishness!' 8 | * * * 'A most serious matter then!' 9 | - - - He nodded again. 10 | * * * 'But can we win?' 11 | 'That is what we will endeavour to find out,' he answered. 12 | * * * 'A modest wager, I trust?' 13 | 'Twenty thousand pounds,' he replied, quite flatly. 14 | * * * I asked nothing further of him then[.], and after a final, polite cough, he offered nothing more to me. <> 15 | * * 'Ah[.'],' I replied, uncertain what I thought. 16 | - - After that, <> 17 | * ... but I said nothing[] and <> 18 | - we passed the day in silence. 19 | - -> END 20 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Story.ink.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: afa751c66278a4261a93b7e06dab71b0 3 | timeCreated: 1464240670 4 | licenseType: Free 5 | DefaultImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Story.json: -------------------------------------------------------------------------------- 1 | {"inkVersion":11,"root":[[["^I looked at Monsieur Fogg","\n",["ev","str",{"f()":".^.s"},"/str","/ev",{"*":".^.c","flg":18},{"s":["^... and I could contain myself no longer.",null],"c":[{"f()":".^.^.s"},"\n","^'What is the purpose of our journey, Monsieur?'","\n","^'A wager,' he replied.","\n",[["ev","str",{"f()":".^.s"},"/str","/ev",{"*":".^.c","flg":18},{"s":["^'A wager!'",null],"c":[{"f()":".^.^.s"},"^ I returned.","\n","\n","^He nodded.","\n",[["ev","str",{"f()":".^.s"},"/str","/ev",{"*":".^.c","flg":18},{"s":["^'But surely that is foolishness!'",null],"c":[{"f()":".^.^.s"},"\n",{"->":".^.^.^.g-0"},{"#f":7}]}],["ev","str",{"f()":".^.s"},"/str","/ev",{"*":".^.c","flg":18},{"s":["^'A most serious matter then!'",null],"c":[{"f()":".^.^.s"},"\n",{"->":".^.^.^.g-0"},{"#f":7}]}],{"g-0":["^He nodded again.","\n",["ev","str",{"f()":".^.s"},"/str","/ev",{"*":".^.c","flg":18},{"s":["^'But can we win?'",null],"c":[{"f()":".^.^.s"},"\n","^'That is what we will endeavour to find out,' he answered.","\n",{"->":"0.g-0.2.c.6.g-0"},{"#f":7}]}],["ev","str",{"f()":".^.s"},"/str","/ev",{"*":".^.c","flg":18},{"s":["^'A modest wager, I trust?'",null],"c":[{"f()":".^.^.s"},"\n","^'Twenty thousand pounds,' he replied, quite flatly.","\n",{"->":"0.g-0.2.c.6.g-0"},{"#f":7}]}],["ev","str",{"f()":".^.s"},"/str","str","^.","/str","/ev",{"*":".^.c","flg":22},{"s":["^I asked nothing further of him then",null],"c":[{"f()":".^.^.s"},"^, and after a final, polite cough, he offered nothing more to me. ","<>","\n","\n",{"->":"0.g-0.2.c.6.g-0"},{"#f":7}]}],{"#f":7}]}],{"#f":7}]}],["ev","str",{"f()":".^.s"},"/str","str","^.'","/str","/ev",{"*":".^.c","flg":22},{"s":["^'Ah",null],"c":[{"f()":".^.^.s"},"^,' I replied, uncertain what I thought.","\n","\n",{"->":".^.^.^.g-0"},{"#f":7}]}],{"g-0":["^After that, ","<>","\n",{"->":"0.g-0.g-1"},{"#f":7}]}],{"#f":7}]}],["ev","str",{"f()":".^.s"},"/str","/ev",{"*":".^.c","flg":18},{"s":["^... but I said nothing",null],"c":[{"f()":".^.^.s"},"^ and ","<>","\n","\n",{"->":"0.g-0.g-1"},{"#f":7}]}],{"g-1":["^we passed the day in silence.","\n",["end",{"#f":7,"#n":"g-2"}],{"#f":7}],"#f":7,"#n":"g-0"}],null],"done",{"#f":3}]} -------------------------------------------------------------------------------- /Blot-Sample/Assets/Story.json.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: c644a1951809f4a1b8016e88abca6429 3 | timeCreated: 1464240670 4 | licenseType: Free 5 | TextScriptImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/Assets/Text.prefab: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/Assets/Text.prefab -------------------------------------------------------------------------------- /Blot-Sample/Assets/Text.prefab.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1aab6ef84d87c416f8f0b3e1ef3d77d2 3 | timeCreated: 1457983120 4 | licenseType: Free 5 | NativeFormatImporter: 6 | userData: 7 | assetBundleName: 8 | assetBundleVariant: 9 | -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/ClusterInputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/ClusterInputManager.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/NavMeshAreas.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/NavMeshAreas.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/ProjectVersion.txt: -------------------------------------------------------------------------------- 1 | m_EditorVersion: 5.3.2f1 2 | m_StandardAssetsVersion: 0 3 | -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/UnityAdsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/UnityAdsSettings.asset -------------------------------------------------------------------------------- /Blot-Sample/ProjectSettings/UnityConnectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/abroder/blot/133ea7547129b0d85be9d18a6766041a8608f06b/Blot-Sample/ProjectSettings/UnityConnectSettings.asset -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # blot 2 | 3 | A minimal UI in Unity for [inkle](http://www.inklestudios.com/)'s 4 | [Ink language](https://github.com/inkle/ink). Drag-and-drop the script into an 5 | existing Unity project and give it a Canvas and some UI prefabs to work with; 6 | it will automatically handle the story flow and update the options on screen. 7 | 8 | It's (intentionally) very low-fi - I just wanted the bare minimum I could get 9 | by with and still have a functioning UI - which means text will go off the 10 | screen and isn't laid out sensibly. Hopefully it's enough for people to get 11 | started playing with Ink, though! 12 | 13 | If you don't have any interest in fiddling around with the UI elements 14 | themselves, then you should be able to just load Blot-Sample and replace 15 | `story.ink.json` with another compiled `.json` story from Ink; for instructions 16 | on how to do that, check the Ink repo. 17 | 18 | ![Blot-Demo.gif](https://bitbucket.org/repo/BEXdjG/images/330245972-Blot-Demo.gif) -------------------------------------------------------------------------------- /Script.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | using Ink.Runtime; 4 | 5 | public class Script : MonoBehaviour { 6 | [SerializeField] 7 | private TextAsset inkAsset; 8 | private Story _inkStory; 9 | private bool storyNeeded; 10 | 11 | [SerializeField] 12 | private Canvas canvas; 13 | [SerializeField] 14 | private float elementPadding; 15 | 16 | /* UI Prefabs */ 17 | [SerializeField] 18 | private UnityEngine.UI.Text text; 19 | [SerializeField] 20 | private UnityEngine.UI.Button button; 21 | 22 | 23 | void Awake () { 24 | _inkStory = new Story (inkAsset.text); 25 | storyNeeded = true; 26 | } 27 | 28 | // Update is called once per frame 29 | void Update () { 30 | if (storyNeeded == true) { 31 | RemoveChildren (); 32 | 33 | float offset = 0; 34 | while (_inkStory.canContinue) { 35 | UnityEngine.UI.Text storyText = Instantiate (text) as UnityEngine.UI.Text; 36 | storyText.text = _inkStory.Continue (); 37 | storyText.transform.SetParent (canvas.transform, false); 38 | storyText.transform.Translate (new Vector2 (0, offset)); 39 | offset -= (storyText.fontSize + elementPadding); 40 | } 41 | 42 | if(_inkStory.currentChoices.Count > 0) { 43 | for (int ii = 0; ii < _inkStory.currentChoices.Count; ++ii) { 44 | UnityEngine.UI.Button choice = Instantiate (button) as UnityEngine.UI.Button; 45 | choice.transform.SetParent (canvas.transform, false); 46 | choice.transform.Translate (new Vector2 (0, offset)); 47 | 48 | UnityEngine.UI.Text choiceText = choice.GetComponentInChildren (); 49 | choiceText.text = _inkStory.currentChoices [ii].text; 50 | 51 | UnityEngine.UI.HorizontalLayoutGroup layoutGroup = choice.GetComponent (); 52 | 53 | int choiceId = ii; 54 | choice.onClick.AddListener(delegate{ChoiceSelected(choiceId);}); 55 | 56 | offset -= (choiceText.fontSize + layoutGroup.padding.top + layoutGroup.padding.bottom + elementPadding); 57 | } 58 | } 59 | 60 | storyNeeded = false; 61 | } 62 | } 63 | 64 | void RemoveChildren () { 65 | int childCount = canvas.transform.childCount; 66 | for (int i = childCount - 1; i >= 0; --i) { 67 | GameObject.Destroy (canvas.transform.GetChild (i).gameObject); 68 | } 69 | } 70 | 71 | public void ChoiceSelected (int id) { 72 | _inkStory.ChooseChoiceIndex (id); 73 | storyNeeded = true; 74 | } 75 | } 76 | --------------------------------------------------------------------------------