You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
699 lines
26 KiB
C#
699 lines
26 KiB
C#
#nullable enable
|
|
|
|
using Godot;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
using static GodotInk.MarshalUtils;
|
|
|
|
using PropertyList = Godot.Collections.Array<Godot.Collections.Dictionary>;
|
|
|
|
namespace GodotInk;
|
|
|
|
[Tool]
|
|
#if GODOT4_1_OR_GREATER
|
|
[GlobalClass, Icon("../GodotInk.svg")]
|
|
#endif
|
|
public partial class InkStory : Resource
|
|
{
|
|
[Signal]
|
|
public delegate void ContinuedEventHandler();
|
|
|
|
[Signal]
|
|
public delegate void MadeChoiceEventHandler(InkChoice choice);
|
|
|
|
protected virtual string RawStory
|
|
{
|
|
get => rawStory;
|
|
set
|
|
{
|
|
rawStory = value;
|
|
#if TOOLS
|
|
// There's really no need to instantiate Ink.Runtime in the editor itself.
|
|
// if (Engine.IsEditorHint()) return; <- Commenting for now, because it prevents
|
|
// the InkDock from running stories.
|
|
#endif
|
|
InitializeRuntimeStory();
|
|
}
|
|
}
|
|
|
|
private string rawStory = string.Empty;
|
|
private Ink.Runtime.Story runtimeStory = null!;
|
|
|
|
private readonly Dictionary<string, HashSet<Callable>> observers = new();
|
|
private readonly Dictionary<string, Ink.Runtime.Story.VariableObserver> internalObservers = new();
|
|
|
|
public static InkStory Create(string rawStory)
|
|
{
|
|
return new InkStory()
|
|
{
|
|
rawStory = rawStory
|
|
};
|
|
}
|
|
|
|
private void InitializeRuntimeStory()
|
|
{
|
|
if (runtimeStory != null)
|
|
{
|
|
runtimeStory.onDidContinue -= OnContinued;
|
|
runtimeStory.onMakeChoice -= OnMadeChoice;
|
|
}
|
|
|
|
runtimeStory = new Ink.Runtime.Story(rawStory);
|
|
|
|
runtimeStory.onDidContinue += OnContinued;
|
|
runtimeStory.onMakeChoice += OnMadeChoice;
|
|
}
|
|
|
|
public string CurrentText => runtimeStory.currentText;
|
|
|
|
public IReadOnlyList<InkChoice> CurrentChoices => ToVariants(runtimeStory.currentChoices);
|
|
|
|
public IReadOnlyList<string> CurrentTags => runtimeStory.currentTags;
|
|
|
|
public bool HasWarning => runtimeStory.hasWarning;
|
|
|
|
public IReadOnlyList<string> CurrentWarnings => runtimeStory.currentWarnings;
|
|
|
|
public bool HasError => runtimeStory.hasError;
|
|
|
|
public IReadOnlyList<string> CurrentErrors => runtimeStory.currentErrors;
|
|
|
|
/// <summary>
|
|
/// Check whether more content is available if you were to call <c>Continue()</c> - i.e.
|
|
/// are we mid story rather than at a choice point or at the end.
|
|
/// </summary>
|
|
public bool CanContinue => runtimeStory.canContinue;
|
|
|
|
/// <summary>
|
|
/// Continue the story for one line of content, if possible.
|
|
/// If you're not sure if there's more content available, for example if you
|
|
/// want to check whether you're at a choice point or at the end of the story,
|
|
/// you should call <c>canContinue</c> before calling this function.
|
|
/// </summary>
|
|
/// <returns>The line of text content.</returns>
|
|
public string Continue()
|
|
{
|
|
return runtimeStory.Continue();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Continue the story until the next choice point or until it runs out of content.
|
|
/// This is as opposed to the Continue() method which only evaluates one line of
|
|
/// output at a time.
|
|
/// </summary>
|
|
/// <returns>The resulting text evaluated by the ink engine, concatenated together.</returns>
|
|
public string ContinueMaximally()
|
|
{
|
|
return runtimeStory.ContinueMaximally();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Chooses the Choice from the currentChoices list with the given
|
|
/// index. Internally, this sets the current content path to that
|
|
/// pointed to by the Choice, ready to continue story evaluation.
|
|
/// </summary>
|
|
/// <param name="choiceIdx">The index of the choice to choose.</param>
|
|
public void ChooseChoiceIndex(int choiceIdx)
|
|
{
|
|
runtimeStory.ChooseChoiceIndex(choiceIdx);
|
|
}
|
|
|
|
public void ChoosePathString(string path, bool resetCallstack = true, params Variant[] arguments)
|
|
{
|
|
runtimeStory.ChoosePathString(path, resetCallstack, FromVariants(arguments));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Unwinds the callstack. Useful to reset the Story's evaluation
|
|
/// without actually changing any meaningful state, for example if
|
|
/// you want to exit a section of story prematurely and tell it to
|
|
/// go elsewhere with a call to ChoosePathString(...).
|
|
/// Doing so without calling ResetCallstack() could cause unexpected
|
|
/// issues if, for example, the Story was in a tunnel already.
|
|
/// </summary>
|
|
public void ResetCallstack()
|
|
{
|
|
runtimeStory.ResetCallstack();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset the Story back to its initial state as it was when it was
|
|
/// first constructed.
|
|
/// </summary>
|
|
public void ResetState()
|
|
{
|
|
runtimeStory.ResetState();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get any global tags associated with the story. These are defined as
|
|
/// hash tags defined at the very top of the story.
|
|
/// </summary>
|
|
public IReadOnlyList<string> GlobalTags => runtimeStory.globalTags;
|
|
|
|
/// <summary>
|
|
/// Gets any tags associated with a particular knot or knot.stitch.
|
|
/// These are defined as hash tags defined at the very top of a knot or stitch.
|
|
/// </summary>
|
|
/// <param name="path">The path of the knot or stitch, in the form "knot" or "knot.stitch".</param>
|
|
/// <returns>The list of tags.</returns>
|
|
public IReadOnlyList<string> TagsForContentAtPath(string path)
|
|
{
|
|
return runtimeStory.TagsForContentAtPath(path);
|
|
}
|
|
|
|
public string CurrentFlowName => runtimeStory.currentFlowName;
|
|
|
|
public bool CurrentFlowIsDefaultFlow => runtimeStory.currentFlowIsDefaultFlow;
|
|
|
|
public IReadOnlyList<string> AliveFlowNames => runtimeStory.aliveFlowNames;
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="flowName"></param>
|
|
public void RemoveFlow(string flowName)
|
|
{
|
|
runtimeStory.RemoveFlow(flowName);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="flowName"></param>
|
|
public void SwitchFlow(string flowName)
|
|
{
|
|
runtimeStory.SwitchFlow(flowName);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
public void SwitchToDefaultFlow()
|
|
{
|
|
runtimeStory.SwitchToDefaultFlow();
|
|
}
|
|
|
|
public int VisitCountAtPathString(string pathString)
|
|
{
|
|
return runtimeStory.state.VisitCountAtPathString(pathString);
|
|
}
|
|
|
|
public Variant FetchVariable(string variableName)
|
|
{
|
|
return ToVariant(runtimeStory.variablesState[variableName]);
|
|
}
|
|
|
|
public T FetchVariable<[MustBeVariant] T>(string variableName)
|
|
{
|
|
return FetchVariable(variableName).As<T>();
|
|
}
|
|
|
|
public void StoreVariable(string variableName, Variant value)
|
|
{
|
|
runtimeStory.variablesState[variableName] = FromVariant(value);
|
|
}
|
|
|
|
public void StoreVariable<[MustBeVariant] T>(string variableName, T value)
|
|
{
|
|
StoreVariable(variableName, Variant.From(value));
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="variableName"></param>
|
|
/// <param name="observer"></param>
|
|
public void ObserveVariable(string variableName, Callable observer)
|
|
{
|
|
if (!internalObservers.ContainsKey(variableName))
|
|
{
|
|
Ink.Runtime.Story.VariableObserver internalObserver = BuildObserver();
|
|
runtimeStory.ObserveVariable(variableName, internalObserver);
|
|
internalObservers[variableName] = internalObserver;
|
|
}
|
|
|
|
if (observers.ContainsKey(variableName))
|
|
_ = observers[variableName].Add(observer);
|
|
else
|
|
observers[variableName] = new() { observer };
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="variableNames"></param>
|
|
/// <param name="observer"></param>
|
|
public void ObserveVariable(string[] variableNames, Callable observer)
|
|
{
|
|
foreach (string variableName in variableNames)
|
|
ObserveVariable(variableName, observer);
|
|
}
|
|
|
|
private Ink.Runtime.Story.VariableObserver BuildObserver()
|
|
{
|
|
return delegate (string name, object? value)
|
|
{
|
|
if (!observers.TryGetValue(name, out var callables)) return;
|
|
|
|
Variant variant = ToVariant(value);
|
|
foreach (Callable callable in callables)
|
|
_ = callable.Call(name, variant);
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="callable"></param>
|
|
public void RemoveVariableObserver(Callable callable)
|
|
{
|
|
foreach (string variableName in observers.Keys)
|
|
RemoveVariableObserver(callable, variableName);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="specificVariableName"></param>
|
|
public void RemoveVariableObserver(string specificVariableName)
|
|
{
|
|
runtimeStory.RemoveVariableObserver(null, specificVariableName);
|
|
_ = internalObservers.Remove(specificVariableName);
|
|
_ = observers.Remove(specificVariableName);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="callable"></param>
|
|
/// <param name="specificVariableName"></param>
|
|
public void RemoveVariableObserver(Callable callable, string specificVariableName)
|
|
{
|
|
var callables = observers[specificVariableName];
|
|
if (!callables.Contains(callable)) return;
|
|
|
|
_ = callables.Remove(callable);
|
|
if (callables.Count > 0) return;
|
|
|
|
runtimeStory.RemoveVariableObserver(null, specificVariableName);
|
|
_ = internalObservers.Remove(specificVariableName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// An ink file can provide a fallback functions for when when an EXTERNAL has been left
|
|
/// unbound by the client, and the fallback function will be called instead. Useful when
|
|
/// testing a story in play mode, when it's not possible to write a client-side C# external
|
|
/// function, but you don't want it to fail to run.
|
|
/// </summary>
|
|
public bool AllowExternalFunctionFallbacks => runtimeStory.allowExternalFunctionFallbacks;
|
|
|
|
/// <summary>
|
|
/// Checks if a function exists.
|
|
/// </summary>
|
|
/// <param name="functionName">The name of the function as declared in ink.</param>
|
|
/// <returns>True if the function exists, else false.</returns>
|
|
public bool HasFunction(string functionName)
|
|
{
|
|
return runtimeStory.HasFunction(functionName);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Evaluates a function defined in ink.
|
|
/// </summary>
|
|
/// <param name="functionName">The name of the function as declared in ink.</param>
|
|
/// <param name="arguments">
|
|
/// The arguments that the ink function takes, if any. Note that we don't (can't) do any
|
|
/// validation on the number of arguments right now, so make sure you get it right!
|
|
/// </param>
|
|
/// <returns>
|
|
/// The return value as returned from the ink function with `~ return myValue`, or a nil
|
|
/// variant if nothing is returned.
|
|
/// </returns>
|
|
public Variant EvaluateFunction(string functionName, params Variant[] arguments)
|
|
{
|
|
object? result = runtimeStory.EvaluateFunction(functionName, FromVariants(arguments));
|
|
return ToVariant(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Evaluates a function defined in ink.
|
|
/// </summary>
|
|
/// <param name="functionName">The name of the function as declared in ink.</param>
|
|
/// <param name="arguments">
|
|
/// The arguments that the ink function takes, if any. Note that we don't (can't) do any
|
|
/// validation on the number of arguments right now, so make sure you get it right!
|
|
/// </param>
|
|
/// <returns>
|
|
/// The return value as returned from the ink function with `~ return myValue`, or a nil
|
|
/// variant if nothing is returned.
|
|
/// </returns>
|
|
public Variant EvaluateFunction(string functionName, Godot.Collections.Array<Variant> arguments)
|
|
{
|
|
object? result = runtimeStory.EvaluateFunction(functionName, FromVariants(arguments));
|
|
return ToVariant(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Evaluates a function defined in ink, and gathers the possibly multi-line text as generated
|
|
/// by the function. This text output is any text written as normal content within the function,
|
|
/// as opposed to the return value, as returned with `~ return`.
|
|
/// </summary>
|
|
/// <param name="functionName">The name of the function as declared in ink.</param>
|
|
/// <param name="textOutput">The text content produced by the function via normal ink, if any.</param>
|
|
/// <param name="arguments">
|
|
/// The arguments that the ink function takes, if any. Note that we don't (can't) do any
|
|
/// validation on the number of arguments right now, so make sure you get it right!
|
|
/// </param>
|
|
/// <returns>
|
|
/// The return value as returned from the ink function with `~ return myValue`, or a nil
|
|
/// variant if nothing is returned.
|
|
/// </returns>
|
|
public Variant EvaluateFunction(string functionName, out string textOutput, params Variant[] arguments)
|
|
{
|
|
object? result = runtimeStory.EvaluateFunction(functionName, out textOutput, FromVariants(arguments));
|
|
return ToVariant(result);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# function to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="callable">The Godot Callable to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction(string funcName, Callable callable, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunctionGeneral(funcName, trampoline, lookaheadSafe);
|
|
|
|
object? trampoline(object?[] arguments) => FromVariant(callable.Call(ToVariants(arguments)));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# function to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="func">The C# function to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction(string funcName, Func<Variant> func, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, trampoline, lookaheadSafe);
|
|
|
|
object? trampoline() => FromVariant(func.Invoke());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# function to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="func">The C# function to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction<T>(string funcName, Func<T, Variant> func, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, (Func<T, object?>)trampoline, lookaheadSafe);
|
|
|
|
object? trampoline(T a) => FromVariant(func.Invoke(a));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# function to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="func">The C# function to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction<T1, T2>(string funcName, Func<T1, T2, Variant> func, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, (Func<T1, T2, object?>)trampoline, lookaheadSafe);
|
|
|
|
object? trampoline(T1 a, T2 b) => FromVariant(func.Invoke(a, b));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# function to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="func">The C# function to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction<T1, T2, T3>(string funcName, Func<T1, T2, T3, Variant> func, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, (Func<T1, T2, T3, object?>)trampoline, lookaheadSafe);
|
|
|
|
object? trampoline(T1 a, T2 b, T3 c) => FromVariant(func.Invoke(a, b, c));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# function to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="func">The C# function to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction<T1, T2, T3, T4>(string funcName, Func<T1, T2, T3, T4, Variant> func, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, (Func<T1, T2, T3, T4, object?>)trampoline, lookaheadSafe);
|
|
|
|
object? trampoline(T1 a, T2 b, T3 c, T4 d) => FromVariant(func.Invoke(a, b, c, d));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# Action to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="action">The C# action to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction(string funcName, Action action, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, action, lookaheadSafe);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# Action to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="action">The C# action to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction<T>(string funcName, Action<T> action, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, action, lookaheadSafe);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# Action to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="action">The C# action to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction<T1, T2>(string funcName, Action<T1, T2> action, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, action, lookaheadSafe);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# Action to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="action">The C# action to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction<T1, T2, T3>(string funcName, Action<T1, T2, T3> action, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, action, lookaheadSafe);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Bind a C# Action to an ink EXTERNAL function declaration.
|
|
/// </summary>
|
|
/// <param name="funcName">EXTERNAL ink function name to bind to.</param>
|
|
/// <param name="action">The C# action to bind.</param>
|
|
/// <param name="lookaheadSafe">The ink engine often evaluates further
|
|
/// than you might expect beyond the current line just in case it sees
|
|
/// glue that will cause the two lines to become one. In this case it's
|
|
/// possible that a function can appear to be called twice instead of
|
|
/// just once, and earlier than you expect. If it's safe for your
|
|
/// function to be called in this way (since the result and side effect
|
|
/// of the function will not change), then you can pass 'true'.
|
|
/// Usually, you want to pass 'false', especially if you want some action
|
|
/// to be performed in game code when this function is called.</param>
|
|
public void BindExternalFunction<T1, T2, T3, T4>(string funcName, Action<T1, T2, T3, T4> action, bool lookaheadSafe = false)
|
|
{
|
|
runtimeStory.BindExternalFunction(funcName, action, lookaheadSafe);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Remove a binding for a named EXTERNAL ink function.
|
|
/// </summary>
|
|
/// <param name="funcName">The name of the EXTERNAL ink function to unbind.</param>
|
|
public void UnbindExternalFunction(string funcName)
|
|
{
|
|
runtimeStory.UnbindExternalFunction(funcName);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
public void Error(string message)
|
|
{
|
|
runtimeStory.Error(message);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
/// <param name="useEndLineNumber"></param>
|
|
public void Error(string message, bool useEndLineNumber)
|
|
{
|
|
runtimeStory.Error(message, useEndLineNumber);
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="message"></param>
|
|
public void Warning(string message)
|
|
{
|
|
runtimeStory.Warning(message);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save the current story state a JSON string.
|
|
/// </summary>
|
|
/// <returns>The current state serialized into a JSON string.</returns>
|
|
public string SaveState()
|
|
{
|
|
return runtimeStory.state.ToJson();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save the current story state to a JSON file.
|
|
/// </summary>
|
|
/// <param name="filePath">The path to the file we will be writing to.</param>
|
|
public void SaveStateFile(string filePath)
|
|
{
|
|
using FileAccess file = FileAccess.Open(filePath, FileAccess.ModeFlags.Write);
|
|
file.StoreString(SaveState());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load a JSON string as the current story state.
|
|
/// </summary>
|
|
/// <param name="jsonState">The JSON string to load.</param>
|
|
public void LoadState(string jsonState)
|
|
{
|
|
runtimeStory.state.LoadJson(jsonState);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Load the content of a JSON file as the current story state.
|
|
/// </summary>
|
|
/// <param name="filePath">The path to the file we will be reading from.</param>
|
|
public void LoadStateFile(string filePath)
|
|
{
|
|
using FileAccess file = FileAccess.Open(filePath, FileAccess.ModeFlags.Read);
|
|
LoadState(file.GetAsText());
|
|
}
|
|
|
|
private void OnContinued()
|
|
{
|
|
_ = EmitSignal(SignalName.Continued);
|
|
}
|
|
|
|
private void OnMadeChoice(Ink.Runtime.Choice choice)
|
|
{
|
|
_ = EmitSignal(SignalName.MadeChoice, new InkChoice(choice));
|
|
}
|
|
|
|
public override PropertyList _GetPropertyList()
|
|
{
|
|
PropertyList properties = base._GetPropertyList() ?? new PropertyList();
|
|
|
|
properties.Add(new Godot.Collections.Dictionary()
|
|
{
|
|
{ "name", PropertyName.RawStory },
|
|
{ "type", Variant.From(Variant.Type.Object) },
|
|
{ "usage", Variant.From(PropertyUsageFlags.NoEditor) },
|
|
});
|
|
|
|
return properties;
|
|
}
|
|
}
|