### Start Local Development Server Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/misc/handbook-contribution.md Commands to set up the development environment and start the local documentation server. Ensure you have Node.js and Corepack installed. ```bash cd ``` ```bash corepack enable ``` ```bash yarn ``` ```bash yarn start:en ``` ```bash yarn start:zh ``` ```bash yarn start:all ``` -------------------------------- ### Example Data Input Declaration Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/watchers.md An example of a data input that allows the user to select a file from a specific directory. This demonstrates how data inputs are declared and can be used with watchers. ```csharp [DataInput] [AutoCompleteList(nameof(AutoCompletePluginFiles), forceSelection: true)] public string SelectedFile; protected async UniTask AutoCompletePluginFiles() { return AutoCompleteList.Single(Context.PersistentDataManager.GetFileEntries("MyPluginFiles").Select(it => new AutoCompleteEntry { label = it.fileName, value = it.path })); } ``` -------------------------------- ### Example Asset Data Input Declaration Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/watchers.md An example of an asset data input, specifically a CharacterAsset. This demonstrates how to declare an asset as a data input, which can then be watched. ```csharp [DataInput] public CharacterAsset Character; ``` -------------------------------- ### Markdown Example Node Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/ports-and-triggers.md A comprehensive example node demonstrating various Markdown text block styles, dynamic content updates using `WatchAll` and `OnUpdate`, and embedded HTML. ```csharp using UnityEngine; using Warudo.Core.Attributes; using Warudo.Core.Graphs; [NodeType( Id = "Markdown-Example-Node", Title = "Markdown Example Node", Category = "Examples")] public class MarkdownExampleNode : Node { [Markdown] public string Markdown = "### Title\n\nHello1\nHello2 \nHello3\n\n

Hello4

\n\n- list1\n- list2\n\n**bold** *italic* `code`"; [Markdown(Primary = true)] public string MarkdownPrimary = "### Title\n\nHello1\nHello2 \nHello3\n\n

Hello4

\n\n- list1\n- list2\n\n**bold** *italic* `code`"; [DataInput] [FloatSlider(0, 1)] public float A = 0.5f; [DataInput] [FloatSlider(0, 1)] public float B = 0.5f; [Markdown(Primary = true)] public string MarkdownDynamicPrimary = "A: 0.5 \nB: 0.5"; protected override void OnCreate() { base.OnCreate(); WatchAll(new[] { nameof(A), nameof(B), }, () => { MarkdownDynamicPrimary = "A: " + A.ToString() + " \nB: " + B.ToString(); BroadcastDataInput(nameof(MarkdownDynamicPrimary)); }); } [Markdown] public string MarkdownDynamic = ""; public override void OnUpdate() { base.OnUpdate(); string time = "RealTime: " + Time.time.ToString(); MarkdownDynamic = time; BroadcastDataInput(nameof(MarkdownDynamic)); } } ``` -------------------------------- ### Binary Math Functions Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/nodes/math-expression.md These functions require two input values. Examples include minimum, maximum, power, and logarithm. ```csharp min => Mathf.Min(float, float) ``` ```csharp max => Mathf.Max(float, float) ``` ```csharp pow => Mathf.Pow(float, float) ``` ```csharp log => Mathf.Log(float, float) ``` -------------------------------- ### Generating Blueprints Programmatically in C# Source: https://context7.com/hakuyalabs/warudo-docs/llms.txt Construct blueprints (graphs) entirely in code, useful for setup wizards or plugin initialization. This example shows creating a graph that displays a toast on every frame update. ```csharp using Warudo.Core.Graphs; // Build a blueprint that shows a toast on every frame update var graph = new Graph { Name = "My Auto Blueprint", Enabled = true }; var onUpdateNode = graph.AddNode(); var toastNode = graph.AddNode(); toa stNode.Header = "Live!"; toa stNode.Caption = "Blueprint running every frame."; // Wire Exit of OnUpdate → Enter of ShowToast graph.AddFlowConnection( onUpdateNode, nameof(onUpdateNode.Exit), toastNode, nameof(toastNode.Enter) ); Context.OpenedScene.AddGraph(graph); Context.Service.BroadcastOpenedScene(); ``` -------------------------------- ### Add Warudo SDK via Git URL Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/modding/sdk-installation.md Use this Git URL in Unity's Package Manager to install the SDK. Ensure your Unity project has '.NET Framework' API Compatibility Level and 'Assembly Version Validation' unchecked. ```bash https://github.com/HakuyaLabs/Warudo-Mod-Tool.git#0.14.3.10 ``` -------------------------------- ### Get and Use Integer Variable for Scaling Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/tutorials/squashing.md Retrieve an integer variable's value using Get Integer Variable and use it to scale a Vector3. This example shows how to make a bone grow. ```blueprint Set Character Bone Scale Get Integer Variable Scale Vector3 ``` -------------------------------- ### Listen to Asset Selection State Change Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/assets.md This example shows how to listen to the OnSelectedStateChange event to perform actions, such as showing or hiding a model, when the asset's selection state changes in the editor. ```csharp public override void OnCreate() { base.OnCreate(); OnSelectedStateChange.AddListener(selected => { if (selected) { // Show the model } else { // Hide the model } }); } ``` -------------------------------- ### Custom Type Converter Implementation Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/nodes.md Provides an example of implementing a custom type converter by inheriting from DataConverter. This allows Warudo to automatically convert data types between connected nodes. ```csharp public class MyFloatToIntConverter : DataConverter { public override int Convert(float data) => (int) data; } ``` -------------------------------- ### Play Sound Effect on Microphone Toggle Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/tutorials/karaoke.md Integrate a 'Play Sound' node to play a 'whoosh' sound effect when the microphone is toggled. This setup plays the sound every time the hotkey is pressed. ```blueprint On Keystroke Pressed -> Toggle Asset Enabled (Asset: Microphone Prop, Enabled: True) -> Play Character Idle Animation (Animation: "010_0970") -> Play Sound (Sound Source: Whoosh Sound Effect) ``` -------------------------------- ### Create a 'Hello World' Node in C# Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/creating-your-first-script.md This C# code defines a custom node for Warudo. It uses attributes to register the node and its entry point, and a service to display a message when the node is triggered. ```csharp using Warudo.Core; using Warudo.Core.Attributes; using Warudo.Core.Graphs; [NodeType(Id = "c76b2fef-a7e7-4299-b942-e0b6dec52660", Title = "Hello World")] public class HelloWorldNode : Node { [FlowInput] public Continuation Enter() { Context.Service.PromptMessage("Hello World!", "This node is working!"); return Exit; } [FlowOutput] public Continuation Exit; } ``` -------------------------------- ### Update 'Hello World' Node with Data Input and Hot Reload Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/creating-your-first-script.md This C# code updates the 'Hello World' node to include an integer data input. It demonstrates how to integrate user-defined data into the node's functionality and how Warudo's hot-reloading updates the node in real-time. ```csharp using Warudo.Core; using Warudo.Core.Attributes; using Warudo.Core.Graphs; [NodeType(Id = "c76b2fef-a7e7-4299-b942-e0b6dec52660", Title = "Hello World")] public class HelloWorldNode : Node { // New code below [DataInput] [IntegerSlider(1, 100)] public int LuckyNumber = 42; // New code above [FlowInput] public Continuation Enter() { // Changed code below Context.Service.PromptMessage("Hello World!", "This node is working! My lucky number: " + LuckyNumber); return Exit; } [FlowOutput] public Continuation Exit; } ``` -------------------------------- ### Accessing Scene Data Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/scene.md Retrieve the currently opened scene and access its assets and blueprints. You can get all assets or filter them by type. ```csharp var scene = Context.OpenedScene; var assets = scene.GetAssets(); var characterAssets = scene.GetAssets(); var blueprints = scene.GetGraphs(); ``` -------------------------------- ### Load Unity Prefab in Plugin Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/plugins.md Use ModHost.LoadAsset to load a prefab from your plugin's mod folder. Ensure the asset path is correct. ```csharp var prefab = ModHost.LoadAsset("Assets/MyModFolder/MyPrefab.prefab"); ``` -------------------------------- ### Spinning Character and Chair Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/tutorials/spinning.md This setup rotates both the character and the chair simultaneously. Ensure the chair prop has a similar default orientation to the character to avoid visual discrepancies. ```blueprint On Update Set Asset Rotation (Character) Set Asset Rotation (Chair) ``` -------------------------------- ### Register Custom Resource Provider and Resolver Source: https://context7.com/hakuyalabs/warudo-docs/llms.txt Implement IResourceProvider to list custom assets and IResourceUriResolver to load them. Register both in OnCreate. ```csharp // Resource provider: lists available resources for a given query type public class PrimitivePropResourceProvider : IResourceProvider { public string ResourceProviderName => "Primitives"; public List ProvideResources(string query) { if (query != "Prop") return null; return new List { new Resource { category = "Primitives", label = "Cube", uri = new Uri("prop://primitives/cube") }, new Resource { category = "Primitives", label = "Sphere", uri = new Uri("prop://primitives/sphere") } }; } } // URI resolver: loads the actual Unity object from a resource URI public class PrimitivePropUriResolver : IResourceUriResolver { public object Resolve(Uri uri) { if (uri.Scheme != "prop" || uri.Authority != "primitives") return null; return uri.LocalPath.TrimStart('/') switch { "cube" => GameObject.CreatePrimitive(PrimitiveType.Cube), "sphere" => GameObject.CreatePrimitive(PrimitiveType.Sphere), _ => throw new Exception("Unknown primitive: " + uri) }; } } // Register both in the plugin protected override void OnCreate() { base.OnCreate(); Context.ResourceManager.RegisterProvider(new PrimitivePropResourceProvider(), this); Context.ResourceManager.RegisterUriResolver(new PrimitivePropUriResolver(), this); } ``` -------------------------------- ### Combine Conditions with Boolean OR Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/tutorials/dance.md Combine multiple conditions using Boolean OR. This example triggers an action if a Twitch redeem costs 500 points or more OR if it comes from the user 'hakuyatira'. ```blueprint Integer Greater Than Or Equal Boolean OR Twitch redeem name User 'hakuyatira' ``` -------------------------------- ### Saving and Loading Plugin Data Source: https://context7.com/hakuyalabs/warudo-docs/llms.txt Use [DataInput] for automatic serialization, Context.PersistentDataManager for file I/O, and ModHost.LoadAsset for bundled assets. ```csharp // Serialized automatically with the scene (hidden from user) [DataInput] [Hidden] public string SerializedState; // e.g., store JSON here // Persist JSON-serializable state MyState state = new MyState { Count = 42 }; SerializedState = JsonConvert.SerializeObject(state); var loaded = JsonConvert.DeserializeObject(SerializedState); // Read/write arbitrary files in the data folder (async) var bytes = await Context.PersistentDataManager.ReadFileBytesAsync("MyPlugin/config.json"); await Context.PersistentDataManager.WriteFileBytesAsync("MyPlugin/config.json", bytes); // Load a text asset bundled inside the plugin mod folder var json = ModHost.LoadAsset("Assets/MyModFolder/Animations.json"); // Load a prefab from the mod folder var prefab = ModHost.LoadAsset("Assets/MyModFolder/Effects/Explosion.prefab"); prefab.SetActive(false); // Keep it dormant until instantiated ``` -------------------------------- ### Combine Conditions with Boolean AND Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/tutorials/dance.md Combine multiple conditions using Boolean AND. This example checks if a Twitch redeem name is 'DJ' AND if the redeemer's name contains 'bot'. ```blueprint String Equal Boolean AND Twitch redeem name Redeemed user name contains 'bot' ``` -------------------------------- ### Define a Custom Node Type Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/nodes.md Inherit from the Node class and apply the NodeType attribute to define a new node type. Ensure a unique GUID is provided for the Id. ```csharp using Warudo.Core.Attributes; using Warudo.Core.Graphs; [NodeType( Id = "c76b2fef-a7e7-4299-b942-e0b6dec52660", Title = "Hello World", Category = "CATEGORY_DEBUG", Width = 1f )] public class HelloWorldNode : Node { // Node implementation } ``` -------------------------------- ### Programmatically Creating Structured Data Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/structured-data.md Demonstrates how to create new structured data instances using `StructuredData.Create()` and populate them with data. It also shows how to add new instances to an existing data input. ```APIDOC ## Programmatically Creating Structured Data To manually populate the structured data array, or to add structured data elements programmatically, use the `StructuredData.Create()` method: ```csharp public override void OnCreate() { base.OnCreate(); // Create a new structured data instance var mySampleData = StructuredData.Create(); mySampleData.Position = new Vector3(1, 2, 3); // Or equivalently // var mySampleData = StructuredData.Create(sd => sd.Position = new Vector3(1, 2, 3)); SetDataInput(nameof(MyTransforms), new [] { mySampleData }, broadcast: true); } [Trigger] public void AddNewRandomTransform() { var newTransform = StructuredData.Create(); newTransform.Position = new Vector3(Random.value, Random.value, Random.value); newTransform.Rotation = new Vector3(Random.value, Random.value, Random.value); newTransform.Scale = new Vector3(Random.value, Random.value, Random.value); var newTransforms = new List(MyTransforms); newTransforms.Add(newTransform); SetDataInput(nameof(MyTransforms), newTransforms.ToArray(), broadcast: true); } ``` :::caution Do not use `new MyTransformData()` to create structured data instances. Always use `StructuredData.Create()` to ensure proper entity initialization. ::: ``` -------------------------------- ### Define a Custom Asset Type Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/assets.md Create a new asset type by inheriting from `Asset` and applying the `[AssetType]` attribute. Ensure a unique GUID is generated for the `Id` parameter. ```csharp [AssetType( Id = "c6500f41-45be-4cbe-9a13-37b5ff60d057", Title = "Hello World", Category = "CATEGORY_DEBUG", Singleton = false )] public class HelloWorldAsset : Asset { // Asset implementation } ``` -------------------------------- ### Create and Set Structured Data Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/structured-data.md Use `StructuredData.Create()` to instantiate structured data. This method ensures proper initialization. You can set properties directly or use a lambda expression for initialization. Use `SetDataInput` to update the data, with `broadcast: true` to notify other systems. ```csharp public override void OnCreate() { base.OnCreate(); // Create a new structured data instance var mySampleData = StructuredData.Create(); mySampleData.Position = new Vector3(1, 2, 3); // Or equivalently // var mySampleData = StructuredData.Create(sd => sd.Position = new Vector3(1, 2, 3)); SetDataInput(nameof(MyTransforms), new [] { mySampleData }, broadcast: true); } ``` ```csharp [Trigger] public void AddNewRandomTransform() { var newTransform = StructuredData.Create(); newTransform.Position = new Vector3(Random.value, Random.value, Random.value); newTransform.Rotation = new Vector3(Random.value, Random.value, Random.value); newTransform.Scale = new Vector3(Random.value, Random.value, Random.value); var newTransforms = new List(MyTransforms); newTransforms.Add(newTransform); SetDataInput(nameof(MyTransforms), newTransforms.ToArray(), broadcast: true); } ``` -------------------------------- ### Locate Warudo Data Folder Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/tutorials/data-folder.md This shows the typical file path for the Warudo data folder on a Steam installation. Access it via the 'Open data folder' menu option. ```plaintext \steamapps\common\Warudo\Warudo_Data\StreamingAssets ``` -------------------------------- ### Implementing Localization in Plugins Source: https://context7.com/hakuyalabs/warudo-docs/llms.txt Store JSON localization files in the 'Localizations' subfolder and use the .Localized() extension method. Strings can also be set programmatically. ```json // Localizations/strings.json inside the plugin mod folder: // { // "en": { "GREETING": "Hello, VTuber!" }, // "zh_CN": { "GREETING": "你好,虚拟主播!" }, // "ja": { "GREETING": "こんにちは、VTuberさん!" } // } ``` ```csharp // Retrieve localized string at runtime (falls back to "en" if key missing) string greeting = "GREETING".Localized(); // → "Hello, VTuber!" in English // Add strings programmatically (useful in Playground where there is no mod folder) Context.LocalizationManager.SetLocalizedString("GREETING", "en", "Hello, VTuber!"); Context.LocalizationManager.SetLocalizedString("GREETING", "zh_CN", "你好,虚拟主播!"); Context.LocalizationManager.SetLocalizedString("GREETING", "ja", "こんにちは、VTuberさん!"); // HumanBodyBones also supports .Localized() string boneName = HumanBodyBones.Head.Localized(); // "Head" / "头" / "頭" ``` -------------------------------- ### Randomize Dance Animation Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/tutorials/dance.md Use Get Random Character Animation to select a random dance from the Character Animations list when the Play One Shot Overlay Animation node is triggered. ```blueprint Play One Shot Overlay Animation Get Random Character Animation ``` -------------------------------- ### Structured Data Input Dialog Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/structured-data.md Illustrates how to create and display a structured data input dialog using `Context.Service.PromptStructuredDataInput()`. This allows users to input data through a user-friendly interface. ```APIDOC ## Structured Data Input Dialog {#input} A very common use case for structured data is to allow the user to input data in a dialog. Remember the onboarding assistant? Every popup window that showed up during the onboarding process was actually a structured data! To create a structured data input dialog, you simply define a structured data type and call `Context.Service.PromptStructuredDataInput`: ```csharp [Trigger] public async void PromptUserInput() { var sd = await Context.Service.PromptStructuredDataInput("Customize Your Transform"); if (sd == null) return; // The user clicked cancel Context.Service.Toast(ToastSeverity.Success, "Thank you for your input!", "Your new transform is: " + sd.GetHeader()); SetDataInput(nameof(MyTransform), sd, broadcast: true); } ``` When combined with `[Markdown(primary: true)]` on string data inputs and `[CardSelect]` on enum data inputs, plus some clever use of `[HiddenIf]`, you will be able to create great-looking and user-friendly dialogs in your plugins! ``` -------------------------------- ### Generate Blueprint with Nodes by Code Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/generating-blueprints.md Use C# code to create a new blueprint with 'On Update' and 'Show Toast' nodes, connecting them to display a message each frame. The blueprint is automatically enabled and added to the current scene. ```csharp var graph = new Graph { Name = "My Awesome Blueprint", Enabled = true // Enable the blueprint by default }; // Add two nodes: On Update and Show Toast var onUpdateNode = graph.AddNode(); var toastNode = graph.AddNode(); toa stNode.Header = "Hey!"; toastNode.Caption = "This is a toast message!"; // Connect the nodes so that the toast is shown each frame graph.AddFlowConnection(onUpdateNode, nameof(onUpdateNode.Exit), toastNode, nameof(toastNode.Enter)); // Add the graph to the opened scene Context.OpenedScene.AddGraph(graph); // Send the updated scene to the editor Context.Service.BroadcastOpenedScene(); ``` -------------------------------- ### Plugin Localization JSON Structure Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/localization.md Structure your plugin's localization data in a JSON file within the mod's `Localizations` directory. This example shows how to define strings for English, Simplified Chinese, and Japanese. ```json { "en": { "MY_STRING": "My String" }, "zh_CN": { "MY_STRING": "我的字符串" }, "ja": { "MY_STRING": "私の文字列" } } ``` -------------------------------- ### Create and Destroy GameObject Asset Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/assets.md This snippet demonstrates creating a primitive cube GameObject when the asset is created and destroying it when the asset is destroyed. It requires manual GameObject management. ```csharp private GameObject gameObject; public override void OnCreate() { base.OnCreate(); gameObject = GameObject.CreatePrimitive(PrimitiveType.Cube); } public override void OnDestroy() { base.OnDestroy(); Object.Destroy(gameObject); } ``` -------------------------------- ### Get Character BlendShape Value Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/tutorials/balloon.md Retrieves the current value of a character's blendshape. This is useful for detecting facial expressions or specific movements. Ensure your character model supports the blendshape you are trying to access. ```Warudo Blueprint Get Character BlendShape BlendShape: cheekPuff ``` -------------------------------- ### Instantiating Scene Elements Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/scene.md Add new assets or nodes to the scene. Assets can be instantiated by type or by their type ID. Nodes can be added to existing blueprints, also by type or type ID. ```csharp var newCharacterAsset = scene.AddAsset(); // Instantiate a new character asset var newCharacterAssetByTypeId = scene.AddAsset("726ab674-a550-474e-8b92-66526a5ad55e"); // Instantiate a new character asset by type ID var blueprint = scene.GetGraphs().Values.First(); // Get the first blueprint in the scene var newNode = blueprint.AddNode(); // Instantiate a new node var newNodeByTypeId = blueprint.AddNode("e931f780-e41e-40ce-96d0-a4d47ca64853"); // Instantiate a new node by type ID ``` ```csharp Context.Service.BroadcastOpenedScene(); // Send the updated scene to the editor ``` -------------------------------- ### Create a Custom Cookie Clicker Asset (C#) Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/creating-your-first-script.md Define a custom asset with properties like 'Status' and 'Multiplier', and a trigger 'GimmeCookie' to increment a cookie count. This script is intended for use within the Warudo environment. ```csharp using Warudo.Core.Attributes; using Warudo.Core.Scenes; [AssetType(Id = "82ae6c21-e202-4e0e-9183-318e2e607672", Title = "Cookie Clicker")] public class CookieClickerAsset : Asset { [Markdown] public string Status = "You don't have any cookies."; [DataInput] [IntegerSlider(1, 10)] [Description("Increase me to get more cookies each time!")] public int Multiplier = 1; private int count; [Trigger] public void GimmeCookie() { count += Multiplier; SetDataInput(nameof(Status), "You have " + count + " cookie(s).", broadcast: true); } protected override void OnCreate() { base.OnCreate(); SetActive(true); } } ``` -------------------------------- ### Implement Primitive Resource URI Resolver Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/resource-providers-and-resolvers.md This C# class implements the IResourceUriResolver interface to handle custom URIs for primitive game objects. It resolves URIs starting with 'prop://primitives/' to create Cube or Sphere GameObjects. ```csharp public class PrimitivePropResourceUriResolver : IResourceUriResolver { public object Resolve(Uri uri) { if (uri.Scheme != "prop" || uri.Authority != "primitives") return null; var path = uri.LocalPath.TrimStart('/'); return path switch { "cube" => GameObject.CreatePrimitive(PrimitiveType.Cube), "sphere" => GameObject.CreatePrimitive(PrimitiveType.Sphere), _ => throw new Exception("Unknown primitive prop: " + path) }; } } ``` -------------------------------- ### Trigger Flow Output Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/nodes.md Demonstrates how to trigger an output flow by returning a Continuation. Returning null indicates the flow has ended. ```csharp [FlowInput] public Continuation Enter() { // Do something return Exit; } [FlowOutput] public Continuation Exit; ``` ```csharp [FlowInput] public Continuation Enter() { // Do something return null; } ``` -------------------------------- ### Define Custom Node with Warudo API Source: https://context7.com/hakuyalabs/warudo-docs/llms.txt Inherit from `Node` and use the `[NodeType]` attribute to define a custom node. Declare inputs and outputs using attributes like `[DataInput]`, `[FlowInput]`, and `[FlowOutput]`. Ensure a unique GUID is provided for the `Id`. ```csharp using UnityEngine; using Warudo.Core.Attributes; using Warudo.Core.Graphs; using Warudo.Plugins.Core.Assets.Character; [NodeType( Id = "95cd88ae-bebe-4dc0-b52b-ba94799f08e9", Title = "Character Play Random Expression", Category = "CATEGORY_CHARACTERS" )] public class CharacterPlayRandomExpressionNode : Node { [DataInput] public CharacterAsset Character; // Asset reference dropdown in editor [FlowInput] public Continuation Enter() { if (Character.IsNullOrInactive()) return Exit; // Guard: asset must be active if (Character.Expressions.Length == 0) return Exit; Character.ExitAllExpressions(); var randomExpression = Character.Expressions[Random.Range(0, Character.Expressions.Length)]; Character.EnterExpression(randomExpression.Name, transient: false); return Exit; // Trigger the Exit flow output } [FlowOutput] public Continuation Exit; } ``` -------------------------------- ### Define a Warudo Plugin Script Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/creating-your-first-plugin-mod.md This C# script defines a plugin for Warudo, specifying its metadata, asset types, and node types. It inherits from the base `Plugin` class and includes an `OnCreate` method for initialization logic. ```csharp using UnityEngine; using Warudo.Core.Attributes; using Warudo.Core.Plugins; [PluginType( Id = "hakuyatira.helloworld", Name = "Hello World", Description = "A simple plugin that says hello to the world.", Version = "1.0.0", Author = "Hakuya Tira", SupportUrl = "https://docs.warudo.app", AssetTypes = new [] { typeof(CookieClickerAsset) }, NodeTypes = new [] { typeof(HelloWorldNode) })] public class HelloWorldPlugin : Plugin { protected override void OnCreate() { base.OnCreate(); Debug.Log("The Hello World plugin is officially enabled! Hooray!"); } } ``` -------------------------------- ### Generic Data Input/Output Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/nodes.md Illustrates defining generic data input and output ports using the 'object' type. This allows for flexible data passing between nodes, including Unity objects. ```csharp [DataInput] public object MyGenericInput; // Note it's 'object', not 'Object' [DataOutput public object MyGenericOutput() => ... ``` -------------------------------- ### Connect BlendShape to Bone Scale Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/tutorials/balloon.md Connects the scaled blendshape value to the character bone scale node. This setup allows facial expressions to dynamically control the size of character bones. Ensure the output of Scale Vector is connected to the Scale input of Set Character Bone Scale. ```Warudo Blueprint Set Character Bone Scale Scale: (from Scale Vector) Enter: (from Get Character BlendShape) ``` -------------------------------- ### Exponential and Logarithmic Functions Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/blueprints/nodes/math-expression.md Includes functions for exponentiation, natural logarithm, and base-10 logarithm. ```csharp exp => Mathf.Exp ``` ```csharp ln => Mathf.Log(float) ``` ```csharp log10 => Mathf.Log10 ``` -------------------------------- ### Executing a Command Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/mod-interoperability.md Demonstrates how to send a command to a target entity and handle the response. Command request and response types are interoperable if their internal structure matches. ```APIDOC ## Executing a Command To send a command you need a valid **target** `Plugin`/`Asset`/`Node` instance. Use the `PluginRouter`'s `ExecuteCommand` method to send the command: ```csharp public class MyCommandRequestOnB { public string message; } public class MyCommandResponseOnB { public int time; public string message; } [DataInput] [TypeIdFilter("AMod.Asset")] Asset targetEntity; // Execute command CommandResult commandResult = Context.PluginRouter.ExecuteCommand( targetEntity, // Target entity, can be a Plugin/Asset/Node instance "MyCommand", // Command name new MyCommandRequestOnB { // Command request parameter message = "Hello from BMod!" } ); ``` The method returns a `CommandResult` instance, which contains the result of the command: ```csharp public class CommandResult { public CommandResultStatus Status; public T Data; } ``` `CommandResultStatus` is an enum that indicates the execution status of the command: ```csharp public enum CommandResultStatus { SUCCESS, // Successfully executed ENTITY_NOT_FOUND, // Target entity not found COMMAND_NOT_FOUND, // Target entity has not registered this command EXECUTION_ERROR // An error occurred while executing the command } ``` You can check the `Status` field to determine whether the command succeeded, and access the response via the `Data` field: ```csharp if (commandResult.Status == CommandResultStatus.SUCCESS) { Debug.Log($"BMod received response: {commandResult.Data.message} at {commandResult.Data.time}"); } else { Debug.LogError($"Command execution failed with status: {commandResult.Status}"); } ``` ``` -------------------------------- ### CookieClickerAsset Script for Loading Unity Assets Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/creating-your-first-plugin-mod.md This script defines a custom asset that can spawn particle effects. It loads a Unity prefab from the mod's assets folder and instantiates it when a trigger is activated. Ensure the particle prefab is correctly placed and named within your mod's folder. ```csharp using System; using Cysharp.Threading.Tasks; using UnityEngine; using Warudo.Core.Attributes; using Warudo.Core.Scenes; using Object = UnityEngine.Object; using Random = UnityEngine.Random; [AssetType(Id = "82ae6c21-e202-4e0e-9183-318e2e607672", Title = "Cookie Clicker")] public class CookieClickerAsset : Asset { [Markdown] public string Status = "You don't have any cookies."; [DataInput] [IntegerSlider(1, 10)] [Description("Increase me to get more cookies each time!")] public int Multiplier = 1; private int count; private GameObject particlePrefab; // New field to store the particle prefab [Trigger] public async void GimmeCookie() { // Note the async keyword count += Multiplier; SetDataInput(nameof(Status), "You have " + count + " cookie(s).", broadcast: true); // Spawn the particle prefab Multiplier times for (var i = 0; i < Multiplier; i++) { var particle = Object.Instantiate(particlePrefab, Random.insideUnitSphere * 2f, Quaternion.identity); particle.SetActive(true); Object.Destroy(particle, 3f); // Automatically destroy the cloned particle after 3 seconds await UniTask.Delay(TimeSpan.FromSeconds(0.2f)); // Delay 0.2 seconds before spawning the next particle } } protected override void OnCreate() { base.OnCreate(); SetActive(true); // Load the particle prefab from the mod folder. Change this path if your prefab is in a different folder particlePrefab = Plugin.ModHost.Assets.Instantiate("Assets/HelloWorldPlugin/Particle.prefab"); // Disable it so that it doesn't show up in the scene particlePrefab.SetActive(false); } } ``` -------------------------------- ### Execute Command with Request and Response Types Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/mod-interoperability.md Send a command to a target entity and receive a typed response. Ensure the request and response types match the command's definition. The command result status should be checked for success. ```csharp public class MyCommandRequestOnB { public string message; } public class MyCommandResponseOnB { public int time; public string message; } [DataInput] [TypeIdFilter("AMod.Asset")] Asset targetEntity; // Execute command CommandResult commandResult = Context.PluginRouter.ExecuteCommand( targetEntity, // Target entity, can be a Plugin/Asset/Node instance "MyCommand", // Command name new MyCommandRequestOnB { // Command request parameter message = "Hello from BMod!" } ); ``` ```csharp public class CommandResult { public CommandResultStatus Status; public T Data; } ``` ```csharp public enum CommandResultStatus { SUCCESS, // Successfully executed ENTITY_NOT_FOUND, // Target entity not found COMMAND_NOT_FOUND, // Target entity has not registered this command EXECUTION_ERROR // An error occurred while executing the command } ``` ```csharp if (commandResult.Status == CommandResultStatus.SUCCESS) { Debug.Log($"BMod received response: {commandResult.Data.message} at {commandResult.Data.time}"); } else { Debug.LogError($"Command execution failed with status: {commandResult.Status}"); } ``` -------------------------------- ### Define Custom Plugin with Warudo API Source: https://context7.com/hakuyalabs/warudo-docs/llms.txt Inherit from `Plugin` and use the `[PluginType]` attribute to define a global singleton plugin. Register associated `AssetTypes` and `NodeTypes`, and hook into scene lifecycle events like `OnSceneLoaded`. ```csharp using Warudo.Core.Attributes; using Warudo.Core.Plugins; [PluginType( Id = "hakuyatira.helloworld", Name = "Hello World", Description = "A simple plugin that says hello to the world.", Version = "1.0.0", Author = "Hakuya Tira", SupportUrl = "https://docs.warudo.app", AssetTypes = new[] { typeof(CookieClickerAsset) }, NodeTypes = new[] { typeof(CharacterPlayRandomExpressionNode) } )] public class HelloWorldPlugin : Plugin { protected override void OnCreate() { base.OnCreate(); // Called once when the plugin loads (startup or hot-reload) } public override void OnSceneLoaded(Scene scene, SerializedScene serializedScene) { // Called each time a scene finishes loading } } ``` -------------------------------- ### Localize Strings with Extension Method Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/localization.md Use the `Localized()` extension method on a string ID to retrieve its localized version based on the current user language. Ensure the `Warudo.Core.Localization` namespace is imported. Falls back to English if the string is not found in the current language. ```csharp using Warudo.Core.Localization; // Import the namespace that contains the extension method // Assume the user language is set to English "FACE_TRACKING".Localized() // "Face Tracking" "ALIVE_TIME_DESCRIPTION".Localized() // "The prop will be destroyed after this time." ``` -------------------------------- ### Global Event Bus Subscription and Broadcasting in C# Source: https://context7.com/hakuyalabs/warudo-docs/llms.txt Use Context.EventBus to broadcast and subscribe to global events. Custom events can be defined and managed for loosely coupled communication. ```csharp // Define a custom event public class TrackingLostEvent : Warudo.Core.Events.Event { public string Reason { get; } public TrackingLostEvent(string reason) { Reason = reason; } } // Inside a Plugin or Asset — subscribing (auto-unsubscribed on entity destroy) Subscribe(e => { Context.Service.Toast(ToastSeverity.Warning, "Tracking Lost", e.Reason); }); // Broadcasting from anywhere Context.EventBus.Broadcast(new TrackingLostEvent("Camera occluded")); // Manual subscription with explicit unsubscription var id = Context.EventBus.Subscribe(e => Debug.Log(e.Reason)); // Later: Context.EventBus.Unsubscribe(id); ``` -------------------------------- ### ShowProgress Source: https://github.com/hakuyalabs/warudo-docs/blob/master/docs/scripting/api/service.md Display a progress bar in the editor. The progress value should be between 0 and 1. An optional timeout can be set. ```APIDOC ## ShowProgress ### Description Show a progress bar in the editor. The `progress` value should be between 0 and 1. If `timeout` is specified, the progress bar will automatically hide after the specified duration. ### Method `void ShowProgress(string message, float progress, TimeSpan timeout = default)` ### Parameters #### Path Parameters - **message** (string) - Required - The message to display with the progress bar. - **progress** (float) - Required - The progress value, between 0 and 1. - **timeout** (TimeSpan) - Optional - The duration after which the progress bar will automatically hide. ### Response None ```