同步
This commit is contained in:
14
Packages/dev.yarnspinner.unity/Runtime/AssemblyInfo.cs
Normal file
14
Packages/dev.yarnspinner.unity/Runtime/AssemblyInfo.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: AssemblyVersion("3.1.4.0")]
|
||||
[assembly: AssemblyFileVersion("3.1.4.0")]
|
||||
[assembly: AssemblyInformationalVersion("3.1.4.Branch.hotfix/textanim-build-errors.Sha.c2b119c5eda7fdd3cd0b13a689f95d54d456fb69")]
|
||||
[assembly: InternalsVisibleTo("YarnSpinner.Unity.Tests")]
|
||||
[assembly: InternalsVisibleTo("YarnSpinner.Unity.Tests.Editor")]
|
||||
[assembly: InternalsVisibleTo("YarnSpinner.Unity.Editor")]
|
||||
|
||||
11
Packages/dev.yarnspinner.unity/Runtime/AssemblyInfo.cs.meta
Normal file
11
Packages/dev.yarnspinner.unity/Runtime/AssemblyInfo.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ff46021d5efd5094f90ef7c5b0804d46
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/dev.yarnspinner.unity/Runtime/Attributes.meta
Normal file
8
Packages/dev.yarnspinner.unity/Runtime/Attributes.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ecb7c21cb1e5a4e799c41fb52ba6f6b2
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
386
Packages/dev.yarnspinner.unity/Runtime/Attributes/Attributes.cs
Normal file
386
Packages/dev.yarnspinner.unity/Runtime/Attributes/Attributes.cs
Normal file
@@ -0,0 +1,386 @@
|
||||
using System;
|
||||
|
||||
namespace Yarn.Unity.Attributes
|
||||
{
|
||||
|
||||
#nullable enable
|
||||
|
||||
/// <summary>
|
||||
/// The abstract base class for all Yarn Editor attributes.
|
||||
/// </summary>
|
||||
public abstract class YarnEditorAttribute : Attribute { }
|
||||
|
||||
/// <summary>
|
||||
/// Indents a property in the Unity Inspector.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||
public class IndentAttribute : YarnEditorAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The amount to indent this property by.
|
||||
/// </summary>
|
||||
public int indentLevel = 1;
|
||||
|
||||
/// <inheritdoc cref="IndentAttribute" path="/summary"/>
|
||||
/// <param name="indentLevel"><inheritdoc cref="indentLevel" path="/summary/node()"/> </param>
|
||||
public IndentAttribute(int indentLevel = 1)
|
||||
{
|
||||
if (this.indentLevel < 0)
|
||||
{
|
||||
indentLevel = 0;
|
||||
}
|
||||
this.indentLevel = indentLevel;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether a property is visible or not in the Unity Inspector.
|
||||
/// </summary>
|
||||
public abstract class VisibilityAttribute : YarnEditorAttribute
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// The type of test represented by <see cref="Condition"/>.
|
||||
/// </summary>
|
||||
public enum AttributeMode
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="Condition"/> is the name of a <see langword="bool"/>
|
||||
/// variable or a reference to a <see cref="UnityEngine.Object"/>,
|
||||
/// and the test passes when the variable is <see langword="true"/>
|
||||
/// (if bool) or non-null (if an object).
|
||||
/// </summary>
|
||||
BooleanCondition,
|
||||
/// <summary>
|
||||
/// <see cref="Condition"/> is the name of an enum variable, and the
|
||||
/// test passes when the variable's value is equal to <see
|
||||
/// cref="EnumValue"/>.
|
||||
/// </summary>
|
||||
EnumEquality,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of test that <see cref="Condition"/> represents.
|
||||
/// </summary>
|
||||
public AttributeMode Mode { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Controls whether the property appears when the condition passes
|
||||
/// (<see langword="true"/>), or fails (<see langword="false"/>).
|
||||
/// </summary>
|
||||
public bool Invert { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The name of another property on the object that determines whether
|
||||
/// this property is visible or not.
|
||||
/// </summary>
|
||||
public string? Condition { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// The value that the variable indicated by <see cref="Condition"/> is
|
||||
/// compared to.
|
||||
/// </summary>
|
||||
/// <remarks>This value is only used when <see cref="Mode"/> is <see
|
||||
/// cref="AttributeMode.EnumEquality"/>.</remarks>
|
||||
public int EnumValue { get; protected set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows this property only when a condition is true.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
||||
public class ShowIfAttribute : VisibilityAttribute
|
||||
{
|
||||
|
||||
/// <inheritdoc cref="ShowIfAttribute" path="/summary"/>
|
||||
/// <param name="condition"><inheritdoc cref="VisibilityAttribute.Condition" path="/summary/node()"/></param>
|
||||
public ShowIfAttribute(string condition)
|
||||
{
|
||||
this.Invert = false;
|
||||
this.Condition = condition;
|
||||
this.Mode = AttributeMode.BooleanCondition;
|
||||
this.EnumValue = default;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="ShowIfAttribute" path="/summary"/>
|
||||
/// <param name="condition"><inheritdoc cref="VisibilityAttribute.Condition" path="/summary/node()"/> This variable must be an enum.</param>
|
||||
/// <param name="value"><inheritdoc cref="VisibilityAttribute.EnumValue" path="/summary/node()"/></param>
|
||||
public ShowIfAttribute(string condition, object value)
|
||||
{
|
||||
this.Invert = false;
|
||||
this.Condition = condition;
|
||||
this.Mode = AttributeMode.EnumEquality;
|
||||
this.EnumValue = (int)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hides this property when a condition is true.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
||||
public class HideIfAttribute : VisibilityAttribute
|
||||
{
|
||||
/// <inheritdoc cref="HideIfAttribute" path="/summary"/>
|
||||
/// <param name="condition"><inheritdoc cref="VisibilityAttribute.Condition" path="/summary/node()"/></param>
|
||||
public HideIfAttribute(string condition)
|
||||
{
|
||||
this.Invert = true;
|
||||
this.Condition = condition;
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="HideIfAttribute" path="/summary"/>
|
||||
/// <param name="condition"><inheritdoc cref="VisibilityAttribute.Condition" path="/summary/node()"/> This variable must be an enum.</param>
|
||||
/// <param name="value"><inheritdoc cref="VisibilityAttribute.EnumValue" path="/summary/node()"/></param>
|
||||
public HideIfAttribute(string condition, object value)
|
||||
{
|
||||
this.Invert = true;
|
||||
this.Condition = condition;
|
||||
this.Mode = AttributeMode.EnumEquality;
|
||||
this.EnumValue = (int)value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows a header above this property and the following properties that
|
||||
/// have the same group name, optionally as a foldout.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||
public class GroupAttribute : YarnEditorAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the group.
|
||||
/// </summary>
|
||||
public string GroupName { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether to show this group as a fold-out.
|
||||
/// </summary>
|
||||
public bool FoldOut { get; }
|
||||
|
||||
/// <inheritdoc cref="GroupAttribute" path="/summary"/>
|
||||
/// <param name="groupName"><inheritdoc cref="GroupName" path="/summary/node()"/></param>
|
||||
/// <param name="foldOut"><inheritdoc cref="FoldOut" path="/summary/node()"/></param>
|
||||
public GroupAttribute(string groupName, bool foldOut = false)
|
||||
{
|
||||
this.GroupName = groupName;
|
||||
this.FoldOut = foldOut;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the displayed label of the property in the Unity Inspector.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||
public class LabelAttribute : YarnEditorAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The label to show for this property.
|
||||
/// </summary>
|
||||
public string Label { get; }
|
||||
|
||||
/// <inheritdoc cref="LabelAttribute" path="/summary"/>
|
||||
/// <param name="label"><inheritdoc cref="Label" path="/summary/node()"/></param>
|
||||
public LabelAttribute(string label)
|
||||
{
|
||||
this.Label = label;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Overrides the displayed label of the property in the Unity Inspector by
|
||||
/// getting a label from a named method.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||
public class LabelFromAttribute : YarnEditorAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The method to invoke that will return the label to display. The
|
||||
/// method must be an instance method, take no parameters, and return a
|
||||
/// <see cref="string"/>.
|
||||
/// </summary>
|
||||
public string SourceMethod { get; }
|
||||
|
||||
/// <inheritdoc cref="LabelAttribute" path="/summary"/>
|
||||
/// <param name="methodName"><inheritdoc cref="SourceMethod"
|
||||
/// path="/summary/node()"/></param>
|
||||
public LabelFromAttribute(string methodName)
|
||||
{
|
||||
this.SourceMethod = methodName;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows an error message box if this property is null.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||
public class MustNotBeNullAttribute : YarnEditorAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The text of the error message to show if the property is null. If
|
||||
/// not provided, a generic error message is shown.
|
||||
/// </summary>
|
||||
public string? Label { get; }
|
||||
|
||||
/// <inheritdoc cref="MustNotBeNullAttribute" path="/summary"/>
|
||||
/// <param name="label"><inheritdoc cref="Label" path="/summary/node()"/></param>
|
||||
public MustNotBeNullAttribute(string? label = null)
|
||||
{
|
||||
this.Label = label;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows an error message box if this property is null and the variable
|
||||
/// indicated by <see cref="Condition"/> is false.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false)]
|
||||
public class MustNotBeNullWhenAttribute : YarnEditorAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of another property on this object to compare to. This
|
||||
/// variable must be either a boolean value, or a <see
|
||||
/// cref="UnityEngine.Object"/> reference.
|
||||
/// </summary>
|
||||
public string Condition { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The text of the error message to show if the property is null and
|
||||
/// the condition is met. If not provided, a generic error message is
|
||||
/// shown.
|
||||
/// </summary>
|
||||
public string? Label { get; }
|
||||
|
||||
/// <inheritdoc cref="MustNotBeNullWhenAttribute" path="/summary"/>
|
||||
/// <param name="condition"><inheritdoc cref="Condition" path="/summary/node()"/></param>
|
||||
/// <param name="label"><inheritdoc cref="Label" path="/summary/node()"/></param>
|
||||
public MustNotBeNullWhenAttribute(string condition, string? label = null)
|
||||
{
|
||||
this.Condition = condition;
|
||||
this.Label = label;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows a message box on the property.
|
||||
/// </summary>
|
||||
[AttributeUsage(AttributeTargets.Field, AllowMultiple = true)]
|
||||
public class MessageBoxAttribute : YarnEditorAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of a method that will be called to determine the contents
|
||||
/// of the message box. The method must return a <see cref="Message"/>.
|
||||
/// </summary>
|
||||
public string SourceMethod { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The type of the message box to display.
|
||||
/// </summary>
|
||||
/// <seealso cref="UnityEditor.MessageType"/>
|
||||
public enum Type
|
||||
{
|
||||
/// <summary>
|
||||
/// Do not show an icon in the message box.
|
||||
/// </summary>
|
||||
/// <seealso cref="UnityEditor.MessageType.None"/>
|
||||
None,
|
||||
/// <summary>
|
||||
/// Show an information icon in the message box.
|
||||
/// </summary>
|
||||
/// <seealso cref="UnityEditor.MessageType.Info"/>
|
||||
Info,
|
||||
/// <summary>
|
||||
/// Show a warning icon in the message box.
|
||||
/// </summary>
|
||||
/// <seealso cref="UnityEditor.MessageType.Warning"/>
|
||||
Warning,
|
||||
/// <summary>
|
||||
/// Show an error icon in the message box.
|
||||
/// </summary>
|
||||
/// <seealso cref="UnityEditor.MessageType.Error"/>
|
||||
Error
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// A description for a message box to show in the Unity Inspector.
|
||||
/// </summary>
|
||||
public struct Message
|
||||
{
|
||||
/// <summary>
|
||||
/// The type of the message to show.
|
||||
/// </summary>
|
||||
public Type type;
|
||||
|
||||
/// <summary>
|
||||
/// The text to show in the message box. If this value is <see
|
||||
/// langword="null"/>, no message box is displayed.
|
||||
/// </summary>
|
||||
public string? text;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new message with the given string.
|
||||
/// </summary>
|
||||
/// <param name="text"><inheritdoc cref="text" path="/summary/node()"/></param>
|
||||
public static implicit operator Message(string? text)
|
||||
{
|
||||
return new Message
|
||||
{
|
||||
type = Type.Error,
|
||||
text = text,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="MessageBoxAttribute" path="/summary"/>
|
||||
/// <param name="sourceMethod"><inheritdoc cref="SourceMethod" path="/summary/node()"/></param>
|
||||
public MessageBoxAttribute(string sourceMethod)
|
||||
{
|
||||
this.SourceMethod = sourceMethod;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new error <see cref="Message"/> using the provided text.
|
||||
/// </summary>
|
||||
/// <param name="message"><inheritdoc cref="Message.text" path="/summary/node()"/></param>
|
||||
/// <returns>A new <see cref="Message"/>.</returns>
|
||||
public static Message Error(string message)
|
||||
{
|
||||
return new Message { type = Type.Error, text = message };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new warning <see cref="Message"/> using the provided text.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="Error" path="/param"/>
|
||||
/// <inheritdoc cref="Error" path="/returns"/>
|
||||
public static Message Warning(string message)
|
||||
{
|
||||
return new Message { type = Type.Warning, text = message };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new information <see cref="Message"/> using the provided text.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="Error" path="/param"/>
|
||||
/// <inheritdoc cref="Error" path="/returns"/>
|
||||
public static Message Info(string message)
|
||||
{
|
||||
return new Message { type = Type.Info, text = message };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new neutral <see cref="Message"/> using the provided text.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="Error" path="/param"/>
|
||||
/// <inheritdoc cref="Error" path="/returns"/>
|
||||
public static Message Neutral(string message)
|
||||
{
|
||||
return new Message { type = Type.None, text = message };
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a new <see cref="Message"/> that represents an instruction
|
||||
/// to not draw a message box at all.
|
||||
/// </summary>
|
||||
public static Message NoMessage => new Message { type = Type.None, text = null };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9a7b9b5f6e7540d58e7a204b9004b38
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
|
||||
namespace Yarn.Unity.Attributes
|
||||
{
|
||||
public class LanguageAttribute : PropertyAttribute
|
||||
{
|
||||
// No data or methods on this attribute; it's purely a marker.
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb088ccd3b8c7490d896013b643447fc
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Yarn.Unity.Attributes
|
||||
{
|
||||
/// <summary>
|
||||
/// Specifies that a field represents a reference to a named Yarn node that
|
||||
/// exists in a Yarn project.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This attribute causes the inspector to draw a popup that allows
|
||||
/// selecting a node from a list of all nodes available in a Yarn project.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This attribute may only be used with <see cref="string"/> fields.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class YarnNodeAttribute : PropertyAttribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of a property that specifies the YarnProject containing the desired node.
|
||||
/// </summary>
|
||||
public readonly string yarnProjectAttribute;
|
||||
|
||||
public readonly bool requiresYarnProject;
|
||||
|
||||
/// <summary>
|
||||
/// Initialises a new instance of <see cref="YarnNodeAttribute"/>.
|
||||
/// </summary>
|
||||
/// <param name="yarnProjectAttribute"><inheritdoc
|
||||
/// cref="yarnProjectAttribute" path="/summary/node()"/></param>
|
||||
public YarnNodeAttribute(string yarnProjectAttribute, bool requiresYarnProject = true)
|
||||
{
|
||||
this.yarnProjectAttribute = yarnProjectAttribute;
|
||||
this.requiresYarnProject = requiresYarnProject;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d02a60aff1376451ba23995ee5a69c1e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/dev.yarnspinner.unity/Runtime/Commands.meta
Normal file
8
Packages/dev.yarnspinner.unity/Runtime/Commands.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eed85fa11a3a5a84a85090bc54800bbd
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
798
Packages/dev.yarnspinner.unity/Runtime/Commands/Actions.cs
Normal file
798
Packages/dev.yarnspinner.unity/Runtime/Commands/Actions.cs
Normal file
@@ -0,0 +1,798 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
using ActionRegistrationMethod = System.Action<IActionRegistration, RegistrationType>;
|
||||
using Converter = System.Func<string, int, object?>;
|
||||
|
||||
public enum RegistrationType
|
||||
{
|
||||
/// <summary>
|
||||
/// Actions are being registered during a Yarn script compilation.
|
||||
/// </summary>
|
||||
Compilation,
|
||||
/// <summary>
|
||||
/// Actions are being registered for runtime use (i.e. during gameplay.)
|
||||
/// </summary>
|
||||
Runtime
|
||||
}
|
||||
|
||||
public interface ICommand
|
||||
{
|
||||
string Name { get; }
|
||||
}
|
||||
|
||||
internal static class DiagnosticUtility
|
||||
{
|
||||
public static string EnglishPluraliseNounCount(int count, string name, bool prefixCount = false)
|
||||
{
|
||||
string result = count == 1 ? name : name + "s";
|
||||
return prefixCount ? $"{count} {result}" : result;
|
||||
}
|
||||
|
||||
public static string EnglishPluraliseWasVerb(int count) => count == 1 ? "was" : "were";
|
||||
}
|
||||
|
||||
public class Actions : ICommandDispatcher
|
||||
{
|
||||
internal class CommandRegistration : ICommand
|
||||
{
|
||||
public CommandRegistration(string name, Delegate @delegate)
|
||||
{
|
||||
Name = name;
|
||||
Method = @delegate.Method;
|
||||
Target = @delegate.Target;
|
||||
Converters = CreateConverters(Method);
|
||||
DynamicallyFindsTarget = false;
|
||||
}
|
||||
|
||||
public CommandRegistration(string name, MethodInfo method)
|
||||
{
|
||||
if (method.IsStatic)
|
||||
{
|
||||
DynamicallyFindsTarget = false;
|
||||
}
|
||||
else if (typeof(Component).IsAssignableFrom(method.DeclaringType))
|
||||
{
|
||||
// This method is an instance method on a Component (or one
|
||||
// of its subclasses). We'll dynamically find a target to
|
||||
// invoke the method on at runtime.
|
||||
DynamicallyFindsTarget = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// The instance method's declaring type is not a Component,
|
||||
// which means we won't be able to look up a target.
|
||||
throw new ArgumentException($"Cannot register method {GetFullMethodName(method)} as a command: instance methods must declared on {nameof(Component)} classes.");
|
||||
}
|
||||
|
||||
Name = name;
|
||||
Method = method;
|
||||
Target = null;
|
||||
|
||||
Converters = CreateConverters(method);
|
||||
}
|
||||
|
||||
public string Name { get; set; }
|
||||
public MethodInfo Method { get; set; }
|
||||
private object? Target { get; set; }
|
||||
|
||||
public Type DeclaringType => Method.DeclaringType;
|
||||
public Type ReturnType => Method.ReturnType;
|
||||
public bool IsStatic => Method.IsStatic;
|
||||
|
||||
public readonly Converter[] Converters;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating that this command finds a target to
|
||||
/// invoke its method on by name, each time it is invoked.
|
||||
/// </summary>
|
||||
private bool DynamicallyFindsTarget { get; }
|
||||
|
||||
public CommandType Type
|
||||
{
|
||||
get
|
||||
{
|
||||
Type returnType = ReturnType;
|
||||
|
||||
if (typeof(void).IsAssignableFrom(returnType))
|
||||
{
|
||||
return CommandType.IsVoid;
|
||||
}
|
||||
if (typeof(IEnumerator).IsAssignableFrom(returnType))
|
||||
{
|
||||
return CommandType.IsCoroutine;
|
||||
}
|
||||
if (typeof(Coroutine).IsAssignableFrom(returnType))
|
||||
{
|
||||
return CommandType.ReturnsCoroutine;
|
||||
}
|
||||
return CommandType.Invalid;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CommandType
|
||||
{
|
||||
/// <summary>
|
||||
/// The method returns <see cref="void"/>.
|
||||
/// </summary>
|
||||
IsVoid,
|
||||
/// <summary>
|
||||
/// The method returns a <see cref="Coroutine"/> object.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
ReturnsCoroutine,
|
||||
/// <summary>
|
||||
/// The method returns <see cref="IEnumerator"/> (that is, it is
|
||||
/// a coroutine).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Code that invokes this command should use <see
|
||||
/// cref="MonoBehaviour.StartCoroutine(IEnumerator)"/> to begin
|
||||
/// the coroutine.
|
||||
/// </remarks>
|
||||
IsCoroutine,
|
||||
/// <summary>
|
||||
/// The method is not a valid command (that is, it does not
|
||||
/// return <see cref="void"/>, <see cref="Coroutine"/>, or <see
|
||||
/// cref="IEnumerator"/>.)
|
||||
/// </summary>
|
||||
Invalid,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempt to parse the arguments with cached converters.
|
||||
/// </summary>
|
||||
public CommandDispatchResult.ParameterParseStatusType TryParseArgs(string[] args, out object?[]? result, out string? message)
|
||||
{
|
||||
var parameters = Method.GetParameters();
|
||||
|
||||
var lastParameterIsArray = parameters.Length > 0 && parameters[parameters.Length - 1].ParameterType.IsArray;
|
||||
|
||||
var (min, max) = ParameterCount;
|
||||
|
||||
int argumentCount = args.Length;
|
||||
if (argumentCount < min || (argumentCount > max && !lastParameterIsArray))
|
||||
{
|
||||
// Wrong number of arguments.
|
||||
string requirementDescription;
|
||||
if (min == 0)
|
||||
{
|
||||
requirementDescription = $"at most {max} {DiagnosticUtility.EnglishPluraliseNounCount(max, "parameter")}";
|
||||
}
|
||||
else if (min != max)
|
||||
{
|
||||
requirementDescription = $"between {min} and {max} {DiagnosticUtility.EnglishPluraliseNounCount(max, "parameter")}";
|
||||
}
|
||||
else
|
||||
{
|
||||
requirementDescription = $"{min} {DiagnosticUtility.EnglishPluraliseNounCount(max, "parameter")}";
|
||||
}
|
||||
message = $"{this.Name} requires {requirementDescription}, but {argumentCount} {DiagnosticUtility.EnglishPluraliseWasVerb(argumentCount)} provided.";
|
||||
result = default;
|
||||
return CommandDispatchResult.ParameterParseStatusType.InvalidParameterCount;
|
||||
}
|
||||
|
||||
var finalArgs = new object?[parameters.Length];
|
||||
|
||||
var argsQueue = new Queue(args);
|
||||
|
||||
var paramsArgs = new List<object>();
|
||||
|
||||
for (int i = 0; i < argumentCount; i++)
|
||||
{
|
||||
var parameterIsArray = parameters[i].ParameterType.IsArray;
|
||||
|
||||
string arg = args[i];
|
||||
Converter converter = Converters[i];
|
||||
|
||||
if (parameterIsArray)
|
||||
{
|
||||
if (i < parameters.Length - 1)
|
||||
{
|
||||
// The parameter is an array, but it isn't the last
|
||||
// parameter. That's not allowed.
|
||||
message = $"Parameter {i} ({parameters[i].Name}): is an array, but is not the last parameter of {parameters[i].Member.Name}.";
|
||||
result = default;
|
||||
return CommandDispatchResult.ParameterParseStatusType.InvalidParameterType;
|
||||
}
|
||||
|
||||
// Consume all remaining arguments, passing them through
|
||||
// the final converter, and produce an array from the
|
||||
// results. This array will be the final parameter to
|
||||
// the method.
|
||||
var parameterArrayElementType = parameters[i].ParameterType.GetElementType();
|
||||
var paramIndex = i;
|
||||
// var paramsArray = new List<object?>();
|
||||
var paramsArray = Array.CreateInstance(parameterArrayElementType, argumentCount - i);
|
||||
while (i < argumentCount)
|
||||
{
|
||||
arg = args[i];
|
||||
if (converter == null)
|
||||
{
|
||||
// Use relative index into paramsArray
|
||||
paramsArray.SetValue(arg, i - paramIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
paramsArray.SetValue(converter.Invoke(arg, i), i - paramIndex);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
message = $"Can't convert parameter {i} to {parameterArrayElementType.Name}: {e.Message}";
|
||||
result = default;
|
||||
return CommandDispatchResult.ParameterParseStatusType.InvalidParameterType;
|
||||
}
|
||||
}
|
||||
i += 1;
|
||||
}
|
||||
finalArgs[paramIndex] = paramsArray;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Consume a single argument
|
||||
if (converter == null)
|
||||
{
|
||||
finalArgs[i] = arg;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
finalArgs[i] = converter.Invoke(arg, i);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
message = $"Can't convert parameter {i} to {parameters[i].ParameterType.Name}: {e.Message}";
|
||||
result = default;
|
||||
return CommandDispatchResult.ParameterParseStatusType.InvalidParameterType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (int i = argumentCount; i < finalArgs.Length; i++)
|
||||
{
|
||||
var parameter = parameters[i];
|
||||
if (parameter.IsOptional)
|
||||
{
|
||||
// If this parameter is optional, provide the Missing
|
||||
// type.
|
||||
finalArgs[i] = System.Type.Missing;
|
||||
}
|
||||
else if (parameter.GetCustomAttribute<ParamArrayAttribute>() != null)
|
||||
{
|
||||
// If the parameter is a params array, provide an empty
|
||||
// array of the appropriate type.
|
||||
finalArgs[i] = Array.CreateInstance(parameter.ParameterType.GetElementType(), 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException($"Can't provide a default value for parameter {parameter.Name}");
|
||||
}
|
||||
}
|
||||
result = finalArgs;
|
||||
message = default;
|
||||
return CommandDispatchResult.ParameterParseStatusType.Succeeded;
|
||||
}
|
||||
|
||||
private (int Min, int Max) ParameterCount
|
||||
{
|
||||
get
|
||||
{
|
||||
var parameters = Method.GetParameters();
|
||||
int optional = 0;
|
||||
bool lastCommandIsParams = false;
|
||||
foreach (var parameter in parameters)
|
||||
{
|
||||
if (parameter.IsOptional)
|
||||
{
|
||||
optional += 1;
|
||||
}
|
||||
if (parameter.ParameterType.IsArray && parameter.GetCustomAttribute<ParamArrayAttribute>() != null)
|
||||
{
|
||||
// If the parameter is a params array, then:
|
||||
// 1. It's 'optional' in that you can pass in no
|
||||
// values (so, for our purposes, the minimum
|
||||
// number of parameters you need to pass is not
|
||||
// changed)
|
||||
// 2. The maximum number of parameters you can pass
|
||||
// is now effectively unbounded.
|
||||
lastCommandIsParams = true;
|
||||
optional += 1;
|
||||
}
|
||||
}
|
||||
|
||||
int min = parameters.Length - optional;
|
||||
int max = parameters.Length;
|
||||
if (lastCommandIsParams)
|
||||
{
|
||||
max = int.MaxValue;
|
||||
}
|
||||
return (min, max);
|
||||
}
|
||||
}
|
||||
|
||||
internal CommandDispatchResult Invoke(MonoBehaviour dispatcher, List<string> parameters)
|
||||
{
|
||||
object? target;
|
||||
|
||||
if (DynamicallyFindsTarget)
|
||||
{
|
||||
// We need to find a target to call this method on.
|
||||
|
||||
if (parameters.Count == 0)
|
||||
{
|
||||
// We need at least one parameter, which is the
|
||||
// component to look for
|
||||
return new CommandDispatchResult(CommandDispatchResult.StatusType.InvalidParameterCount, YarnTask.CompletedTask)
|
||||
{
|
||||
Message = $"{this.Name} needs a target, but none was specified",
|
||||
};
|
||||
}
|
||||
|
||||
// First parameter is the name of a game object that has the
|
||||
// component we're trying to call.
|
||||
|
||||
var gameObjectName = parameters[0];
|
||||
|
||||
parameters.RemoveAt(0);
|
||||
|
||||
var gameObject = GameObject.Find(gameObjectName);
|
||||
|
||||
if (gameObject == null)
|
||||
{
|
||||
// We couldn't find a target with this name.
|
||||
return new CommandDispatchResult(CommandDispatchResult.StatusType.TargetMissingComponent)
|
||||
{
|
||||
Message = $"No game object named \"{gameObjectName}\" exists",
|
||||
};
|
||||
}
|
||||
|
||||
// We've found a target. Does it have a component that's
|
||||
// the right type of object to call the method on?
|
||||
var targetComponent = gameObject.GetComponent(this.DeclaringType);
|
||||
|
||||
if (targetComponent == null)
|
||||
{
|
||||
return new CommandDispatchResult(CommandDispatchResult.StatusType.TargetMissingComponent)
|
||||
{
|
||||
Message = $"{this.Name} can't be called on {gameObjectName}, because it doesn't have a {this.DeclaringType.Name}",
|
||||
};
|
||||
}
|
||||
|
||||
target = targetComponent;
|
||||
}
|
||||
else if (Method.IsStatic)
|
||||
{
|
||||
// The method is static; it therefore doesn't need a target.
|
||||
target = null;
|
||||
}
|
||||
else if (Target != null)
|
||||
{
|
||||
// The method is an instance method, so use the target we've
|
||||
// stored.
|
||||
target = Target;
|
||||
}
|
||||
else
|
||||
{
|
||||
// We don't know what to call this method on.
|
||||
throw new InvalidOperationException($"Internal error: {nameof(CommandRegistration)} \"{this.Name}\" has no {nameof(Target)}, but method is not static and ${DynamicallyFindsTarget} is false");
|
||||
}
|
||||
|
||||
var parseArgsStatus = this.TryParseArgs(parameters.ToArray(), out var finalParameters, out var errorMessage);
|
||||
|
||||
if (parseArgsStatus != CommandDispatchResult.ParameterParseStatusType.Succeeded)
|
||||
{
|
||||
var status = parseArgsStatus switch
|
||||
{
|
||||
CommandDispatchResult.ParameterParseStatusType.Succeeded => CommandDispatchResult.StatusType.Succeeded,
|
||||
CommandDispatchResult.ParameterParseStatusType.InvalidParameterType => CommandDispatchResult.StatusType.InvalidParameter,
|
||||
CommandDispatchResult.ParameterParseStatusType.InvalidParameterCount => CommandDispatchResult.StatusType.InvalidParameterCount,
|
||||
_ => throw new InvalidOperationException("Internal error: invalid parameter parse result " + parseArgsStatus),
|
||||
};
|
||||
|
||||
return new CommandDispatchResult(status)
|
||||
{
|
||||
Message = errorMessage,
|
||||
};
|
||||
}
|
||||
|
||||
var returnValue = this.Method.Invoke(target, finalParameters);
|
||||
|
||||
if (returnValue is Coroutine coro)
|
||||
{
|
||||
// The method returned a Coroutine object.
|
||||
return new CommandDispatchResult(
|
||||
CommandDispatchResult.StatusType.Succeeded,
|
||||
dispatcher.WaitForCoroutine(coro)
|
||||
);
|
||||
}
|
||||
else if (returnValue is YarnTask yarnTask)
|
||||
{
|
||||
return new CommandDispatchResult(
|
||||
CommandDispatchResult.StatusType.Succeeded,
|
||||
yarnTask
|
||||
);
|
||||
}
|
||||
#if UNITY_2023_1_OR_NEWER
|
||||
else if (returnValue is Awaitable awaitable)
|
||||
{
|
||||
// The method returned an Awaitable. Convert it to a
|
||||
// YarnTask. (Awaitables implement IEnumerator, so check
|
||||
// this before testing against other IEnumerators like
|
||||
// coroutines.)
|
||||
return new CommandDispatchResult(
|
||||
CommandDispatchResult.StatusType.Succeeded,
|
||||
awaitable
|
||||
);
|
||||
}
|
||||
#endif
|
||||
else if (returnValue is IEnumerator enumerator)
|
||||
{
|
||||
// The method returned an IEnumerator.
|
||||
return new CommandDispatchResult(
|
||||
CommandDispatchResult.StatusType.Succeeded,
|
||||
dispatcher.WaitForCoroutine(enumerator)
|
||||
);
|
||||
}
|
||||
else if (returnValue is System.Threading.Tasks.Task task)
|
||||
{
|
||||
// The method returned a task. Convert it to a YarnTask.
|
||||
return new CommandDispatchResult(
|
||||
CommandDispatchResult.StatusType.Succeeded,
|
||||
task
|
||||
);
|
||||
}
|
||||
#if USE_UNITASK
|
||||
else if (returnValue is Cysharp.Threading.Tasks.UniTask unitask)
|
||||
{
|
||||
// The method returned a UniTask. Convert it to a YarnTask.
|
||||
return new CommandDispatchResult(
|
||||
CommandDispatchResult.StatusType.Succeeded,
|
||||
unitask
|
||||
);
|
||||
}
|
||||
#endif
|
||||
else
|
||||
{
|
||||
// The method returned no value.
|
||||
return new CommandDispatchResult(CommandDispatchResult.StatusType.Succeeded);
|
||||
}
|
||||
}
|
||||
|
||||
public string UsageString
|
||||
{
|
||||
get
|
||||
{
|
||||
var components = new List<string>();
|
||||
|
||||
components.Add(Name);
|
||||
|
||||
if (DynamicallyFindsTarget)
|
||||
{
|
||||
var declaringTypeName = DeclaringType.Name;
|
||||
components.Add($"target <i>({declaringTypeName})</i>");
|
||||
}
|
||||
|
||||
foreach (var parameter in Method.GetParameters())
|
||||
{
|
||||
var type = parameter.ParameterType;
|
||||
string typeName;
|
||||
|
||||
if (TypeFriendlyNames.TryGetValue(type, out typeName) == false)
|
||||
{
|
||||
typeName = type.Name;
|
||||
}
|
||||
|
||||
string displayName = $"{parameter.Name} <i>({typeName})</i>";
|
||||
|
||||
if (parameter.IsOptional)
|
||||
{
|
||||
displayName = $"[{displayName} = {parameter.DefaultValue}]";
|
||||
|
||||
}
|
||||
|
||||
components.Add(displayName);
|
||||
}
|
||||
|
||||
return string.Join(" ", components);
|
||||
}
|
||||
}
|
||||
|
||||
readonly Dictionary<Type, string> TypeFriendlyNames = new Dictionary<Type, string> {
|
||||
{ typeof(int), "number" },
|
||||
{ typeof(float), "number" },
|
||||
{ typeof(double), "number" },
|
||||
{ typeof(Decimal), "number" },
|
||||
{ typeof(string), "string" },
|
||||
{ typeof(bool), "bool" },
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
private Dictionary<string, CommandRegistration> _commands = new Dictionary<string, CommandRegistration>();
|
||||
|
||||
public Library Library { get; }
|
||||
public IActionRegistration ActionRegistrar { get; }
|
||||
|
||||
public IEnumerable<ICommand> Commands => _commands.Values;
|
||||
|
||||
public Actions(IActionRegistration actionRegistrar, Library library)
|
||||
{
|
||||
Library = library;
|
||||
ActionRegistrar = actionRegistrar;
|
||||
}
|
||||
|
||||
private static string GetFullMethodName(MethodInfo method)
|
||||
{
|
||||
return $"{method.DeclaringType.FullName}.{method.Name}";
|
||||
}
|
||||
|
||||
public void RegisterActions()
|
||||
{
|
||||
foreach (var registrationFunction in ActionRegistrationMethods)
|
||||
{
|
||||
registrationFunction.Invoke(ActionRegistrar, RegistrationType.Runtime);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddCommandHandler(string commandName, Delegate handler)
|
||||
{
|
||||
if (commandName.Contains(' '))
|
||||
{
|
||||
Debug.LogError($"Failed to register command {commandName}: command names are not allowed to contain spaces.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_commands.ContainsKey(commandName))
|
||||
{
|
||||
Debug.LogError($"Failed to register command {commandName}: a command by this name has already been registered.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if YARN_SOURCE_GENERATION_DEBUG_LOGGING
|
||||
Debug.Log($"Registering command {commandName}");
|
||||
#endif
|
||||
_commands.Add(commandName, new CommandRegistration(commandName, handler));
|
||||
}
|
||||
}
|
||||
|
||||
public void AddFunction(string name, Delegate implementation)
|
||||
{
|
||||
if (name.Contains(' '))
|
||||
{
|
||||
Debug.LogError($"Cannot add function {name}: function names are not allowed to contain spaces.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Library.FunctionExists(name))
|
||||
{
|
||||
Debug.LogError($"Cannot add function {name}: one already exists");
|
||||
return;
|
||||
}
|
||||
#if YARN_SOURCE_GENERATION_DEBUG_LOGGING
|
||||
Debug.Log($"Registering command {name} from method {implementation.Method.DeclaringType.FullName}.{implementation.Method.Name}");
|
||||
#endif
|
||||
|
||||
Library.RegisterFunction(name, implementation);
|
||||
}
|
||||
|
||||
public void AddCommandHandler(string commandName, MethodInfo methodInfo)
|
||||
{
|
||||
if (commandName.Contains(' '))
|
||||
{
|
||||
Debug.LogError($"Failed to register command {commandName}: command names are not allowed to contain spaces.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (_commands.ContainsKey(commandName))
|
||||
{
|
||||
Debug.LogError($"Failed to register command {commandName}: a command by this name has already been registered.");
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
_commands.Add(commandName, new CommandRegistration(commandName, methodInfo));
|
||||
}
|
||||
}
|
||||
public void RemoveCommandHandler(string commandName)
|
||||
{
|
||||
if (_commands.Remove(commandName) == false)
|
||||
{
|
||||
Debug.LogError($"Can't remove command {commandName}, because no command with this name is currently registered.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void RemoveFunction(string name)
|
||||
{
|
||||
if (Library.FunctionExists(name) == false)
|
||||
{
|
||||
Debug.LogError($"Cannot remove function {name}: no function with that name exists in the library");
|
||||
return;
|
||||
}
|
||||
Library.DeregisterFunction(name);
|
||||
}
|
||||
|
||||
public void SetupForProject(YarnProject yarnProject)
|
||||
{
|
||||
// no-op
|
||||
}
|
||||
|
||||
CommandDispatchResult ICommandDispatcher.DispatchCommand(string command, MonoBehaviour coroutineHost)
|
||||
{
|
||||
var commandPieces = new List<string>(DialogueRunner.SplitCommandText(command));
|
||||
|
||||
if (commandPieces.Count == 0)
|
||||
{
|
||||
// No text was found inside the command, so we won't be able to
|
||||
// find it.
|
||||
return new CommandDispatchResult(CommandDispatchResult.StatusType.CommandUnknown, YarnTask.CompletedTask);
|
||||
}
|
||||
|
||||
if (_commands.TryGetValue(commandPieces[0], out var registration))
|
||||
{
|
||||
// The first part of the command is the command name itself.
|
||||
// Remove it to get the collection of parameters that were
|
||||
// passed to the command.
|
||||
commandPieces.RemoveAt(0);
|
||||
|
||||
return registration.Invoke(coroutineHost, commandPieces);
|
||||
}
|
||||
else
|
||||
{
|
||||
return new CommandDispatchResult(CommandDispatchResult.StatusType.CommandUnknown);
|
||||
}
|
||||
}
|
||||
|
||||
private static Converter[] CreateConverters(MethodInfo method)
|
||||
{
|
||||
ParameterInfo[] parameterInfos = method.GetParameters();
|
||||
|
||||
Converter[] result = new Converter[parameterInfos.Length];
|
||||
|
||||
int i = 0;
|
||||
|
||||
foreach (var parameterInfo in parameterInfos)
|
||||
{
|
||||
if (parameterInfo.ParameterType.IsArray)
|
||||
{
|
||||
// Array parameters are permitted, but only if they're the
|
||||
// last parameter
|
||||
if (i != parameterInfos.Length - 1)
|
||||
{
|
||||
throw new ArgumentException($"Can't register method {method.Name}: Parameter {i + 1} ({parameterInfo.Name}): array parameters are required to be last.");
|
||||
}
|
||||
}
|
||||
result[i] = CreateConverter(parameterInfo, i);
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static System.Func<string, int, object?> CreateConverter(ParameterInfo parameter, int index)
|
||||
{
|
||||
var targetType = parameter.ParameterType;
|
||||
string name = parameter.Name;
|
||||
|
||||
if (targetType.IsArray)
|
||||
{
|
||||
// This parameter is a params array. Make a converter for that
|
||||
// array's element type; at dispatch time, we'll repeatedly call
|
||||
// it with the arguments found in the command.
|
||||
|
||||
var paramsArrayType = targetType.GetElementType();
|
||||
var elementConverter = CreateConverterFunction(paramsArrayType, name);
|
||||
return elementConverter;
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// This parameter is for a single value. Make a converter that
|
||||
// receives a single string,
|
||||
return CreateConverterFunction(targetType, name);
|
||||
}
|
||||
}
|
||||
|
||||
private static Converter CreateConverterFunction(Type targetType, string parameterName)
|
||||
{
|
||||
|
||||
// well, I mean...
|
||||
if (targetType == typeof(string)) { return (arg, i) => arg; }
|
||||
|
||||
// find the GameObject.
|
||||
if (typeof(GameObject).IsAssignableFrom(targetType))
|
||||
{
|
||||
return (arg, i) => GameObject.Find(arg);
|
||||
}
|
||||
|
||||
// find components of the GameObject with the component, if
|
||||
// available
|
||||
if (typeof(Component).IsAssignableFrom(targetType))
|
||||
{
|
||||
return (arg, i) =>
|
||||
{
|
||||
GameObject gameObject = GameObject.Find(arg);
|
||||
if (gameObject == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return gameObject.GetComponentInChildren(targetType);
|
||||
};
|
||||
}
|
||||
|
||||
// bools can take "true" or "false", or the parameter name.
|
||||
if (typeof(bool).IsAssignableFrom(targetType))
|
||||
{
|
||||
return (arg, i) =>
|
||||
{
|
||||
// If the argument is the name of the parameter, interpret
|
||||
// the argument as 'true'.
|
||||
if (arg.Equals(parameterName, StringComparison.InvariantCultureIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// If the argument can be parsed as boolean true or false,
|
||||
// return that result.
|
||||
if (bool.TryParse(arg, out bool res))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
// We can't parse the argument.
|
||||
throw new ArgumentException(
|
||||
$"Can't convert the given parameter at position {i + 1} (\"{arg}\") to parameter " +
|
||||
$"{parameterName} of type {typeof(bool).FullName}.");
|
||||
};
|
||||
}
|
||||
|
||||
// Fallback: try converting using IConvertible.
|
||||
return (arg, i) =>
|
||||
{
|
||||
try
|
||||
{
|
||||
return Convert.ChangeType(arg, targetType, CultureInfo.InvariantCulture);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new ArgumentException(
|
||||
$"Can't convert the given parameter at position {i + 1} (\"{arg}\") to parameter " +
|
||||
$"{parameterName} of type {targetType.FullName}: {e}", e);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
internal static HashSet<ActionRegistrationMethod> ActionRegistrationMethods = new HashSet<ActionRegistrationMethod>();
|
||||
|
||||
|
||||
public static void AddRegistrationMethod(ActionRegistrationMethod registerActions)
|
||||
{
|
||||
ActionRegistrationMethods.Add(registerActions);
|
||||
}
|
||||
|
||||
public void AddCommandHandler(string commandName, Func<object> handler)
|
||||
{
|
||||
this.AddCommandHandler(commandName, (Delegate)handler);
|
||||
}
|
||||
|
||||
public void RegisterFunctionDeclaration(string name, Type returnType, Type[] parameterTypes) { /* no-op */ }
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4391b446168f14c48b3836dc19ddc1b6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents the result of attempting to locate and call a command.
|
||||
/// </summary>
|
||||
/// <seealso cref="DispatchCommandToGameObject(Command, Action)"/>
|
||||
/// <seealso cref="DispatchCommandToRegisteredHandlers(Command, Action)"/>
|
||||
internal struct CommandDispatchResult
|
||||
{
|
||||
|
||||
internal enum ParameterParseStatusType
|
||||
{
|
||||
Succeeded,
|
||||
InvalidParameterType,
|
||||
InvalidParameterCount,
|
||||
}
|
||||
|
||||
internal enum StatusType
|
||||
{
|
||||
|
||||
Succeeded,
|
||||
|
||||
NoTargetFound,
|
||||
|
||||
TargetMissingComponent,
|
||||
|
||||
InvalidParameterCount,
|
||||
|
||||
InvalidParameter,
|
||||
|
||||
/// <summary>
|
||||
/// The command could not be found.
|
||||
/// </summary>
|
||||
CommandUnknown,
|
||||
};
|
||||
|
||||
internal StatusType Status;
|
||||
|
||||
internal string? Message;
|
||||
|
||||
internal YarnTask Task;
|
||||
|
||||
public CommandDispatchResult(StatusType status)
|
||||
{
|
||||
Status = status;
|
||||
Task = YarnTask.CompletedTask;
|
||||
Message = null;
|
||||
}
|
||||
public CommandDispatchResult(StatusType status, YarnTask task)
|
||||
{
|
||||
Status = status;
|
||||
Task = task;
|
||||
Message = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 30bd9b6f785e7433091f2a4e6eae7397
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
internal class DemoAction
|
||||
{
|
||||
public static async System.Threading.Tasks.Task DemoCommandAsync()
|
||||
{
|
||||
await System.Threading.Tasks.Task.Delay(1000);
|
||||
}
|
||||
}
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
internal class DefaultActions : MonoBehaviour
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.InitializeOnLoadMethod]
|
||||
#endif
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
public static void AddRegisterFunction()
|
||||
{
|
||||
// When the domain is reloaded, scripts are recompiled, or the game
|
||||
// starts, add RegisterActions as a method that populates a
|
||||
// DialogueRunner or Library with commands and functions.
|
||||
Actions.AddRegistrationMethod(RegisterActions);
|
||||
}
|
||||
|
||||
public static void RegisterActions(IActionRegistration target, RegistrationType registrationType)
|
||||
{
|
||||
// Register the built-in methods and commands from Yarn Spinner for Unity.
|
||||
target.AddCommandHandler<float>("wait", Wait);
|
||||
}
|
||||
|
||||
#region Commands
|
||||
/// <summary>
|
||||
/// Yarn Spinner defines two built-in commands: "wait", and "stop".
|
||||
/// Stop is defined inside the Virtual Machine (the compiler traps it
|
||||
/// and makes it a special case.) Wait is defined here in Unity.
|
||||
/// </summary>
|
||||
/// <param name="duration">How long to wait, in seconds.</param>
|
||||
[YarnCommand("wait")]
|
||||
public static IEnumerator Wait(float duration)
|
||||
{
|
||||
yield return new WaitForSeconds(duration);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b4a0cf97a220d5a4cb56a2509f5d51cb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,327 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains methods that allow adding and removing Yarn commands and
|
||||
/// functions.
|
||||
/// </summary>
|
||||
public interface IActionRegistration
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds a command handler. Dialogue will pause execution after the
|
||||
/// command is called.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When this command handler has been added, it can be called
|
||||
/// from your Yarn scripts like so:</para>
|
||||
///
|
||||
/// <code lang="yarn">
|
||||
/// <<commandName param1 param2>>
|
||||
/// </code>
|
||||
///
|
||||
/// <para>If <paramref name="handler"/> is a method that returns a <see
|
||||
/// cref="Coroutine"/>, when the command is run, the <see
|
||||
/// cref="DialogueRunner"/> will wait for the returned coroutine to stop
|
||||
/// before delivering any more content.</para>
|
||||
/// <para>If <paramref name="handler"/> is a method that returns an <see
|
||||
/// cref="IEnumerator"/>, when the command is run, the <see
|
||||
/// cref="DialogueRunner"/> will start a coroutine using that method and
|
||||
/// wait for that coroutine to stop before delivering any more content.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="commandName">The name of the command.</param>
|
||||
/// <param name="handler">The <see cref="CommandHandler"/> that will be
|
||||
/// invoked when the command is called.</param>
|
||||
void AddCommandHandler(string commandName, Delegate handler);
|
||||
|
||||
/// <inheritdoc cref="AddCommandHandler(string, Delegate)"/>
|
||||
/// <param name="commandName">The name of the command.</param>
|
||||
/// <param name="methodInfo">The method that will be invoked when the
|
||||
/// command is called.</param>
|
||||
void AddCommandHandler(string commandName, MethodInfo methodInfo);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a command handler.
|
||||
/// </summary>
|
||||
/// <param name="commandName">The name of the command to remove.</param>
|
||||
void RemoveCommandHandler(string commandName);
|
||||
|
||||
/// <summary>
|
||||
/// Add a new function that returns a value, so that it can be called
|
||||
/// from Yarn scripts.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>When this function has been registered, it can be called from
|
||||
/// your Yarn scripts like so:</para>
|
||||
///
|
||||
/// <code lang="yarn">
|
||||
/// <<if myFunction(1, 2) == true>>
|
||||
/// myFunction returned true!
|
||||
/// <<endif>>
|
||||
/// </code>
|
||||
///
|
||||
/// <para>The <c>call</c> command can also be used to invoke the function:</para>
|
||||
///
|
||||
/// <code lang="yarn">
|
||||
/// <<call myFunction(1, 2)>>
|
||||
/// </code>
|
||||
/// </remarks>
|
||||
/// <param name="name">The name of the function to add.</param>
|
||||
/// <param name="implementation">The <see cref="Delegate"/> that
|
||||
/// should be invoked when this function is called.</param>
|
||||
/// <seealso cref="Library"/>
|
||||
void AddFunction(string name, Delegate implementation);
|
||||
|
||||
/// <summary>
|
||||
/// Remove a registered function.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// After a function has been removed, it cannot be called from
|
||||
/// Yarn scripts.
|
||||
/// </remarks>
|
||||
/// <param name="name">The name of the function to remove.</param>
|
||||
/// <seealso cref="AddFunction(string, Delegate)"/>
|
||||
void RemoveFunction(string name);
|
||||
|
||||
/// <summary>
|
||||
/// Registers a function as existing, without supplying an implementation.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the function.</param>
|
||||
/// <param name="returnType">The return type of the function.</param>
|
||||
/// <param name="parameterTypes">The types of the function's parameters.</param>
|
||||
void RegisterFunctionDeclaration(string name, Type returnType, Type[] parameterTypes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Contains extension methods for <see cref="IActionRegistration"/>
|
||||
/// objects.
|
||||
/// </summary>
|
||||
public static class ActionRegistrationExtension
|
||||
{
|
||||
// These registrations for System.Action were generated by action-gyb.py
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler(this IActionRegistration registration, string commandName, System.Action handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1>(this IActionRegistration registration, string commandName, System.Action<T1> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2>(this IActionRegistration registration, string commandName, System.Action<T1, T2> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7, T8> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7, T8, T9> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(this IActionRegistration registration, string commandName, System.Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
|
||||
// These registrations for System.Threading.Tasks.Task were generated by action-gyb.py
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler(this IActionRegistration registration, string commandName, System.Func<System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1>(this IActionRegistration registration, string commandName, System.Func<T1, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2>(this IActionRegistration registration, string commandName, System.Func<T1, T2, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, System.Threading.Tasks.Task> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
|
||||
// These registrations for YarnTask were generated by action-gyb.py
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler(this IActionRegistration registration, string commandName, System.Func<YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1>(this IActionRegistration registration, string commandName, System.Func<T1, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2>(this IActionRegistration registration, string commandName, System.Func<T1, T2, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, YarnTask> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
|
||||
// These registrations for IEnumerator were generated by action-gyb.py
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler(this IActionRegistration registration, string commandName, System.Func<IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1>(this IActionRegistration registration, string commandName, System.Func<T1, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2>(this IActionRegistration registration, string commandName, System.Func<T1, T2, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, IEnumerator> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
|
||||
// These registrations for Coroutine were generated by action-gyb.py
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler(this IActionRegistration registration, string commandName, System.Func<Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1>(this IActionRegistration registration, string commandName, System.Func<T1, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2>(this IActionRegistration registration, string commandName, System.Func<T1, T2, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>
|
||||
public static void AddCommandHandler<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16>(this IActionRegistration registration, string commandName, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, Coroutine> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);
|
||||
|
||||
|
||||
/// <inheritdoc cref="IActionRegistration.AddFunction(string, Delegate)"/>
|
||||
/// <typeparam name="TResult">The result of the function.</typeparam>
|
||||
public static void AddFunction<TResult>(this IActionRegistration registration, string name, System.Func<TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, TResult>(this IActionRegistration registration, string name, System.Func<T1, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, T2, TResult>(this IActionRegistration registration, string name, System.Func<T1, T2, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, T2, T3, TResult>(this IActionRegistration registration, string name, System.Func<T1, T2, T3, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, T2, T3, T4, TResult>(this IActionRegistration registration, string name, System.Func<T1, T2, T3, T4, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, T2, T3, T4, T5, TResult>(this IActionRegistration registration, string name, System.Func<T1, T2, T3, T4, T5, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, T2, T3, T4, T5, T6, TResult>(this IActionRegistration registration, string name, System.Func<T1, T2, T3, T4, T5, T6, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, T2, T3, T4, T5, T6, T7, TResult>(this IActionRegistration registration, string name, System.Func<T1, T2, T3, T4, T5, T6, T7, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, T2, T3, T4, T5, T6, T7, T8, TResult>(this IActionRegistration registration, string name, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult>(this IActionRegistration registration, string name, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
/// <inheritdoc cref="AddFunction{TResult}(IActionRegistration, string, Func{TResult})"/>
|
||||
public static void AddFunction<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult>(this IActionRegistration registration, string name, System.Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, TResult> implementation) => registration.AddFunction(name, (Delegate)implementation);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c2617d5a5b00346a095c9910f778653e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
interface ICommandDispatcher : IActionRegistration
|
||||
{
|
||||
CommandDispatchResult DispatchCommand(string command, MonoBehaviour coroutineHost);
|
||||
|
||||
void SetupForProject(YarnProject yarnProject);
|
||||
|
||||
IEnumerable<ICommand> Commands { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4a7e8437a5e3540c7b04f1a91aec8809
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
|
||||
public abstract class YarnActionAttribute : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the command or function, as it exists in Yarn.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This value does not have to be the same as the name of the method.
|
||||
/// For example, you could have a method named "`WalkToPoint`", and
|
||||
/// expose it to Yarn as a command named "`walk_to_point`".
|
||||
/// </remarks>
|
||||
public string? Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="YarnActionAttribute"/>
|
||||
/// class.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the action. If not provided or <see
|
||||
/// langword="null"/>, the name of the method is used instead.</param>
|
||||
public YarnActionAttribute(string? name = null) => Name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2f0c2f9c8c291c54a866e5d2cbda947f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
#region Class/Interface
|
||||
|
||||
/// <summary>
|
||||
/// An attribute that marks a method on an object as a command.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// When a <see cref="DialogueRunner"/> receives a <see cref="Command"/>,
|
||||
/// and no command handler has been installed for the command, it splits it
|
||||
/// by spaces, and then checks to see if the second word, if any, is the
|
||||
/// name of an object.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// By default, it checks for any <see cref="GameObject"/>s in the scene. If
|
||||
/// one is found, it is checked to see if any of the <see
|
||||
/// cref="MonoBehaviour"/>s attached to the class has a <see
|
||||
/// cref="YarnCommandAttribute"/> whose <see
|
||||
/// cref="YarnActionAttribute.Name"/> matches the first word of the command.
|
||||
/// </para>
|
||||
/// <para>If the method is static, it will not try to use an object.</para>
|
||||
/// <para>If a method is found, its parameters are checked:</para>
|
||||
/// <list type="bullet">
|
||||
/// <item>
|
||||
/// If the method takes a single <see cref="string"/>[] parameter, the
|
||||
/// method is called, and will be passed an array containing all words in
|
||||
/// the command after the first two.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// If the method takes a number of parameters equal to the number of words
|
||||
/// in the command after the first two, it will be called with those words
|
||||
/// as parameters.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// If a parameter is a <see cref="GameObject"/>, we look up the object
|
||||
/// using <see cref="GameObject.Find(string)"/>. As per the API, the game
|
||||
/// object must be active.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// If a parameter is assignable to <see cref="Component"/>, we will locate
|
||||
/// the component based on the name of the object. As per the API of <see
|
||||
/// cref="GameObject.Find(string)"/>, the game object must be active.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// If a parameter is a <see cref="bool"/>, the string must be <c>true</c>
|
||||
/// or <c>false</c> (as defined by the standard converter for <see
|
||||
/// cref="string"/> to <see cref="bool"/>). However, we also allow for the
|
||||
/// string to equal the parameter name, case insensitive. This allows us to
|
||||
/// write commands with more self-documenting parameters, eg for a certain
|
||||
/// <c>Move(bool wait)</c>, you could write <c><<move wait>></c>
|
||||
/// instead of <c><<move true>></c>.
|
||||
/// </item>
|
||||
/// <item>
|
||||
/// For any other type, we will attempt to convert using <see
|
||||
/// cref="Convert.ChangeType(object, Type, IFormatProvider)"/> using the
|
||||
/// <see cref="System.Globalization.CultureInfo.InvariantCulture"/> culture.
|
||||
/// This means that you can implement <see cref="IConvertible"/> to add new
|
||||
/// accepted types. (Do be aware that it's a non-CLS compliant interface,
|
||||
/// according to its docs. Mono for Unity seems to implement it, but you may
|
||||
/// have trouble if you use any other CLS implementation.)
|
||||
/// </item>
|
||||
/// <item>Otherwise, it will not be called, and a warning will be
|
||||
/// issued.</item>
|
||||
/// </list>
|
||||
/// <para>This attribute may be attached to a coroutine or to an async
|
||||
/// method.</para>
|
||||
/// <para style="note">
|
||||
/// The <see cref="DialogueRunner"/> determines if the method is a coroutine
|
||||
/// if the method returns <see cref="IEnumerator"/>, or if the method
|
||||
/// returns a <see cref="Coroutine"/> or a task.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If the method is a coroutine, returns a <see cref="Coroutine"/>, or
|
||||
/// returns a task, the DialogueRunner will pause execution until the the
|
||||
/// coroutine or task ends.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class YarnCommandAttribute : YarnActionAttribute
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public YarnCommandAttribute(string? name = null) => Name = name;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 02d713a1d2f0543e1bc85a3a92d8b0be
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity.Attributes
|
||||
{
|
||||
public class YarnEnumParameterAttribute: YarnParameterAttribute
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
public YarnEnumParameterAttribute(string name) => Name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 16f4bd44fa86c426896ef49b7fd5b8ae
|
||||
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// Marks the method as a function to be registered with the running
|
||||
/// instance's library.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// See <see cref="Library.RegisterFunction(string, Delegate)"/> and the
|
||||
/// generic overloads for what is and is not valid.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// This will throw an error if you attempt to add a function that has
|
||||
/// more than 16 parameters, as that is the largest overload that
|
||||
/// <see cref="Func{TResult}"/> etc has.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Yarn Spinner for Unity finds methods with the YarnFunction attribute by
|
||||
/// reading your source code. If your project uses Unity 2021.1 or earlier,
|
||||
/// you will need to tell Yarn Spinner for Unity to do this manually, by
|
||||
/// opening the Window method and choosing Yarn Spinner -> Update Yarn
|
||||
/// Commands. You don't need to do this on later versions of Unity, as it
|
||||
/// will be done for you automatically when your code compiles.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public class YarnFunctionAttribute : YarnActionAttribute
|
||||
{
|
||||
public YarnFunctionAttribute(string? name = null) => Name = name;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 28f9b4504ad08b44eacded4f51e04639
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity.Attributes
|
||||
{
|
||||
public class YarnNodeParameterAttribute: YarnParameterAttribute {}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d85dfb63b41e44754af57565cc72b827
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity.Attributes
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
|
||||
public abstract class YarnParameterAttribute : Attribute
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 27cc4a9336b0d4543a8885b64dfde71a
|
||||
@@ -0,0 +1,33 @@
|
||||
all_types = ["System.Action", "System.Threading.Tasks.Task", "YarnTask", "IEnumerator", "Coroutine", "Awaitable", "UniTask"]
|
||||
|
||||
doc_template = '/// <inheritdoc cref="IActionRegistration.AddCommandHandler(string, Delegate)"/>'
|
||||
imp_template = "public static void AddCommandHandler(this IActionRegistration registration, string commandName, System.Func<{0}> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);"
|
||||
imp_numeric_template = "public static void AddCommandHandler<{1}>(this IActionRegistration registration, string commandName, System.Func<{1}, {0}> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);"
|
||||
|
||||
# System.Action has a slightly different signature so needs a tweaked template
|
||||
imp_action_template = "public static void AddCommandHandler(this IActionRegistration registration, string commandName, System.Action handler) => registration.AddCommandHandler(commandName, (Delegate)handler);"
|
||||
imp_action_numeric_template = "public static void AddCommandHandler<{1}>(this IActionRegistration registration, string commandName, System.Action<{1}> handler) => registration.AddCommandHandler(commandName, (Delegate)handler);"
|
||||
|
||||
def gyb(count, types):
|
||||
for type in types:
|
||||
print(f"// These registrations for {type} were generated by action-gyb.py")
|
||||
|
||||
# generating the registration for the 0 parameter form
|
||||
print(doc_template)
|
||||
|
||||
# true' if True else 'false'
|
||||
template = imp_template if type != "System.Action" else imp_action_template
|
||||
print(template.format(type))
|
||||
|
||||
# generating the registration for the 1->count parameter forms
|
||||
for i in range(count):
|
||||
print(doc_template)
|
||||
|
||||
# generating the <T1, T2> etc type values
|
||||
r = ["T" + str(x) for x in range(1, i + 2)]
|
||||
s = ", ".join(r)
|
||||
template = imp_numeric_template if type != "System.Action" else imp_action_numeric_template
|
||||
print(template.format(type, s))
|
||||
print()
|
||||
|
||||
gyb(16, all_types)
|
||||
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e1b7988914af942c8b524809f9eff521
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/dev.yarnspinner.unity/Runtime/DLLs.meta
Normal file
8
Packages/dev.yarnspinner.unity/Runtime/DLLs.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6275387d5a4a84c27ac9635ce0d8c14b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 66d59d06f4b6042ebbdc6ddb5cfd6846
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Packages/dev.yarnspinner.unity/Runtime/DLLs/Yarn.CsvHelper.dll
Normal file
BIN
Packages/dev.yarnspinner.unity/Runtime/DLLs/Yarn.CsvHelper.dll
Normal file
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3dc11aead6660429d84842694fd78636
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5186e8e45cbcd4996af2b12528a7c8b4
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b721597c44e042dbb3686427df41b0d
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd93870844160498a959623f2d278129
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2efe7f53fb2f2435098f70df43f076b8
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bac77fc94c754fd6b790add55fa8d74
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1284a3be320c944d289b7853b84200d4
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b0cfe671eb0b54c9c84da51f48b5f20e
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: db1ffba9cf3ef4df29c9f1672084c041
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91dd46f259b3f467db8cf76bb5577dc2
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fd6dd81f4573f40a58a4bcae7e0581ef
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fc92f810bab0c459cadf8864aa8e1215
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d15976e3adf9a487396757d18384de5c
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
7034
Packages/dev.yarnspinner.unity/Runtime/DLLs/YarnSpinner.Compiler.xml
Normal file
7034
Packages/dev.yarnspinner.unity/Runtime/DLLs/YarnSpinner.Compiler.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19880de710276429eb9ecebce681f8c7
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
BIN
Packages/dev.yarnspinner.unity/Runtime/DLLs/YarnSpinner.dll
Normal file
BIN
Packages/dev.yarnspinner.unity/Runtime/DLLs/YarnSpinner.dll
Normal file
Binary file not shown.
@@ -0,0 +1,33 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d55bd69d2ea984d18a2f662c811c1196
|
||||
PluginImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
iconMap: {}
|
||||
executionOrder: {}
|
||||
defineConstraints: []
|
||||
isPreloaded: 0
|
||||
isOverridable: 1
|
||||
isExplicitlyReferenced: 0
|
||||
validateReferences: 1
|
||||
platformData:
|
||||
- first:
|
||||
Any:
|
||||
second:
|
||||
enabled: 1
|
||||
settings: {}
|
||||
- first:
|
||||
Editor: Editor
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
DefaultValueInitialized: true
|
||||
- first:
|
||||
Windows Store Apps: WindowsStoreApps
|
||||
second:
|
||||
enabled: 0
|
||||
settings:
|
||||
CPU: AnyCPU
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
3645
Packages/dev.yarnspinner.unity/Runtime/DLLs/YarnSpinner.xml
Normal file
3645
Packages/dev.yarnspinner.unity/Runtime/DLLs/YarnSpinner.xml
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,7 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 45ef7c8cb3b944679826ad6a5fa6accb
|
||||
TextScriptImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 207dca81862fd4284b4352709cb2f513
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
public class DialogueOption
|
||||
{
|
||||
/// <summary>
|
||||
/// The ID of this dialogue option
|
||||
/// </summary>
|
||||
public int DialogueOptionID;
|
||||
|
||||
/// <summary>
|
||||
/// The ID of the dialogue option's text
|
||||
/// </summary>
|
||||
public string TextID = "<unknown>";
|
||||
|
||||
/// <summary>
|
||||
/// The line for this dialogue option
|
||||
/// </summary>
|
||||
public LocalizedLine Line = LocalizedLine.InvalidLine;
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this value should be presented as available
|
||||
/// or not.
|
||||
/// </summary>
|
||||
public bool IsAvailable;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a54122019ab1c4d0ca3bba3f0e6326aa
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,33 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Reflection;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
|
||||
public partial class DialogueRunner : IActionRegistration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public void AddCommandHandler(string commandName, Delegate handler) => CommandDispatcher.AddCommandHandler(commandName, handler);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddCommandHandler(string commandName, MethodInfo method) => CommandDispatcher.AddCommandHandler(commandName, method);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveCommandHandler(string commandName) => CommandDispatcher.RemoveCommandHandler(commandName);
|
||||
|
||||
|
||||
/// <inheritdoc />
|
||||
public void AddFunction(string name, Delegate implementation) => CommandDispatcher.AddFunction(name, implementation);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void RemoveFunction(string name) => CommandDispatcher.RemoveFunction(name);
|
||||
|
||||
public void RegisterFunctionDeclaration(string name, Type returnType, Type[] parameterTypes) { /* no-op */ }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6377372abcfac4709a752deefbf304c1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,333 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
public partial class DialogueRunner
|
||||
{
|
||||
/// <summary>
|
||||
/// Loads all variables from the requested file in persistent storage
|
||||
/// into the Dialogue Runner's variable storage.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method loads the file <paramref name="saveFileName"/> from the
|
||||
/// persistent data storage and attempts to read it as JSON. This is
|
||||
/// then deserialised and loaded into the <see cref="VariableStorage"/>.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// The loaded information can be stored via the <see
|
||||
/// cref="SaveStateToPersistentStorage"/> method.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="saveFileName">the name the save file should have on
|
||||
/// disc, including any file extension</param>
|
||||
/// <returns><see langword="true"/> if the variables were successfully
|
||||
/// loaded from the player preferences; <see langword="false"/>
|
||||
/// otherwise.</returns>
|
||||
public bool LoadStateFromPersistentStorage(string saveFileName)
|
||||
{
|
||||
if (this.variableStorage == null)
|
||||
{
|
||||
Debug.LogWarning($"Can't load state from persistent storage: {nameof(variableStorage)} is not set");
|
||||
return false;
|
||||
}
|
||||
|
||||
var path = System.IO.Path.Combine(Application.persistentDataPath, saveFileName);
|
||||
|
||||
try
|
||||
{
|
||||
var saveData = System.IO.File.ReadAllText(path);
|
||||
var dictionaries = DeserializeAllVariablesFromJSON(saveData);
|
||||
|
||||
this.variableStorage.SetAllVariables(dictionaries.Item1, dictionaries.Item2, dictionaries.Item3);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to load save state at {path}: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Saves all variables from variable storage into the persistent
|
||||
/// storage.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method attempts to writes the contents of <see
|
||||
/// cref="VariableStorage"/> as a JSON file and saves it to the
|
||||
/// persistent data storage under the file name <paramref
|
||||
/// name="saveFileName"/>. The saved information can be loaded via the
|
||||
/// <see cref="LoadStateFromPersistentStorage"/> method.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// If <paramref name="saveFileName"/> already exists, it will be
|
||||
/// overwritten, not appended.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="saveFileName">the name the save file should have on
|
||||
/// disc, including any file extension</param>
|
||||
/// <returns><see langword="true"/> if the variables were successfully
|
||||
/// written into the player preferences; <see langword="false"/>
|
||||
/// otherwise.</returns>
|
||||
public bool SaveStateToPersistentStorage(string saveFileName)
|
||||
{
|
||||
var data = SerializeAllVariablesToJSON();
|
||||
var path = System.IO.Path.Combine(Application.persistentDataPath, saveFileName);
|
||||
|
||||
try
|
||||
{
|
||||
System.IO.File.WriteAllText(path, data);
|
||||
return true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Debug.LogError($"Failed to save state to {path}: {e.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// takes in a JSON string and converts it into a tuple of dictionaries
|
||||
// intended to let you just dump these straight into the variable storage
|
||||
// throws exceptions if unable to convert or if the conversion half works
|
||||
private (Dictionary<string, float>, Dictionary<string, string>, Dictionary<string, bool>) DeserializeAllVariablesFromJSON(string jsonData)
|
||||
{
|
||||
SaveData data = JsonUtility.FromJson<SaveData>(jsonData);
|
||||
|
||||
if (data.floatKeys == null || data.floatValues == null)
|
||||
{
|
||||
throw new ArgumentException("Provided JSON string was not able to extract numeric variables");
|
||||
}
|
||||
if (data.stringKeys == null || data.stringValues == null)
|
||||
{
|
||||
throw new ArgumentException("Provided JSON string was not able to extract string variables");
|
||||
}
|
||||
if (data.boolKeys == null || data.boolValues == null)
|
||||
{
|
||||
throw new ArgumentException("Provided JSON string was not able to extract boolean variables");
|
||||
}
|
||||
|
||||
if (data.floatKeys.Length != data.floatValues.Length)
|
||||
{
|
||||
throw new ArgumentException("Number of keys and values of numeric variables does not match");
|
||||
}
|
||||
if (data.stringKeys.Length != data.stringValues.Length)
|
||||
{
|
||||
throw new ArgumentException("Number of keys and values of string variables does not match");
|
||||
}
|
||||
if (data.boolKeys.Length != data.boolValues.Length)
|
||||
{
|
||||
throw new ArgumentException("Number of keys and values of boolean variables does not match");
|
||||
}
|
||||
|
||||
var floats = new Dictionary<string, float>();
|
||||
for (int i = 0; i < data.floatValues.Length; i++)
|
||||
{
|
||||
floats.Add(data.floatKeys[i], data.floatValues[i]);
|
||||
}
|
||||
var strings = new Dictionary<string, string>();
|
||||
for (int i = 0; i < data.stringValues.Length; i++)
|
||||
{
|
||||
strings.Add(data.stringKeys[i], data.stringValues[i]);
|
||||
}
|
||||
var bools = new Dictionary<string, bool>();
|
||||
for (int i = 0; i < data.boolValues.Length; i++)
|
||||
{
|
||||
bools.Add(data.boolKeys[i], data.boolValues[i]);
|
||||
}
|
||||
|
||||
return (floats, strings, bools);
|
||||
}
|
||||
private string SerializeAllVariablesToJSON()
|
||||
{
|
||||
if (this.variableStorage == null)
|
||||
{
|
||||
throw new InvalidOperationException("Can't save variables to JSON: {nameof(variableStorage)} is not set");
|
||||
}
|
||||
|
||||
(var floats, var strings, var bools) = this.variableStorage.GetAllVariables();
|
||||
|
||||
SaveData data = new SaveData();
|
||||
data.floatKeys = floats.Keys.ToArray();
|
||||
data.floatValues = floats.Values.ToArray();
|
||||
data.stringKeys = strings.Keys.ToArray();
|
||||
data.stringValues = strings.Values.ToArray();
|
||||
data.boolKeys = bools.Keys.ToArray();
|
||||
data.boolValues = bools.Values.ToArray();
|
||||
|
||||
return JsonUtility.ToJson(data, true);
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
private struct SaveData
|
||||
{
|
||||
public string[]? floatKeys;
|
||||
public float[]? floatValues;
|
||||
public string[]? stringKeys;
|
||||
public string[]? stringValues;
|
||||
public string[]? boolKeys;
|
||||
public bool[]? boolValues;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Splits input into a number of non-empty sub-strings, separated by
|
||||
/// whitespace, and grouping double-quoted strings into a single
|
||||
/// sub-string.
|
||||
/// </summary>
|
||||
/// <param name="input">The string to split.</param>
|
||||
/// <returns>A collection of sub-strings.</returns>
|
||||
/// <remarks>
|
||||
/// This method behaves similarly to the <see cref="string.Split(char[],
|
||||
/// StringSplitOptions)"/> method with the <see
|
||||
/// cref="StringSplitOptions"/> parameter set to <see
|
||||
/// cref="StringSplitOptions.RemoveEmptyEntries"/>, with the following
|
||||
/// differences:
|
||||
///
|
||||
/// <list type="bullet">
|
||||
/// <item>Text that appears inside a pair of double-quote characters
|
||||
/// will not be split.</item>
|
||||
///
|
||||
/// <item>Text that appears after a double-quote character and before
|
||||
/// the end of the input will not be split (that is, an unterminated
|
||||
/// double-quoted string will be treated as though it had been
|
||||
/// terminated at the end of the input.)</item>
|
||||
///
|
||||
/// <item>When inside a pair of double-quote characters, the string
|
||||
/// <c>\\</c> will be converted to <c>\</c>, and the string <c>\"</c>
|
||||
/// will be converted to <c>"</c>.</item>
|
||||
/// </list>
|
||||
/// </remarks>
|
||||
public static IEnumerable<string> SplitCommandText(string input)
|
||||
{
|
||||
var reader = new System.IO.StringReader(input.Normalize());
|
||||
|
||||
int c;
|
||||
|
||||
var results = new List<string>();
|
||||
var currentComponent = new System.Text.StringBuilder();
|
||||
|
||||
while ((c = reader.Read()) != -1)
|
||||
{
|
||||
if (char.IsWhiteSpace((char)c))
|
||||
{
|
||||
if (currentComponent.Length > 0)
|
||||
{
|
||||
// We've reached the end of a run of visible characters.
|
||||
// Add this run to the result list and prepare for the
|
||||
// next one.
|
||||
results.Add(currentComponent.ToString());
|
||||
currentComponent.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
// We encountered a whitespace character, but didn't
|
||||
// have any characters queued up. Skip this character.
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
else if (c == '\"')
|
||||
{
|
||||
// We've entered a quoted string!
|
||||
while (true)
|
||||
{
|
||||
c = reader.Read();
|
||||
if (c == -1)
|
||||
{
|
||||
// Oops, we ended the input while parsing a quoted
|
||||
// string! Dump our current word immediately and
|
||||
// return.
|
||||
results.Add(currentComponent.ToString());
|
||||
return results;
|
||||
}
|
||||
else if (c == '\\')
|
||||
{
|
||||
// Possibly an escaped character!
|
||||
var next = reader.Peek();
|
||||
if (next == '\\' || next == '\"')
|
||||
{
|
||||
// It is! Skip the \ and use the character after
|
||||
// it.
|
||||
reader.Read();
|
||||
currentComponent.Append((char)next);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Oops, an invalid escape. Add the \ and
|
||||
// whatever is after it.
|
||||
currentComponent.Append((char)c);
|
||||
}
|
||||
}
|
||||
else if (c == '\"')
|
||||
{
|
||||
// The end of a string!
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Any other character. Add it to the buffer.
|
||||
currentComponent.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
results.Add(currentComponent.ToString());
|
||||
currentComponent.Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
currentComponent.Append((char)c);
|
||||
}
|
||||
}
|
||||
|
||||
if (currentComponent.Length > 0)
|
||||
{
|
||||
results.Add(currentComponent.ToString());
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public static bool IsInPlaymode
|
||||
{
|
||||
get
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!UnityEditor.EditorApplication.isPlaying)
|
||||
{
|
||||
// We are not in playmode at all.
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!UnityEditor.EditorApplication.isPlayingOrWillChangePlaymode)
|
||||
{
|
||||
// We are in playmode, but we're about to change out of
|
||||
// playmode.
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public static DialogueRunner? FindRunner(Component component)
|
||||
{
|
||||
var runner = component.GetComponentInParent<DialogueRunner>();
|
||||
if (runner == null)
|
||||
{
|
||||
runner = FindAnyObjectByType<DialogueRunner>();
|
||||
}
|
||||
return runner;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 99fee005c8f914882ba054b728737260
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4bec29c0a230741bdac901dba8da47ee
|
||||
timeCreated: 1444251733
|
||||
licenseType: Pro
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: edc8578c24bab4640a120cef545e2244
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
#nullable enable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
using Yarn.Unity.Attributes;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
public sealed class BuiltinLocalisedLineProvider : LineProviderBehaviour, ILineProvider
|
||||
{
|
||||
public override string LocaleCode
|
||||
{
|
||||
get => _textLocaleCode;
|
||||
set => _textLocaleCode = value;
|
||||
}
|
||||
|
||||
[SerializeField, Language] private string _textLocaleCode = System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
|
||||
[SerializeField, Language] private string _assetLocaleCode = System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
|
||||
|
||||
[SerializeField] private bool _useFallback = false;
|
||||
|
||||
[ShowIf(nameof(_useFallback))]
|
||||
[SerializeField, Language] private string _fallbackLocaleCode = System.Globalization.CultureInfo.CurrentCulture.TwoLetterISOLanguageName;
|
||||
|
||||
public string AssetLocaleCode
|
||||
{
|
||||
get => _assetLocaleCode;
|
||||
set => _assetLocaleCode = value;
|
||||
}
|
||||
|
||||
private Markup.LineParser lineParser = new Markup.LineParser();
|
||||
private Markup.BuiltInMarkupReplacer builtInReplacer = new Markup.BuiltInMarkupReplacer();
|
||||
|
||||
public override void RegisterMarkerProcessor(string attributeName, Markup.IAttributeMarkerProcessor markerProcessor)
|
||||
{
|
||||
lineParser.RegisterMarkerProcessor(attributeName, markerProcessor);
|
||||
}
|
||||
public override void DeregisterMarkerProcessor(string attributeName)
|
||||
{
|
||||
lineParser.DeregisterMarkerProcessor(attributeName);
|
||||
}
|
||||
|
||||
void Awake()
|
||||
{
|
||||
lineParser.RegisterMarkerProcessor("select", builtInReplacer);
|
||||
lineParser.RegisterMarkerProcessor("plural", builtInReplacer);
|
||||
lineParser.RegisterMarkerProcessor("ordinal", builtInReplacer);
|
||||
}
|
||||
|
||||
public override async YarnTask<LocalizedLine> GetLocalizedLineAsync(Line line, CancellationToken cancellationToken)
|
||||
{
|
||||
string sourceLineID = line.ID;
|
||||
|
||||
string[] metadata = System.Array.Empty<string>();
|
||||
|
||||
// Check to see if this line shadows another. If it does, we'll use
|
||||
// that line's text and asset.
|
||||
if (YarnProject != null)
|
||||
{
|
||||
metadata = YarnProject.lineMetadata?.GetMetadata(line.ID) ?? System.Array.Empty<string>();
|
||||
|
||||
var shadowLineSource = YarnProject.lineMetadata?.GetShadowLineSource(line.ID);
|
||||
|
||||
if (shadowLineSource != null)
|
||||
{
|
||||
sourceLineID = shadowLineSource;
|
||||
}
|
||||
}
|
||||
|
||||
string? text = GetLocalizedString(sourceLineID);
|
||||
Object? asset = await GetLocalizedAssetAsync(sourceLineID);
|
||||
|
||||
if (text == null)
|
||||
{
|
||||
// No line available.
|
||||
Debug.LogWarning($"Localization {LocaleCode} does not contain an entry for line {line.ID}", this);
|
||||
return LocalizedLine.InvalidLine;
|
||||
}
|
||||
|
||||
var parseResult = lineParser.ParseString(Markup.LineParser.ExpandSubstitutions(text, line.Substitutions), LocaleCode);
|
||||
|
||||
return new LocalizedLine
|
||||
{
|
||||
Text = parseResult,
|
||||
RawText = text,
|
||||
TextID = line.ID,
|
||||
Asset = asset,
|
||||
Metadata = metadata,
|
||||
};
|
||||
}
|
||||
|
||||
private Localization GetLocalization(string locale)
|
||||
{
|
||||
if (YarnProject == null)
|
||||
{
|
||||
throw new System.InvalidOperationException($"Can't get localized line: no Yarn Project set");
|
||||
}
|
||||
|
||||
Localization loc = YarnProject.GetLocalization(locale);
|
||||
|
||||
if (loc == null)
|
||||
{
|
||||
throw new System.InvalidOperationException($"Can't get localized line: Yarn Project has no localisation for {locale}");
|
||||
}
|
||||
|
||||
return loc;
|
||||
}
|
||||
|
||||
private string? GetLocalizedString(string sourceLineID)
|
||||
{
|
||||
|
||||
var baseLoc = GetLocalization(_textLocaleCode);
|
||||
string? text = baseLoc.GetLocalizedString(sourceLineID);
|
||||
|
||||
if (text != null)
|
||||
{
|
||||
return text;
|
||||
}
|
||||
|
||||
if (_useFallback)
|
||||
{
|
||||
var fallbackLoc = GetLocalization(_fallbackLocaleCode);
|
||||
return fallbackLoc.GetLocalizedString(sourceLineID);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private async YarnTask<Object?> GetLocalizedAssetAsync(string sourceLineID)
|
||||
{
|
||||
var baseLoc = GetLocalization(_assetLocaleCode);
|
||||
Object? result = await baseLoc.GetLocalizedObjectAsync<Object>(sourceLineID);
|
||||
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (_useFallback)
|
||||
{
|
||||
var fallbackLoc = GetLocalization(_fallbackLocaleCode);
|
||||
return await fallbackLoc.GetLocalizedObjectAsync<Object>(sourceLineID);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async override YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken)
|
||||
{
|
||||
if (YarnProject == null)
|
||||
{
|
||||
// We don't have a Yarn Project, so there's no preparation we
|
||||
// can do. do.
|
||||
return;
|
||||
}
|
||||
|
||||
var assetLocalization = YarnProject.GetLocalization(AssetLocaleCode);
|
||||
|
||||
if (assetLocalization.UsesAddressableAssets)
|
||||
{
|
||||
// The localization uses addressable assets. Ensure that these
|
||||
// assets are pre-loaded.
|
||||
var tasks = new List<YarnTask<Object?>>();
|
||||
|
||||
foreach (var id in lineIDs)
|
||||
{
|
||||
var task = assetLocalization.GetLocalizedObjectAsync<Object>(id);
|
||||
tasks.Add(task);
|
||||
}
|
||||
|
||||
await YarnTask.WhenAll(tasks);
|
||||
}
|
||||
else
|
||||
{
|
||||
// The localization uses direct references. No need to pre-load
|
||||
// the assets - they were loaded with the scene.
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6b6798449caec4a32a5ad27badb06624
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains methods for accessing assets of a given type stored within an
|
||||
/// object.
|
||||
/// </summary>
|
||||
public interface IAssetProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to fetch an asset of type <typeparamref name="T"/> from the
|
||||
/// object.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the assets.</typeparam>
|
||||
/// <param name="result">On return, the fetched asset, or <see
|
||||
/// langword="null"/>.</param>
|
||||
/// <returns><see langword="true"/> if an asset was fetched; <see
|
||||
/// langword="false"/> otherwise.</returns>
|
||||
public bool TryGetAsset<T>([System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out T? result) where T : UnityEngine.Object;
|
||||
|
||||
/// <summary>
|
||||
/// Gets a collection of assets of type <typeparamref name="T"/> from
|
||||
/// the target.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of the asset.</typeparam>
|
||||
/// <returns>A collection of assets. This collection may be
|
||||
/// empty.</returns>
|
||||
public IEnumerable<T> GetAssetsOfType<T>() where T : UnityEngine.Object;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1e028f35c1f4a4b06bc175cf06aa72c6
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
using Yarn.Markup;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains methods for retrieving user-facing localized content, given
|
||||
/// non-localized line IDs.
|
||||
/// </summary>
|
||||
public interface ILineProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="YarnProject"/> that contains the localized data for
|
||||
/// lines.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This property is set at run-time by the object that will be requesting
|
||||
/// content (typically a <see cref="DialogueRunner"/>).
|
||||
/// </remarks>
|
||||
public YarnProject? YarnProject { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the line provider's current locale identifier, as a BCP-47 code.
|
||||
/// </summary>
|
||||
public string LocaleCode { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Prepares and returns a <see cref="LocalizedLine"/> from the specified
|
||||
/// <see cref="Yarn.Line"/>.
|
||||
/// </summary>
|
||||
/// <param name="line">The <see cref="Yarn.Line"/> to produce the <see
|
||||
/// cref="LocalizedLine"/> from.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that indicates
|
||||
/// whether the process of fetching the localised version of <paramref
|
||||
/// name="line"/> should be cancelled.</param>
|
||||
/// <returns>A localized line, ready to be presented to the
|
||||
/// player.</returns>
|
||||
public YarnTask<LocalizedLine> GetLocalizedLineAsync(Line line, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Signals to the line provider that lines with the provided line IDs may
|
||||
/// be presented shortly.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// This method allows implementing classes a chance to prepare any
|
||||
/// neccessary resources needed to present these lines, like pre-loading
|
||||
/// voice-over audio. The default implementation does nothing.
|
||||
/// </para>
|
||||
/// <para style="info">
|
||||
/// Not every line may run; this method serves as a way to give the line
|
||||
/// provider advance notice that a line <i>may</i> run, not <i>will</i> run.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
/// <param name="lineIDs">A collection of line IDs that the line provider
|
||||
/// should prepare for.</param>
|
||||
/// <param name="cancellationToken">A cancellation token that indicates
|
||||
/// whether the operation should be cancelled.</param>
|
||||
public YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new marker processor to the line provider.
|
||||
/// </summary>
|
||||
/// <param name="attributeName">The name of the markers to use <paramref
|
||||
/// name="markerProcessor"/> for.</param>
|
||||
/// <param name="markerProcessor">The marker processor to add.</param>
|
||||
public void RegisterMarkerProcessor(string attributeName, Yarn.Markup.IAttributeMarkerProcessor markerProcessor);
|
||||
|
||||
/// <summary>
|
||||
/// Removes all marker processors that handle markers named <paramref
|
||||
/// name="attributeName"/>.
|
||||
/// </summary>
|
||||
/// <param name="attributeName">The name of the marker to remove processors
|
||||
/// for.</param>
|
||||
public void DeregisterMarkerProcessor(string attributeName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="MonoBehaviour"/> that produces <see
|
||||
/// cref="LocalizedLine"/>s, for use in Dialogue Presenters.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <see cref="DialogueRunner"/>s use a <see cref="LineProviderBehaviour"/>
|
||||
/// to get <see cref="LocalizedLine"/>s, which contain the localized
|
||||
/// information that is presented to the player through dialogue presenters.
|
||||
/// </remarks>
|
||||
public abstract class LineProviderBehaviour : MonoBehaviour, ILineProvider
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
public abstract YarnTask<LocalizedLine> GetLocalizedLineAsync(Line line, CancellationToken cancellationToken);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public YarnProject? YarnProject { get; set; }
|
||||
|
||||
/// <inheritdoc/>
|
||||
public virtual YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken)
|
||||
{
|
||||
// No-op by default.
|
||||
return YarnTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract string LocaleCode { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called by Unity when the <see cref="LineProviderBehaviour"/> has
|
||||
/// first appeared in the scene.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is <see langword="public"/> <see langword="virtual"/> to
|
||||
/// allow subclasses to override it.
|
||||
/// </remarks>
|
||||
public virtual void Start()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract void RegisterMarkerProcessor(string attributeName, IAttributeMarkerProcessor markerProcessor);
|
||||
|
||||
/// <inheritdoc/>
|
||||
public abstract void DeregisterMarkerProcessor(string attributeName);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1724d534cf292400da295a39a3a8d97c
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using UnityEngine;
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Represents a line, ready to be presented to the user in the localisation
|
||||
/// they have specified.
|
||||
/// </summary>
|
||||
public class LocalizedLine
|
||||
{
|
||||
/// <summary>
|
||||
/// DialogueLine's ID
|
||||
/// </summary>
|
||||
public string TextID = "<unknown>";
|
||||
|
||||
/// <summary>
|
||||
/// DialogueLine's inline expression's substitution
|
||||
/// </summary>
|
||||
public string[] Substitutions = System.Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// DialogueLine's text
|
||||
/// </summary>
|
||||
public string? RawText;
|
||||
|
||||
/// <summary>
|
||||
/// Any metadata associated with this line.
|
||||
/// </summary>
|
||||
public string[] Metadata = System.Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// The name of the character, if present.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This value will be <see langword="null"/> if the line does not have
|
||||
/// a character name.
|
||||
/// </remarks>
|
||||
public string? CharacterName
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Text.TryGetAttributeWithName("character", out var characterNameAttribute))
|
||||
{
|
||||
if (characterNameAttribute.Properties.TryGetValue("name", out var value))
|
||||
{
|
||||
return value.StringValue;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The asset associated with this line, if any.
|
||||
/// </summary>
|
||||
public Object? Asset;
|
||||
|
||||
/// <summary>
|
||||
/// The object that created this line.
|
||||
/// Most of the time will be the <see cref="DialogueRunner"/> that passed the presenter the line.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This exists for situations where you need the dialogue runner (or your custom equivalent) to send back messages.
|
||||
/// In particular this is used by the <see cref="VoiceOverPresenter"/> to get a reference to the dialogue runner to advance lines after playback is finished without needing a specific reference.
|
||||
/// Allowing the presenter to be reused across multiple runners.
|
||||
/// </remarks>
|
||||
public object? Source;
|
||||
|
||||
/// <summary>
|
||||
/// The underlying <see cref="Yarn.Markup.MarkupParseResult"/> for this
|
||||
/// line.
|
||||
/// </summary>
|
||||
public Markup.MarkupParseResult Text { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The underlying <see cref="Yarn.Markup.MarkupParseResult"/> for this
|
||||
/// line, with any `character` attribute removed.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the line has no `character` attribute, this method returns the
|
||||
/// same value as <see cref="Text"/>.
|
||||
/// </remarks>
|
||||
public Markup.MarkupParseResult TextWithoutCharacterName
|
||||
{
|
||||
get
|
||||
{
|
||||
// If a 'character' attribute is present, remove its text
|
||||
if (Text.TryGetAttributeWithName("character", out var characterNameAttribute))
|
||||
{
|
||||
// because of how we delete the text we also clear up the attributes
|
||||
// most of the time this is the right play
|
||||
// however the character feels important enough to add it back in
|
||||
var characterless = Text.DeleteRange(characterNameAttribute);
|
||||
characterless.Attributes.Add(characterNameAttribute);
|
||||
return characterless;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="LocalizedLine"/> object that represents content not
|
||||
/// being found.
|
||||
/// </summary>
|
||||
public static readonly LocalizedLine InvalidLine = new LocalizedLine
|
||||
{
|
||||
Asset = null,
|
||||
Metadata = System.Array.Empty<string>(),
|
||||
RawText = "!! ERROR: Missing line!",
|
||||
Substitutions = System.Array.Empty<string>(),
|
||||
TextID = "<missing>",
|
||||
Text = new Markup.MarkupParseResult("!! ERROR: Missing line!", new System.Collections.Generic.List<Markup.MarkupAttribute>())
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5fca305f6aaeb461381b51f8f37c31b1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,228 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
#if USE_UNITY_LOCALIZATION
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Localization;
|
||||
using UnityEngine.Localization.Metadata;
|
||||
using UnityEngine.Localization.Settings;
|
||||
using UnityEngine.Localization.Tables;
|
||||
using UnityEngine.ResourceManagement.Exceptions;
|
||||
using Yarn.Markup;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity.UnityLocalization
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Contains Yarn Spinner related metadata for Unity string table entries.
|
||||
/// </summary>
|
||||
[System.Serializable]
|
||||
public class LineMetadata : IMetadata
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the Yarn node that this line came from.
|
||||
/// </summary>
|
||||
public string nodeName = "";
|
||||
|
||||
/// <summary>
|
||||
/// The <c>#hashtags</c> present on the line.
|
||||
/// </summary>
|
||||
public string[] tags = System.Array.Empty<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the line ID indicated by any shadow tag contained in this
|
||||
/// metadata, if present.
|
||||
/// </summary>
|
||||
public string? ShadowLineSource
|
||||
{
|
||||
get
|
||||
{
|
||||
foreach (var metadataEntry in tags)
|
||||
{
|
||||
if (metadataEntry.StartsWith("shadow:"))
|
||||
{
|
||||
// This is a shadow line. Return the line ID that it's
|
||||
// shadowing.
|
||||
return "line:" + metadataEntry.Substring("shadow:".Length);
|
||||
}
|
||||
}
|
||||
|
||||
// The line had metadata, but it wasn't a shadow line.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A line provider that uses the Unity Localization system to get localized
|
||||
/// content for Yarn lines.
|
||||
/// </summary>
|
||||
public partial class UnityLocalisedLineProvider : LineProviderBehaviour
|
||||
{
|
||||
// the string table asset that has all of our (hopefully) localised
|
||||
// strings inside
|
||||
[SerializeField] internal LocalizedStringTable? stringsTable;
|
||||
[SerializeField] internal LocalizedAssetTable? assetTable;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string LocaleCode
|
||||
{
|
||||
get => LocalizationSettings.SelectedLocale.Identifier.Code;
|
||||
set
|
||||
{
|
||||
Locale? locale = LocalizationSettings.AvailableLocales.GetLocale(value);
|
||||
if (locale == null)
|
||||
{
|
||||
throw new System.InvalidOperationException($"Can't set locale to {value}: no such locale has been configured");
|
||||
}
|
||||
LocalizationSettings.SelectedLocale = locale;
|
||||
}
|
||||
}
|
||||
|
||||
private LineParser lineParser = new LineParser();
|
||||
private BuiltInMarkupReplacer builtInReplacer = new BuiltInMarkupReplacer();
|
||||
|
||||
void Awake()
|
||||
{
|
||||
lineParser.RegisterMarkerProcessor("select", builtInReplacer);
|
||||
lineParser.RegisterMarkerProcessor("plural", builtInReplacer);
|
||||
lineParser.RegisterMarkerProcessor("ordinal", builtInReplacer);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override async YarnTask<LocalizedLine> GetLocalizedLineAsync(Line line, CancellationToken cancellationToken)
|
||||
{
|
||||
if (stringsTable == null || stringsTable.IsEmpty)
|
||||
{
|
||||
throw new System.InvalidOperationException($"Tried to get localised line for {line.ID}, but no string table has been set.");
|
||||
}
|
||||
|
||||
var getStringOp = LocalizationSettings.StringDatabase.GetTableEntryAsync(stringsTable.TableReference, line.ID, null, FallbackBehavior.UseFallback);
|
||||
var entry = await YarnTask.WaitForAsyncOperation(getStringOp, cancellationToken);
|
||||
|
||||
// Attempt to fetch metadata tags for this line from the string
|
||||
// table
|
||||
var metadata = entry.Entry?.SharedEntry.Metadata.GetMetadata<LineMetadata>();
|
||||
|
||||
// Get the text from the entry
|
||||
var text = entry.Entry?.LocalizedValue
|
||||
?? $"!! Error: Missing localisation for line {line.ID} in string table {entry.Table.LocaleIdentifier}";
|
||||
|
||||
string? shadowLineID = metadata?.ShadowLineSource;
|
||||
|
||||
if (shadowLineID != null)
|
||||
{
|
||||
// This line actually shadows another line. Fetch that line, and
|
||||
// use its text (but not its metadata)
|
||||
var getShadowLineOp = LocalizationSettings.StringDatabase.GetTableEntryAsync(stringsTable.TableReference, shadowLineID, null, FallbackBehavior.UseFallback);
|
||||
var shadowEntry = await YarnTask.WaitForAsyncOperation(getShadowLineOp, cancellationToken);
|
||||
if (shadowEntry.Entry == null)
|
||||
{
|
||||
Debug.LogWarning($"Line {line.ID} shadows line {shadowLineID}, but no such entry was found in the string table {stringsTable.TableReference}");
|
||||
}
|
||||
else
|
||||
{
|
||||
text = shadowEntry.Entry.LocalizedValue;
|
||||
}
|
||||
}
|
||||
|
||||
// We now have our text; parse it as markup
|
||||
var markup = lineParser.ParseString(LineParser.ExpandSubstitutions(text, line.Substitutions), this.LocaleCode);
|
||||
|
||||
// Lastly, attempt to fetch an asset for this line
|
||||
Object? asset = null;
|
||||
|
||||
if (this.assetTable != null && this.assetTable.IsEmpty == false)
|
||||
{
|
||||
// Fetch the asset for this line, if one is available.
|
||||
var loadOp = LocalizationSettings.AssetDatabase.GetLocalizedAssetAsync<Object>(assetTable.TableReference, shadowLineID ?? line.ID, null, FallbackBehavior.UseFallback);
|
||||
asset = await YarnTask.WaitForAsyncOperation(loadOp, cancellationToken);
|
||||
}
|
||||
|
||||
// Construct the localized line
|
||||
LocalizedLine localizedLine = new LocalizedLine()
|
||||
{
|
||||
Text = markup,
|
||||
TextID = line.ID,
|
||||
Substitutions = line.Substitutions,
|
||||
RawText = text,
|
||||
Metadata = metadata?.tags ?? System.Array.Empty<string>(),
|
||||
Asset = asset,
|
||||
};
|
||||
|
||||
return localizedLine;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void RegisterMarkerProcessor(string attributeName, IAttributeMarkerProcessor markerProcessor)
|
||||
{
|
||||
lineParser.RegisterMarkerProcessor(attributeName, markerProcessor);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DeregisterMarkerProcessor(string attributeName)
|
||||
{
|
||||
lineParser.DeregisterMarkerProcessor(attributeName);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken)
|
||||
{
|
||||
// Nothing to do. If a user wants to ensure ahead of time that
|
||||
// localized content is already in memory, they should use the Unity
|
||||
// Localization preload support.
|
||||
return YarnTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace Yarn.Unity.UnityLocalization.Editor
|
||||
{
|
||||
using System;
|
||||
using UnityEditor;
|
||||
|
||||
[CustomEditor(typeof(UnityLocalisedLineProvider))]
|
||||
internal class UnityLocalisedLineProviderEditor : UnityEditor.Editor
|
||||
{
|
||||
private SerializedProperty? stringsTableProperty;
|
||||
private SerializedProperty? assetTableProperty;
|
||||
|
||||
/// <summary>
|
||||
/// Called by Unity to draw the inspector GUI.
|
||||
/// </summary>
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.PropertyField(stringsTableProperty);
|
||||
|
||||
var stringTableName = stringsTableProperty?.FindPropertyRelative("m_TableReference").FindPropertyRelative("m_TableCollectionName").stringValue;
|
||||
|
||||
if (string.IsNullOrEmpty(stringTableName))
|
||||
{
|
||||
EditorGUI.indentLevel += 1;
|
||||
EditorGUILayout.HelpBox("Choose a strings table to make this line provider able to deliver line text.", MessageType.Warning);
|
||||
EditorGUI.indentLevel -= 1;
|
||||
}
|
||||
EditorGUILayout.PropertyField(assetTableProperty);
|
||||
|
||||
serializedObject.ApplyModifiedProperties();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called by Unity when the editor is enabled to set up cached properties.
|
||||
/// </summary>
|
||||
protected void OnEnable()
|
||||
{
|
||||
this.stringsTableProperty = serializedObject.FindProperty(nameof(UnityLocalisedLineProvider.stringsTable));
|
||||
this.assetTableProperty = serializedObject.FindProperty(nameof(UnityLocalisedLineProvider.assetTable));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif // UNITY_EDITOR
|
||||
#endif // USE_UNITY_LOCALIZATION
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d23c0c44d94eb467780430dc536b6ea1
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
#if !USE_UNITY_LOCALIZATION
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity.UnityLocalization
|
||||
{
|
||||
/// <summary>
|
||||
/// A line provider that uses the Unity Localization system to get localized
|
||||
/// content for Yarn lines.
|
||||
/// </summary>
|
||||
public partial class UnityLocalisedLineProvider : LineProviderBehaviour
|
||||
{
|
||||
// When Unity Localization is not installed, types like TableReference
|
||||
// no longer exist, and can't be deserialized into. This causes a loss
|
||||
// of data. To get around this, we declare a new type with the same
|
||||
// shape as TableReference to keep the data in. If and when Unity
|
||||
// Localization is added to the project, the data stored in these fields
|
||||
// is deserialized into actual TableReferences.
|
||||
[System.Serializable]
|
||||
public struct PlaceholderTableReference
|
||||
{
|
||||
[System.Serializable]
|
||||
public struct PlaceholderTableIdentifier
|
||||
{
|
||||
[SerializeField] private string m_TableCollectionName;
|
||||
}
|
||||
|
||||
[SerializeField] private PlaceholderTableIdentifier m_TableReference;
|
||||
}
|
||||
|
||||
[SerializeField] internal PlaceholderTableReference? stringsTable;
|
||||
[SerializeField] internal PlaceholderTableReference? assetTable;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override string LocaleCode { get => "error"; set { } }
|
||||
|
||||
private const string NotInstalledError = nameof(UnityLocalisedLineProvider) + "requires that the Unity Localization package is installed in the project. To fix this, install Unity Localization.";
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override YarnTask PrepareForLinesAsync(IEnumerable<string> lineIDs, CancellationToken cancellationToken)
|
||||
{
|
||||
Debug.LogError(NotInstalledError);
|
||||
return YarnTask.CompletedTask;
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void Start()
|
||||
{
|
||||
Debug.LogError(NotInstalledError);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override YarnTask<LocalizedLine> GetLocalizedLineAsync(Yarn.Line line, CancellationToken cancellationToken)
|
||||
{
|
||||
Debug.LogError($"{nameof(UnityLocalisedLineProvider)}: Can't create a localised line for ID {line.ID} because the Unity Localization package is not installed in this project. To fix this, install Unity Localization.");
|
||||
|
||||
return YarnTask.FromResult(new LocalizedLine()
|
||||
{
|
||||
TextID = line.ID,
|
||||
RawText = $"{line.ID}: Unable to create a localised line, because the Unity Localization package is not installed in this project.",
|
||||
Substitutions = line.Substitutions,
|
||||
});
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void RegisterMarkerProcessor(string attributeName, Markup.IAttributeMarkerProcessor markerProcessor)
|
||||
{
|
||||
Debug.LogWarning($"Unable to add a marker processor for {attributeName}, as the Unity Localization package is not installed in this project");
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override void DeregisterMarkerProcessor(string attributeName)
|
||||
{
|
||||
Debug.LogWarning($"Unable to remove a marker processor for {attributeName}, as the Unity Localization package is not installed in this project");
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace Editor
|
||||
{
|
||||
using UnityEditor;
|
||||
[CustomEditor(typeof(UnityLocalisedLineProvider))]
|
||||
public class UnityLocalisedLineProviderPlaceholderEditor : Editor
|
||||
{
|
||||
public override void OnInspectorGUI()
|
||||
{
|
||||
EditorGUILayout.HelpBox("Unity Localization is not installed.", MessageType.Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
#endif
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dcd24d82774e4ea1b71ab83bdf69c3a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity.UnityLocalization
|
||||
{
|
||||
// This partial declaration exists to ensure that Unity is able to work with
|
||||
// this class, because MonoBehaviour types are required to be defined in a
|
||||
// file whose name matches the type's name. The actual definition of
|
||||
// UnityLocalisedLineProvider is found in either
|
||||
// UnityLocalisedLineProvider.Installed.cs (if Unity Localization is
|
||||
// installed), or UnityLocalisedLineProvider.NotInstalled.cs (if not).
|
||||
public sealed partial class UnityLocalisedLineProvider : LineProviderBehaviour { }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f9c1f487b798d402db42bc11cee5b810
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Packages/dev.yarnspinner.unity/Runtime/Localisation.meta
Normal file
8
Packages/dev.yarnspinner.unity/Runtime/Localisation.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 75b688e2b8f304ff191271bcff7731a6
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// Holds information about a language.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct Culture
|
||||
{
|
||||
/// <summary>
|
||||
/// The unique language ID used to identify a language as RFC 4646. Will
|
||||
/// be "de-CH" for "German (Switzerland)". Use this for storing settings
|
||||
/// or identifying a language.
|
||||
/// </summary>
|
||||
public string Name;
|
||||
|
||||
/// <summary>
|
||||
/// The display name of a language. Will be "German (Switzerland)" for
|
||||
/// "de-CH". Use this value to present the language in an English UI.
|
||||
/// </summary>
|
||||
public string DisplayName;
|
||||
|
||||
/// <summary>
|
||||
/// The languages name as called in the language itself. Will be
|
||||
/// "Deutsch (Schweiz)" for "de-CH". Use this to present the language
|
||||
/// in-game so people can find their native language.
|
||||
/// </summary>
|
||||
public string NativeName;
|
||||
|
||||
public bool IsNeutralCulture;
|
||||
|
||||
internal System.Globalization.CultureInfo? CultureInfo
|
||||
{
|
||||
get
|
||||
{
|
||||
try
|
||||
{
|
||||
return System.Globalization.CultureInfo.GetCultureInfo(this.Name);
|
||||
}
|
||||
catch (System.Globalization.CultureNotFoundException)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Culture(System.Globalization.CultureInfo cultureInfo)
|
||||
{
|
||||
this.Name = cultureInfo.Name;
|
||||
this.DisplayName = cultureInfo.DisplayName;
|
||||
this.NativeName = cultureInfo.NativeName;
|
||||
this.IsNeutralCulture = cultureInfo.IsNeutralCulture;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b333bea34d842c14592b64107ba0ada8
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
113
Packages/dev.yarnspinner.unity/Runtime/Localisation/Cultures.cs
Normal file
113
Packages/dev.yarnspinner.unity/Runtime/Localisation/Cultures.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides access to all <see cref="Culture"/>s supported by Yarn Spinner.
|
||||
/// </summary>
|
||||
public static class Cultures
|
||||
{
|
||||
private static Lazy<IEnumerable<Culture>> _allCultures = new Lazy<IEnumerable<Culture>>(() => MakeCultureList());
|
||||
|
||||
private static Lazy<Dictionary<string, Culture>> _allCulturesTable = new Lazy<Dictionary<string, Culture>>(() =>
|
||||
{
|
||||
var dict = new Dictionary<string, Culture>();
|
||||
foreach (var entry in _allCultures.Value)
|
||||
{
|
||||
dict[entry.Name] = entry;
|
||||
}
|
||||
return dict;
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Get all <see cref="Culture"/>s supported by Yarn Spinner.
|
||||
/// </summary>
|
||||
private static IEnumerable<Culture> MakeCultureList() => CultureInfo.GetCultures(CultureTypes.AllCultures)
|
||||
.Where(c => c.Name != "")
|
||||
.Select(c => new Culture
|
||||
{
|
||||
Name = c.Name,
|
||||
DisplayName = c.DisplayName,
|
||||
NativeName = c.NativeName,
|
||||
IsNeutralCulture = c.IsNeutralCulture,
|
||||
})
|
||||
.Append(new Culture { Name = "mi", DisplayName = "Maori", NativeName = "Māori", IsNeutralCulture = true })
|
||||
.OrderBy(c => c.DisplayName);
|
||||
|
||||
public static IEnumerable<Culture> GetCultures() => _allCultures.Value;
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="Culture"/> represented by the language code
|
||||
/// in <paramref name="name"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the <see cref="Culture"/> to
|
||||
/// retrieve.</param>
|
||||
/// <returns>The <see cref="Culture"/>.</returns>
|
||||
/// <exception cref="ArgumentException">Thrown when no <see
|
||||
/// cref="Culture"/> with the given language ID can be
|
||||
/// found.</exception>
|
||||
[Obsolete("Use " + nameof(TryGetCulture) + ", which does not throw if the culture can't be found.")]
|
||||
public static Culture GetCulture(string name)
|
||||
{
|
||||
var exists = _allCulturesTable.Value.TryGetValue(name, out var result);
|
||||
|
||||
if (exists)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentException($"Culture {name} not found", name);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the <see cref="Culture"/> represented by the language code in
|
||||
/// <paramref name="name"/>.
|
||||
/// </summary>
|
||||
/// <param name="name">The name of the <see cref="Culture"/> to
|
||||
/// retrieve.</param>
|
||||
/// <param name="culture">On return, the <see cref="Culture"/> if one
|
||||
/// was found, or a default <see cref="Culture"/> if otherwise.</param>
|
||||
/// <returns><see langword="true"/> if a Culture was found; <see
|
||||
/// langword="false"/> otherwise.</returns>
|
||||
public static bool TryGetCulture(string name, out Culture culture)
|
||||
{
|
||||
return _allCulturesTable.Value.TryGetValue(name, out culture);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a boolean value indicating whether <paramref name="name"/>
|
||||
/// is a valid identifier for retrieving a <see cref="Culture"/> from
|
||||
/// <see cref="GetCulture"/>.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns><see langword="true"/> if name is a valid <see cref="Culture"/> name; <see langword="false"/> otherwise.</returns>
|
||||
public static bool HasCulture(string name)
|
||||
{
|
||||
return _allCulturesTable.Value.ContainsKey(name);
|
||||
}
|
||||
|
||||
public static Culture CurrentNeutralCulture
|
||||
{
|
||||
get
|
||||
{
|
||||
var current = System.Globalization.CultureInfo.CurrentCulture;
|
||||
if (current.IsNeutralCulture == false)
|
||||
{
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
TryGetCulture(current.Name, out var culture);
|
||||
return culture;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bf6047d95bf6b0649b6c7c04d02b5779
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,388 @@
|
||||
/*
|
||||
Yarn Spinner is licensed to you under the terms found in the file LICENSE.md.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
using System;
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
using UnityEditor;
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
#nullable enable
|
||||
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
[CreateAssetMenu(fileName = "NewLocalization", menuName = "Yarn Spinner/Built-In Localization/Localization", order = 105)]
|
||||
public class Localization : ScriptableObject
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the address that should be used to fetch an asset suitable
|
||||
/// for a specific line in a specific language.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method is useful for creating an address for use with the
|
||||
/// Addressable Assets system.
|
||||
/// </remarks>
|
||||
/// <param name="lineID">The line ID to use when generating the
|
||||
/// address.</param>
|
||||
/// <param name="language">The language to use when generating the
|
||||
/// address.</param>
|
||||
/// <returns>The address to use.</returns>
|
||||
internal static string GetAddressForLine(string lineID, string language)
|
||||
{
|
||||
return $"line_{language}_{lineID.Replace("line:", "")}";
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public sealed class LocalizationTableEntry
|
||||
{
|
||||
public string? localizedString;
|
||||
public UnityEngine.Object? localizedAsset;
|
||||
|
||||
#if USE_ADDRESSABLES
|
||||
public UnityEngine.AddressableAssets.AssetReference? localizedAssetReference;
|
||||
#endif
|
||||
}
|
||||
|
||||
[SerializeField] internal SerializableDictionary<string, LocalizationTableEntry> entries = new();
|
||||
|
||||
private Dictionary<string, string> _runtimeStringTable = new Dictionary<string, string>();
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating whether this <see cref="Localization"/>
|
||||
/// makes use of Addressable Assets (<see langword="true"/>), or if it
|
||||
/// stores its assets as direct references (<see langword="false"/>).
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If this property is <see langword="true"/>, <see
|
||||
/// cref="GetLocalizedObjectAsync"/> and <see
|
||||
/// cref="ContainsLocalizedObject"/> should not be used to retrieve
|
||||
/// localised objects. Instead, the Addressable Assets API should be
|
||||
/// used.
|
||||
/// </remarks>
|
||||
public bool UsesAddressableAssets { get => _usesAddressableAssets; internal set => _usesAddressableAssets = value; }
|
||||
|
||||
[SerializeField]
|
||||
private bool _containsLocalizedAssets;
|
||||
|
||||
[SerializeField]
|
||||
internal bool _usesAddressableAssets;
|
||||
|
||||
#region Localized Strings
|
||||
public string? GetLocalizedString(string key)
|
||||
{
|
||||
if (_runtimeStringTable.TryGetValue(key, out string result))
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
if (entries.TryGetValue(key, out var entry))
|
||||
{
|
||||
return entry.localizedString;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a boolean value indicating whether this <see
|
||||
/// cref="Localization"/> contains a string with the given key.
|
||||
/// </summary>
|
||||
/// <param name="key">The key to search for.</param>
|
||||
/// <returns><see langword="true"/> if this Localization has a string
|
||||
/// for the given key; <see langword="false"/> otherwise.</returns>
|
||||
public bool ContainsLocalizedString(string key) => _runtimeStringTable.ContainsKey(key) || entries.ContainsKey(key);
|
||||
|
||||
#if UNITY_EDITOR
|
||||
/// <summary>
|
||||
/// Adds a new string to the string table.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>This method updates the localisation asset on disk. It is not
|
||||
/// recommended to call this method during play mode, because changes
|
||||
/// will persist after you leave and may cause conflicts. </para>
|
||||
/// <para>This method is only available in the Editor.</para>
|
||||
/// </remarks>
|
||||
/// <param name="key">The key for this string (generally, the line
|
||||
/// ID.)</param>
|
||||
/// <param name="value">The user-facing text for this string, in the
|
||||
/// language specified by <see cref="LocaleCode"/>.</param>
|
||||
internal void AddLocalisedStringToAsset(string key, string value)
|
||||
{
|
||||
GetOrCreateEntry(key).localizedString = value;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new string to the runtime string table.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method updates the localisation's runtime string table, which
|
||||
/// is useful for adding or changing the localisation during gameplay or
|
||||
/// in a built player. It doesn't modify the asset on disk, and any
|
||||
/// changes made will be lost when gameplay ends.
|
||||
/// </remarks>
|
||||
/// <param name="key">The key for this string (generally, the line
|
||||
/// ID.)</param>
|
||||
/// <param name="value">The user-facing text for this string, in the
|
||||
/// language specified by <see cref="LocaleCode"/>.</param>
|
||||
public void AddLocalizedString(string key, string value)
|
||||
{
|
||||
_runtimeStringTable.Add(key, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection of strings to the runtime string table.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="AddLocalizedString(string, string)"
|
||||
/// path="/remarks"/>
|
||||
/// <param name="strings">The collection of keys and strings to
|
||||
/// add.</param>
|
||||
public void AddLocalizedStrings(IEnumerable<KeyValuePair<string, string>> strings)
|
||||
{
|
||||
foreach (var entry in strings)
|
||||
{
|
||||
AddLocalizedString(entry.Key, entry.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a collection of strings to the runtime string table.
|
||||
/// </summary>
|
||||
/// <inheritdoc cref="AddLocalizedString(string, string)"
|
||||
/// path="/remarks"/>
|
||||
/// <param name="strings">The collection of <see
|
||||
/// cref="StringTableEntry"/> objects to add.</param>
|
||||
public void AddLocalizedStrings(IEnumerable<StringTableEntry> stringTableEntries)
|
||||
{
|
||||
foreach (var entry in stringTableEntries)
|
||||
{
|
||||
if (entry.Text != null)
|
||||
{
|
||||
AddLocalizedString(entry.ID, entry.Text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Localised Objects
|
||||
|
||||
#if USE_ADDRESSABLES
|
||||
public async YarnTask<T?> GetLocalizedObjectAsync<T>(string key) where T : UnityEngine.Object
|
||||
{
|
||||
if (!entries.TryGetValue(key, out var entry))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (_usesAddressableAssets)
|
||||
{
|
||||
if (entry.localizedAssetReference == null || entry.localizedAssetReference.RuntimeKeyIsValid() == false) { return null; }
|
||||
|
||||
// Try to fetch the referenced asset
|
||||
return await UnityEngine.AddressableAssets.Addressables.LoadAssetAsync<T>(entry.localizedAssetReference).Task;
|
||||
}
|
||||
|
||||
if (entry.localizedAsset is T resultAsTargetObject)
|
||||
{
|
||||
return resultAsTargetObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#else
|
||||
public YarnTask<T?> GetLocalizedObjectAsync<T>(string key) where T : UnityEngine.Object
|
||||
{
|
||||
if (!entries.TryGetValue(key, out var entry))
|
||||
{
|
||||
return YarnTask<T?>.FromResult(null);
|
||||
}
|
||||
|
||||
if (entry.localizedAsset is T resultAsTargetObject)
|
||||
{
|
||||
return YarnTask.FromResult<T?>(resultAsTargetObject);
|
||||
}
|
||||
|
||||
return YarnTask<T?>.FromResult(null);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if UNITY_EDITOR
|
||||
internal T? GetLocalizedObjectSync<T>(string key) where T : UnityEngine.Object
|
||||
{
|
||||
if (!entries.TryGetValue(key, out var entry))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
#if USE_ADDRESSABLES
|
||||
if (_usesAddressableAssets)
|
||||
{
|
||||
if (entry.localizedAssetReference == null || entry.localizedAssetReference.RuntimeKeyIsValid() == false) { return null; }
|
||||
|
||||
// Try to fetch the referenced asset
|
||||
return entry.localizedAssetReference.editorAsset as T;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (entry.localizedAsset is T resultAsTargetObject)
|
||||
{
|
||||
return resultAsTargetObject;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
#endif
|
||||
|
||||
private LocalizationTableEntry GetOrCreateEntry(string key)
|
||||
{
|
||||
if (entries.TryGetValue(key, out var entry))
|
||||
{
|
||||
return entry;
|
||||
}
|
||||
entry = new LocalizationTableEntry();
|
||||
entries.Add(key, entry);
|
||||
return entry;
|
||||
}
|
||||
|
||||
public bool ContainsLocalizedObject<T>(string key) where T : UnityEngine.Object => entries.TryGetValue(key, out var asset) && asset is T;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
public void AddLocalizedObjectToAsset<T>(string key, T value) where T : UnityEngine.Object
|
||||
{
|
||||
var entry = GetOrCreateEntry(key);
|
||||
|
||||
#if USE_ADDRESSABLES
|
||||
if (this.UsesAddressableAssets)
|
||||
{
|
||||
// This Localization uses Addressables, so rather than storing a
|
||||
// direct reference to the asset, we'll use an indirect
|
||||
// AssetReference.
|
||||
entry.localizedAssetReference = new UnityEngine.AddressableAssets.AssetReference();
|
||||
entry.localizedAssetReference.SetEditorAsset(value);
|
||||
entry.localizedAsset = null;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Addressables are available, but we're not using addressable
|
||||
// assets, so clear out any asset references.
|
||||
entry.localizedAssetReference = null;
|
||||
}
|
||||
#endif
|
||||
entry.localizedAsset = value;
|
||||
}
|
||||
#endif
|
||||
#endregion
|
||||
|
||||
public virtual void Clear()
|
||||
{
|
||||
entries.Clear();
|
||||
_runtimeStringTable.Clear();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the line IDs present in this localization.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The line IDs can be used to access the localized text or asset
|
||||
/// associated with a line.
|
||||
/// </remarks>
|
||||
/// <returns>The line IDs.</returns>
|
||||
public IEnumerable<string> GetLineIDs()
|
||||
{
|
||||
var allKeys = new List<string>();
|
||||
|
||||
var runtimeKeys = _runtimeStringTable.Keys;
|
||||
var compileTimeKeys = entries.Keys;
|
||||
|
||||
allKeys.AddRange(runtimeKeys);
|
||||
allKeys.AddRange(compileTimeKeys);
|
||||
|
||||
return allKeys;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
namespace Yarn.Unity
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides methods for finding voice over <see cref="AudioClip"/>s in the
|
||||
/// project matching a Yarn linetag/string ID and a language ID.
|
||||
/// </summary>
|
||||
public static class FindVoiceOver
|
||||
{
|
||||
/// <summary>
|
||||
/// Finds all voice over <see cref="AudioClip"/>s in the project with a
|
||||
/// filename matching a Yarn linetag and a language ID.
|
||||
/// </summary>
|
||||
/// <param name="linetag">The linetag/string ID the voice over filename
|
||||
/// should match.</param>
|
||||
/// <param name="language">The language ID the voice over filename
|
||||
/// should match.</param>
|
||||
/// <returns>A string array with GUIDs of all matching <see
|
||||
/// cref="AudioClip"/>s.</returns>
|
||||
public static string[] GetMatchingVoiceOverAudioClip(string linetag, string language)
|
||||
{
|
||||
var lineTagContents = linetag.Replace("line:", "");
|
||||
|
||||
string[] result = Array.Empty<String>();
|
||||
string[] searchPatterns = new string[] {
|
||||
$"t:AudioClip {lineTagContents} ({language})",
|
||||
$"t:AudioClip {lineTagContents} {language}",
|
||||
$"t:AudioClip {lineTagContents}"
|
||||
};
|
||||
|
||||
foreach (var searchPattern in searchPatterns)
|
||||
{
|
||||
result = SearchAssetDatabase(searchPattern, language);
|
||||
if (result.Length > 0)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string[] SearchAssetDatabase(string searchPattern, string language)
|
||||
{
|
||||
var result = AssetDatabase.FindAssets(searchPattern);
|
||||
// Check if result is ambiguous and try to improve the situation
|
||||
if (result.Length > 1)
|
||||
{
|
||||
var assetsInMatchingLanguageDirectory = GetAsseetsInMatchingLanguageDirectory(result, language);
|
||||
// Check if this improved the situation
|
||||
if (assetsInMatchingLanguageDirectory.Length == 1 || (assetsInMatchingLanguageDirectory.Length != 0 && assetsInMatchingLanguageDirectory.Length < result.Length))
|
||||
{
|
||||
result = assetsInMatchingLanguageDirectory;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public static string[] GetAsseetsInMatchingLanguageDirectory(string[] result, string language)
|
||||
{
|
||||
var list = new List<string>();
|
||||
foreach (var assetId in result)
|
||||
{
|
||||
var testPath = AssetDatabase.GUIDToAssetPath(assetId);
|
||||
if (AssetDatabase.GUIDToAssetPath(assetId).Contains($"/{language}/"))
|
||||
{
|
||||
list.Add(assetId);
|
||||
}
|
||||
}
|
||||
return list.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user